Chapter 17. Modeler. Part 2

Now back to the modeler. Time to put all these new classes together.

Windows

1. Start VS. Open C:\CPP\a997modeler\p_windows\p_windows.sln.


2. Under modeler add new header file ModelBuilder.h

Location - C:\CPP\engine\modeler

Code:

#pragma once
#include <string>
#include <vector>
#include "Vertex01.h"
#include "Triangle01.h"
#include "VirtualShape.h"
#include "Group01.h"
#include "Material.h"
#include "GameSubj.h"

class ModelBuilder
{
public:
	std::vector<Vertex01*> vertices;
	std::vector<Triangle01*> triangles;
	std::vector<VirtualShape*> vShapesStack;
	std::vector<Group01*> groupsStack;
	std::vector<Material*> materialsList;
	std::vector<int> subjNumbersList;
	int usingSubjN = -1;
	int usingMaterialN = -1;
	Group01* pCurrentGroup = NULL;
public:
	virtual ~ModelBuilder();
	int useSubjN(int subjN) { return useSubjN(this, subjN); };
	static int useSubjN(ModelBuilder* pMB, int subjN);
	int useMaterial(Material* pMT) { return useMaterial(this, pMT); };
	static int useMaterial(ModelBuilder* pMB, Material* pMT);
	int buildBoxFace(std::string applyTo, VirtualShape* pVS) { return buildBoxFace(this, applyTo, pVS); };
	static int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);
	static void startGroup(ModelBuilder* pMB);
	static void endGroup(ModelBuilder* pMB);
	static int addVertex(ModelBuilder* pMB, float kx, float ky, float kz, float nx, float ny, float nz);
	static int add2triangles(ModelBuilder* pMB, int nNW, int nNE, int nSW, int nSE, int n);
	static int addTriangle(ModelBuilder* pMB, int n0, int n1, int n2);
	int buildDrawJobs(std::vector<GameSubj*> gameSubjs) { return buildDrawJobs(this, gameSubjs); };
	static int buildDrawJobs(ModelBuilder* pMB, std::vector<GameSubj*> gameSubjs);
	static int rearrangeArraysForDrawJob(ModelBuilder* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
	static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
};

In this sample we will create a red box with 1 blue side (the right one). For this we will need to create 1 GameSubject, 2 materials, a "box" virtual shape, then add 6 "faces" to it. Then we'll need to convert created vertices and triangles arrays into DrawJobs. The set of commands/functions above is defined by these needs.


Now - implementations:

3. Under modeler add new C++ file ModelBuilder.cpp

Location - C:\CPP\engine\modeler

Code:

#include "ModelBuilder.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"

extern float degrees2radians;

