Теперь давайте наложим текстуру на более сложную много-вертексную поверхность, на правую (синюю) строну например. Надеюсь, понятно как написать текстурированный Phong шейдер, но мы сделаем это как-нибудь в другой раз. Пока обойдемся и простым (без света, который у нас уже есть). Нам понадобится:
- передать желаемые координаты изображения в Modeler
- и вычислить tUV координаты для каждого вовлеченного вертекса
1. Запускаем VS, Открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
- Старые проекты, a998engine и a999hello, можно удалить.
Возьмем картинку C:\CPP\a997modeler\dt\sample_img.png из предыдущих глав.
Допустим, мы хотим не всю картину, а только 256×128 фрагмент с 11×12 позиции (2 левых рыцаря):
Также, скажем, мы хотим перевернуть ее вверх ногами.
Во-первых, нам надо пересчитать эти числа в GL-овский “tUV” формат, от 0 до 1. Потом надо реорганизовать полученные координаты чтобы отразить разворот. Сделаем это в отдельном классе.
2. Под modeler добавим новый header file TexCoords.h
Location – C:\CPP\engine\modeler
Код:
#pragma once
#include <string>
class TexCoords
{
public:
float tuvTopLeft[2] = { 0.0f,0.0f };
float tuvBottomRight[2] = { 1.0f,1.0f };
public:
void set(int texN, float x, float y, float w, float h, std::string flip) { set(this, texN, x, y, w, h, flip); };
static void set(TexCoords* pTC, int texN, float x, float y, float w, float h, std::string flipStr);
static void set_GL(TexCoords* pTC, float x, float y, float w, float h, std::string flipStr);
static void flip(TexCoords* pTC, std::string flipStr); //possible flips: "90" (CCW), "-90" (CW), "180", "h" (horizontal), "v" (vertical)
};
3. Под modeler добавим новый C++ file TexCoords.cpp
Location – C:\CPP\engine\modeler
Код:
#include "TexCoords.h"
#include "Texture.h"
void TexCoords::set(TexCoords* pTC, int texN, float x, float y, float w, float h, std::string flipStr) {
if (texN < 0)
return;
Texture* pTex = Texture::textures.at(texN);
x = x / pTex->size[0];
y = y / pTex->size[1];
w = w / pTex->size[0];
h = h / pTex->size[1];
set_GL(pTC, x, y, w, h, flipStr);
}
void TexCoords::set_GL(TexCoords* pTC, float x, float y, float w, float h, std::string flipStr) {
//assuming that x,y,w,h - in GL 0-to-1 format
pTC->tuvTopLeft[0] = x;
pTC->tuvTopLeft[1] = y;
pTC->tuvBottomRight[0] = x + w;
pTC->tuvBottomRight[1] = y + h;
flip(pTC, flipStr);
}
void TexCoords::flip(TexCoords* pTC, std::string flipStr) {
//possible flips: "90" (CCW), "-90" (CW), "180", "h" (horizontal), "v" (vertical)
if (flipStr.compare("") == 0)
return;
TexCoords outTC;
if (flipStr.find("90") == 0) { //CCW
outTC.tuvTopLeft[0] = pTC->tuvBottomRight[0];
outTC.tuvTopLeft[1] = pTC->tuvTopLeft[1];
outTC.tuvBottomRight[0] = pTC->tuvTopLeft[0];
outTC.tuvBottomRight[1] = pTC->tuvBottomRight[1];
}
else if (flipStr.find("-90") == 0) { //CW
outTC.tuvTopLeft[0] = pTC->tuvTopLeft[0];
outTC.tuvTopLeft[1] = pTC->tuvBottomRight[1];
outTC.tuvBottomRight[0] = pTC->tuvBottomRight[0];
outTC.tuvBottomRight[1] = pTC->tuvTopLeft[1];
}
else if (flipStr.find("180") == 0) {
outTC.tuvTopLeft[0] = pTC->tuvBottomRight[0];
outTC.tuvTopLeft[1] = pTC->tuvBottomRight[1];
outTC.tuvBottomRight[0] = pTC->tuvTopLeft[0];
outTC.tuvBottomRight[1] = pTC->tuvTopLeft[1];
}
else if (flipStr.find("h") == 0) { //horizontal
outTC.tuvTopLeft[0] = pTC->tuvBottomRight[0];
outTC.tuvTopLeft[1] = pTC->tuvTopLeft[1];
outTC.tuvBottomRight[0] = pTC->tuvTopLeft[0];
outTC.tuvBottomRight[1] = pTC->tuvBottomRight[1];
}
else if (flipStr.find("v") == 0) { //vertical
outTC.tuvTopLeft[0] = pTC->tuvTopLeft[0];
outTC.tuvTopLeft[1] = pTC->tuvBottomRight[1];
outTC.tuvBottomRight[0] = pTC->tuvBottomRight[0];
outTC.tuvBottomRight[1] = pTC->tuvTopLeft[1];
}
memcpy(pTC, &outTC, sizeof(TexCoords));
}
В TheGame.cpp вместо смены цвета для правой стороны, заменим Material целиком. Еще будет нужно передать текстурные координаты TexCoords в функцию pMB->buildBoxFace.
4. Заменим TheGame.cpp код на:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
extern std::string filesRoot;
std::vector<GameSubj*> TheGame::gameSubjs;
int TheGame::getReady() {
bExitGame = false;
Shader::loadShaders();
glEnable(GL_CULL_FACE);
//=== create box ========================
GameSubj* pGS = new GameSubj();
gameSubjs.push_back(pGS);
pGS->name.assign("box1");
pGS->ownCoords.setPosition(0, 0, 0);
pGS->ownCoords.setDegrees(0, 0, 0);
pGS->ownSpeed.setDegrees(0,1,0);
ModelBuilder* pMB = new ModelBuilder();
pMB->useSubjN(gameSubjs.size() - 1);
//define VirtualShape
VirtualShape vs;
vs.setShapeType("box-tank");
vs.whl[0] = 60;
vs.whl[1] = 160;
vs.whl[2] = 390;
vs.setExt(20);
vs.extD = 0;
vs.extF = 0; //to make front face "flat"
vs.sectionsR = 2;
Material mt;
//define material - flat red
mt.shaderN = Shader::spN_phong_ucolor;
mt.primitiveType = GL_TRIANGLES;
mt.uColor.setRGBA(255, 0, 0,255); //red
pMB->useMaterial(&mt);
pMB->buildBoxFace(pMB,"front v", &vs);
pMB->buildBoxFace(pMB, "back v", &vs);
pMB->buildBoxFace(pMB, "top", &vs);
pMB->buildBoxFace(pMB, "bottom", &vs);
pMB->buildBoxFace(pMB, "left all", &vs);
mt.uColor.clear(); // set to zero;
mt.uTex0 = Texture::loadTexture(filesRoot + "/dt/sample_img.png");
mt.shaderN = Shader::spN_flat_tex;
pMB->useMaterial(&mt);
TexCoords tc;
tc.set(mt.uTex0, 11, 12, 256, 128, "180");
pMB->buildBoxFace(pMB, "right all", &vs, &tc);
pMB->buildDrawJobs(gameSubjs);
delete pMB;
//===== set up camera
v3set(mainCamera.ownCoords.pos, 0, 200, 1000); //set position
float cameraDir[3];
v3set(cameraDir, 0, -200, -1000); //set direction vector
float cameraYawDg = v3yawDg(cameraDir);
float cameraPitchDg = v3pitchDg(cameraDir);
//mylog("cameraYaw=%f, cameraPitch=%f\n", cameraYawDg, cameraPitchDg);
mainCamera.ownCoords.setDegrees(cameraPitchDg, cameraYawDg, 0);
float cameraUp[4] = { 0,1,0,0 }; //y - up
mat4x4_mul_vec4plus(cameraUp, *mainCamera.ownCoords.getRotationMatrix(), cameraUp, 0);
mat4x4_look_at(mainCamera.lookAtMatrix, mainCamera.ownCoords.pos, pGS->ownCoords.pos, cameraUp);
//===== set up light
v3set(dirToMainLight, -1, 1, 1);
vec3_norm(dirToMainLight, dirToMainLight);
return 1;
}
int TheGame::drawFrame() {
myPollEvents();
//glClearColor(0.0, 0.0, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//calculate halfVector
float dirToCamera[4] = { 0,0,-1,0 }; //-z
mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);
float uHalfVector[4] = { 0,0,0,0 };
for (int i = 0; i < 3; i++)
uHalfVector[i] = (dirToCamera[i] + dirToMainLight[i]) / 2;
vec3_norm(uHalfVector, uHalfVector);
mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;
//mat4x4_ortho(mProjection, -(float)screenSize[0] / 2, (float)screenSize[0] / 2, -(float)screenSize[1] / 2, (float)screenSize[1] / 2, 100.f, 500.f);
mat4x4_perspective(mProjection, 3.14f / 6.0f, (float)screenSize[0] / screenSize[1], 700.f, 1300.f);
mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);
//mViewProjection[1][3] = 0; //keystone effect
//scan subjects
int subjsN = gameSubjs.size();
for (int subjN = 0; subjN < subjsN; subjN++) {
GameSubj* pGS = gameSubjs.at(subjN);
//behavior - apply rotation speed
pGS->moveSubj();
//prepare subject for rendering
pGS->buildModelMatrix(pGS);
//build MVP matrix for given subject
mat4x4_mul(mMVP, mViewProjection, pGS->ownModelMatrix);
//build Model-View (rotation) matrix for normals
mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());
//convert to 3x3 matrix
float mMV3x3[3][3];
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
mMV3x3[y][x] = mMV4x4[y][x];
//render subject
for (int i = 0; i < pGS->djTotalN; i++) {
DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
pDJ->execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);
}
}
mySwapBuffers();
return 1;
}
int TheGame::cleanUp() {
int itemsN = gameSubjs.size();
//delete all UISubjs
for (int i = 0; i < itemsN; i++) {
GameSubj* pGS = gameSubjs.at(i);
delete pGS;
}
gameSubjs.clear();
//clear all other classes
Texture::cleanUp();
Shader::cleanUp();
DrawJob::cleanUp();
return 1;
}
int TheGame::onScreenResize(int width, int height) {
if (screenSize[0] == width && screenSize[1] == height)
return 0;
screenSize[0] = width;
screenSize[1] = height;
screenRatio = (float)width / (float)height;
glViewport(0, 0, width, height);
mylog(" screen size %d x %d\n", width, height);
return 1;
}
int TheGame::run() {
getReady();
while (!bExitGame) {
drawFrame();
}
cleanUp();
return 1;
}
Теперь в функции ModelBuilder::buildBoxFace(…) у нас новые параметры TexCoords*. И новая функция ModelBuilder::groupApplyTexture2(…).
5. Заменим ModelBuilder.h код на:
#pragma once
#include "ModelBuilder1base.h"
#include "TexCoords.h"
class ModelBuilder : public ModelBuilder1base
{
public:
virtual ~ModelBuilder();
static int buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC = NULL, TexCoords* pTC2nm = NULL);
static int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC = NULL, TexCoords* pTC2nm = NULL);
static int buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);
static int buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);
static int cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);
static int capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);
static int groupApplyTexture2(ModelBuilder* pMB, std::string applyTo, TexCoords* pTC, bool isNormakMap);
};
6. Заменим ModelBuilder.cpp еод на:
#include "ModelBuilder.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder::~ModelBuilder() {
}
int ModelBuilder::buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC, TexCoords* pTC2nm) {
if (strstr(pVS->shapeType, "box") == pVS->shapeType)
return buildBoxFace(pMB, applyTo, pVS, pTC, pTC2nm);
return -1;
}
int ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC, TexCoords* pTC2nm) {
//this code is for simple box
VirtualShape vs; //face VS,
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
vs.sectionsR = pVS->sectionsR;
//rotate desirable side to face us.
if (applyTo.find("front") == 0) {
//Side <front> is facing us as is.
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extF;
vs.extL = pVS->extL;
vs.extR = pVS->extR;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//define how to move/place generated face back to the VirtualShape
//just shift closer to us by length/2
mat4x4_translate(transformMatrix, 0, 0, pVS->whl[2] / 2);
}
else if (applyTo.find("back") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extB;
vs.extL = pVS->extR;
vs.extR = pVS->extL;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate 180 degrees around Y and shift farther from us by half-length
mat4x4_translate(transformMatrix, 0, 0, -pVS->whl[2] / 2);
mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 180);
}
else if (applyTo.find("left") == 0) {
vs.whl[0] = pVS->whl[2]; //width = original length
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[2];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extL;
vs.extL = pVS->extB;
vs.extR = pVS->extF;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate -90 degrees around Y (CW) and shift half-width to the left
mat4x4_translate(transformMatrix, -pVS->whl[0] / 2, 0, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 90);
}
else if (applyTo.find("right") == 0) {
vs.whl[0] = pVS->whl[2]; //width = original length
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[2];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extR;
vs.extL = pVS->extF;
vs.extR = pVS->extB;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate +90 degrees around Y (CCW) and shift half-width to the right
mat4x4_translate(transformMatrix, pVS->whl[0] / 2, 0, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 90);
}
else if (applyTo.find("top") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[2]; //height = original length
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[2];
//extensions
vs.extF = pVS->extU;
vs.extL = pVS->extR;
vs.extR = pVS->extL;
vs.extU = pVS->extF;
vs.extD = pVS->extB;
//rotate -90 degrees around X (CW) and 180 around Y, and shift half-height up
mat4x4_translate(transformMatrix, 0, pVS->whl[1] / 2, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 180);
mat4x4_rotate_X(transformMatrix, transformMatrix, -degrees2radians * 90);
}
else if (applyTo.find("bottom") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[2]; //height = original length
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[2];
//extensions
vs.extF = pVS->extD;
vs.extL = pVS->extL;
vs.extR = pVS->extR;
vs.extU = pVS->extF;
vs.extD = pVS->extB;
//rotate 90 around X (CCW) and shift half-height down
mat4x4_translate(transformMatrix, 0, -pVS->whl[1] / 2, 0);
mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * 90);
}
lockGroup(pMB);
//create vertices
if (strstr(pVS->shapeType, "tank") != nullptr)
buildBoxFaceTank(pMB, applyTo, &vs);
else
buildBoxFacePlain(pMB, applyTo, &vs);
groupApplyTexture2(pMB, "front", pTC, false);
groupApplyTexture2(pMB, "front", pTC2nm, true); //for normal map
//move face to it's place (apply transform matrix)
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);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {
if (pVS->whl[0] == 0 || pVS->whl[1] == 0)
return 0;
//create vertices
int sectionsX = pVS->sections[0];
int sectionsY = pVS->sections[1];
int pointsX = sectionsX + 1;
int pointsY = sectionsY + 1;
float stepX = pVS->whl[0] / sectionsX;
float stepY = pVS->whl[1] / sectionsY;
float kY = pVS->whl[1] / 2;
for (int iy = 0; iy < pointsY; iy++) {
float kX = -pVS->whl[0] / 2;
for (int ix = 0; ix < pointsX; ix++) {
int nSE = addVertex(pMB, kX, kY, pVS->extF, 0, 0, 1); //vertex number on south-east
if (iy > 0 && ix > 0) {
//add 2 triangles
int nSW = nSE - 1; //vertex number south-west
int nNE = nSE - pointsX; //north-east
int nNW = nSW - pointsX; //north-west
add2triangles(pMB, nNW, nNE, nSW, nSE, iy + ix);
}
kX += stepX;
}
kY -= stepY;
}
return 1;
}
int ModelBuilder::buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {
//for diamond effect - sectionsRad=1, don't merge normals
bool drawMiddle = true;
//edges
bool drawTop = false;
bool drawBottom = false;
bool drawLeft = false;
bool drawRight = false;
//corners
bool drawTopLeft = false;
bool drawTopRight = false;
bool drawBottomLeft = false;
bool drawBottomRight = false;
if (pVS->extF == 0 || applyTo.find(" all") != std::string::npos) {
drawTop = true;
drawBottom = true;
drawLeft = true;
drawRight = true;
drawTopLeft = true;
drawTopRight = true;
drawBottomLeft = true;
drawBottomRight = true;
}
else if (applyTo.find(" h") != std::string::npos) {
drawLeft = true;
drawRight = true;
}
else if (applyTo.find(" v") != std::string::npos) {
drawTop = true;
drawBottom = true;
}
if (applyTo.find(" no") != std::string::npos) {
if (applyTo.find(" noM") != std::string::npos) {
//middle
if (applyTo.find(" noMrow") != std::string::npos) {
drawMiddle = false;
drawLeft = false;
drawRight = false;
}
if (applyTo.find(" noMcol") != std::string::npos) {
drawMiddle = false;
drawTop = false;
drawBottom = false;
}
if (applyTo.find(" noMid") != std::string::npos)
drawMiddle = false;
}
if (applyTo.find(" noN") != std::string::npos) {
//north
if (applyTo.find(" noNrow") != std::string::npos) {
drawTop = false;
drawTopLeft = false;
drawTopRight = false;
}
if (applyTo.find(" noNedge") != std::string::npos)
drawTop = false;
if (applyTo.find(" noNW") != std::string::npos)
drawTopLeft = false;
if (applyTo.find(" noNE") != std::string::npos)
drawTopRight = false;
}
if (applyTo.find(" noS") != std::string::npos) {
//south
if (applyTo.find(" noSrow") != std::string::npos) {
drawBottom = false;
drawBottomLeft = false;
drawBottomRight = false;
}
if (applyTo.find(" noSedge") != std::string::npos)
drawBottom = false;
if (applyTo.find(" noSW") != std::string::npos)
drawBottomLeft = false;
if (applyTo.find(" noSE") != std::string::npos)
drawBottomRight = false;
}
if (applyTo.find(" noW") != std::string::npos) {
//west
if (applyTo.find(" noWcol") != std::string::npos) {
drawLeft = false;
drawTopLeft = false;
drawBottomLeft = false;
}
if (applyTo.find(" noWedge") != std::string::npos)
drawLeft = false;
}
if (applyTo.find(" noE") != std::string::npos) {
//east
if (applyTo.find(" noEcol") != std::string::npos) {
drawRight = false;
drawTopRight = false;
drawBottomRight = false;
}
if (applyTo.find(" noEedge") != std::string::npos)
drawRight = false;
}
}
lockGroup(pMB);
//middle
if (pVS->whl[0] > 0 && pVS->whl[1] > 0 && drawMiddle) {
buildBoxFacePlain(pMB, applyTo, pVS);
}
VirtualShape vs;
//edges
//vs.type.assign("cylinder");
vs.sectionsR = pVS->sectionsR;
if (pVS->whl[0] > 0) {
vs.sections[2] = pVS->sections[0]; //cylinder Z sections n
vs.whl[2] = pVS->whl[0]; //cylinder length Z
vs.whl[0] = pVS->extF * 2; //cylinder diameter X
if (pVS->extU > 0 && drawTop) {
vs.whl[1] = pVS->extU * 2; //cylinder diameter Y
lockGroup(pMB);
cylinderWrap(pMB, &vs, 0, 90);
//rotate -90 degrees around Y and shift up
moveGroupDg(pMB, 0, -90, 0, 0, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extD > 0 && drawBottom) {
vs.whl[1] = pVS->extD * 2; //cylinder diameter Y
lockGroup(pMB);
cylinderWrap(pMB, &vs, -90, 0);
//rotate -90 degrees around Y and shift down
moveGroupDg(pMB, 0, -90, 0, 0, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->whl[1] > 0) {
vs.sections[2] = pVS->sections[1]; //cylinder Z sections n
vs.whl[2] = pVS->whl[1]; //cylinder length Z
vs.whl[1] = pVS->extF * 2; //cylinder diameter Y
if (pVS->extL > 0 && drawLeft) {
vs.whl[0] = pVS->extL * 2; //cylinder diameter X
lockGroup(pMB);
cylinderWrap(pMB, &vs, 90, 180);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 90, 0, 0, -pVS->whl[0] * 0.5f, 0, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawRight) {
vs.whl[0] = pVS->extR * 2; //cylinder diameter X
lockGroup(pMB);
cylinderWrap(pMB, &vs, 0, 90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 90, 0, 0, pVS->whl[0] * 0.5f, 0, 0);
releaseGroup(pMB);
}
}
//corners
//vs.type.assign("cap");
vs.sectionsR = pVS->sectionsR;
vs.sections[2] = pVS->sectionsR;
vs.whl[2] = pVS->extF;
if (pVS->extU > 0) {
//top corners
vs.whl[1] = pVS->extU * 2;
if (pVS->extL > 0 && drawTopLeft) {
vs.whl[0] = pVS->extL * 2;
lockGroup(pMB);
capWrap(pMB, &vs, 90, 180);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawTopRight) {
vs.whl[0] = pVS->extR * 2;
lockGroup(pMB);
capWrap(pMB, &vs, 0, 90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->extD > 0) {
//bottom corners
vs.whl[1] = pVS->extD * 2;
if (pVS->extL > 0 && drawBottomLeft) {
vs.whl[0] = pVS->extL * 2;
lockGroup(pMB);
capWrap(pMB, &vs, -180, -90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawBottomRight) {
vs.whl[0] = pVS->extR * 2;
lockGroup(pMB);
capWrap(pMB, &vs, -90, 0);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->extF == 0) {
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
//normal
v3set(pVX->aNormal, 0, 0, 1);
}
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {
// angleFrom/To - in degrees
lockGroup(pMB);
float stepZ = pVS->whl[2] / pVS->sections[2];
float stepDg = (angleTo - angleFrom) / pVS->sectionsR; //in degrees
for (int nz = 0; nz <= pVS->sections[2]; nz++) {
float kz = stepZ * nz - pVS->whl[2] * 0.5f;
for (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {
// rpn - radial point number
float angleRd = (angleFrom + stepDg * rpn) * degrees2radians;
float kx = cosf(angleRd);
float ky = sinf(angleRd);
int nSE = addVertex(pMB, kx, ky, kz, kx, ky, 0);
if (nz > 0 && rpn > 0) {
int nSW = nSE - 1;
int nNW = nSW - pVS->sectionsR - 1;
int nNE = nSE - pVS->sectionsR - 1;
add2triangles(pMB, nNE, nNW, nSE, nSW, nz + rpn);
}
}
}
//scale to desirable diameters
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 1);
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);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {
// angleFrom/To - in degrees
lockGroup(pMB);
//center point
int n0 = addVertex(pMB, 0, 0, 1, 0, 0, 1);
float stepZdg = 90.0f / pVS->sections[2]; //in degrees
float stepRdg = (angleTo - angleFrom) / pVS->sectionsR; //in degrees
for (int nz = 1; nz <= pVS->sections[2]; nz++) {
float angleZrd = stepZdg * nz * degrees2radians;
float kz = cosf(angleZrd);
float R = sinf(angleZrd);
for (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {
// rpn - radial point number
float angleRd = (angleFrom + stepRdg * rpn) * degrees2radians;
float kx = cosf(angleRd) * R;
float ky = sinf(angleRd) * R;
int nSE = addVertex(pMB, kx, ky, kz, kx, ky, kz);
if (rpn > 0) {
if (nz == 1) {
int nSW = nSE - 1;
addTriangle(pMB, n0, nSW, nSE);
}
else {
int nSW = nSE - 1;
int nNW = nSW - pVS->sectionsR - 1;
int nNE = nSE - pVS->sectionsR - 1;
add2triangles(pMB, nNW, nNE, nSW, nSE, nz + rpn);
}
}
}
}
//scale to desirable diameters
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, pVS->whl[2]);
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);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::groupApplyTexture2(ModelBuilder* pMB, std::string applyTo, TexCoords* pTC, bool isNormakMap) {
if (pTC == NULL)
return 0;
float posMin[3];
float posMax[3];
float posRange[3];
for (int i = 0; i < 3; i++) {
posMin[i] = 1000000;
posMax[i] = -1000000;
}
int vertsN = pMB->vertices.size();
for (int vN = pMB->pCurrentGroup->fromVertexN; vN < vertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag < 0) //ignore
continue;
for (int i = 0; i < 3; i++) {
if (posMin[i] > pVX->aPos[i])
posMin[i] = pVX->aPos[i];
if (posMax[i] < pVX->aPos[i])
posMax[i] = pVX->aPos[i];
}
}
//here we have coordinates range
for (int i = 0; i < 3; i++)
posRange[i] = posMax[i] - posMin[i];
//for "front"
int xRateIndex = 0;
bool xRateInverse = false;
int yRateIndex = 1;
bool yRateInverse = true;
if (applyTo.find("front") == 0)
; //do nothing
else if (applyTo.find("back") == 0)
xRateInverse = true;
else if (applyTo.find("left") == 0)
xRateIndex = 2;
else if (applyTo.find("right") == 0) {
xRateIndex = 2;
xRateInverse = true;
}
else if (applyTo.find("top") == 0) {
xRateInverse = true;
yRateIndex = 2;
}
else if (applyTo.find("bottom") == 0)
yRateIndex = 2;
float xRate = 0;
float yRate = 0;
float tuvRange[2];
tuvRange[0] = pTC->tuvBottomRight[0] - pTC->tuvTopLeft[0];
tuvRange[1] = pTC->tuvBottomRight[1] - pTC->tuvTopLeft[1];
for (int vN = pMB->pCurrentGroup->fromVertexN; vN < vertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag < 0) //ignore
continue;
if (posRange[xRateIndex] == 0)
xRate = 0;
else {
xRate = (pVX->aPos[xRateIndex] - posMin[xRateIndex]) / posRange[xRateIndex];
if (xRateInverse)
xRate = 1.0f - xRate;
}
if (posRange[yRateIndex] == 0)
yRate = 0;
else {
yRate = (pVX->aPos[yRateIndex] - posMin[yRateIndex]) / posRange[yRateIndex];
if (yRateInverse)
yRate = 1.0f - yRate;
}
float* pTuv = pVX->aTuv;
if(isNormakMap)
pTuv = pVX->aTuv2;
pTuv[0] = pTC->tuvTopLeft[0] + tuvRange[0] * xRate;
pTuv[1] = pTC->tuvTopLeft[1] + tuvRange[1] * yRate;
}
return 1;
}
7. Компиляция и запуск:
Отлично.
Теперь на
Андроиде
8. Пере-запускаем VS. Открываем C:\CPP\a997modeler\p_android\p_android.sln.
9. Под modeler добавим Existing Items
из C:\CPP\engine\modeler
- TexCoords.cpp
- TexCoords.h
Add.
10. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск:
Работает. Но, как обычно, не очень вписывается в экран.
Попробовал развернуть экран – еще и хуже:
Ну что ж, озадачимся этим в следующей главе…