Глава 35. “Проволочный” шейдер

Здесь я собираюсь добавить золотую ленточку (пломбу). Это будет линия, последовательность точек вместо обычного набора треугольников. Для этого потребуются:

  • Набор новых тагов в ModelLoader
  • Новая переменная lineWidth в классе Material
  • В DrawJob будет нужно перекалибровывать lineWidth в зависимости от дистанции
  • Дополнительный функционал в шейдерах
  • И, конечно, новый model descriptor

1. Скопируем нижеследующий код в Текстовый редактор и сохраним его (overwrite) to/as

C:\CPP\a997modeler\dt\models\misc\marlboro01\root01.txt

<texture_as="tx0" src="marlboro03small.png" ckey="#00ff00"/>
<mt_type="phong" uTex0_use="tx0" />
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<a="front v" xywh="2,1,323,495" mark="box_front"/>
<a="back v"  xywh="2,1,323,495" mark="box_back"/>
<a="right all" xywh="327,1,128,495" mark="box_right"/>
<a="left all" xywh="457,1,128,495" mark="box_left"/>
<a="top" xywh="588,1,323,133"/>
<a="bottom" xywh="587,136,324,134"/>
//golden prints
<vs="box" whl="55.1,85.1,23.1" />
<texture_as="whitenoise" src="/dt/common/img/whitenoise/wn64_blur3.bmp"/>
<texture_as="gold" src="/dt/common/img/materials/gold02roman.bmp" />
<mt_type="mirror" uAlphaBlending uTex1mask_use="tx0" uTex1alphaChannelN=1 uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
//side golden prints
<a="right" xywh="342,12,101,10" whl="x,1.8,18.1" pxyz="x,39.8, -0.3" /> //Please do not litter
<a="right" xywh="339,144,105,89" whl="x,15.35,18.9" pxyz="x,10.3,-0.12" /> //For special offers...
<a="left" xywh="475,15,95,48" whl="x,8.4,17" pxyz="x,36, 0.3" /> //Underage sale...
//front prints
<group>
	//bottom golden print "20 class a..."
	<a="front" xywh="20,498,289,13" whl="47.5,2,x" pxyz="1,-36,x" />
	//blazon/emblem
	<mt_type="mirror" uAlphaBlending uTex2nm_use="tx0" uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
	<a="front" xywh2nm="589,415,128,94" whl="20.7,16,x" pxyz="0.3,6.1,x" /> //emblem
	//"Marlboro
	<mt_type="phong" uAlphaBlending uTex2nm_use="tx0" uColor="#1E211E" />
	<a="front" xywh2nm="590,275,301,136" whl="49.2,23.3,x" pxyz="0.21,-18,x" /> //marlboro
</group> 
<clone ay=180 />
//joint (slit) between the pack and the lid
<group>
	<mt_adjust uTex2nm_use="tx0" >
		<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_right" onThe="right" py=24.6 az=31 />
		<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_left"  onThe="left"  py=24.6 az=-31 />
		<a2mesh wh="53,1" xywh2nm="582,497,1,4" all markedAs="box_front"               py=17.8 />
		<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back"  onThe="back"  py=31.5 px=23.5 />
		<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back"  onThe="back"  py=31.5 px=-23.5 />
	</mt_adjust> 
</group sizeD="0.1,0,0.1"> 
//sealing ribbon
<mt_type="wire" lineWidth=1.5 uColor="130,90,0" >
<line>
	<p pxyz="-27.6,16.5 ,0" />
	<p dz=10.5 /> //left side half
	<p dxyz="1.1,0,1.1" /> //front left rib
	<p dx=53 /> //front side
	<p dxyz="1.1,0,-1.1" /> //front right rib
	<p dz=-21 /> //right side
	<p dxyz="-1.1,0,-1.1" /> //back right rib
	<p dx=-53 /> //back side
	<p dxyz="-1.1,0,1.1" /> //back left rib
	<p dz=16 /> //left half
	<p dxyz="-1,0,5" /> //ribbon "tail"
	<p dz=1 />
</line >

Обратите внимание на:

  • Новый mt_type “wire” (строка 52)
  • Новая переменная “lineWidth” в Material-е
  • Новый таг “line”
  • Новые таги “p” (for “point”)
  • Новые переменные “dxyz”, “dx” и т.д. Это “delta”(разница) от предыдущей точки

Еще понадобится дополнительный код в шейдерах. Также, пользуясь случаем, я решил перенести вычисления HalfVector-а изTheGame.cpp в vertex shader. Раньше мы вычисляли 1 HalfVector для всей модели. Теперь он будет считаться для каждого вертекса. Так будет реалистичнее.

Phong vertex shader:

2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as

C:\CPP\engine\dt\shaders\phong_v.txt

//#version 320 es
precision lowp float;
uniform mat4 uMVP; // transform matrix (Model-View-Projection)
uniform mat3 uMV3x3; // Model-View matrix (for calculating normals into eye space)

in vec3 aPos; // position attribute (x,y,z)
#if defined(USE_NORMALS)
	in vec3 aNormal; // normal attribute (x,y,z)
	out vec3 vNormal; // varying normal (to pass to fragment shader)
#endif
#if defined(USE_TUV0)
	in vec2 aTuv; //attribute TUV (texture coordinates)
	out vec2 vTuv; //varying TUV (pass to fragment shader)
#endif
#if defined(MIRROR)
	out vec2 vTuvMirror; //varying TUV (pass to fragment shader)
#endif
#if defined(PHONG)
	uniform mat4 uMM; // Model matrix (for vHalfVector for glares)
	uniform vec3 uVectorToLight;
	uniform vec3 uCameraPosition; //for calculating half vector for glares
	uniform float uSpecularIntencity; //for calculating half vector for glares
	out vec3 vHalfVector;
#endif

void main(void) { 
	gl_Position = uMVP * vec4(aPos, 1.0);
#if defined(USE_NORMALS)	
	// Transform the normal's orientation into eye space. 
	vNormal = uMV3x3 * aNormal;	
#endif
#if defined(USE_TUV0)
	vTuv = aTuv;
#endif
#if defined(MIRROR)
	vTuvMirror[0] =  (gl_Position[0]/gl_Position[3]*0.1+vNormal[0]*0.4)+0.5;
	vTuvMirror[1] = -(gl_Position[1]/gl_Position[3]*0.1+vNormal[1]*0.4)+0.5;
#endif
#if defined(PHONG)
	if(uSpecularIntencity > 0.0){ //for glares
		vec4 vxPos = uMM * vec4(aPos, 1.0); //vertex position
		vec3 dirToCamera = normalize(uCameraPosition - vec3(vxPos));
		vHalfVector = normalize(dirToCamera + uVectorToLight);
	}
#endif
}

Обратите внимание:

  • Мы больше не используем юниформу uHalfVector
  • Теперь у нас uniform mat4 uMM; (Model matrix для вычисления vHalfVector для бликов)
  • и uniform vec3 uCameraPosition; (для того же)
  • Мы вычисляем varying vHalfVector для каждого вертекса

Phong fragment shader:

3. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as

C:\CPP\engine\dt\shaders\phong_f.txt

//#version 320 es
precision lowp float;
out vec4 FragColor; //output pixel color
uniform float uAlphaFactor; //for semi-transparency
uniform int uAlphaBlending; //for semi-transparency

#if defined(USE_NORMALS)
	in vec3 vNormal; //normal passed from rasterizer
#endif
#if defined(USE_TEX0)
	uniform sampler2D uTex0;  //texture id
	uniform sampler2D uTex3;  //translate texture id
	uniform int uTex0translateChannelN;
#else
	uniform vec4 uColor;
#endif
#if defined(USE_TUV0)
	in vec2 vTuv; //varying TUV (passed from vertex shader)
#endif
#if defined(MIRROR)
	in vec2 vTuvMirror; //varying TUV (passed from vertex shader)
#endif
#if defined(OVERMASK)
	uniform sampler2D uTex1mask;  //texture id
	uniform int uTex1alphaChannelN;
	uniform int uTex1alphaNegative;
#endif

#if defined(PHONG)
	uniform float uAmbient;
	uniform float uSpecularIntencity;
	uniform float uSpecularMinDot;
	uniform float uSpecularPowerOf;

	uniform vec3 uVectorToLight;
	in vec3 vHalfVector;
#endif

void main(void) {

	vec4 outColor;
	float alpha = 1.0;
#if defined(OVERMASK)
	outColor = texture(uTex1mask, vTuv);
	alpha = outColor[uTex1alphaChannelN];
	if(uTex1alphaNegative > 0)
		alpha = 1.0 - alpha;
	if(alpha < 0.5){
		if(uAlphaBlending > 0){
			if(alpha == 0.0){
				discard;
				return;
			}
		}
		else{ //no AlphaBlending
			discard;
			return;
		}
	}
#endif
#if defined(USE_TEX0)
	#if defined(MIRROR)
		outColor = texture(uTex0, vTuvMirror);
	#else
		outColor = texture(uTex0, vTuv);
	#endif
	if(uTex0translateChannelN >= 0){ //translate channel
		vec2 tuv3;
		tuv3[0] = outColor[uTex0translateChannelN];
		tuv3[1] = 0.0;
		outColor = texture(uTex3, tuv3);
	}
	FragColor = outColor;
#else
	FragColor = uColor;
#endif
	if(FragColor.a != 1.0){
		alpha *= FragColor.a;
		if(alpha < 0.5){
			if(uAlphaBlending > 0){
				if(alpha == 0.0){
					discard;
					return;
				}
			}
			else{ //no AlphaBlending
				discard;
				return;
			}
		}
	}

#if defined(USE_NORMALS)
	vec3 vNormalNormal = normalize(vNormal);
#endif

#if defined(PHONG)
	if(uAmbient<1.0){
		// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
		// pointing in the same direction then it will get max illumination.
		float dotProduct = dot(vNormalNormal, uVectorToLight);
#if defined(WIRE)
		if(dotProduct < 0.0)
			dotProduct = -dotProduct;
		dotProduct = 1.0 - dotProduct;
#endif		
		// count ambient component
		dotProduct += uAmbient;
		if(dotProduct < uAmbient)
			dotProduct = uAmbient;

		// Multiply the color by the lightIntencity illumination level to get final output color.
		FragColor *= dotProduct;
	}
	if(uSpecularIntencity>0.0){
		//specular light
		// INTENSITY OF THE SPECULAR LIGHT
		// DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS
		vec3 vNormalHalfVector = normalize(vHalfVector);
		float dotProduct = dot(vNormalNormal, vNormalHalfVector);
#if defined(WIRE)
		if(dotProduct < 0.0)
			dotProduct = -dotProduct;
		dotProduct = 1.0 - dotProduct;
#endif		
		if(dotProduct>uSpecularMinDot){
			float specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;		
			if(specularIntencity > uSpecularIntencity)
				specularIntencity = uSpecularIntencity;
			FragColor += specularIntencity;
		}
	}
#endif
	if(uAlphaFactor != 1.0)
		alpha *= uAlphaFactor;	
	FragColor.a = alpha;
}

  • Изменения касающиеся линий – отмечены

