Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the antispam-bee domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/ruwritingagame/public_html/wp-includes/functions.php on line 6121
Глава 36. “Стеклянный” эффект – Игра в Написание Игры

Глава 36. “Стеклянный” эффект

Модель почти готова. Не хватает только:

  • Слюдяной пленки
  • и Акцизной марки

Тут нам даже не придется писать новых шейдеров. Для пленки мы возьмем наш “зеркальный” шейдер (как для позолоченных принтов) с белым шумом в качестве главной текстуры (uTex0) и “странслируем” ее в полупрозрачную 8×1 текстуру glass01.bmp (uTex3), имитирующую стеклянное отражение. Это просто белая прозрачная текстура с 1 НЕпрозрачным пикселем (сгенерирована программно).

1. Сгрузим glass01.bmp файл здесь

и сохраним его в C:\CPP\engine\dt\common\img\materials\glass01.bmp


Обновленный model descriptor:

2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (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" zBufferUpdate=no />
<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 >
//clear-film
<texture_as="glass" src="/dt/common/img/materials/glass01.bmp" />
<texture_as="whitenoise2" src="/dt/common/img/whitenoise/wn64_blur1.bmp"/>
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<group>
	<mt_type="mirror" uAlphaFactor=0.9 uTex0_use="whitenoise2" uTex0translateChannelN=1 uTex3_use="glass" />
		<a="front h,back h,right,left" />
	<mt_type="mirror" uAlphaFactor=0.9 uTex0_use="whitenoise2" uTex0translateChannelN=1 uTex3_use="glass" uTex2nm_use="tx0" />
		<a="top all" xywh2nm="724,420,223,90"/>
		<a="bottom all" xywh2nm="724,420,223,90" mark="film_bottom"/>
</group sizeD="0.2,0.1,0.2" >
//Excise stamp
<mt_type="phong" uTex0_use="tx0" />
	<a2mesh wh="22,13" xywh="916,3,100,57" flip=180 all markedAs="film_bottom" onThe="bottom" pxyz="-15,4,0" az=-5 detachBy=0.05 />

Новый код – с 57-й строки.

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

  • Для верхней и нижней проекций мы также используем normal map (строка 64), прямоугольное голубое изображение внизу справа.
  • Для акцизной марки мы использовали не “mt_adjust” как раньше, а новый material (строка 69).
  • В 70-й строке, в a2mesh, новый параметр – detachBy=0.05. Это инструкция ModelLoader-у сдвинуть вырезанный фрагмент чуть-чуть от “родительской” поверхности чтобы избежать взаимо-наложения с поверхностью пленки.

Поскольку у нас появились полу-прозрачные поверхности, это поднимает новую проблему – прозрачные поверхности не должны переписывать (update) z-buffer на случай если придется что-то рендрить под (за) такой поверхностью. Для этого в классе Material добавлены 2 новые переменные:

  • zBuffer – использовать ли z-buffer testing или нет. По умолчанию – да.
  • zBufferUpdate – обновлять ли z-buffer или нет. По умолчанию – да.

3. Заменим 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;
	int zBuffer = 1;
	int zBufferUpdate = 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);
};


Эти новые переменные будут использоваться классом DrawJob для под-настройки рендринга. Функция – executeDrawJob(..).

4. Заменим 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 (lineWidthIsImportant(pMt->primitiveType)) {
		float lw = sizeUnitPixelsSize * pMt->lineWidth;
		glLineWidth(lw);
	}

	if (pMt->zBuffer > 0) {
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
	}
	else
		glDisable(GL_DEPTH_TEST);

	if (pMt->zBufferUpdate > 0)
		glDepthMask(GL_TRUE);
	else
		glDepthMask(GL_FALSE);

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

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


Чтение/установки – в ModelLoader-е, в функции fillProps_mt(..).

5. Заменим 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);
	setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
	if (pMT->uAlphaFactor < 1)
		pMT->uAlphaBlending = 1;
	setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
	if (pMT->uAlphaBlending > 0)
		pMT->zBufferUpdate = 0;
	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);
	setIntBoolValue(&pMT->zBuffer, "zBuffer", tagStr);
	if (pMT->zBuffer < 1)
		pMT->zBufferUpdate = 0;
	setIntBoolValue(&pMT->zBufferUpdate, "zBufferUpdate", 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);
	}

	float detachBy =0;
	setFloatValue(&detachBy, "detachBy", tagStr);
	if (detachBy != 0) {
		mat4x4 mx;
		mat4x4_translate(mx, 0, 0, detachBy);
		gt.transformFlaggedMx(&vx2, &mx);
	}
	//move/rotate back
	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;
}

  • Другое изменение тут – обработка параметра detachBy для тага a2mesh (строки 562-568).

TheGame.cpp тоже задет. Поскольку мы теперь отключаем z-buffer для каких-то DrawJobs, теперь нам надо убедиться, что он включен прежде чем чистить z-buffer (строка 50).

6.Заменим 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(1.5, 1, 0.5);
    pGS->ownCoords.setDegrees(0,30, 0);

    //===== set up camera
    mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
    mainCamera.viewRangeDg = 20;
    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);
    glDepthMask(GL_TRUE);
    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 - 55;
    float farClip = mainCamera.focusDistance + 55;
    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;
}
GameSubj* TheGame::newGameSubj(std::string subjClass) {
    return (new GameSubj());
}
int TheGame::run() {
    getReady();
    while (!bExitGame) {
        drawFrame();
    }
    cleanUp();
    return 1;
}

  • Другое изменение тут – mainCamera.viewRangeDg = 20; 30 градусов было широковато, слишком сильная перспектива, 20 градусов выглядит натуральнее.
  • И новая скорость вращения (строка 29).

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

До:

(просто проекции)

После:

С золочеными и тиснеными принтами, со стыком пачка-крышка как normal map, с золотой лентой/пломбой/линией.

А теперь еще и с пленкой и с акцизной маркой.

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

Теперь модель полностью готова. Наконец, могу спокойно эту пачку выкурить. Или оставить ее на память о Проекте?


Leave a Reply

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