Just a friendly reminder: we are still trying to "draw" the joint (slit) between the pack and the lid, as a small normal map applied to existing textured mesh's fragment. Like this:
In previous chapter we implemented a concept of selecting and manipulating "marked vertices/triangles groups". So, now we can select one (in this sample - "box_right"), duplicate it, rotate and move at our discretion. Particularly - facing us, like this:
The next task is to cut this white rectangle out of the mesh.
We will scan mesh's triangles and will build intersections between these triangles and white rectangle. Both triangle and rectangle are polygons. So, we are talking about Polygons Intersection.
- The intersection of red triangle and white rectangle is 4-points polygon (2 triangles).
- The intersection of left blue - is a 5-points polygon (3 triangles).
New concepts here are: Polygons and Polygons Intersection. The polygon is basically an array of Polygon Ribs.
Now - implementation:
Windows
1. Start VS, open C:\CPP\a997modeler\p_windows\p_windows.sln.
2. Under modeler add new header file PolygonRib.h
Location: C:\CPP\engine\modeler
Code:
#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. Under modeler add new C++ file PolygonRib.cpp
Location: C:\CPP\engine\modeler
Code:
#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. Under modeler add new header file Polygon.h
Location: C:\CPP\engine\modeler
Code:
#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. Under modeler add new C++ file Polygon.cpp
Location: C:\CPP\engine\modeler
Code:
#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;
}
Now - model description.
6. Copy following code to a Text Editor and save (overwrite) it 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">
- New code starts at line 32.
- We have here new tags "a2mesh". It's like "a" (apply), but "apply to mesh".
To read and to execute it we need to add new functionality to ModelLoader.
7. Open ModelLoader.h and replace code by:
#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);
};
- New function here is processTag_a2mesh(..)
Implementation:
8. Open ModelLoader.cpp and replace code by:
#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;
}
How it works:
Everything happens in processTag_a2mesh(..) (line 351).
Line 355. Reading position where we are applying it to.
Line 360. Cloning selected mesh.
Line 369. Placing cloned mesh facing us:
Line 374. Reading sizes what we want to cut.
Lines 381-390. Scanning mesh's triangles and building intersections (line 384). Resulting intersection is stored in Polygon intersection (line 383).
If intersection exists, we are building triangles (line 486) and saving result into vx2 (vertices) and tr2 (triangles) arrays (vectors) (line 388).
After all triangles processed, in vx2 / tr2 we have our desired fragment cutted out of initial mesh's copy:
To make it visible after moving it back to it's place, we are changing it's material (lines 394-401).
We'll remove changing material code a bit later.
Line 404. Positioning cutted fragment to it's final place.
Line 406. Cloning fragment back to ModelBuilder. However, it has the same coordinates as original mesh, so they are overlapping each other:
To shift it a bit out of original mesh, we have an extra command in root01.txt:
</group sizeD="0.1,0,0.1">
It's the last one, line 38.
ModelLoader will translate sizeD to scale, it's lines 327-339 in ModelLoader.cpp, function fillProps_gt(..).
9. Build and run. Result:
The goal is achieved.
Android
10. Close and re-open VS. Open C:\CPP\a997modeler\p_android\p_android.sln.
11. Under modeler add Existing Item from C:\CPP\engine\modeler
- Polygon.cpp
- Polygon.h
- PolygonRib.cpp
- PolygonRib. h
Add
12. Turn on, unlock, plug in, allow.
Build and run.
Ok.
VS top menu -> Debug -> Stop Debugging.
Our next task is to apply normal map to cutted fragments, preserving their original materials and textures.