Normal map vertex shader:

4. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as

C:\CPP\engine\dt\shaders\nm_v.txt

//#version 320 es
precision lowp float;
uniform mat4 uMVP; // transform matrix (Model-View-Projection)
uniform mat3 uMV3x3; // Model-View matrix (for calculating normals into eye space)
uniform mat4 uMM; // Model matrix (for vHalfVector for glares)
in vec3 aPos; // position attribute (x,y,z)
in vec3 aNormal; // normal attribute (x,y,z)
//normal map
in vec3 aTangent;
in vec3 aBinormal;
in vec2 aTuv2; //attribute TUV2 (texture coordinates)
out vec2 vTuv2; //varying TUV2 (pass to fragment shader)
uniform vec3 uVectorToLight;
uniform vec3 uCameraPosition; //for calculating half vector for glares
uniform float uSpecularIntencity; //for calculating half vector for glares

out vec3 tbnVectorToLight;
out vec3 tbnHalfVector;
#if defined(MIRROR)
	out vec2 vScreenPosition01;
	out mat3 inversedTBN;
#endif
#if defined(USE_TUV0)
	in vec2 aTuv; //attribute TUV (texture coordinates)
	out vec2 vTuv; //varying TUV (pass to fragment shader)
#endif

void main(void) { 
	gl_Position = uMVP * vec4(aPos, 1.0);
#if defined(USE_TUV0)
	vTuv = aTuv;
#endif

	vTuv2 = aTuv2;

	// Transform the normal's orientation into eye space.    
	vec3 N = uMV3x3 * aNormal;
	vec3 T = uMV3x3 * aTangent;
	vec3 B = uMV3x3 * aBinormal;
	//build TBN matrix
	mat3 TBN = mat3(
			T[0],B[0],N[0],
			T[1],B[1],N[1],
			T[2],B[2],N[2]
			);
	tbnVectorToLight = TBN * uVectorToLight;
	if(uSpecularIntencity > 0.0){ //for glares
		vec4 vxPos = uMM * vec4(aPos, 1.0); //vertex position
		vec3 dirToCamera = normalize(uCameraPosition - vec3(vxPos));
		vec3 vHalfVector = normalize(dirToCamera + uVectorToLight);
		tbnHalfVector = TBN * vHalfVector;
	}
#if defined(MIRROR)
	vScreenPosition01[0] =  (gl_Position[0]/gl_Position[3])*0.1;
	vScreenPosition01[1] = -(gl_Position[1]/gl_Position[3])*0.1;
	inversedTBN = inverse(TBN);
#endif
}


Normal map fragment shader:

5. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as

C:\CPP\engine\dt\shaders\nm_f.txt

//#version 320 es
precision lowp float;
out vec4 FragColor; //output pixel color
uniform float uAlphaFactor; //for semi-transparency
uniform int uAlphaBlending; //for semi-transparency

in vec2 vTuv2;
uniform sampler2D uTex2nm;
in vec3 tbnVectorToLight;
in vec3 tbnHalfVector;

#if defined(USE_TEX0)
	uniform sampler2D uTex0;  //texture id
	uniform sampler2D uTex3;  //translate texture id
	uniform int uTex0translateChannelN;
#else
	uniform vec4 uColor;
#endif
#if defined(USE_TUV0)
	in vec2 vTuv; //varying TUV (passed from vertex shader)
#endif
#if defined(OVERMASK)
	uniform sampler2D uTex1mask;  //texture id
	uniform int uTex1alphaChannelN;
	uniform int uTex1alphaNegative;
#endif
#if defined(MIRROR)
	in vec2 vScreenPosition01;
	in mat3 inversedTBN;
#endif
uniform float uAmbient;
uniform float uSpecularIntencity;
uniform float uSpecularMinDot;
uniform float uSpecularPowerOf;

void main(void) {
	vec4 tbnNormal4 = texture(uTex2nm, vTuv2);
	float alpha = tbnNormal4.a;

	if(alpha < 0.5){
		if(uAlphaBlending > 0){
			if(alpha == 0.0){
				discard;
				return;
			}
		}
		else{ //no AlphaBlending
			discard;
			return;
		}
	}
	//black?
	if(tbnNormal4.b < 0.3){
		FragColor = vec4(0.0,0.0,0.0,alpha);
		return;
	}	
	vec4 outColor;
#if defined(OVERMASK)
	outColor = texture(uTex1mask, vTuv);
	float alpha2 = outColor[uTex1alphaChannelN];
	if(uTex1alphaNegative > 0)
		alpha2 = 1.0 - alpha2;
	if(alpha2 < 1.0){
		alpha *= alpha2;
		if(alpha < 0.5){
			if(uAlphaBlending > 0){
				if(alpha == 0.0){
					discard;
					return;
				}
			}
			else{ //no AlphaBlending
				discard;
				return;
			}
		}
	}
#endif
	vec3 vNormalNormal = normalize(vec3(tbnNormal4) * 2.0 - 1.0);

#if defined(USE_TEX0)
	#if defined(MIRROR)
		vec3 inversedNormal = normalize(inversedTBN * vNormalNormal);
		vec2 vTuvMirror;
		vTuvMirror[0] =  (vScreenPosition01[0]+inversedNormal[0]*0.4)+0.5;
		vTuvMirror[1] = -(vScreenPosition01[1]+inversedNormal[1]*0.4)+0.5;
		outColor = texture(uTex0, vTuvMirror);
	#else
		outColor = texture(uTex0, vTuv);
	#endif
	if(uTex0translateChannelN >= 0){ //translate channel
		vec2 tuv3;
		tuv3[0] = outColor[uTex0translateChannelN];
		tuv3[1] = 0.0;
		outColor = texture(uTex3, tuv3);
	}
	FragColor = outColor;
#else
	FragColor = uColor;
#endif
	if(FragColor.a != 1.0){
		alpha *= FragColor.a;
		if(alpha < 0.5){
			if(uAlphaBlending > 0){
				if(alpha == 0.0){
					discard;
					return;
				}
			}
			else{ //no AlphaBlending
				discard;
				return;
			}
		}
	}
	if(uAmbient<1.0){
		 // Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
		 // pointing in the same direction then it will get max illumination.
		 float dotProduct = dot(vNormalNormal, normalize(tbnVectorToLight));
#if defined(WIRE)
		if(dotProduct < 0.0)
			dotProduct = -dotProduct;
		dotProduct = 1.0 - dotProduct;
#endif		
		 // count ambient component
		 dotProduct += uAmbient;
		 if(dotProduct < uAmbient)
			dotProduct = uAmbient;

		 // Multiply the color by the lightIntencity illumination level to get final output color.
		 FragColor *= dotProduct;
	}
	if(uSpecularIntencity>0.0){
		//specular light
		// INTENSITY OF THE SPECULAR LIGHT
		// DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS
		float dotProduct = dot(vNormalNormal, normalize(tbnHalfVector));
#if defined(WIRE)
		if(dotProduct < 0.0)
			dotProduct = -dotProduct;
		dotProduct = 1.0 - dotProduct;
#endif		
		if(dotProduct>uSpecularMinDot){
			float specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;		
			if(specularIntencity > uSpecularIntencity)
				specularIntencity = uSpecularIntencity;
			FragColor += specularIntencity;
		}
	}
	if(uAlphaFactor != 1.0)
		alpha *= uAlphaFactor;	
	FragColor.a = alpha;
}


Теперь – программная часть.

Windows

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


7. Заменим Material.h код на:

#pragma once
#include "MyColor.h"
#include <string>

class Material
{
public:
	char shaderType[20] = "";
	int shaderN = -1;
	int primitiveType = GL_TRIANGLES;
	MyColor uColor;
	int uTex0 = -1;
	int uTex1mask = -1;
	int uTex2nm = -1;
	int uTex3 = -1;
	int uTex1alphaChannelN = 3; //default - alpha channel for mask
	int uTex1alphaNegative = 0; //default - alpha channel not negative
	int uTex0translateChannelN = -1; //translate tex0 to tex3 by channelN. Default -1 - don't translate

	int uAlphaBlending = 0; //for semi-transparency
	float uAlphaFactor = 1; //for semi-transparency
	float uAmbient = 0.4f; //ambient light
	//specular light parameters
	float uSpecularIntencity = 0.8f;
	float uSpecularMinDot = 0.8f;
	float uSpecularPowerOf = 20.0f;

	float lineWidth = 1;

public:
	int pickShaderNumber() { return pickShaderNumber(this); };
	static int pickShaderNumber(Material* pMT);
	void setShaderType(std::string needType) { setShaderType(this, needType); };
	static void setShaderType(Material* pMT, std::string needType) { myStrcpy_s(pMT->shaderType, 20, (char*)needType.c_str()); };
	void clear() { clear(this); };
	static void clear(Material* pMT);
	int assignShader(std::string needType) { return assignShader(this, needType); };
	static int assignShader(Material* pMT, std::string needType);
};

  • Новая переменная lineWidth
  • Другое измененме – uSpecularMinDot = 0.8f; (сделает блики помягче)

В классе ModelLoader – новая переменная и функционал (отмечены).

8. Заменим ModelLoader.h код на:

#pragma once
#include "XMLparser.h"
#include "ModelBuilder.h"
#include "GroupTransform.h"
#include "MaterialAdjust.h"

class ModelLoader : public XMLparser
{
public:
	ModelBuilder* pModelBuilder = NULL;
	bool ownModelBuilder = false;
	std::vector<GameSubj*>* pSubjsVector = NULL;
	MaterialAdjust* pMaterialAdjust = NULL;
	int lineStartsAt = -1;
public:
	ModelLoader(std::vector<GameSubj*>* pSubjsVector0, int subjN, ModelBuilder* pMB, std::string filePath) : XMLparser(filePath) {
		pSubjsVector = pSubjsVector0;
		if (pMB != NULL) {
			ownModelBuilder = false;
			pModelBuilder = pMB;
		}
		else {
			ownModelBuilder = true;
			pModelBuilder = new ModelBuilder();
			pModelBuilder->lockGroup(pModelBuilder);
		}
		pModelBuilder->useSubjN(pModelBuilder,subjN);
	};
	virtual ~ModelLoader() {
		if (!ownModelBuilder)
			return;
		pModelBuilder->buildDrawJobs(pModelBuilder, *pSubjsVector);
		delete pModelBuilder;
	};
	static int processTag_a(ModelLoader* pML); //apply
	static int setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr);
	static int setTexture(ModelLoader* pML, int* pInt, std::string txName);
	static int setMaterialTextures(ModelLoader* pML, Material* pMT);
	static int fillProps_vs(VirtualShape* pVS, std::string tagStr); //virtual shape
	static int fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML); //Material
	static int fillProps_gt(GroupTransform* pGS, ModelBuilder* pMB, std::string tagStr);
	virtual int processTag() { return processTag(this); };
	static int processTag(ModelLoader* pML);
	static int loadModel(std::vector<GameSubj*>* pSubjsVector0, std::string sourceFile, std::string subjClass);
	static int processTag_clone(ModelLoader* pML);
	static int addMark(char* marks, std::string newMark);
	static int processTag_do(ModelLoader* pML);
	static int processTag_a2mesh(ModelLoader* pML);
};


