Модель полностью готова, но остался еще 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 повтора
Не по теме:
Пока трудился над этой главой, запустил одну пленку (без коробки, для отладки). Думаю, заслуживает отдельного видео:
Впечатляет, правда?