ModelBuilder::~ModelBuilder() {
	//clear all vectors
	int itemsN = vertices.size();
	for (int i = 0; i < itemsN; i++)
		delete vertices.at(i);
	vertices.clear();

	itemsN = triangles.size();
	for (int i = 0; i < itemsN; i++)
		delete triangles.at(i);
	triangles.clear();

	itemsN = vShapesStack.size();
	for (int i = 0; i < itemsN; i++)
		delete vShapesStack.at(i);
	vShapesStack.clear();

	itemsN = groupsStack.size();
	for (int i = 0; i < itemsN; i++)
		delete groupsStack.at(i);
	groupsStack.clear();

	itemsN = materialsList.size();
	for (int i = 0; i < itemsN; i++)
		delete materialsList.at(i);
	materialsList.clear();

	subjNumbersList.clear();
}
int ModelBuilder::useSubjN(ModelBuilder* pMB, int subjN) {
	pMB->usingSubjN = subjN;
	int itemsN = pMB->subjNumbersList.size();
	bool newN = true;
	if (itemsN > 0)
		for (int i = 0; i < itemsN; i++)
			if (pMB->subjNumbersList.at(i) == subjN) {
				newN = false;
				break;
			}
	if (newN)
		pMB->subjNumbersList.push_back(subjN);
	return subjN;
}
int ModelBuilder::useMaterial(ModelBuilder* pMB, Material* pMT) {
	int itemsN = pMB->materialsList.size();
	if (itemsN > 0)
		for (int i = 0; i < itemsN; i++)
			if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
				pMB->usingMaterialN = i;
				return i;
			}
	//if here - add new material to the list
	pMB->usingMaterialN = itemsN;
	//create a copy of new Material and add to the list
	Material* pMTnew = new Material(*pMT);
	pMB->materialsList.push_back(pMTnew);
	return itemsN;
}
int ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {
	//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 };
	//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];
		//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];
		//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];
		//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];
		//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
		//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
		//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);
	}
	startGroup(pMB);
	//create vertices
	int sectionsX = 1;
	int sectionsY = 1;
	int pointsX = sectionsX + 1;
	int pointsY = sectionsY + 1;
	float stepX = vs.whl[0] / sectionsX;
	float stepY = vs.whl[1] / sectionsY;
	float kY = vs.whl[1] / 2;
	for (int iy = 0; iy < pointsY; iy++) {
		float kX = -vs.whl[0] / 2;
		for (int ix = 0; ix < pointsX; ix++) {
			int nSE = addVertex(pMB, kX, kY, 0, 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;
	}
	//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);
	}
	endGroup(pMB);
	return 1;
}
int ModelBuilder::add2triangles(ModelBuilder* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
	//indexes: NorthWest, NorthEast, SouthWest,SouthEast
	if (n % 2 == 0) { //even number
		addTriangle(pMB, nNW, nSW, nNE);
		addTriangle(pMB, nNE, nSW, nSE);
	}
	else { //odd number
		addTriangle(pMB, nNW, nSE, nNE);
		addTriangle(pMB, nNW, nSW, nSE);
	}
	return pMB->triangles.size() - 1;
}
int ModelBuilder::addTriangle(ModelBuilder* pMB, int i0, int i1, int i2) {
	Triangle01* pTR = new Triangle01();
	pMB->triangles.push_back(pTR);
	pTR->idx[0] = i0;
	pTR->idx[1] = i1;
	pTR->idx[2] = i2;
	pTR->subjN = pMB->usingSubjN;
	pTR->materialN = pMB->usingMaterialN;
	return pMB->triangles.size() - 1;
}
void ModelBuilder::startGroup(ModelBuilder* pMB) {
	pMB->pCurrentGroup = new Group01();
	pMB->groupsStack.push_back(pMB->pCurrentGroup);
	pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
	pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
}
void ModelBuilder::endGroup(ModelBuilder* pMB) {
	delete pMB->pCurrentGroup;
	pMB->pCurrentGroup = pMB->groupsStack.back();
	pMB->groupsStack.pop_back();
}
int ModelBuilder::addVertex(ModelBuilder* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
	Vertex01* pVX = new Vertex01();
	pMB->vertices.push_back(pVX);
	pVX->aPos[0] = kx;
	pVX->aPos[1] = ky;
	pVX->aPos[2] = kz;
	//normal
	pVX->aNormal[0] = nx;
	pVX->aNormal[1] = ny;
	pVX->aNormal[2] = nz;
	pVX->subjN = pMB->usingSubjN;
	pVX->materialN = pMB->usingMaterialN;
	return pMB->vertices.size() - 1;
}
int ModelBuilder::buildDrawJobs(ModelBuilder* pMB, std::vector<GameSubj*> gameSubjs) {
	int totalSubjsN = pMB->subjNumbersList.size();
	if (totalSubjsN < 1) {
		pMB->subjNumbersList.push_back(-1);
		totalSubjsN = 1;
	}
	int totalMaterialsN = pMB->materialsList.size();
	if (totalSubjsN < 2 && totalMaterialsN < 2) {
		//simple single DrawJob
		Material* pMT = pMB->materialsList.at(0);
		GameSubj* pGS = NULL;
		int gsN = pMB->subjNumbersList.at(0);
		if (gsN >= 0)
			pGS = gameSubjs.at(gsN);
		if (pGS != NULL)
			pGS->djStartN = DrawJob::drawJobs.size();
		buildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);
		if (pGS != NULL)
			pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
		return 1;
	}
	int totalVertsN = pMB->vertices.size();
	int totalTrianglesN = pMB->triangles.size();
	//clear flags
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = pMB->vertices.at(vN);
		pVX->flag = 0;
	}
	for (int tN = 0; tN < totalTrianglesN; tN++) {
		Triangle01* pTR = pMB->triangles.at(tN);
		pTR->flag = 0;
	}
	int addedDJs = 0;
	for (int sN = 0; sN < totalSubjsN; sN++) {
		GameSubj* pGS = NULL;
		int gsN = pMB->subjNumbersList.at(sN);
		if (gsN >= 0)
			pGS = gameSubjs.at(gsN);
		if (pGS != NULL)
			pGS->djStartN = DrawJob::drawJobs.size();
		for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
			Material* pMT = pMB->materialsList.at(mtN);
			std::vector<Vertex01*> useVertices;
			std::vector<Triangle01*> useTriangles;
			for (int vN = 0; vN < totalVertsN; vN++) {
				Vertex01* pVX = pMB->vertices.at(vN);
				if (pVX->flag != 0)
					continue;
				if (pVX->subjN != gsN)
					continue;
				if (pVX->materialN != mtN)
					continue;
				//if here - make a copy
				Vertex01* pVX2 = new Vertex01(*pVX);
				useVertices.push_back(pVX2);
				pVX2->altN = vN;
				pVX->flag = 1;
				if (pVX->endOfSequence > 0) {
					rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
					buildSingleDrawJob(pMT, useVertices, useTriangles);
					addedDJs++;
					//clear and proceed to next sequence
					int useVerticesN = useVertices.size();
					for (int i = 0; i < useVerticesN; i++)
						delete useVertices.at(i);
					useVertices.clear();
				}
			}
			int useVerticesN = useVertices.size();
			if (useVerticesN < 1)
				continue; //to next material
			//pick triangles
			for (int tN = 0; tN < totalTrianglesN; tN++) {
				Triangle01* pTR = pMB->triangles.at(tN);
				if (pTR->flag != 0)
					continue;
				if (pTR->subjN != gsN)
					continue;
				if (pTR->materialN != mtN)
					continue;
				//if here - make a copy
				Triangle01* pTR2 = new Triangle01(*pTR);
				useTriangles.push_back(pTR2);
				pTR->flag = 1;
			}
			rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
			buildSingleDrawJob(pMT, useVertices, useTriangles);
			addedDJs++;
			//clear all for next material
			for (int i = 0; i < useVerticesN; i++)
				delete useVertices.at(i);
			useVertices.clear();
			int useTrianglesN = useTriangles.size();
			for (int i = 0; i < useTrianglesN; i++)
				delete useTriangles.at(i);
			useTriangles.clear();
		}
		if (pGS != NULL)
			pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
	}
	return addedDJs;
}
int ModelBuilder::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
	int totalVertsN = useVertices.size();
	if (totalVertsN < 1)
		return 1;
	DrawJob* pDJ = new DrawJob();
	//copy material to DJ
	memcpy(&pDJ->mt, pMT, sizeof(Material));
	//calculate VBO element size (stride) and variables offsets in VBO
	int VBOid = DrawJob::newBufferId();
	int stride = 0;
	pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
	//create an array for VBO
	int bufferSize = totalVertsN * stride;
	float* vertsBuffer = new float[bufferSize];
	//fill vertsBuffer
	Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
	int floatSize = sizeof(float);
	for (int vN = 0; vN < totalVertsN; vN++) {
		Vertex01* pVX = useVertices.at(vN);
		int idx = vN * stride / floatSize;
		//pick data from vertex and move to the buffer
		memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
		if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
			memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
	}
	//buffer is ready, create VBO
	glBindBuffer(GL_ARRAY_BUFFER, VBOid);
	glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
	delete[] vertsBuffer;
	pDJ->pointsN = totalVertsN;

	int totalTrianglesN = useTriangles.size();
	if (totalTrianglesN > 0) {
		//create EBO
		int totalIndexesN = totalTrianglesN * 3;
		//create buffer
		GLushort* indexBuffer = new GLushort[totalIndexesN];
		for (int tN = 0; tN < totalTrianglesN; tN++) {
			Triangle01* pTR = useTriangles[tN];
			int idx = tN * 3;
			indexBuffer[idx + 0] = (GLushort)pTR->idx[0];
			indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
			indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
		}
		//buffer is ready, create IBO
		pDJ->glEBOid = DrawJob::newBufferId();
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
		delete[] indexBuffer;
		pDJ->pointsN = totalIndexesN;
	}
	//create and fill vertex attributes array (VAO)
	pDJ->buildVAO();
	return 1;
}
int ModelBuilder::rearrangeArraysForDrawJob(ModelBuilder* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
	int totalTrianglesN = useTriangles.size();
	if (totalTrianglesN < 1)
		return 0;
	int totalVerticesN = useVertices.size();
	//save new vertices order in original vertices array
	//since triangles indices refer to original vertices order
	for (int i = 0; i < totalVerticesN; i++) {
		Vertex01* pVX1 = useVertices.at(i);
		Vertex01* pVX0 = allVertices.at(pVX1->altN);
		pVX0->altN = i;
	}
	//replace triangle original indices by new numbers saved in original vertices altN
	for (int tN = 0; tN < totalTrianglesN; tN++) {
		Triangle01* pTR = useTriangles.at(tN);
		for (int i = 0; i < 3; i++) {
			Vertex01* pVX0 = allVertices.at(pTR->idx[i]);
			pTR->idx[i] = pVX0->altN;
		}
	}
	return 1;
}