9. Заменим ModelLoader.cpp код на:

#include "ModelLoader.h"
#include "platform.h"
#include "TheGame.h"
#include "DrawJob.h"
#include "Texture.h"
#include "utils.h"
#include "Polygon.h"

extern TheGame theGame;

int ModelLoader::loadModel(std::vector<GameSubj*>* pSubjsVector0, std::string sourceFile, std::string subjClass) {
	//returns element's (Subj) number or -1
	int subjN = pSubjsVector0->size();
	GameSubj* pGS = theGame.newGameSubj(subjClass);
	pSubjsVector0->push_back(pGS);
	//pGS->djStartN = DrawJob::drawJobs.size();
	ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
	processSource(pML);
	delete pML;
	//pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
	return subjN;
}

int ModelLoader::setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr) {
	if (!varExists(varName, tagStr))
		return 0;
	std::string str0 = getStringValue(varName, tagStr);
	if (intHashMap.find(str0) == intHashMap.end()) {
		mylog("ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\n", varName.c_str(), tagStr.c_str());
		return -1;
	}
	*pInt = intHashMap[getStringValue(varName, tagStr)];
	return 1;
}
int ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {
	ModelBuilder* pMB = pML->pModelBuilder;
	bool resetTexture = false;
	std::string varName = txName + "_use";
	if (varExists(varName, pML->currentTag)) {
		if (setValueFromIntHashMap(pInt, pMB->texturesHashMap, varName, pML->currentTag) == 0) {
			mylog("ERROR in ModelLoader::setTexture: texture not in hashMap: %s\n", pML->currentTag.c_str());
			return -1;
		}
		resetTexture = true;
	}
	else{
		varName = txName + "_src";
		if (varExists(varName, pML->currentTag)) {
			std::string txFile = getStringValue(varName, pML->currentTag);
			varName = txName + "_ckey";
			unsigned int intCkey = 0;
			setUintColorValue(&intCkey, varName, pML->currentTag);
			*pInt = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
			resetTexture = true;
		}
	}
	if(resetTexture)
		return 1;
	return 0; //texture wasn't reset
}
int ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {
	if (setTexture(pML, &pMT->uTex0, "uTex0") > 0)
		pMT->uColor.clear();
	setTexture(pML, &pMT->uTex1mask, "uTex1mask");
	setTexture(pML, &pMT->uTex2nm, "uTex2nm");
	setTexture(pML, &pMT->uTex3, "uTex3");
	return 1;
}
int ModelLoader::fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML) {
	setCharsValue(pMT->shaderType, 20, "mt_type", tagStr);
	setMaterialTextures(pML, pMT);
	//color
	if (varExists("uColor", tagStr)) {
		unsigned int uintColor = 0;
		setUintColorValue(&uintColor, "uColor", tagStr);
		pMT->uColor.setUint32(uintColor);
		pMT->uTex0 = -1;
	}
	//mylog("mt.uTex0=%d, mt.uTex1mask=%d\n", mt.uTex0, mt.uTex1mask);
	if (varExists("primitiveType", tagStr)) {
		std::string str0 = getStringValue("primitiveType", tagStr);
		if (str0.compare("GL_POINTS") == 0) pMT->primitiveType = GL_POINTS;
		else if (str0.compare("GL_LINES") == 0) pMT->primitiveType = GL_LINES;
		else if (str0.compare("GL_LINE_STRIP") == 0) pMT->primitiveType = GL_LINE_STRIP;
		else if (str0.compare("GL_LINE_LOOP") == 0) pMT->primitiveType = GL_LINE_LOOP;
		else if (str0.compare("GL_TRIANGLE_STRIP") == 0) pMT->primitiveType = GL_TRIANGLE_STRIP;
		else if (str0.compare("GL_TRIANGLE_FAN") == 0) pMT->primitiveType = GL_TRIANGLE_FAN;
		else pMT->primitiveType = GL_TRIANGLES;
	}
	setIntValue(&pMT->uTex1alphaChannelN, "uTex1alphaChannelN", tagStr);
	setIntValue(&pMT->uTex0translateChannelN, "uTex0translateChannelN", tagStr);
	setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
	setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
	setFloatValue(&pMT->uAmbient, "uAmbient", tagStr);
	setFloatValue(&pMT->uSpecularIntencity, "uSpecularIntencity", tagStr);
	setFloatValue(&pMT->uSpecularMinDot, "uSpecularMinDot", tagStr);
	setFloatValue(&pMT->uSpecularPowerOf, "uSpecularPowerOf", tagStr);

	setFloatValue(&pMT->lineWidth, "lineWidth", tagStr);
	return 1;
}
int ModelLoader::processTag(ModelLoader* pML) {
	ModelBuilder* pMB = pML->pModelBuilder;
	if (pML->tagName.compare("texture_as") == 0) {
		//saves texture N in texturesMap under given name
		std::string keyName = getStringValue("texture_as", pML->currentTag);
		if (pMB->texturesHashMap.find(keyName) != pMB->texturesHashMap.end())
			return pMB->texturesHashMap[keyName];
		else { //add new
			std::string txFile = getStringValue("src", pML->currentTag);
			unsigned int intCkey = 0;
			setUintColorValue(&intCkey, "ckey", pML->currentTag);
			int txN = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
			pMB->texturesHashMap[keyName] = txN;
			//mylog("%s=%d\n", keyName.c_str(), pMB->texturesMap[keyName]);
			return txN;
		}
	}
	if (pML->tagName.compare("mt_type") == 0) {
		//sets current material
		ModelBuilder* pMB = pML->pModelBuilder;
		if (!pML->closedTag) {
			//save previous material in stack
			if (pMB->usingMaterialN >= 0)
				pMB->materialsStack.push_back(pMB->usingMaterialN);
		}
		Material mt;
		fillProps_mt(&mt, pML->currentTag, pML);
		pMB->usingMaterialN = pMB->getMaterialN(pMB, &mt);
		return 1;
	}
	if (pML->tagName.compare("/mt_type") == 0) {
		//restore previous material
		if (pMB->materialsStack.size() > 0) {
			pMB->usingMaterialN = pMB->materialsStack.back();
			pMB->materialsStack.pop_back();
		}
		return 1;
	}
	if (pML->tagName.compare("vs") == 0) {
		//sets virtual shape
		ModelBuilder* pMB = pML->pModelBuilder;
		if (pML->closedTag) {
			if (pMB->pCurrentVShape != NULL)
				delete pMB->pCurrentVShape;
		}
		else { //open tag
			//save previous vshape in stack
			if (pMB->pCurrentVShape != NULL)
				pMB->vShapesStack.push_back(pMB->pCurrentVShape);
		}
		pMB->pCurrentVShape = new VirtualShape();
		fillProps_vs(pMB->pCurrentVShape, pML->currentTag);
		return 1;
	}
	if (pML->tagName.compare("/vs") == 0) {
		//restore previous virtual shape
		if (pMB->vShapesStack.size() > 0) {
			if (pMB->pCurrentVShape != NULL)
				delete(pMB->pCurrentVShape);
			pMB->pCurrentVShape = pMB->vShapesStack.back();
			pMB->vShapesStack.pop_back();
		}
		return 1;
	}
	if (pML->tagName.compare("group") == 0) {
		std::string notAllowed[] = { "pxyz","axyz","align","headTo" };
		int notAllowedLn = sizeof(notAllowed) / sizeof(notAllowed[0]);
		for (int i = 0; i < notAllowedLn; i++)
			if (varExists(notAllowed[i], pML->currentTag)) {
				mylog("ERROR in ModelLoader::processTag: use %s in </group>: %s\n", notAllowed[i].c_str(), pML->currentTag.c_str());
				return -1;
			}
		pMB->lockGroup(pMB);
		//mark
		if (varExists("mark", pML->currentTag))
			addMark(pMB->pCurrentGroup->marks, getStringValue("mark", pML->currentTag));
		return 1;
	}
	if (pML->tagName.compare("/group") == 0) {
		GroupTransform gt;
		fillProps_gt(&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(&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(&gt, &pMB->vertices, &pMB->triangles);

		//cloning
		pMB->lockGroup(pMB);
		gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &pMB->vertices, &pMB->triangles);
	}
	GroupTransform gt;
	fillProps_gt(&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(&gt, pMB, pML->currentTag);
	gt.flagSelection(&gt, &pMB->vertices, &pMB->triangles);
	gt.transformFlagged(&gt, &pMB->vertices);
	return 1;
}
int ModelLoader::processTag_a2mesh(ModelLoader* pML) {
	ModelBuilder* pMB = pML->pModelBuilder;
	std::string tagStr = pML->currentTag;
	GroupTransform gt;
	fillProps_gt(&gt, pMB, pML->currentTag);
	gt.flagSelection(&gt, &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(&gt, &transformMatrix);
	mat4x4 transformMatrixInverted;
	mat4x4_invert(transformMatrixInverted, transformMatrix);
	//move/rotate cloned
	gt.flagAll(&vx1, &tr1);
	//gt.transformFlagged(&pMB->vertices, &transformMatrixInverted);
	gt.transformFlaggedMx(&vx1, &transformMatrixInverted);

	//gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &vx1, &tr1);

	float wh[2];
	setFloatArray(wh, 2, "wh", tagStr);
	Polygon frame;
	frame.setRectangle(&frame, wh[0], wh[1]);
	//destination arrays
	std::vector<Vertex01*> vx2;
	std::vector<Triangle01*> tr2;
	Polygon triangle;
	for (int i = tr1.size() - 1; i >= 0; i--) {
		triangle.setTriangle(&triangle, tr1.at(i), &vx1);
		Polygon intersection;
		int pointsN = Polygon::xyIntersection(&intersection, &frame, &triangle);
		if (pointsN > 2) {
			Polygon::buildTriangles(&intersection);
			GroupTransform::flagAll(&intersection.vertices, &intersection.triangles);
			GroupTransform::cloneFlagged(NULL, &vx2, &tr2, &intersection.vertices, &intersection.triangles);
		}
	}
	gt.flagAll(&vx2, &tr2);
	//at this point we have cutted fragment facing us
	int vxTotal = vx2.size();
	int trTotal = tr2.size();
	//apply adjusted material ?
	if (pML->pMaterialAdjust != NULL) {
		//scan vertices to find new (unupdated) material
		int materialNsrc = -1; //which N to replace
		int materialNdst = -1; //replace by N 
		for (int vN = 0; vN < vxTotal; vN++) {
			Vertex01* pV = vx2.at(vN);
			if (pV->flag < 0)
				continue;
			if (materialNsrc == pV->materialN)
				continue;
			//have new material
			materialNsrc = pV->materialN;
			Material mt;
			Material* pMt0 = pMB->materialsList.at(materialNsrc);
			memcpy(&mt, pMt0, sizeof(Material));
			//modify material
			MaterialAdjust::adjust(&mt, pML->pMaterialAdjust);
			materialNdst = pMB->getMaterialN(pMB, &mt);
			if (materialNsrc != materialNdst) {
				//replace mtN in vx and tr arrays
				for (int vN2 = vN; vN2 < vxTotal; vN2++) {
					Vertex01* pV2 = vx2.at(vN2);
					if (pV2->flag < 0)
						continue;
					if (materialNsrc == pV2->materialN)
						pV2->materialN = materialNdst;
				}
				for (int tN2 = 0; tN2 < trTotal; tN2++) {
					Triangle01* pT2 = tr2.at(tN2);
					if (pT2->flag < 0)
						continue;
					if (materialNsrc == pT2->materialN)
						pT2->materialN = materialNdst;
				}
				materialNsrc = materialNdst;
			}
		}
	}
	else { // pML->pMaterialAdjust == NULL, use pMB->usingMaterialN
		for (int vN2 = 0; vN2 < vxTotal; vN2++) {
			Vertex01* pV2 = vx2.at(vN2);
			if (pV2->flag < 0)
				continue;
			pV2->materialN = pMB->usingMaterialN;
		}
		for (int tN2 = 0; tN2 < trTotal; tN2++) {
			Triangle01* pT2 = tr2.at(tN2);
			if (pT2->flag < 0)
				continue;
			pT2->materialN = pMB->usingMaterialN;
		}
	}
	//apply xywh/2nm ?
	if (varExists("xywh", tagStr) || varExists("xywh2nm", tagStr)) {
		Material* pMT = pMB->materialsList.at(vx2.at(0)->materialN);
		float xywh[4] = { 0,0,1,1 };
		TexCoords* pTC = NULL;
		if (varExists("xywh", tagStr)) {
			setFloatArray(xywh, 4, "xywh", tagStr);
			std::string flipStr = getStringValue("flip", tagStr);
			int texN = pMT->uTex1mask;
			if (texN < 0)
				texN = pMT->uTex0;
			TexCoords tc;
			tc.set(texN, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
			pTC = &tc;
		}
		TexCoords* pTC2nm = NULL;

		if (varExists("xywh2nm", tagStr)) {
			setFloatArray(xywh, 4, "xywh2nm", tagStr);
			std::string flipStr = getStringValue("flip2nm", tagStr);
			TexCoords tc2nm;
			tc2nm.set(pMT->uTex2nm, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
			pTC2nm = &tc2nm;
		}
		pMB->applyTexture2flagged(&vx2, "front", pTC, false);
		pMB->applyTexture2flagged(&vx2, "front", pTC2nm, true);
	}
	//move/rotate
	gt.transformFlaggedMx(&vx2, &transformMatrix);
	//clone back to modelBuilder arrays
	gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &vx2, &tr2);

	//clear memory
	for (int i = vx1.size() - 1; i >= 0; i--)
		delete vx1.at(i);
	vx1.clear();
	for (int i = tr1.size() - 1; i >= 0; i--)
		delete tr1.at(i);
	tr1.clear();
	for (int i = vx2.size() - 1; i >= 0; i--)
		delete vx2.at(i);
	vx2.clear();
	for (int i = tr2.size() - 1; i >= 0; i--)
		delete tr2.at(i);
	tr2.clear();

	return 1;
}


Когда линия загружена, надо б позаботиться о нормалях для вычисления освещения в шейдере. Только единственный вектор, который мы можем из нее вычислить – это направление линии, которое перпендикулярно любым нормалям.

Дополнительный код в шейдерах опирается на направления вместо нормалей. Направления мы будем вычислять в классе ModelBuilder.

10. Заменим ModelBuilder1base.h код на:

#pragma once
#include <string>
#include <vector>
#include "Vertex01.h"
#include "Triangle01.h"
#include "VirtualShape.h"
#include "Group01.h"
#include "Material.h"
#include "GameSubj.h"
#include <map>

class ModelBuilder1base
{
public:
	std::vector<Vertex01*> vertices;
	std::vector<Triangle01*> triangles;
	std::vector<int> subjNumbersList;
	int usingSubjN = -1;

	std::vector<Group01*> groupsStack;
	Group01* pCurrentGroup = NULL;
	Group01* pLastClosedGroup = NULL;
	
	std::vector<VirtualShape*> vShapesStack;
	VirtualShape* pCurrentVShape = NULL;

	std::vector<Material*> materialsList;
	int usingMaterialN = -1;
	std::vector<int> materialsStack;

	std::map<std::string, int> texturesHashMap;
public:
	virtual ~ModelBuilder1base();
	static int useSubjN(ModelBuilder1base* pMB, int subjN);
	static int getMaterialN(ModelBuilder1base* pMB, Material* pMT);
	static void lockGroup(ModelBuilder1base* pMB);
	static void releaseGroup(ModelBuilder1base* pMB);
	static int addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz);
	static int add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n);
	static int addTriangle(ModelBuilder1base* pMB, int n0, int n1, int n2);
	static int buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs);
	static int rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
	static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
	static int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);
	static int calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
	static int finalizeLine(std::vector<Vertex01*> verts, int lineStartsAt=0, int lineEndsAt=0);
};


