Глава 37. Оптимизация мешей

Модель полностью готова, но остался еще 1 вопрос:

Сейчас модель состоит из 316 вертексов и 144 треугольников (432 индекса), что не так уж и много по сравнению с 3D редакторами. Но все-таки, откуда СТОЛЬКО-то??

Ну, сама коробка например:

  • 6 сторон, по 4 вертекса каждая = 24 вертекса
  • 12 ребер, по 4 вертекса каждое = 48 вертексов
  • 8 углов, по 3 вертекса каждый = 24 вертекса

Итого – 96 вертексов.

  • Плюс еще 96 для пленки
  • Плюс тисненые и золоченые проекции, 4 вертекса каждая
  • Плюс золотая ленточка (линия), 12 вертексов
  • Плюс 6 a2mesh проекций

Так что, 316 вертексов – вполне оправдано.

Однако, многие из них на самом деле одинаковые. Например, углы, где каждая из 3-х вершин дублируется

  • в самОм угловом треугольнике,
  • плюс в 2-х прилегающих ребрах,
  • плюс в 1 стороне.

То есть, не просто дублируется, а аж в 4-х экземплярах !

Наша следующая задача – выявить и убрать такие повторы.

Сделаем это в классе ModelBuilder1base, в функции buildSingleDrawJob(..).

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


2. Заменим 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*>* pGameSubjs);
	static int rearrangeArraysForDrawJob(std::vector<Vertex01*>* pAllVertices, std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles);
	static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles);
	static int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);
	static int calculateTangentSpace(std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles);
	static int finalizeLine(std::vector<Vertex01*>* pVerts, int lineStartsAt = 0, int lineEndsAt = 0);
	static int optimizeMesh(std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles);
};

  • Новая функция – optimizeMesh(..)