Now we have everything we need to create our first 3D model. In this sample it will be a red box with 1 blue side.

In TheGame.cpp we won't need hard-coded vertex arrays any longer. Instead, we will create them programmatically.

4. Open TheGame.cpp and replace the code by following:

#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.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(1.5, -2, 1);

    ModelBuilder* pMB = new ModelBuilder();
    pMB->useSubjN(gameSubjs.size() - 1);

    //define VirtualShape
    VirtualShape vs;
    vs.type.assign("box");
    vs.whl[0] = 100;
    vs.whl[1] = 200;
    vs.whl[2] = 400;

    Material mt;
    //define material - flat red
    mt.shaderN = Shader::spN_flat_ucolor;
    mt.primitiveType = GL_TRIANGLES;
    mt.uColor.setRGBA(255, 0, 0,255); //red
    pMB->useMaterial(&mt);

    pMB->buildBoxFace("front", &vs);
    pMB->buildBoxFace("back", &vs);
    pMB->buildBoxFace("top", &vs);
    pMB->buildBoxFace("bottom", &vs);
    pMB->buildBoxFace("left", &vs);

    mt.uColor.setRGBA(0, 0, 255,255); pMB->useMaterial(&mt); //blue
    pMB->buildBoxFace("right", &vs);

    pMB->buildDrawJobs(gameSubjs);

    delete pMB;

    return 1;
}
int TheGame::drawFrame() {
    myPollEvents();

    //glClearColor(0.0, 0.0, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    mat4x4 mProjection, mMVP;
    mat4x4_ortho(mProjection, -(float)screenSize[0] / 2, (float)screenSize[0] / 2, -(float)screenSize[1] / 2, (float)screenSize[1] / 2, 400.f, -400.f);

    //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, mProjection, pGS->ownModelMatrix);
        //render subject
        for (int i = 0; i < pGS->djTotalN; i++) {
            DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
            pDJ->execute((float*)mMVP, 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;
}

Let's take a closer look. Everything happens in the TheGame::getReady() (from line 20).

First, we are creating a GameSubj and setting initial position and rotation speeds (lines 19 to 26).

In line 28 we're creating ModelBuilder's instance, which we will use for building vertex arrays and DrawJobs.

The next line (29), pMB->useSubjN, instructs ModelBuilder which GameSubj model number is it for (which makes possible to build a few sub-models simultaneously in the future).

Lines 32 to 36 are setting a VirtualShape, so all following commands know what we are building,

Lines 40 to 43 are defining the Material.

pMB->useMaterial(&mt) command (line 43) tells ModelBuilder which material number we will use for all newly created vertices and triangles. Open Vertex01.h or Triangle01.h, you will see subjN and materialN references.

In line 45 we're starting to fill vertices and triangles arrays, the command buildBoxFace(). Let's take a closer look.

Open ModelBuilder.cpp, line 67. The idea is to create a simple 2D rectangle which faces us, and then rotate and shift it to it's final place. We are getting this rectangle sizes (width and height) from given VirtualShape (parameter pVS) and saving them into local VirtualShape vs. It's lines 72 to 115. Also in this code we are building transformMatrix which will move created rectangle to it's place.

For example, "front" side case, lines 72 to 79. This side faces us as is, so we'll need just to shift it along Z axis closer to us, in +Z direction. "+Z" is because OpenGL has right-handed coordinate system:

Line 116, startGroup(). We are about to create a set of actual vertices and triangles, then we will need to move them. The concept of "group" will allow us to move only what we need, without touching previously created vertices. startGroup() saves starting array positions of our future vertices and triangles.

Lines 118 to 139 - were we're actually creating vertices and triangles. This code allows to build not only simple 4 vertices rectangle, but also a "net".

Then, lines 141 to 146 - moving vertices to their final positions.

Now back to TheGame.cpp.

Please note, line 51: we are adjusting current Material (setting color to Blue), then calling pMB->useMaterial(&mt). This command checks if we have such material in ModelBuilder's materialsList already, if not - creates a new one and adds it to the list. And sets usingMaterialN variable to corresponding value. So all following vertices will have new materialN.

So, after line 52 we have 24 vertices and 12 triangles (6 sides, 36 indices), all with subjN 0 and with 2 different material numbers (first 5 sides with material #0, and the last one - with material #1).

Now the key function, pMB->buildDrawJobs(), line 54. This function takes these vertices and triangles arrays, extracts from them sub-arrays for each subj/material and builds corresponding DrawJobs. You can see it in ModelBuilder.cpp, line 197.

After extracting sub-arrays for a single DrawJob it rearranges vertices order and builds corresponding VBOs, EBOs and VAOs.

Line 56 - don't forget to delete created objects to avoid memory leaks.


Hope now everything is more-or-less clear. Let's try it.

5. Build an run. Result:


Now

Android

6. Re-start VS. Open C:\CPP\a997modeler\p_android\p_android.sln.


7. Under xEngine add New Filter, name - modeler


8. Under modeler add Existing Item

Browse to C:\CPP\engine\modeler

Pick all 6 files over there,

Add.


9. Add new path to project Properties:

Right-click on p_android.NativeActivity project, open project Properties, All Configurations / Active (ARM64), C/C++ -> General -> Additional Include Directories -> Edit,

add new line, browse to C:\CPP\engine\modeler,

Select Folder, Ok, Apply, Ok.


10. Switch on, unlock, plug in Android, allow debugging.

Re-build solution, run. Good.


Leave a Reply

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