11. Заменим ModelBuilder1base.cpp код на:

#include "ModelBuilder1base.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"

extern float degrees2radians;

ModelBuilder1base::~ModelBuilder1base() {
	releaseGroup(this);

	//clear all vectors
	int itemsN = vertices.size();
	for (int i = 0; i < itemsN; i++)
		delete vertices.at(i);
	vertices.clear();

	itemsN = triangles.size();
	for (int i = 0; i < itemsN; i++)
		delete triangles.at(i);
	triangles.clear();

	itemsN = vShapesStack.size();
	for (int i = 0; i < itemsN; i++)
		delete vShapesStack.at(i);
	vShapesStack.clear();

	itemsN = groupsStack.size();
	for (int i = 0; i < itemsN; i++)
		delete groupsStack.at(i);
	groupsStack.clear();
	if (pCurrentGroup != NULL)
		delete pCurrentGroup;
	if (pLastClosedGroup != NULL)
		delete pLastClosedGroup;

	itemsN = materialsList.size();
	for (int i = 0; i < itemsN; i++)
		delete materialsList.at(i);
	materialsList.clear();

	subjNumbersList.clear();
}
int ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {
	pMB->usingSubjN = subjN;
	int itemsN = pMB->subjNumbersList.size();
	bool newN = true;
	if (itemsN > 0)
		for (int i = 0; i < itemsN; i++)
			if (pMB->subjNumbersList.at(i) == subjN) {
				newN = false;
				break;
			}
	if (newN)
		pMB->subjNumbersList.push_back(subjN);
	return subjN;
}
int ModelBuilder1base::getMaterialN(ModelBuilder1base* pMB, Material* pMT) {
	int itemsN = pMB->materialsList.size();
	if (itemsN > 0)
		for (int i = 0; i < itemsN; i++)
			if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
				return i;
			}
	//if here - add new material to the list
	Material* pMTnew = new Material(*pMT);
	pMB->materialsList.push_back(pMTnew);
	return itemsN;
}
int ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
	//indexes: NorthWest, NorthEast, SouthWest,SouthEast
	if (n % 2 == 0) { //even number
		addTriangle(pMB, nNW, nSW, nNE);
		addTriangle(pMB, nNE, nSW, nSE);
	}
	else { //odd number
		addTriangle(pMB, nNW, nSE, nNE);
		addTriangle(pMB, nNW, nSW, nSE);
	}
	return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {
	Triangle01* pTR = new Triangle01();
	pMB->triangles.push_back(pTR);
	pTR->idx[0] = i0;
	pTR->idx[1] = i1;
	pTR->idx[2] = i2;
	pTR->subjN = pMB->usingSubjN;
	pTR->materialN = pMB->usingMaterialN;
	//mark
	if (pMB->pCurrentGroup != NULL)
		if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
			myStrcpy_s(pTR->marks, 124, pMB->pCurrentGroup->marks);
	return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
	Vertex01* pVX = new Vertex01();
	pMB->vertices.push_back(pVX);
	pVX->aPos[0] = kx;
	pVX->aPos[1] = ky;
	pVX->aPos[2] = kz;
	//normal
	pVX->aNormal[0] = nx;
	pVX->aNormal[1] = ny;
	pVX->aNormal[2] = nz;
	pVX->subjN = pMB->usingSubjN;
	pVX->materialN = pMB->usingMaterialN;
	//mark
	if (pMB->pCurrentGroup != NULL)
		if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
			myStrcpy_s(pVX->marks, 124, pMB->pCurrentGroup->marks);

	return pMB->vertices.size() - 1;
}
int ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs) {
	int totalSubjsN = pMB->subjNumbersList.size();
	if (totalSubjsN < 1) {
		pMB->subjNumbersList.push_back(-1);
		totalSubjsN = 1;
	}
	int totalMaterialsN = pMB->materialsList.size();
	if (totalSubjsN < 2 && totalMaterialsN < 2) {
		//simple single DrawJob
		Material* pMT = pMB->materialsList.at(0);
		GameSubj* pGS = NULL;
		int gsN = pMB->subjNumbersList.at(0);
		if (gsN >= 0)
			pGS = gameSubjs.at(gsN);
		if (pGS != NULL)
			pGS->djStartN = DrawJob::drawJobs.size();
		buildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);
		if (pGS != NULL)
			pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
		return 1;
	}
	int totalVertsN = pMB->vertices.size();
	int totalTrianglesN = pMB->triangles.size();
	//clear flags
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = pMB->vertices.at(vN);
		pVX->flag = 0;
	}
	for (int tN = 0; tN < totalTrianglesN; tN++) {
		Triangle01* pTR = pMB->triangles.at(tN);
		pTR->flag = 0;
	}
	int addedDJs = 0;
	for (int sN = 0; sN < totalSubjsN; sN++) {
		GameSubj* pGS = NULL;
		int gsN = pMB->subjNumbersList.at(sN);
		if (gsN >= 0)
			pGS = gameSubjs.at(gsN);
		if (pGS != NULL)
			pGS->djStartN = DrawJob::drawJobs.size();
		for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
			Material* pMT = pMB->materialsList.at(mtN);
			std::vector<Vertex01*> useVertices;
			std::vector<Triangle01*> useTriangles;
			for (int vN = 0; vN < totalVertsN; vN++) {
				Vertex01* pVX = pMB->vertices.at(vN);
				if (pVX->flag != 0)
					continue;
				if (pVX->subjN != gsN)
					continue;
				if (pVX->materialN != mtN)
					continue;
				//if here - make a copy
				Vertex01* pVX2 = new Vertex01(*pVX);
				useVertices.push_back(pVX2);
				pVX2->altN = vN;
				pVX->flag = 1;
				if (pVX->endOfSequence > 0) {
					//rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
					buildSingleDrawJob(pMT, useVertices, useTriangles);
					addedDJs++;
					//clear and proceed to next sequence
					int useVerticesN = useVertices.size();
					for (int i = 0; i < useVerticesN; i++)
						delete useVertices.at(i);
					useVertices.clear();
				}
			}
			int useVerticesN = useVertices.size();
			if (useVerticesN < 1)
				continue; //to next material
			//pick triangles
			for (int tN = 0; tN < totalTrianglesN; tN++) {
				Triangle01* pTR = pMB->triangles.at(tN);
				if (pTR->flag != 0)
					continue;
				if (pTR->subjN != gsN)
					continue;
				if (pTR->materialN != mtN)
					continue;
				//if here - make a copy
				Triangle01* pTR2 = new Triangle01(*pTR);
				useTriangles.push_back(pTR2);
				pTR->flag = 1;
			}
			rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
			buildSingleDrawJob(pMT, useVertices, useTriangles);
			addedDJs++;
			//clear all for next material
			for (int i = 0; i < useVerticesN; i++)
				delete useVertices.at(i);
			useVertices.clear();
			int useTrianglesN = useTriangles.size();
			for (int i = 0; i < useTrianglesN; i++)
				delete useTriangles.at(i);
			useTriangles.clear();
		}
		if (pGS != NULL)
			pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
	}
	return addedDJs;
}
int ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
	int totalVertsN = useVertices.size();
	if (totalVertsN < 1)
		return 0;
	if (DrawJob::lineWidthIsImportant(pMT->primitiveType))
		if(strcmp(pMT->shaderType,"wire")==0)
			finalizeLine(useVertices);
	if (pMT->uTex2nm >= 0)
		calculateTangentSpace(useVertices, useTriangles);
	pMT->pickShaderNumber();
	DrawJob* pDJ = new DrawJob();
	//copy material to DJ
	memcpy(&pDJ->mt, pMT, sizeof(Material));
	//calculate VBO element size (stride) and variables offsets in VBO
	int VBOid = DrawJob::newBufferId();
	int stride = 0;
	pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
	//create an array for VBO
	int bufferSize = totalVertsN * stride;
	float* vertsBuffer = new float[bufferSize];
	//fill vertsBuffer
	Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
	int floatSize = sizeof(float);
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = useVertices.at(vN);
		int idx = vN * stride / floatSize;
		//pick data from vertex and move to the buffer
		memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
		if (pSh->l_aNormal >= 0) //normal
			memcpy(&vertsBuffer[idx + pDJ->aNormal.offset / floatSize], pVX->aNormal, 3 * floatSize);
		if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
			memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
		if (pSh->l_aTuv2 >= 0) //attribute TUV2 (normal maps)
			memcpy(&vertsBuffer[idx + pDJ->aTuv2.offset / floatSize], pVX->aTuv2, 2 * floatSize);
		if (pSh->l_aTangent >= 0)
			memcpy(&vertsBuffer[idx + pDJ->aTangent.offset / floatSize], pVX->aTangent, 3 * floatSize);
		if (pSh->l_aBinormal >= 0)
			memcpy(&vertsBuffer[idx + pDJ->aBinormal.offset / floatSize], pVX->aBinormal, 3 * floatSize);
	}
	//buffer is ready, create VBO
	glBindBuffer(GL_ARRAY_BUFFER, VBOid);
	glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
	delete[] vertsBuffer;
	pDJ->pointsN = totalVertsN;

	int totalTrianglesN = useTriangles.size();
	if (totalTrianglesN > 0) {
		//create EBO
		int totalIndexesN = totalTrianglesN * 3;
		//create buffer
		GLushort* indexBuffer = new GLushort[totalIndexesN];
		for (int tN = 0; tN < totalTrianglesN; tN++) {
			Triangle01* pTR = useTriangles[tN];
			int idx = tN * 3;
			indexBuffer[idx] = (GLushort)pTR->idx[0];
			indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
			indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
		}
		//buffer is ready, create IBO
		pDJ->glEBOid = DrawJob::newBufferId();
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
		delete[] indexBuffer;
		pDJ->pointsN = totalIndexesN;
	}
	//create and fill vertex attributes array (VAO)
	pDJ->buildVAO();
	return 1;
}

int ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
	int totalTrianglesN = useTriangles.size();
	if (totalTrianglesN < 1)
		return 0;
	int totalVerticesN = useVertices.size();
	//save new vertices order in original vertices array
	//since triangles indices refer to original vertices order
	for (int i = 0; i < totalVerticesN; i++) {
		Vertex01* pVX1 = useVertices.at(i);
		Vertex01* pVX0 = allVertices.at(pVX1->altN);
		pVX0->altN = i;
	}
	//replace triangle original indices by new numbers saved in original vertices altN
	for (int tN = 0; tN < totalTrianglesN; tN++) {
		Triangle01* pTR = useTriangles.at(tN);
		for (int i = 0; i < 3; i++) {
			Vertex01* pVX0 = allVertices.at(pTR->idx[i]);
			pTR->idx[i] = pVX0->altN;
		}
	}
	return 1;
}

int ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {
	//moves and rotates vertex group
	//rotation angles are set in degrees
	mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
	mat4x4_translate(transformMatrix, kX, kY, kZ);
	//rotation order: Z-X-Y
	if (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);
	if (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);
	if (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);

	int vertsN = pMB->vertices.size();
	for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
		Vertex01* pVX = pMB->vertices.at(i);
		mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
		mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
	}
	return 1;
}

int ModelBuilder1base::calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
	int totalVertsN = useVertices.size();
	if (totalVertsN < 1)
		return 0;
	int totalTrianglesN = useTriangles.size();
	//assuming that GL_TRIANGLES
	//clear flags
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pV = useVertices.at(vN);
		pV->flag = 0;
	}
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = useVertices.at(vN);
		if (pVX->flag != 0)
			continue;
		Triangle01* pT = NULL;
		for (int tN = 0; tN < totalTrianglesN; tN++) {
			pT = useTriangles.at(tN);
			bool haveTriangle = false;
			for (int i = 0; i < 3; i++)
				if (pT->idx[i] == vN) {
					haveTriangle = true;
					break;
				}
			if (haveTriangle)
				break;
		}
		Vertex01* pV[3];
		for (int i = 0; i < 3; i++)
			pV[i] = useVertices.at(pT->idx[i]);

		float dPos1[3];
		float dPos2[3];
		float dUV1[2];
		float dUV2[2];
		for (int i = 0; i < 3; i++) {
			dPos1[i] = pV[1]->aPos[i] - pV[0]->aPos[i];
			dPos2[i] = pV[2]->aPos[i] - pV[0]->aPos[i];
		}
		for (int i = 0; i < 2; i++) {
			dUV1[i] = pV[1]->aTuv2[i] - pV[0]->aTuv2[i];
			dUV2[i] = pV[2]->aTuv2[i] - pV[0]->aTuv2[i];
		}

		float tangent[3];
		float binormal[3];
		float divider = dUV1[0] * dUV2[1] - dUV1[1] * dUV2[0];
		if (divider == 0) {
			v3set(tangent, 1, 0, 0);
			v3set(binormal, 0, -1, 0);
		}
		else {
			float r = 1.0f / divider;
			for (int i = 0; i < 3; i++) {
				tangent[i] = (dPos1[i] * dUV2[1] - dPos2[i] * dUV1[1]) * r;
				binormal[i] = -(dPos2[i] * dUV1[0] - dPos1[i] * dUV2[0]) * r;
			}
			vec3_norm(tangent, tangent);
			vec3_norm(binormal, binormal);
		}
		//add to all 3 vertices
		for (int n = 0; n < 3; n++) {
			if (pV[n]->flag > 0)
				continue;
			v3copy(pV[n]->aTangent, tangent);
			v3copy(pV[n]->aBinormal, binormal);
			pV[n]->flag = 1;
		}
	}
	//normalize tangent and binormal around normal
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pV = useVertices.at(vN);
		float v3out[3];
		//tangent
		vec3_mul_cross(v3out, pV->aNormal, pV->aBinormal);
		if (v3dotProduct(pV->aTangent, v3out) < 0)
			v3inverse(v3out);
		v3copy(pV->aTangent, v3out);
		//binormal
		vec3_mul_cross(v3out, pV->aNormal, pV->aTangent);
		if (v3dotProduct(pV->aBinormal, v3out) < 0)
			v3inverse(v3out);
		v3copy(pV->aBinormal, v3out);
	}
	return 1;
}
void ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {
	Group01* pPrevGroup = pMB->pCurrentGroup;
	if (pMB->pCurrentGroup != NULL)
		pMB->groupsStack.push_back(pMB->pCurrentGroup);
	pMB->pCurrentGroup = new Group01();
	pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
	pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
	//marks
	if(pPrevGroup != NULL)
		if (strcmp(pPrevGroup->marks, "") != 0)
			myStrcpy_s(pMB->pCurrentGroup->marks, 124, pPrevGroup->marks);
}
void ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {
	if (pMB->pLastClosedGroup != NULL)
		delete pMB->pLastClosedGroup;
	pMB->pLastClosedGroup = pMB->pCurrentGroup;

	if (pMB->groupsStack.size() > 0) {
		pMB->pCurrentGroup = pMB->groupsStack.back();
		pMB->groupsStack.pop_back();
	}
	else
		pMB->pCurrentGroup = NULL;
}
int ModelBuilder1base::finalizeLine(std::vector<Vertex01*> verts, int lineStartsAt, int lineEndsAt) {
	if (lineEndsAt <= 0)
		lineEndsAt = verts.size() - 1;
	Vertex01* pV0 = verts.at(lineStartsAt);
	Vertex01* pV2 = verts.at(lineEndsAt);
	bool closedLine = false;
	if (v3match(pV0->aPos, pV2->aPos))
		closedLine = true;
	for (int vN = lineStartsAt; vN <= lineEndsAt; vN++) {
		Vertex01* pV = verts.at(vN);
		//prev point
		if (vN == lineStartsAt) {
			//first point
			if (closedLine)
				pV0 = verts.at(lineEndsAt);
			else
				pV0 = NULL;
		}
		else
			pV0 = verts.at(vN - 1);
		//next point
		if (vN == lineEndsAt) {
			//last point
			if (closedLine)
				pV2 = verts.at(lineStartsAt);
			else
				pV2 = NULL;
		}
		else
			pV2 = verts.at(vN + 1);
		//distances to neighbor points
		float distFromPrev = 0;
		float dirFromPrev[3] = { 0,0,0 };
		if (pV0 != NULL) {
			distFromPrev = v3lengthFromTo(pV0->aPos, pV->aPos);
			v3dirFromTo(dirFromPrev, pV0->aPos, pV->aPos);
		}
		float distToNext = 0;
		float dirToNext[3] = { 0,0,0 };
		if (pV2 != NULL) {
			distToNext = v3lengthFromTo(pV->aPos, pV2->aPos);
			v3dirFromTo(dirToNext, pV->aPos, pV2->aPos);
		}
		float distTotal = distFromPrev + distToNext;
		float kPrev = distFromPrev / distTotal;
		float kNext = distToNext / distTotal;
		if (kPrev > kNext * 3)
			v3copy(pV->aNormal, dirFromPrev);
		else if (kNext > kPrev * 3)
			v3copy(pV->aNormal, dirToNext);
		else
			for (int i = 0; i < 3; i++)
				pV->aNormal[i] = kPrev * dirFromPrev[i] + kNext * dirToNext[i];
		vec3_norm(pV->aNormal, pV->aNormal);
	}
	return 1;
}

  • Другое изменение тут (строка 260) – количество точек в DrawJob когда нет треугольников (индексов).

