На всякий случай: мы все еще пытаемся “нарисовать” стык (щель) между пачкой и крышкой, как небольшую normal map приложенную к части существующей текстурированной поверхности. Как-то так:
В предыдущей главе мы смогли пометить и переместить “marked vertices/triangles group“. Теперь нам надо выбрать одну (в этом примере – “box_right”), клонировать ее, развернуть и сдвинуть лицом к нам:
Затем – вырезать из этой поверхности белый прямоугольник.
Мы просканируем треугольники меша (поверхности) и построим пересечения этих треугольников с нашим белым прямоугольником. И треугольник, и прямоугольник – это полигоны. То есть мы говорим о пересечении полигонов.
- Пересечение красного треугольника с белым прямоугольником – 4-х-вершинный полигон (2 треугольника).
- Пересечение левого синего треугольника – 5-и-вершинный полигон (3 треугольника).
Новые концепции здесь: Полигоны и Пересечение полигонов. Полигон – это вобщем массив Ребер полигона (Polygon Ribs).
Теперь – реализация:
Windows
1. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
2. Под modeler добавим новый header file PolygonRib.h
Location: C:\CPP\engine\modeler
Код:
#pragma once
#include "Vertex01.h"
#include <vector>
class PolygonRib
{
public:
int i0;
int i1;
float* p0; //rib p0
float* p1; //rib p1
//line equation
float a_slope = 0; //a
float b_intercept = 0; //b
bool isVertical = false;
float x_vertical = 0;
bool isHorizontal = false;
//direction to "inner" side
float xDirIn = 0;
float yDirIn = 0;
public:
PolygonRib(std::vector<Vertex01*>* pVxSrc, int idx0);
static bool matchingLines(PolygonRib* pRibFrame, PolygonRib* pRibSrc);
static bool parallelLines(PolygonRib* pRibFrame, PolygonRib* pRibSrc);
};
3. Под modeler добавим новый C++ file PolygonRib.cpp
Location: C:\CPP\engine\modeler
Код:
#include "PolygonRib.h"
PolygonRib::PolygonRib(std::vector<Vertex01*>* pVxSrc, int idx0) {
//2 points
i0 = idx0;
p0 = pVxSrc->at(idx0)->aPos;
int ribsN = pVxSrc->size();
int idx1 = (idx0 + 1) % ribsN;
i1 = idx1;
p1 = pVxSrc->at(idx1)->aPos;
//3-rd "inner" ref point
float* p2;
int idx2 = (idx0 + 2) % ribsN;
p2 = pVxSrc->at(idx2)->aPos;
//find line equation
if (p0[0] == p1[0]) {
isVertical = true;
x_vertical = p0[0];
//"inner" side
if (p2[0] < x_vertical)
xDirIn = -1;
else
xDirIn = 1;
}
else if (p0[1] == p1[1]) {
isHorizontal = true;
a_slope = 0;
b_intercept = p0[1];
//"inner" side
if (p2[1] < b_intercept)
yDirIn = -1;
else
yDirIn = 1;
}
else {
a_slope = (p1[1]-p0[1]) / (p1[0] - p0[0]);
b_intercept = p0[1] - a_slope * p0[0];
//"inner" side
float y = a_slope * p2[0] + b_intercept;
if(p2[1] < y)
yDirIn = -1;
else
yDirIn = 1;
float x = (p2[1] - b_intercept) / a_slope;
if (p2[0] < x)
xDirIn = -1;
else
xDirIn = 1;
}
}
bool PolygonRib::matchingLines(PolygonRib* pRibFrame, PolygonRib* pRibSrc) {
if (!parallelLines(pRibFrame, pRibSrc))
return false;
if (pRibFrame->b_intercept != pRibSrc->b_intercept)
return false;
if (pRibFrame->x_vertical != pRibSrc->x_vertical)
return false;
return true;
}
bool PolygonRib::parallelLines(PolygonRib* pRibFrame, PolygonRib* pRibSrc) {
if (pRibFrame->isVertical != pRibSrc->isVertical)
return false;
if (pRibFrame->a_slope != pRibSrc->a_slope)
return false;
return true;
}
4. Под modeler добавим новый header file Polygon.h
Location: C:\CPP\engine\modeler
Код:
#pragma once
#include "Vertex01.h"
#include "Triangle01.h"
#include "PolygonRib.h"
#include <vector>
class Polygon
{
public:
std::vector<Vertex01*> vertices;
std::vector<PolygonRib*> ribs;
std::vector<Triangle01*> triangles;
float normal[3] = {0,0,0};
int ribsN = 0;
//bounding box
float bbMin[3] = { 0,0,0 };
float bbMax[3] = { 0,0,0 };
public:
virtual ~Polygon() { clearAll(this); };
static void clearAll(Polygon* pPL);
static int addVertex(Polygon* pPL, Vertex01* pV);
static int addVertex(Polygon* pPL, float x, float y, float z);
static int finalizePolygon(Polygon* pPL);
static int setTriangle(Polygon* pPL, Triangle01* pT, std::vector<Vertex01*>* pVxSrc);
static int setRectangle(Polygon* pPL, float w, float h);
static int xyIntersection(Polygon* pDst, Polygon* pFrame, Polygon* pSrc);
static int addLinesIntersection(Polygon* pDst, Polygon* pFrame, int ribNframe, Polygon* pSrc, int ribNsrc);
static bool dotFits(float* p0, PolygonRib* pPR);
static bool dotFits(float* p0, Polygon* pPL);
static bool correctSide(float* p0, PolygonRib* pPR);
static int addVert(Polygon* pDst, float* p0, Vertex01* pV0, Vertex01* pV1);
static int addVert(Polygon * pDst, float* p0, Polygon * pSrc);
static int buildTriangles(Polygon* pPL);
};
5. Под modeler добавим новый C++ file Polygon.cpp
Location: C:\CPP\engine\modeler
Код:
#include "Polygon.h"
#include "linmath.h"
#include "utils.h"
#include "platform.h"
#include <algorithm>
extern float radians2degrees;
void Polygon::clearAll(Polygon* pPL) {
for (int i = pPL->vertices.size() - 1; i >= 0; i--)
delete pPL->vertices.at(i);
pPL->vertices.clear();
for (int i = pPL->ribs.size() - 1; i >= 0; i--)
delete pPL->ribs.at(i);
pPL->ribs.clear();
for (int i = pPL->triangles.size() - 1; i >= 0; i--)
delete pPL->triangles.at(i);
pPL->triangles.clear();
}
int Polygon::addVertex(Polygon* pPL, float x, float y, float z) {
Vertex01* pV = new Vertex01();
pV->aPos[0] = x;
pV->aPos[1] = y;
pV->aPos[2] = z;
pPL->vertices.push_back(pV);
return 1;
}
int Polygon::addVertex(Polygon* pPL, Vertex01* pV0) {
Vertex01* pV = new Vertex01(*pV0);
pPL->vertices.push_back(pV);
//mylog("====Adding vertexs %f x %f\n",pV->aPos[0], pV->aPos[1]);
return 1;
}
int Polygon::setTriangle(Polygon* pPL, Triangle01* pT, std::vector<Vertex01*>* pVxSrc) {
clearAll(pPL);
for (int i = 0; i < 3; i++) {
int vN = pT->idx[i];
Vertex01* pV = pVxSrc->at(vN);
addVertex(pPL, pV);
}
finalizePolygon(pPL);
return 1;
}
int Polygon::setRectangle(Polygon* pPL, float w, float h) {
clearAll(pPL);
w /= 2;
h /= 2;
//CCW
addVertex(pPL, -w, h, 0); //NW
addVertex(pPL, -w, -h, 0); //SW
addVertex(pPL, w, -h, 0); //SE
addVertex(pPL, w, h, 0); //NE
finalizePolygon(pPL);
return 1;
}
int Polygon::finalizePolygon(Polygon* pPL) {
pPL->ribsN = pPL->vertices.size();
for (int i = 0; i < pPL->ribsN; i++) {
PolygonRib* pPR = new PolygonRib(&pPL->vertices,i);
pPL->ribs.push_back(pPR);
}
//calculate polygon's normal
float v0[3];
float v2[3];
for (int i = 0; i < 3; i++) {
v0[i] = pPL->vertices.at(1)->aPos[i] - pPL->vertices.at(0)->aPos[i];
v2[i] = pPL->vertices.at(2)->aPos[i] - pPL->vertices.at(0)->aPos[i];
}
vec3_mul_cross(pPL->normal, v0, v2);
vec3_norm(pPL->normal, pPL->normal);
//bounding box
Vertex01* pV = pPL->vertices.at(0);
v3copy(pPL->bbMin, pV->aPos);
v3copy(pPL->bbMax, pV->aPos);
for (int vN = pPL->vertices.size() - 1; vN >= 1; vN--) {
pV = pPL->vertices.at(vN);
for (int i = 0; i < 3; i++) {
if (pPL->bbMin[i] > pV->aPos[i])
pPL->bbMin[i] = pV->aPos[i];
if (pPL->bbMax[i] < pV->aPos[i])
pPL->bbMax[i] = pV->aPos[i];
}
}
return 1;
}
int Polygon::addLinesIntersection(Polygon* pDst, Polygon* pFrame, int ribNframe, Polygon* pSrc, int ribNsrc) {
PolygonRib* pRibFrame = pFrame->ribs.at(ribNframe);
PolygonRib* pRibSrc = pSrc->ribs.at(ribNsrc);
/*
mylog("==addLinesIntersection\n");
mylog(" fr %f x %f to %f x %f v=%d h=%d\n", pRibFrame->p0[0], pRibFrame->p0[1], pRibFrame->p1[0], pRibFrame->p1[1], pRibFrame->isVertical, pRibFrame->isHorizontal);
mylog(" tr %f x %f to %f x %f v=%d h=%d\n", pRibSrc->p0[0], pRibSrc->p0[1], pRibSrc->p1[0], pRibSrc->p1[1], pRibSrc->isVertical, pRibSrc->isHorizontal);
*/
if (PolygonRib::matchingLines(pRibFrame, pRibSrc)) {
Vertex01* pV0 = pSrc->vertices.at(pRibSrc->i0);
Vertex01* pV1 = pSrc->vertices.at(pRibSrc->i1);
int dstVertsN0 = pDst->vertices.size();
if (dotFits(pRibFrame->p0, pRibSrc))
addVert(pDst, pRibFrame->p0, pV0,pV1);
if (dotFits(pRibFrame->p1, pRibSrc))
addVert(pDst, pRibFrame->p1, pV0, pV1);
if (dotFits(pRibSrc->p0, pRibFrame))
addVertex(pDst, pV0);
if (dotFits(pRibSrc->p1, pRibFrame))
addVertex(pDst, pV1);
//mylog(" lines are identical\n");
return pDst->vertices.size()- dstVertsN0;
}
if (PolygonRib::parallelLines(pRibFrame, pRibSrc)) {
//mylog(" lines are parallel\n");
return 0;
}
//find lines intersection, assuming lines are not parallel
float x,y;
if (pRibFrame->isVertical) {
x = pRibFrame->p0[0];
y = pRibSrc->a_slope * x + pRibSrc->b_intercept;
}
else { //pRibFrame not vertical
if (pRibSrc->isVertical) {
x = pRibSrc->p0[0];
y = pRibFrame->a_slope * x + pRibFrame->b_intercept;
}
else { //both lines are "normal"
x = (pRibSrc->b_intercept - pRibFrame->b_intercept) / (pRibFrame->a_slope - pRibSrc->a_slope);
y = pRibFrame->a_slope * x + pRibFrame->b_intercept;
}
}
//check if belongs to both PolygonRibs
float xy[2];
xy[0] = x;
xy[1] = y;
if (!dotFits(xy, pRibFrame))
return 0;
if (!dotFits(xy, pRibSrc))
return 0;
addVert(pDst, xy, pSrc->vertices.at(pRibSrc->i0), pSrc->vertices.at(pRibSrc->i1));
return 1;
}
bool Polygon::correctSide(float* p0, PolygonRib* pPR) {
if (pPR->isVertical)
if ((p0[0] - pPR->x_vertical) * pPR->xDirIn < 0)
return false;
if (pPR->isHorizontal)
if ((p0[1] - pPR->b_intercept) * pPR->yDirIn < 0)
return false;
float y = pPR->a_slope * p0[0] + pPR->b_intercept;
if ((p0[1] - y) * pPR->yDirIn < 0)
return false;
return true;
}
int Polygon::addVert(Polygon* pDst, float* p0, Vertex01* pV0, Vertex01* pV1) {
float d[2];
for (int i = 0; i < 2; i++)
d[i] = pV0->aPos[i] - p0[i];
float dist2v0 = v3lengthXY(d);
if (dist2v0 == 0)
return addVertex(pDst, pV0);
for (int i = 0; i < 2; i++)
d[i] = pV1->aPos[i] - p0[i];
float dist2v1 = v3lengthXY(d);
if (dist2v1 == 0)
return addVertex(pDst, pV1);
//if here - find mid values
float k0 = dist2v1 / (dist2v0 + dist2v1);
float k1 = dist2v0 / (dist2v0 + dist2v1);
Vertex01* pVx = new Vertex01(*pV0);
pVx->aPos[0] = p0[0];
pVx->aPos[1] = p0[1];
pVx->aPos[2] = k0 * pV0->aPos[2] + k1 * pV1->aPos[2];
for (int i = 0; i < 3; i++)
pVx->aNormal[i] = k0 * pV0->aNormal[i] + k1 * pV1->aNormal[i];
for (int i = 0; i < 2; i++)
pVx->aTuv[i] = k0 * pV0->aTuv[i] + k1 * pV1->aTuv[i];
for (int i = 0; i < 2; i++)
pVx->aTuv2[i] = k0 * pV0->aTuv2[i] + k1 * pV1->aTuv2[i];
addVertex(pDst, pVx);
delete pVx;
return 0;
}
int Polygon::xyIntersection(Polygon* pDst, Polygon* pFrame, Polygon* pSrc) {
//check bounding boxes, XY only
for (int i = 0; i < 2; i++) {
if (pFrame->bbMin[i] > pSrc->bbMax[i])
return 0;
if (pFrame->bbMax[i] < pSrc->bbMin[i])
return 0;
}
//compare normals
if (v3dotProduct(pFrame->normal, pSrc->normal) <= 0)
return 0;
/*
mylog(">>>pFrame %fx%f to %fx%f to %fx%f \n",
pFrame->vertices.at(0)->aPos[0], pFrame->vertices.at(0)->aPos[1],
pFrame->vertices.at(1)->aPos[0], pFrame->vertices.at(1)->aPos[1],
pFrame->vertices.at(2)->aPos[0], pFrame->vertices.at(2)->aPos[1]
);
mylog(" pSrc %fx%f to %fx%f to %fx%f \n",
pSrc->vertices.at(0)->aPos[0], pSrc->vertices.at(0)->aPos[1],
pSrc->vertices.at(1)->aPos[0], pSrc->vertices.at(1)->aPos[1],
pSrc->vertices.at(2)->aPos[0], pSrc->vertices.at(2)->aPos[1]
);
mylog("---SrcVerts\n");
*/
//if here - have overlap
int addedSrcVerts = 0;
for (int vN = 0; vN < pSrc->ribsN; vN++) {
Vertex01* pV = pSrc->vertices.at(vN);
if (dotFits(pV->aPos, pFrame))
addedSrcVerts += addVertex(pDst, pV);
}
if (addedSrcVerts == pSrc->ribsN)
return addedSrcVerts;
//mylog("---FrameVerts\n");
int addedFrameVerts = 0;
for (int vN = 0; vN < pFrame->ribsN; vN++) {
Vertex01* pV = pFrame->vertices.at(vN);
if (dotFits(pV->aPos, pSrc)) {
int frameVerts = addVert(pDst, pV->aPos, pSrc);
addedFrameVerts += frameVerts;
}
}
if (addedFrameVerts == pFrame->ribsN)
return addedFrameVerts;
//mylog("---CrossVerts\n");
//check ribs intersections
int addedCrossVerts = 0;
for (int ribNframe = 0; ribNframe < pFrame->ribsN; ribNframe++) {
for (int ribNsrc = 0; ribNsrc < pSrc->ribsN; ribNsrc++) {
int crossVerts = addLinesIntersection(pDst, pFrame, ribNframe, pSrc, ribNsrc);
addedCrossVerts += crossVerts;
}
}
return (addedSrcVerts + addedFrameVerts + addedCrossVerts);
}
bool Polygon::dotFits(float* p0, PolygonRib* pPR) {
//mylog("dotFits Rib %f x %f vs %f x %f to %f x %f\n", p0[0], p0[1], pPR->p0[0], pPR->p0[1], pPR->p1[0], pPR->p1[1]);
//assuming that p0 is on the line
int dir0;
int dir1;
if (pPR->isVertical) {
if (pPR->p0[1] == p0[1])
return true;
else if (pPR->p0[1] < p0[1])
dir0 = -1;
else
dir0 = 1;
if (pPR->p1[1] == p0[1])
return true;
else if (pPR->p1[1] < p0[1])
dir1 = -1;
else
dir1 = 1;
}
else{ //"normal" line
if (pPR->p0[0] == p0[0])
return true;
else if (pPR->p0[0] < p0[0])
dir0 = -1;
else
dir0 = 1;
if (pPR->p1[0] == p0[0])
return true;
else if (pPR->p1[0] < p0[0])
dir1 = -1;
else
dir1 = 1;
}
//mylog(" fits?=%d\n", !(dir0 == dir1));
if (dir0 == dir1)
return false;
return true;
}
bool Polygon::dotFits(float* p0, Polygon* pPL) {
//mylog("dotFits Polygon %f x %f\n",p0[0],p0[1]);
for (int i = 0; i < pPL->ribsN; i++)
if (!correctSide(p0, pPL->ribs.at(i))) {
//mylog(" don't Fit side %f x %f to %f x %f\n", pPL->ribs.at(i)->p0[0], pPL->ribs.at(i)->p0[1], pPL->ribs.at(i)->p1[0], pPL->ribs.at(i)->p1[1]);
return false;
}
//mylog(" dotFits!\n");
return true;
}
int Polygon::buildTriangles(Polygon* pPL) {
int vertsN = pPL->vertices.size();
//mid point coords
float p0[2] = { 0,0 };
for (int vN = 0; vN < vertsN; vN++) {
float* p1 = pPL->vertices.at(vN)->aPos;
p0[0] += p1[0];
p0[1] += p1[1];
}
p0[0] /= vertsN;
p0[1] /= vertsN;
for (int vN = 0; vN < vertsN; vN++) {
float* p1 = pPL->vertices.at(vN)->aPos;
float v1[3] ={0,0,0};
v1[0] = p1[0] - p0[0];
v1[1] = p1[1] - p0[1];
float az = -atan2f(v1[0], v1[1]) * radians2degrees;
//aTangent is not used at this point, ok to use it to store az
pPL->vertices.at(vN)->aTangent[0] = az;
}
//sort vertices
std::sort(pPL->vertices.begin(), pPL->vertices.end(),
[](Vertex01* pV0, Vertex01* pV1) {
return pV0->aTangent[0] > pV1->aTangent[0]; });
//check for redundancy
for (int vN = pPL->vertices.size() - 1; vN > 0; vN--) {
Vertex01* pV = pPL->vertices.at(vN);
Vertex01* pVprev = pPL->vertices.at(vN-1);
if (pV->aTangent[0] == pVprev->aTangent[0]) {
delete pV;
pPL->vertices.erase(pPL->vertices.begin() + vN);
}
}
pPL->ribsN = pPL->vertices.size();
//build triangles
Vertex01* pV = pPL->vertices.at(0);
for (int vN = 2; vN < pPL->ribsN; vN++) {
Triangle01* pTR = new Triangle01();
pPL->triangles.push_back(pTR);
pTR->idx[0] = 0;
pTR->idx[1] = vN;
pTR->idx[2] = vN - 1;
pTR->subjN = pV->subjN;
pTR->materialN = pV->materialN;
//mark
myStrcpy_s(pTR->marks, 124, pV->marks);
}
return 1;
}
int Polygon::addVert(Polygon* pDst, float* p0, Polygon* pSrc) {
//check where horizontal line drawn through p0 crosses polygon's ribs
Vertex01 vx0;
Vertex01 vx1;
int vxN = 0;
for (int ribN = 0; ribN < pSrc->ribsN; ribN++) {
PolygonRib* pPR = pSrc->ribs.at(ribN);
if (pPR->isHorizontal)
continue;
float p1[2];
p1[1] = p0[1];
if (pPR->isVertical)
p1[0] = pPR->x_vertical;
else
p1[0] = (p1[1] - pPR->b_intercept) / pPR->a_slope;
if (!dotFits(p1, pPR))
continue;
//if here - 1 intersection found
Vertex01* pVdst = &vx0;
if(vxN > 0)
pVdst = &vx1;
Vertex01* pV0src = pSrc->vertices.at(pPR->i0);
Vertex01* pV1src = pSrc->vertices.at(pPR->i1);
memcpy(pVdst, pV0src, sizeof(Vertex01));
float d[2];
for (int i = 0; i < 2; i++)
d[i] = pV0src->aPos[i] - p1[i];
float dist2v0 = v3lengthXY(d);
if (dist2v0 == 0)
memcpy(pVdst, pV0src, sizeof(Vertex01));
else {
for (int i = 0; i < 2; i++)
d[i] = pV1src->aPos[i] - p1[i];
float dist2v1 = v3lengthXY(d);
if (dist2v1 == 0)
memcpy(pVdst, pV1src, sizeof(Vertex01));
else {
//if here - find mid values
float k0 = dist2v1 / (dist2v0 + dist2v1);
float k1 = dist2v0 / (dist2v0 + dist2v1);
pVdst->aPos[0] = p1[0];
pVdst->aPos[1] = p1[1];
pVdst->aPos[2] = k0 * pV0src->aPos[2] + k1 * pV1src->aPos[2];
for (int i = 0; i < 3; i++)
pVdst->aNormal[i] = k0 * pV0src->aNormal[i] + k1 * pV1src->aNormal[i];
for (int i = 0; i < 2; i++)
pVdst->aTuv[i] = k0 * pV0src->aTuv[i] + k1 * pV1src->aTuv[i];
for (int i = 0; i < 2; i++)
pVdst->aTuv2[i] = k0 * pV0src->aTuv2[i] + k1 * pV1src->aTuv2[i];
}
}
vxN++;
if (vxN > 1)
break;
}
addVert(pDst, p0, &vx0, &vx1);
return 1;
}
Теперь – model description.
6. Копируем нижеследующий код в Текстовый редактор и сохраняем (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>
<a2mesh wh="50,2" all markedAs="box_right" onThe="right" py=24.6 az=31 />
<a2mesh wh="50,2" all markedAs="box_left" onThe="left" py=24.6 az=-31 />
<a2mesh wh="53,2" all markedAs="box_front" py=17.8 />
<a2mesh wh="6 ,2" all markedAs="box_back" onThe="back" py=31.5 px=23.5 />
<a2mesh wh="6 ,2" all markedAs="box_back" onThe="back" py=31.5 px=-23.5 />
</group sizeD="0.1,0,0.1">
- Новый код – со строки 32.
- У нас новые таги “a2mesh“. Это как “a” (apply), только “apply to mesh”.
Чтобы их читать и отрабатывать, в ModelLoader-е – новый функционал.
7. Заменим ModelLoader.h код на:
#pragma once
#include "XMLparser.h"
#include "ModelBuilder.h"
#include "GroupTransform.h"
class ModelLoader : public XMLparser
{
public:
ModelBuilder* pModelBuilder = NULL;
bool ownModelBuilder = false;
std::vector<GameSubj*>* pSubjsVector = NULL;
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(subjN);
};
virtual ~ModelLoader() {
if (!ownModelBuilder)
return;
pModelBuilder->buildDrawJobs(*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);
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);
};
- Новая функция здесь – processTag_a2mesh(..)
Реализация:
8. Заменим 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;
std::string varName = txName + "_use";
if (setValueFromIntHashMap(pInt, pMB->texturesHashMap, varName, pML->currentTag) == 0) {
//the texture is not in hash table
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);
}
}
return 1;
}
int ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {
setTexture(pML, &pMT->uTex0, "uTex0");
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);
}
//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);
pML->pModelBuilder->useMaterial(pMT);
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.find("mt_") == 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;
return fillProps_mt(&mt, pML->currentTag, pML);
}
if (pML->tagName.find("/mt_") == 0) {
//restore previous material
if (pMB->materialsStack.size() > 0) {
pMB->usingMaterialN = pMB->materialsStack.back();
pMB->materialsStack.pop_back();
}
return 1;
}
if (pML->tagName.compare("vs") == 0) {
//sets virtual shape
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->closedTag) {
if (pMB->pCurrentVShape != NULL)
delete pMB->pCurrentVShape;
}
else { //open tag
//save previous vshape in stack
if (pMB->pCurrentVShape != NULL)
pMB->vShapesStack.push_back(pMB->pCurrentVShape);
}
pMB->pCurrentVShape = new VirtualShape();
fillProps_vs(pMB->pCurrentVShape, pML->currentTag);
return 1;
}
if (pML->tagName.compare("/vs") == 0) {
//restore previous virtual shape
if (pMB->vShapesStack.size() > 0) {
if (pMB->pCurrentVShape != NULL)
delete(pMB->pCurrentVShape);
pMB->pCurrentVShape = pMB->vShapesStack.back();
pMB->vShapesStack.pop_back();
}
return 1;
}
if (pML->tagName.compare("group") == 0) {
std::string notAllowed[] = { "pxyz","axyz","align","headTo" };
int notAllowedLn = sizeof(notAllowed) / sizeof(notAllowed[0]);
for (int i = 0; i < notAllowedLn; i++)
if (varExists(notAllowed[i], pML->currentTag)) {
mylog("ERROR in ModelLoader::processTag: use %s in </group>: %s\n", notAllowed[i].c_str(), pML->currentTag.c_str());
return -1;
}
pMB->lockGroup(pMB);
//mark
if (varExists("mark", pML->currentTag))
addMark(pMB->pCurrentGroup->marks, getStringValue("mark", pML->currentTag));
return 1;
}
if (pML->tagName.compare("/group") == 0) {
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.executeGroupTransform(pMB);
pMB->releaseGroup(pMB);
return 1;
}
if (pML->tagName.compare("a") == 0)
return processTag_a(pML); //apply
if (pML->tagName.compare("clone") == 0)
return processTag_clone(pML);
if (pML->tagName.compare("/clone") == 0)
return processTag_clone(pML);
if (pML->tagName.compare("do") == 0)
return processTag_do(pML);
if (pML->tagName.compare("a2mesh") == 0)
return processTag_a2mesh(pML);
//mylog("%s, %s /group?=%d\n",pML->currentTag.c_str(), pML->tagName.c_str(), (pML->tagName.compare("/group") == 0));
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 };
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);
setFloatArray(xywh, 4, "xywh2nm", tagStr);
flipStr = getStringValue("flip2nm", tagStr);
TexCoords tc2nm;
tc2nm.set(pMT->uTex2nm, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
//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), pMB->pCurrentVShape, &tc, &tc2nm);
pMB->buildFace(pMB, applyTosVector.at(aN), pVS_a, &tc, &tc2nm);
}
delete pVS_a;
//mylog("vertsN=%d\n",pMB->vertices.size());
GroupTransform GT_a;
fillProps_gt(>_a, pMB, tagStr);
GT_a.executeGroupTransform(pMB);
pMB->releaseGroup(pMB);
return 1;
}
int ModelLoader::processTag_clone(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("clone") == 0) {
//mark what to clone
GroupTransform gt;
gt.pGroup = pMB->pLastClosedGroup;
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
//cloning
pMB->lockGroup(pMB);
gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &pMB->vertices, &pMB->triangles);
}
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.executeGroupTransform(pMB);
if (pML->tagName.compare("/clone") == 0 || pML->closedTag) {
pMB->releaseGroup(pMB);
}
return 1;
}
int ModelLoader::addMark(char* marks, std::string newMark) {
if (newMark.empty())
return 0;
std::string allMarks;
allMarks.assign(marks);
allMarks.append("<"+ newMark+">");
myStrcpy_s(marks,124, allMarks.c_str());
return 1;
}
int ModelLoader::fillProps_gt(GroupTransform* pGT, ModelBuilder* pMB, std::string tagStr) {
pGT->pGroup = pMB->pCurrentGroup;
//position
setFloatArray(pGT->shift, 3, "pxyz", tagStr);
setFloatValue(&pGT->shift[0], "px", tagStr);
setFloatValue(&pGT->shift[1], "py", tagStr);
setFloatValue(&pGT->shift[2], "pz", tagStr);
//angles
setFloatArray(pGT->spin, 3, "axyz", tagStr);
setFloatValue(&pGT->spin[0], "ax", tagStr);
setFloatValue(&pGT->spin[1], "ay", tagStr);
setFloatValue(&pGT->spin[2], "az", tagStr);
//scale
setFloatArray(pGT->scale, 3, "scale", tagStr);
pGT->onThe = getStringValue("onThe", tagStr);
pGT->allign = getStringValue("allign", tagStr);
pGT->headZto = getStringValue("headZto", tagStr);
//limit to
if(varExists("all",tagStr))
pGT->pGroup = NULL;
if (varExists("lastClosedGroup", tagStr))
pGT->pGroup = pMB->pLastClosedGroup;
if (varExists("markedAs", tagStr))
pGT->limit2mark(pGT, getStringValue("markedAs", tagStr));
setFloatArray(pGT->pMin, 3, "xyzMin", tagStr);
setFloatArray(pGT->pMax, 3, "xyzMax", tagStr);
if (varExists("sizeD", tagStr)) { //re-size
float sizeD[3];
setFloatArray(sizeD, 3, "sizeD", tagStr);
//bounding box
pGT->flagSelection(pGT, &pMB->vertices, NULL);
float bbMin[3];
float bbMax[3];
pGT->buildBoundingBoxFlagged(bbMin, bbMax, &pMB->vertices);
for (int i = 0; i < 3; i++) {
float size = bbMax[i] - bbMin[i];
pGT->scale[i] = (size + sizeD[i]) / size;
}
}
return 1;
}
int ModelLoader::processTag_do(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
gt.transformFlagged(>, &pMB->vertices);
return 1;
}
int ModelLoader::processTag_a2mesh(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
std::string tagStr = pML->currentTag;
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
//clone a copy
std::vector<Vertex01*> vx1;
std::vector<Triangle01*> tr1;
gt.cloneFlagged(NULL, &vx1, &tr1, &pMB->vertices, &pMB->triangles);
// build transform and inverted martrices
mat4x4 transformMatrix;
gt.buildTransformMatrix(>, &transformMatrix);
mat4x4 transformMatrixInverted;
mat4x4_invert(transformMatrixInverted, transformMatrix);
//move/rotate cloned
gt.flagAll(&vx1, &tr1);
//gt.transformFlagged(&pMB->vertices, &transformMatrixInverted);
gt.transformFlaggedMx(&vx1, &transformMatrixInverted);
//gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &vx1, &tr1);
float wh[2];
setFloatArray(wh, 2, "wh", tagStr);
Polygon frame;
frame.setRectangle(&frame, wh[0],wh[1]);
//destination arrays
std::vector<Vertex01*> vx2;
std::vector<Triangle01*> tr2;
Polygon triangle;
for (int i = tr1.size() - 1; i >= 0; i--) {
triangle.setTriangle(&triangle, tr1.at(i), &vx1);
Polygon intersection;
int pointsN = Polygon::xyIntersection(&intersection, &frame, &triangle);
if (pointsN > 2) {
Polygon::buildTriangles(&intersection);
GroupTransform::flagAll (&intersection.vertices, &intersection.triangles);
GroupTransform::cloneFlagged(NULL, &vx2, &tr2, &intersection.vertices, &intersection.triangles);
}
}
gt.flagAll(&vx2, &tr2);
//------------
//replace material
Material mt;
mt.setShaderType("phong");
mt.uColor.setRGBA(0,255,0,255);
int mtN = pMB->useMaterial(pMB, &mt);
for (int i = vx2.size() - 1; i >= 0; i--)
vx2.at(i)->materialN = mtN;
for (int i = tr2.size() - 1; i >= 0; i--)
tr2.at(i)->materialN = mtN;
//------------
//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;
}
Как это работает:
Все происходит в функции processTag_a2mesh(..) (строка 351).
Строка 355. Чтение позиции “куда” мы собираемся “apply”.
Строка 360. Клонируем выбранный (помеченный) меш.
Строка 369. Разворачиваем скопированный меш лицом к нам:
Строка 374. Читаем размеры что мы хотим “вырезать”.
Строки 381-390. Сканируем треугольники меша и строим пересечения (строка 384). Полученное пересечение сохраняем в отдельном Polygon-е под названием intersection (строка 383).
Если пересечение существует, то строим треугольники (строка 486) и сохраняем результат в массивы (vectors) vx2 (vertices) и tr2 (triangles) (строка 388).
Когда все треугольники обработаны, в массивах vx2 / tr2 у нас – желаемый фрагмент, вырезанный из скопированной поверхности (меша):
Чтобы он не слился с фоном после перемещения назад, поменяем его material (строки 394-401).
Мы уберем этот код чуть позже.
Строка 404. Перемещение вырезанного фрагмента на его окончательную (или изначальную?) позицию.
Строка 406. Клонирование полученных вертексов/треугольников обратно в основные массивы ModelBuilder-а. Однако, у них точно такие же координаты как и у исходного меша, поэтому они перекрывают друг друга:
Чтобы их сдвинуть чуток от исходного меша, у нас дополнительная команда в root01.txt:
</group sizeD="0.1,0,0.1">
Это последняя строка 38.
ModelLoader переведет sizeD в scale (масштаб), это строки 327-339 в ModelLoader.cpp, функция fillProps_gt(..).
9. Компиляция и запуск. Результат:
Поставленная цель достигнута.
Android
10. Пере-запускаем VS. Открываем C:\CPP\a997modeler\p_android\p_android.sln.
11. Под modeler добавим Existing Item
из C:\CPP\engine\modeler
- Polygon.cpp
- Polygon.h
- PolygonRib.cpp
- PolygonRib. h
Add
12. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск.
Ok.
VS top menu -> Debug -> Stop Debugging.
Следующая задача – наложить normal map на вырезанные фрагменты, при этом не нарушив их “родные” материалы и текстуры.