3. Заменим 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*>* pGameSubjs) {
	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 = pGameSubjs->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 = pGameSubjs->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
				pVX->altN = useVertices.size();
				Vertex01* pVX2 = new Vertex01(*pVX);
				useVertices.push_back(pVX2);
				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->vertices, &useVertices, &useTriangles);
			buildSingleDrawJob(pMT, &useVertices, &useTriangles);
			useVerticesN = useVertices.size();
			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*>* pVertices, std::vector<Triangle01*>* pTriangles) {
	int totalVertsN = pVertices->size();
	if (totalVertsN < 1)
		return 0;
	if (DrawJob::lineWidthIsImportant(pMT->primitiveType)) {
		if (strcmp(pMT->shaderType, "wire") == 0)
			finalizeLine(pVertices);
	}
	else {
		optimizeMesh(pVertices, pTriangles);
		totalVertsN = pVertices->size();
	}
	if (pMT->uTex2nm >= 0)
		calculateTangentSpace(pVertices, pTriangles);
	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 = pVertices->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 = pTriangles->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 = pTriangles->at(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(std::vector<Vertex01*>* pAllVertices, std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles) {
	int totalTrianglesN = pUseTriangles->size();
	if (totalTrianglesN < 1)
		return 0;
	//replace triangle original indices by new numbers saved in original vertices altN
	for (int tN = 0; tN < totalTrianglesN; tN++) {
		Triangle01* pTR = pUseTriangles->at(tN);
		for (int i = 0; i < 3; i++) {
			Vertex01* pVX0 = pAllVertices->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*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles) {
	int totalVertsN = pUseVertices->size();
	if (totalVertsN < 1)
		return 0;
	int totalTrianglesN = pUseTriangles->size();
	//assuming that GL_TRIANGLES
	//clear flags
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pV = pUseVertices->at(vN);
		pV->flag = 0;
	}
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = pUseVertices->at(vN);
		if (pVX->flag != 0)
			continue;
		Triangle01* pT = NULL;
		for (int tN = 0; tN < totalTrianglesN; tN++) {
			pT = pUseTriangles->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] = pUseVertices->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 = pUseVertices->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*>* pVerts, int lineStartsAt, int lineEndsAt) {
	if (lineEndsAt <= 0)
		lineEndsAt = pVerts->size() - 1;
	Vertex01* pV0 = pVerts->at(lineStartsAt);
	Vertex01* pV2 = pVerts->at(lineEndsAt);
	bool closedLine = false;
	if (v3match(pV0->aPos, pV2->aPos))
		closedLine = true;
	for (int vN = lineStartsAt; vN <= lineEndsAt; vN++) {
		Vertex01* pV = pVerts->at(vN);
		//prev point
		if (vN == lineStartsAt) {
			//first point
			if (closedLine)
				pV0 = pVerts->at(lineEndsAt);
			else
				pV0 = NULL;
		}
		else
			pV0 = pVerts->at(vN - 1);
		//next point
		if (vN == lineEndsAt) {
			//last point
			if (closedLine)
				pV2 = pVerts->at(lineStartsAt);
			else
				pV2 = NULL;
		}
		else
			pV2 = pVerts->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;
}
int ModelBuilder1base::optimizeMesh(std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles) {
	int trianglesN0 = pTriangles->size();
	if (trianglesN0 == 0)
		return 0;
	int vertsN0 = pVertices->size();
	//clear verts first for comparison
	for (int vN = 0; vN < vertsN0; vN++) {
		Vertex01* pV = pVertices->at(vN);
		myStrcpy_s(pV->marks, 124, "");
		pV->altN = -1;
		pV->flag = 0;
		pV->aTangent[0] = 0;
		//round up
		for (int i = 0; i < 3; i++) {
			pV->aPos[i] = round(pV->aPos[i] * 1000.0) / 1000.0;
			pV->aNormal[i] = round(pV->aNormal[i] * 1000.0) / 1000.0;
		}
 	}
	//find the same verts
	int matchesN = 0;
	for (int vN = 0; vN < vertsN0-1; vN++) {
		Vertex01* pV = pVertices->at(vN);
		if (pV->flag < 0)
			continue;
		for (int vN2 = vN+1; vN2 < vertsN0; vN2++) {
			Vertex01* pV2 = pVertices->at(vN2);
			if (pV2->flag < 0)
				continue;
			if (memcmp(pV, pV2, sizeof(Vertex01)) != 0)
				continue;
			//if here - verts are equal
			pV2->flag = -1;
			matchesN++;
			//change refs in useTriangles from vN2 to vN
			for (int tN = 0; tN < trianglesN0; tN++) {
				Triangle01* pT = pTriangles->at(tN);
				for (int i = 0; i < 3; i++)
					if (pT->idx[i] == vN2)
						pT->idx[i] = vN;
			}
		}
	}

	if (matchesN == 0)
		return 0;
	//unflag all verts
	for (int vN = 0; vN < vertsN0 - 1; vN++) {
		Vertex01* pV = pVertices->at(vN);
		pV->flag = -1;
	}
	//flag verts in use
	for (int tN = 0; tN < trianglesN0; tN++) {
		Triangle01* pT = pTriangles->at(tN);
		for (int i = 0; i < 3; i++) {
			int vN = pT->idx[i];
			Vertex01* pV = pVertices->at(vN);
			pV->flag = 0;
		}
	}
	//save original useVertices copy
	std::vector<Vertex01*> oldVertices;
	for (int vN = 0; vN < vertsN0; vN++) {
		Vertex01* pV = pVertices->at(vN);
		oldVertices.push_back(pV);
	}
	pVertices->clear();
	//copy back only verts in use
	for (int vN = 0; vN < vertsN0; vN++) {
		Vertex01* pV = oldVertices.at(vN);
		if (pV->flag < 0)
			continue;
		pV->altN = pVertices->size();
		pVertices->push_back(pV);
	}
	//re-factor triangles
	rearrangeArraysForDrawJob(&oldVertices, pVertices, pTriangles);
	oldVertices.clear();
	return pVertices->size();
}

  • Сделал неожиданное (для меня) “открытие”: когда передаешь vector как параметр, принимающая функция может обрабатывать/изменять элементы вектора как обычно, что создает иллюзию, что работаешь с самим оригинальным вектором, но нет, это КОПИЯ. И когда изменяешь САМ вектор (добавляешь или удаляешь элементы), то в оригинальном векторе это не отражается. Раньше нам просто не приходилось модифицировать сами вектора в таких функциях. Поэтому, вместо передачи векторов я переписал на передачу адресов векторов. Так что, optimizeMesh(..) – не единственное изменение здесь.

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

Картинка та же, но теперь вместо 316 вертексов у нас всего 219, на 31% меньше, даже лучше чем я ожидал.

Подробности:

  • Сама коробка + акцизная марка (это 1 DrawJob): изначально 108 вершин, из них 42 повтора
  • Золоченые принты, 5 проекций: 20 вершин, повторов нет
  • Герб, 2 проекции: 8 вершин, повторов нет
  • Тисненый принт Marlboro, 2 проекции: 8 вершин, повторов нет
  • Стык, 5 проекций: изначально 64 вершины, 9 повторов
  • Пломба (линия): 12 вершин, повторов нет
  • Пленка, 4 стороны: изначально 32 вершины, 14 повторов
  • Пленка, верх и низ (с normal maps): изначально 64 вершины, 32 повтора

Не по теме:

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

Впечатляет, правда?


Leave a Reply

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