В классе DrawJob новая функция lineWidthIsImportant() и новые параметры в executeDrawJob():

  • uMM – матрица Model’s transform (для вычислений HalfVector-а)
  • uCameraPosition
  • sizeUnitPixelsSize – для пересчета ширины линии при рендринге

12. Заменим DrawJob.h код на:

#pragma once
#include "Material.h"
#include <vector>

struct AttribRef //attribute reference/description
{
	unsigned int glVBOid = 0; //buffer object id
	int offset = 0; //variable's offset inside of VBO's element
	int stride = 0; //Buffer's element size in bytes
};

class DrawJob
{
public:
	Material mt;
	int pointsN = 0; //N of points to draw
	unsigned int glVAOid = 0; //will hold data stream attributes mapping/positions
	unsigned int glEBOid = 0; //Element Buffer Object (vertex indices)

	//common attributes
	AttribRef aPos;
	AttribRef aNormal;
	AttribRef aTuv;
	AttribRef aTuv2; //for normal map
	AttribRef aTangent; //for normal map
	AttribRef aBinormal; //for normal map

	//static arrays (vectors) of all loaded DrawJobs, VBO ids
	static std::vector<DrawJob*> drawJobs;
	static std::vector<unsigned int> buffersIds;
public:
	DrawJob();
	virtual ~DrawJob(); //destructor
	static int cleanUp();
	static int newBufferId();
	int buildVAO() { return buildVAOforShader(this, mt.shaderN); };
	static int buildVAOforShader(DrawJob* pDJ, int shaderN);
	static int attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAttribRef);

	virtual int setDesirableOffsets(int* pStride, int shaderN, int VBOid) { return setDesirableOffsetsForSingleVBO(this, pStride, shaderN, VBOid); };
	static int setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid);

	int execute(float* uMVP, float* uMV, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize = 0, Material* pMt=NULL) { return executeDrawJob(this, uMVP, uMV, uMM, uVectorToLight, uCameraPosition, sizeUnitPixelsSize, pMt); };
	static int executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize = 0, Material* pMt=NULL);
	static bool lineWidthIsImportant(int primitiveType);
};


13. Заменим DrawJob.cpp код на:

#include "DrawJob.h"
#include "platform.h"
#include "utils.h"
#include "Shader.h"
#include "Texture.h"

//static arrays (vectors) of all loaded DrawJobs, VBO ids
std::vector<DrawJob*> DrawJob::drawJobs;
std::vector<unsigned int> DrawJob::buffersIds;

DrawJob::DrawJob() {
	drawJobs.push_back(this);
}
DrawJob::~DrawJob() {
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	if (glVAOid > 0)
		glDeleteVertexArrays(1, &glVAOid);
}
int DrawJob::newBufferId() {
	unsigned int bufferId;
	glGenBuffers(1, &bufferId);
	buffersIds.push_back(bufferId);
	return (int)bufferId;
}
unsigned int activeVBOid;
int DrawJob::buildVAOforShader(DrawJob* pDJ, int shaderN) {
	//delete VAO if exists already
	if (pDJ->glVAOid > 0) {
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glDeleteVertexArrays(1, &(pDJ->glVAOid));
	}
	glGenVertexArrays(1, &pDJ->glVAOid);
	glBindVertexArray(pDJ->glVAOid);

	//open shader descriptor to access variables locations
	Shader* pShader = Shader::shaders.at(pDJ->mt.shaderN);

	activeVBOid = 0;
	attachAttribute(pShader->l_aPos, 3, &pDJ->aPos);
	attachAttribute(pShader->l_aNormal, 3, &pDJ->aNormal);
	attachAttribute(pShader->l_aTuv, 2, &pDJ->aTuv);
	attachAttribute(pShader->l_aTuv2, 2, &pDJ->aTuv2); //for normal map
	attachAttribute(pShader->l_aTangent, 3, &pDJ->aTangent); //for normal map
	attachAttribute(pShader->l_aBinormal, 3, &pDJ->aBinormal); //for normal map

	if (pDJ->glEBOid > 0)
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);

	glBindVertexArray(0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	return 1;
}

int DrawJob::attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAR) {
	if (varLocationInShader < 0)
		return 0; //not used in this shader
	if (pAR->glVBOid == 0) {
		mylog("ERROR in DrawJob::attachAttribute, nk such attribute/VBO\n");
		return -1;
	}
	glEnableVertexAttribArray(varLocationInShader);
	if (activeVBOid != pAR->glVBOid) {
		activeVBOid = pAR->glVBOid;
		//attach input stream data
		glBindBuffer(GL_ARRAY_BUFFER, activeVBOid);
	}
	glVertexAttribPointer(varLocationInShader, attributeSizeInFloats, GL_FLOAT, GL_FALSE, pAR->stride, (void*)(long)pAR->offset);
	return 1;
}

int DrawJob::executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV3x3, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize, Material* pMt) {
	if (pMt == NULL)
		pMt = &(pDJ->mt);
	glBindVertexArray(pDJ->glVAOid);
	Shader* pShader = Shader::shaders.at(pMt->shaderN);
	glUseProgram(pShader->GLid);
	//input uniforms
	glUniformMatrix4fv(pShader->l_uMVP, 1, GL_FALSE, (const GLfloat*)uMVP);
	if (pShader->l_uMV3x3 >= 0)
		glUniformMatrix3fv(pShader->l_uMV3x3, 1, GL_FALSE, (const GLfloat*)uMV3x3);
	if (pShader->l_uMM >= 0)
		glUniformMatrix4fv(pShader->l_uMM, 1, GL_FALSE, (const GLfloat*)uMM);
	if (pShader->l_uVectorToLight >= 0)
		glUniform3fv(pShader->l_uVectorToLight, 1, (const GLfloat*)uVectorToLight);
	if (pShader->l_uCameraPosition >= 0)
		glUniform3fv(pShader->l_uCameraPosition, 1, (const GLfloat*)uCameraPosition);

	//attach textures
	if (pShader->l_uTex0 >= 0) {
		int textureId = Texture::getGLid(pMt->uTex0);
		//pass textureId to shader program
		glActiveTexture(GL_TEXTURE0); // activate the texture unit first before binding texture
		glBindTexture(GL_TEXTURE_2D, textureId);
		// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.    
		glUniform1i(pShader->l_uTex0, 0);
	}
	if (pShader->l_uTex1mask >= 0) {
		int textureId = Texture::getGLid(pMt->uTex1mask);
		//pass textureId to shader program
		glActiveTexture(GL_TEXTURE1); // activate the texture unit first before binding texture
		glBindTexture(GL_TEXTURE_2D, textureId);
		// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1.    
		glUniform1i(pShader->l_uTex1mask, 1);
	}
	if (pShader->l_uTex2nm >= 0) {
		int textureId = Texture::getGLid(pMt->uTex2nm);
		//pass textureId to shader program
		glActiveTexture(GL_TEXTURE2); // activate the texture unit first before binding texture
		glBindTexture(GL_TEXTURE_2D, textureId);
		// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 2.    
		glUniform1i(pShader->l_uTex2nm, 2);
	}
	if (pShader->l_uTex0translateChannelN >= 0) {
		glUniform1i(pShader->l_uTex0translateChannelN, pMt->uTex0translateChannelN);
		if (pShader->l_uTex3 >= 0 && pMt->uTex3 >= 0) {
			int textureId = Texture::getGLid(pMt->uTex3);
			//pass textureId to shader program
			glActiveTexture(GL_TEXTURE3); // activate the texture unit first before binding texture
			glBindTexture(GL_TEXTURE_2D, textureId);
			// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 3.    
			glUniform1i(pShader->l_uTex3, 3);
		}
	}
	//material uniforms
	if (pShader->l_uTex1alphaChannelN >= 0)
		glUniform1i(pShader->l_uTex1alphaChannelN, pMt->uTex1alphaChannelN);
	if (pShader->l_uTex1alphaNegative >= 0)
		glUniform1i(pShader->l_uTex1alphaNegative, pMt->uTex1alphaNegative);
	if (pShader->l_uColor >= 0)
		glUniform4fv(pShader->l_uColor, 1, pMt->uColor.forGL());
	if (pShader->l_uAlphaFactor >= 0)
		glUniform1f(pShader->l_uAlphaFactor, pMt->uAlphaFactor);
	if (pShader->l_uAlphaBlending >= 0)
		glUniform1i(pShader->l_uAlphaBlending, pMt->uAlphaBlending);
	if (pShader->l_uAmbient >= 0)
		glUniform1f(pShader->l_uAmbient, pMt->uAmbient);
	if (pShader->l_uSpecularIntencity >= 0)
		glUniform1f(pShader->l_uSpecularIntencity, pMt->uSpecularIntencity);
	if (pShader->l_uSpecularMinDot >= 0)
		glUniform1f(pShader->l_uSpecularMinDot, pMt->uSpecularMinDot);
	if (pShader->l_uSpecularPowerOf >= 0)
		glUniform1f(pShader->l_uSpecularPowerOf, pMt->uSpecularPowerOf);

	//adjust render settings
	if (pShader->l_uAlphaBlending >= 0 && pMt->uAlphaBlending > 0) {
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
	else
		glDisable(GL_BLEND);

	if (lineWidthIsImportant(pMt->primitiveType)) {
		float lw = sizeUnitPixelsSize * pMt->lineWidth;
		glLineWidth(lw);
	}

	//execute
	if (pDJ->glEBOid == 0) {
		glDrawArrays(pMt->primitiveType, 0, pDJ->pointsN);
	}
	else { //use EBO
		glDrawElements(pMt->primitiveType, pDJ->pointsN, GL_UNSIGNED_SHORT, 0);
	}
	glBindVertexArray(0);
	return 1;
}
int DrawJob::cleanUp() {
	int itemsN = drawJobs.size();
	//delete all drawJobs
	for (int i = 0; i < itemsN; i++) {
		DrawJob* pDJ = drawJobs.at(i);
		delete pDJ;
	}
	drawJobs.clear();
	//delete Buffers
	itemsN = buffersIds.size();
	//delete all buffers
	for (int i = 0; i < itemsN; i++) {
		unsigned int id = buffersIds.at(i);
		glDeleteBuffers(1, &id);
	}
	buffersIds.clear();

	return 1;
}
int DrawJob::setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid) {
	//sets desirable offsets and stride according to given shader needs
	//assuming that we have 1 single VBO
	Shader* pSh = Shader::shaders.at(shaderN);
	int stride = 0;
	pDJ->aPos.offset = 0; //attribute o_aPos, always 0
	stride += sizeof(float) * 3; //aPos size - 3 floats (x,y,z)
	if (pSh->l_aNormal >= 0) { //attribute normal
		pDJ->aNormal.offset = stride;
		stride += sizeof(float) * 3;
	}
	if (pSh->l_aTuv >= 0) { //attribute TUV (texture coordinates)
		pDJ->aTuv.offset = stride; //attribute TUV (texture coordinates)
		stride += sizeof(float) * 2;
	}
	if (pSh->l_aTuv2 >= 0) { //for normal map
		pDJ->aTuv2.offset = stride;
		stride += sizeof(float) * 2;
	}
	if (pSh->l_aTangent >= 0) { //for normal map
		pDJ->aTangent.offset = stride;
		stride += sizeof(float) * 3;
	}
	if (pSh->l_aBinormal >= 0) { //for normal map
		pDJ->aBinormal.offset = stride;
		stride += sizeof(float) * 3;
	}
	*pStride = stride;
	//add stride and VBOid to all attributes
	AttribRef* pAR = NULL;
	pAR = &pDJ->aPos; pAR->glVBOid = VBOid; pAR->stride = stride;
	pAR = &pDJ->aNormal; pAR->glVBOid = VBOid; pAR->stride = stride;
	pAR = &pDJ->aTuv; pAR->glVBOid = VBOid; pAR->stride = stride;
	pAR = &pDJ->aTuv2; pAR->glVBOid = VBOid; pAR->stride = stride;
	pAR = &pDJ->aTangent; pAR->glVBOid = VBOid; pAR->stride = stride;
	pAR = &pDJ->aBinormal; pAR->glVBOid = VBOid; pAR->stride = stride;

	return 1;
}
bool DrawJob::lineWidthIsImportant(int primitiveType) {
	if (primitiveType == GL_TRIANGLES) return false;
	if (primitiveType == GL_TRIANGLE_STRIP) return false;
	if (primitiveType == GL_TRIANGLE_FAN) return false;
	return true;
}


Вернемся к шейдерам. У нас новые переменные: uMM (model matrix) и uCameraPosition вместо uHalfVector. Плюс новый #defineWIRE

14. Заменим Shader.h код на:

#pragma once
#include "platform.h"
#include <string>
#include <vector>

class Shader
{
public:
    //Shader program's individual descriptor:
    unsigned int GLid = -1; // GL shader id
    char shaderType[20] = "";
    //common variables, "l_" for "location"
    //attributes
    int l_aPos; //attribute position (3D coordinates)
    int l_aTuv; //attribute TUV (texture coordinates)
    int l_aTuv2; //attribute TUV (texture coordinates for normal map)
    int l_aNormal; //attribute normal (3D vector)
    int l_aTangent; //for normal map
    int l_aBinormal; //for normal map
    //uniforms
    int l_uMVP; // transform matrix (Model-View-Projection)
    int l_uMV3x3; // Model-View matrix for normals
    int l_uMM; // Model matrix for HalfVector
    int l_uVectorToLight; //required for light
    int l_uCameraPosition; //required for specular light
    //material's properties
    int l_uColor;
    int l_uTex0; //texture id
    int l_uTex1mask; //transparency map
    int l_uTex2nm; //normal map
    int l_uTex3; //texture id
    int l_uTex1alphaChannelN; //alpha channel for mask
    int l_uTex1alphaNegative; //alpha channel negative
    int l_uTex0translateChannelN; //translate tex0 to tex3 by channelN.
    int l_uAlphaFactor; //for semi-transparency
    int l_uAlphaBlending; //for semi-transparency
    //light:
    int l_uAmbient; //ambient light
    //specular light parameters
    int l_uSpecularIntencity;
    int l_uSpecularMinDot;
    int l_uSpecularPowerOf;
    //end of descriptor

    //static array (vector) of all loaded shaders
    static std::vector<Shader*> shaders;

public:
    static int loadShaders();
    static int cleanUp();
    static unsigned int getGLid(int shN) { return shaders.at(shN)->GLid; };
    static int shaderErrorCheck(int shaderId, std::string ref);
    static int programErrorCheck(int programId, std::string ref);
    static int fillLocations(Shader* pSh);

    static int buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS);
    static int linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS);
	static int compileShaderFromFile(const char* filePath, GLenum shaderType);

    static int buildShaderObjectWithDefines(std::string shaderType, std::string definesString, char* sourceVertex, char* sourceFragment);
    static int linkShaderProgramWithDefines(std::string definesString, char* sourceVertex, char* sourceFragment);
    static int compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType);

    static int loadShadersGroup(std::string shaderType, std::string optionsString, char* sourceVertex, char* sourceFragment);
};


15. Заменим Shader.cpp код на:

#include "Shader.h"
#include "platform.h"
#include "utils.h"
#include "FileLoader.h"

extern std::string filesRoot;

//static array (vector) of all loaded shaders
std::vector<Shader*> Shader::shaders;

int Shader::loadShaders() {
    FileLoader* pFLvertex = new FileLoader("/dt/shaders/phong_v.txt");
    FileLoader* pFLfragment = new FileLoader("/dt/shaders/phong_f.txt");
    loadShadersGroup("flat", "FLAT; COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
    loadShadersGroup("phong", "PHONG; COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
    loadShadersGroup("mirror", "PHONG;MIRROR; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
    loadShadersGroup("wire", "WIRE;PHONG; COLOR | TEXTURE", pFLvertex->pData, pFLfragment->pData);
    delete pFLvertex;
    delete pFLfragment;
    //Normal Maps
    pFLvertex = new FileLoader("/dt/shaders/nm_v.txt");
    pFLfragment = new FileLoader("/dt/shaders/nm_f.txt");
    loadShadersGroup("phong", "COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
    loadShadersGroup("mirror", "MIRROR; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
    delete pFLvertex;
    delete pFLfragment;
    return 1;
}

int Shader::buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS) {
    //create shader object
    Shader* pSh = new Shader();
    shaders.push_back(pSh);
    pSh->GLid = linkShaderProgramFromFiles((filesRoot + filePathVertexS).c_str(), (filesRoot + filePathFragmentS).c_str());
    //common variables. If not presented, = -1;
    fillLocations(pSh);

    return (shaders.size() - 1);
}

int Shader::fillLocations(Shader* pSh) {
    //common variables. If not presented, = -1;
    //attributes
    pSh->l_aPos = glGetAttribLocation(pSh->GLid, "aPos"); //attribute position (3D coordinates)
    pSh->l_aNormal = glGetAttribLocation(pSh->GLid, "aNormal"); //attribute normal (3D vector)
    pSh->l_aTangent = glGetAttribLocation(pSh->GLid, "aTangent"); //for normal map
    pSh->l_aBinormal = glGetAttribLocation(pSh->GLid, "aBinormal"); //for normal map
    pSh->l_aTuv = glGetAttribLocation(pSh->GLid, "aTuv"); //attribute TUV (texture coordinates)
    pSh->l_aTuv2 = glGetAttribLocation(pSh->GLid, "aTuv2"); //attribute TUV (texture coordinates)
    //uniforms
    pSh->l_uMVP = glGetUniformLocation(pSh->GLid, "uMVP"); // transform matrix (Model-View-Projection)
    pSh->l_uMV3x3 = glGetUniformLocation(pSh->GLid, "uMV3x3"); // Model-View matrix for normals
    pSh->l_uMM = glGetUniformLocation(pSh->GLid, "uMM"); // Model matrix for HalfVector
    pSh->l_uVectorToLight = glGetUniformLocation(pSh->GLid, "uVectorToLight"); // 
    pSh->l_uCameraPosition = glGetUniformLocation(pSh->GLid, "uCameraPosition"); // required for specular light
    //material's properties
    pSh->l_uColor = glGetUniformLocation(pSh->GLid, "uColor");
    pSh->l_uTex0 = glGetUniformLocation(pSh->GLid, "uTex0"); //texture id
    pSh->l_uTex1mask = glGetUniformLocation(pSh->GLid, "uTex1mask"); //texture id
    pSh->l_uTex2nm = glGetUniformLocation(pSh->GLid, "uTex2nm"); //texture id
    pSh->l_uTex3 = glGetUniformLocation(pSh->GLid, "uTex3"); //texture id
    pSh->l_uTex1alphaChannelN = glGetUniformLocation(pSh->GLid, "uTex1alphaChannelN");
    pSh->l_uTex1alphaNegative = glGetUniformLocation(pSh->GLid, "uTex1alphaNegative");
    pSh->l_uTex0translateChannelN = glGetUniformLocation(pSh->GLid, "uTex0translateChannelN");
    pSh->l_uAlphaFactor = glGetUniformLocation(pSh->GLid, "uAlphaFactor"); // for semi-transparency
    pSh->l_uAlphaBlending = glGetUniformLocation(pSh->GLid, "uAlphaBlending"); // for semi-transparency
    pSh->l_uAmbient = glGetUniformLocation(pSh->GLid, "uAmbient"); // ambient light
    pSh->l_uSpecularIntencity = glGetUniformLocation(pSh->GLid, "uSpecularIntencity"); // 
    pSh->l_uSpecularMinDot = glGetUniformLocation(pSh->GLid, "uSpecularMinDot"); // 
    pSh->l_uSpecularPowerOf = glGetUniformLocation(pSh->GLid, "uSpecularPowerOf"); // 
    return 1;
}
int Shader::cleanUp() {
    int shadersN = shaders.size();
    if (shadersN < 1)
        return -1;
    glUseProgram(0);
    for (int i = 0; i < shadersN; i++) {
        Shader* pSh = shaders.at(i);
        glDeleteProgram(pSh->GLid);
        delete pSh;
    }
    shaders.clear();
    return 1;
}

GLchar infoLog[1024];
int logLength;
int Shader::shaderErrorCheck(int shaderId, std::string ref) {
    //use after glCompileShader()
    if (checkGLerrors(ref) > 0)
        return -1;
    glGetShaderInfoLog(shaderId, 1024, &logLength, infoLog);
    if (logLength == 0)
        return 0;
    mylog("%s shader infoLog:\n%s\n", ref.c_str(), infoLog);
    return -1;
}
int Shader::programErrorCheck(int programId, std::string ref) {
    //use after glLinkProgram()
    if (checkGLerrors(ref) > 0)
        return -1;
    glGetProgramInfoLog(programId, 1024, &logLength, infoLog);
    if (logLength == 0)
        return 0;
    mylog("%s program infoLog:\n%s\n", ref.c_str(), infoLog);
    return -1;
}

int Shader::compileShaderFromFile(const char* filePath, GLenum shaderType) {
    int shaderId = glCreateShader(shaderType);
    FILE* pFile;
    myFopen_s(&pFile, filePath, "rt");
    if (pFile != NULL)
    {
        // obtain file size:
        fseek(pFile, 0, SEEK_END);
        int fSize = ftell(pFile);
        rewind(pFile);
        // size obtained, create buffer
        char* shaderSource = new char[fSize + 1];
        fSize = fread(shaderSource, 1, fSize, pFile);
        shaderSource[fSize] = 0;
        fclose(pFile);
        // source code loaded, compile
        glShaderSource(shaderId, 1, (const GLchar**)&shaderSource, NULL);
        //myglErrorCheck("glShaderSource");
        glCompileShader(shaderId);
        if (shaderErrorCheck(shaderId, "glCompileShader") < 0)
            return -1;
        delete[] shaderSource;
    }
    else {
        mylog("ERROR loading %s\n", filePath);
        return -1;
    }
    return shaderId;
}
int Shader::linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS) {
    int vertexShaderId = compileShaderFromFile(filePathVertexS, GL_VERTEX_SHADER);
    int fragmentShaderId = compileShaderFromFile(filePathFragmentS, GL_FRAGMENT_SHADER);
    int programId = glCreateProgram();
    glAttachShader(programId, vertexShaderId);
    glAttachShader(programId, fragmentShaderId);
    glLinkProgram(programId);
    if (programErrorCheck(programId, "glLinkProgram") < 0)
        return -1;
    //don't need shaders any longer - detach and delete them
    glDetachShader(programId, vertexShaderId);
    glDetachShader(programId, fragmentShaderId);
    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);
    return programId;
}

int Shader::buildShaderObjectWithDefines(std::string shaderType, std::string definesString, char* sourceVertex, char* sourceFragment) {
    //create shader object
    Shader* pSh = new Shader();
    shaders.push_back(pSh);
    myStrcpy_s(pSh->shaderType, 20, shaderType.c_str());

    pSh->GLid = linkShaderProgramWithDefines(definesString, sourceVertex, sourceFragment);
    //common variables. If not presented, = -1;
    fillLocations(pSh);

    return (shaders.size() - 1);
}

int Shader::linkShaderProgramWithDefines(std::string definesString00, char* sourceVertex, char* sourceFragment) {
    //build extended definesString
    bool bUSE_NORMALS = false;
    bool bUSE_TEX0 = false;
    bool bUSE_TUV0 = false;
    if (definesString00.find(" PHONG\n") != std::string::npos)
        bUSE_NORMALS = true;
    if (definesString00.find(" TEXTURE\n") != std::string::npos) {
        bUSE_TEX0 = true;
        bUSE_TUV0 = true;
    }
    if (definesString00.find(" MIRROR\n") != std::string::npos) {
        bUSE_NORMALS = true;
        bUSE_TEX0 = true;
    }
    if (definesString00.find(" OVERMASK\n") != std::string::npos) {
        bUSE_TUV0 = true;
    }
    std::string definesString;
    definesString.assign("#version 320 es\n");
    definesString.append(definesString00);
    if (bUSE_NORMALS)
        definesString.append("#define USE_NORMALS\n");
    if (bUSE_TEX0)
        definesString.append("#define USE_TEX0\n");
    if (bUSE_TUV0)
        definesString.append("#define USE_TUV0\n");

    int vertexShaderId = compileShaderWithDefines(definesString, sourceVertex, GL_VERTEX_SHADER);
    int fragmentShaderId = compileShaderWithDefines(definesString, sourceFragment, GL_FRAGMENT_SHADER);

    int programId = glCreateProgram();
    glAttachShader(programId, vertexShaderId);
    glAttachShader(programId, fragmentShaderId);
    glLinkProgram(programId);
    if (programErrorCheck(programId, "glLinkProgram") < 0)
        return -1;
    //don't need shaders any longer - detach and delete them
    glDetachShader(programId, vertexShaderId);
    glDetachShader(programId, fragmentShaderId);
    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);
    //mylog("linking program\n%s\n", definesString.c_str());
    return programId;
}
int Shader::compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType) {
    int shaderId = glCreateShader(shaderType);
    if (definesString.empty())
        glShaderSource(shaderId, 1, (const GLchar**)&shaderSource, NULL);
    else { //2 strings
        const char* sourceStrings[2];
        sourceStrings[0] = definesString.c_str();
        sourceStrings[1] = shaderSource;
        // source code loaded, compile
        glShaderSource(shaderId, 2, (const GLchar**)sourceStrings, NULL);
    }
    //myglErrorCheck("glShaderSource");
    glCompileShader(shaderId);
    if (shaderErrorCheck(shaderId, "glCompileShader") < 0) {
        mylog("ERROR in compileShader,\n%s\n%s\n", definesString.c_str(), shaderSource);
        return -1;
    }
    return shaderId;
}

int Shader::loadShadersGroup(std::string shaderType, std::string optionsString, char* sourceVertex, char* sourceFragment) {
    struct Terms {
        std::vector<std::string> terms;
        int totalN = 0;
        int currentN = 0;
    };
    std::vector<Terms*> terms;
    std::vector<std::string> termGroups = splitString(optionsString, ";");
    int groupsN = termGroups.size();
    for (int groupN = 0; groupN < groupsN; groupN++) {
        Terms* pTerms = new Terms();
        terms.push_back(pTerms);
        pTerms->terms = splitString(termGroups.at(groupN), "|");
        pTerms->totalN = pTerms->terms.size();
    }
    while (1) {
        std::string definesString = "";
        for (int groupN = 0; groupN < groupsN; groupN++) {
            Terms* pTerms = terms.at(groupN);
            std::string term = pTerms->terms.at(pTerms->currentN);
            if (term.compare("NONE") != 0) {
                definesString.append("#define ");
                definesString.append(term);
                definesString.append("\n");
            }
        }
        int shaderObjN = buildShaderObjectWithDefines(shaderType, definesString, sourceVertex, sourceFragment);
        //go to next terms combo
        bool noMoreOptions = false;
        for (int groupN = groupsN - 1; groupN >= 0; groupN--) {
            Terms* pTerms = terms.at(groupN);
            if (pTerms->currentN < pTerms->totalN - 1) {
                pTerms->currentN++;
                break;
            }
            else { // the level exhausted
                pTerms->currentN = 0;
                //proceed to upper level
                if (groupN == 0) {
                    noMoreOptions = true;
                    break;
                }
            }
        }
        if (noMoreOptions)
            break;
    }
    return 1;
}


И наконец – TheGame. Теперь нам не надо здесь считать uHalfVector, но придется считать sizeUnitPixelsSize (для пересчета ширины линий при рендринге). Плюс – новые параметры в pDJ->execute(..)

16. Заменим TheGame.cpp код на:

#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
#include "ModelLoader.h"

extern std::string filesRoot;
extern float degrees2radians;

std::vector<GameSubj*> TheGame::gameSubjs;

int TheGame::getReady() {
    bExitGame = false;
    Shader::loadShaders();
    glEnable(GL_CULL_FACE);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glDepthMask(GL_TRUE);

    int subjN = ModelLoader::loadModel(&gameSubjs, "/dt/models/misc/marlboro01/root01.txt", "");
    GameSubj* pGS = gameSubjs.at(subjN);
    pGS->name.assign("box1");
    pGS->ownSpeed.setDegrees(0, 2, 0);
    //pGS->ownCoords.setDegrees(0, -90, 0);

    //===== set up camera
    mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
    mainCamera.viewRangeDg = 30;
    mainCamera.stageSize[0] = 80;
    mainCamera.stageSize[1] = 120;
    memcpy(mainCamera.lookAtPoint, pGS->ownCoords.pos, sizeof(float) * 3);
    mainCamera.onScreenResize();

    //===== set up light
    v3set(dirToMainLight, -1, 1, 1);
    vec3_norm(dirToMainLight, dirToMainLight);

    return 1;
}

int TheGame::drawFrame() {
    myPollEvents(); 

    //glClearColor(0.0, 0.0, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;
    //mat4x4_ortho(mProjection, -(float)screenSize[0] / 2, (float)screenSize[0] / 2, -(float)screenSize[1] / 2, (float)screenSize[1] / 2, 100.f, 500.f);
    float nearClip = mainCamera.focusDistance - 50;
    float farClip = mainCamera.focusDistance + 50;
    if (nearClip < 0) nearClip = 0;
    mat4x4_perspective(mProjection, mainCamera.viewRangeDg * degrees2radians, screenAspectRatio, nearClip, farClip);
    mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);
    mViewProjection[1][3] = 0; //keystone effect

    //scan subjects
    int subjsN = gameSubjs.size();
    for (int subjN = 0; subjN < subjsN; subjN++) {
        GameSubj* pGS = gameSubjs.at(subjN);
        //behavior - apply rotation speed
        pGS->moveSubj();
        //prepare subject for rendering
        pGS->buildModelMatrix(pGS);
        //build MVP matrix for given subject
        mat4x4_mul(mMVP, mViewProjection, pGS->ownModelMatrix);
        //build Model-View (rotation) matrix for normals
        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());
        //convert to 3x3 matrix
        float mMV3x3[3][3];
        for (int y = 0; y < 3; y++)
            for (int x = 0; x < 3; x++)
                mMV3x3[y][x] = mMV4x4[y][x];
        //subj's distance from camera
        float cameraSpacePos[4];
        mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS->ownCoords.pos, 1);
        float zDistance = abs(cameraSpacePos[2]);
        float cotangentA = 1.0f / tanf(degrees2radians * mainCamera.viewRangeDg / 2.0);
        float halfScreenVertSizeInUnits = zDistance / cotangentA;
        float sizeUnitPixelsSize = screenSize[1] / 2.0 / halfScreenVertSizeInUnits;
        //render subject
        for (int i = 0; i < pGS->djTotalN; i++) {
            DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
            pDJ->execute((float*)mMVP, *mMV3x3, (float*)pGS->ownModelMatrix, dirToMainLight, mainCamera.ownCoords.pos, sizeUnitPixelsSize, NULL);
        }
    }
    //synchronization
    while (1) {
        long long int currentMillis = getSystemMillis();
        long long int millisSinceLastFrame = currentMillis - lastFrameMillis;
        if (millisSinceLastFrame >= millisPerFrame) {
            lastFrameMillis = currentMillis;
            break;
        }
    }
    mySwapBuffers();
    return 1;
}
int TheGame::cleanUp() {
    int itemsN = gameSubjs.size();
    //delete all UISubjs
    for (int i = 0; i < itemsN; i++) {
        GameSubj* pGS = gameSubjs.at(i);
        delete pGS;
    }
    gameSubjs.clear();
    //clear all other classes
    Texture::cleanUp();
    Shader::cleanUp();
    DrawJob::cleanUp();
    return 1;
}
int TheGame::onScreenResize(int width, int height) {
    if (screenSize[0] == width && screenSize[1] == height)
        return 0;
    screenSize[0] = width;
    screenSize[1] = height;
    screenAspectRatio = (float)width / height;
    glViewport(0, 0, width, height);
    mainCamera.onScreenResize();
    mylog(" screen size %d x %d\n", width, height);
    return 1;
}
int TheGame::run() {
    getReady();
    while (!bExitGame) {
        drawFrame();
    }
    cleanUp();
    return 1;
}
GameSubj* TheGame::newGameSubj(std::string subjClass) {
    return (new GameSubj());
}


17. Компиляция и запуск. Результат:

Теперь – с золотой ленточкой.

Кстати, без по-вертексного HalfVector-а не получилось бы такой натуральной скользящей “искры”.

  • На Андроиде тоже проверено, все работает.

Leave a Reply

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