I am trying to draw something like this in OpenGL 2.1 using GLU and GLUT primitives such as gluCylinder, glutSolidCylinder, etc.
Basically it's a solid cylinder with a hole in the middle. So far I was unable to achieved it.
I also tried to import wavefront obj from Blender to my program in C++ but what I got was this.
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++) {
int nx = atoi(wheel->faces.at(i).at(0).substr(wheel->faces.at(i).at(0).find_last_of('/') + 1).c_str()) - 1;
int ny = atoi(wheel->faces.at(i).at(1).substr(wheel->faces.at(i).at(1).find_last_of('/') + 1).c_str()) - 1;
int nz = atoi(wheel->faces.at(i).at(2).substr(wheel->faces.at(i).at(2).find_last_of('/') + 1).c_str()) - 1;
int vx = atoi(wheel->faces.at(i).at(0).substr(0, wheel->faces.at(i).at(0).find_first_of('/')).c_str()) - 1;
int vy = atoi(wheel->faces.at(i).at(1).substr(0, wheel->faces.at(i).at(1).find_first_of('/')).c_str()) - 1;
int vz = atoi(wheel->faces.at(i).at(2).substr(0, wheel->faces.at(i).at(2).find_first_of('/')).c_str()) - 1;
glNormal3f(wheel->normals.at(nx).x, wheel->normals.at(ny).y, wheel->normals.at(nz).z);
glVertex3f(wheel->vertices.at(vx).x, wheel->vertices.at(vy).y, wheel->vertices.at(vz).z);
}
glEnd();
The parsing should be correct, I double checked it line by line. I guess it's the wrong way I tried to draw it.
However I don't want to use obj since my way of drawing makes animation slow (I am fairly new to OpenGL). And I don't want to use OpenGL 3+ either.
Any suggestions?
In your code you're drawing a single point with one face, however a face (a triangle) here is supposed to have three points.
Your may try this code:
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++) {
int v0 = atoi(wheel->faces.at(i).at(0).substr(0, wheel->faces.at(i).at(0).find_first_of('/')).c_str()) - 1;
int v1 = atoi(wheel->faces.at(i).at(1).substr(0, wheel->faces.at(i).at(1).find_first_of('/')).c_str()) - 1;
int v2 = atoi(wheel->faces.at(i).at(2).substr(0, wheel->faces.at(i).at(2).find_first_of('/')).c_str()) - 1;
glVertex3f(wheel->vertices.at(v0).x, wheel->vertices.at(v0).y, wheel->vertices.at(v0).z);
glVertex3f(wheel->vertices.at(v1).x, wheel->vertices.at(v1).y, wheel->vertices.at(v1).z);
glVertex3f(wheel->vertices.at(v2).x, wheel->vertices.at(v2).y, wheel->vertices.at(v2).z);
}
glEnd();
I removed the normal part since I don't think you would need it for now. You may add it later.
EDTIED:
This seems better:
glBegin(GL_TRIANGLES);
for(int i = 0; i < wheel->faces.size(); i++)
for (int j = 0; j < 3; j++)
{
int v_id = atoi(wheel->faces.at(i).at(j).substr(0, wheel->faces.at(i).at(j).find_first_of('/')).c_str()) - 1;
glVertex3f(wheel->vertices.at(v_id).x, wheel->vertices.at(v_id).y, wheel->vertices.at(v_id).z);
}
glEnd();
Found for my self this small lib for opening obj files.
Drawing loaded mesh with next code:
glBegin(GL_TRIANGLES);
for (size_t f = 0; f < shapes[0].mesh.indices.size(); f++) {
glVertex3f(shapes[0].mesh.positions[3*f+0],
shapes[0].mesh.positions[3*f+1],
shapes[0].mesh.positions[3*f+2]);
}
glEnd();
Load mesh:
std::vector<tinyobj::shape_t> shapes;
int loadMesh(const char *file) {
const char* basepath = NULL;
std::string err = tinyobj::LoadObj(shapes, file, basepath);
if (!err.empty()) {
std::cerr << err << std::endl;
return 0;
}
std::cout << "# of shapes : " << shapes.size() << std::endl;
return 1;
}
Related
I have a set of 193 images(Img0, Img1, Img2,...Imgx) of the same size(40x40) which needs to be overlayed onto the first image in this(that is Img0). I tried different solutions available here but it's working only for overlaying 2 images. I want this to be done using OpenCV& C++. I am pasting the code below.
#include <opencv2\highgui\highgui.hpp>
#include <iostream>
#include <stdio.h>using namespace cv;
using namespace std;
int main(int argc, char **argv)
{
string arrOfimages[193];
stringstream str;
int a = 1;
for (int index = 0; index < 193 ; index++)
{
str << "C:/<path>/Img" << a << ".bmp";
arrOfimages[index] = str.str();
str.str("");
a++;
}
Mat src1;
Mat srcOut;
src1 = imread(arrOfimages[0]);
imshow("base", src1);
for(int i = 0; i < 193; i++)
{
addWeighted(src1, 0.5, imread(arrOfimages[i+1]), 0.5, 0.0, srcOut, -1);
}
imshow("summation", srcOut);
waitKey(0);
return 0;
}
}
I am not able to figure out what I am doing wrong here. Can someone help? Thanks a lot!
I am getting out of memory errors, assertion failed etc.
The problem is here:
for(int i = 0; i < 193; i++)
{
addWeighted(src1, 0.5, imread(arrOfimages[i+1]), 0.5, 0.0, srcOut, -1);
}
You are adding +1 to the i which at the end will be 193 and out of bounds. You should just use int i=1 instead, like this:
for(int i = 1; i < 193; i++)
{
addWeighted(src1, 0.5, imread(arrOfimages[i]), 0.5, 0.0, srcOut, -1);
}
Also, you add src1 and a new image and copy the output to srcOut... And in the next iteration you do it but with a next image, at the end you will have in srcOut only the result of adding the first and the last image. If you want to overlay all of them it should be something more like:
for(int i = 1; i < 193; i++)
{
addWeighted(src1, 0.5, imread(arrOfimages[i]), 0.5, 0.0, srcOut, -1);
src1 = srcOut;
}
Just as a note, the previous loop:
stringstream str;
int a = 1;
for (int index = 0; index < 193 ; index++)
{
str << "C:/<path>/KCF" << a << ".bmp";
arrOfimages[index] = str.str();
str.str("");
a++;
}
can be written like this:
for (int index = 0; index < 193 ; index++)
{
stringstream str;
str << R"(C:\<path>\KCF)" << (a+1) << ".bmp";
arrOfimages[index] = str.str();
}
Update:
You would like to have is a blended image that preserves the black spots. To do that you can use bitwise_and from opencv like this:
cv::Mat srcOut = cv::imread(arrOfImages[0], cv::IMREAD_GRAYSCALE);
for(int i = 1; i < 193; i++)
{
cv::bitwise_and(srcOut, cv::imread(arrOfImages[1], cv::IMREAD_GRAYSCALE), srcOut)
}
So i'm learning c++ and decided to try SDL out so I can get used to working with classes. The game is basically a space shooter game , so i wrote some code that permits the "shooting of bullets".The code works fine on my computer , the bullets travel from point X to point Y without a problem.When i tried to run the game on 2 different computers the "bullets" are very buggy (they disappear and reapper at random and such) .The rest of the game works fine. I tested the framerate on both computers and the fps is over 300
Does anyone know what causes this and how to solve it?
Here's some code (for the bullets) if you need it for some reason:
//LOAD BULLET IF FREE
if(bulletEnemyCooldown.getTicks() > bulletEnemyCooldownVar)
{
bulletEnemyCooldown.reset();
for (int i = 0; i < kShips; i++)
{
if(!evilShip[i].getIfDead())
{
for(int j=0; j<99; j++)
{
if (evilShip[i].getIfBulletFree(j))
{
evilShip[i].setKBullets(evilShip[i].getkBullets() + 1);
evilShip[i].setRectPos(evilShip[i].getPosX(),evilShip[i].getPosY(),j);
evilShip[i].setIfBulletFree(false,j);
break;
}
}
}
}
}
//CHANGE BULLET POS IF SLOT OCC AND RENDER
for (int i2 = 0; i2 <= kShips; i2++)
{
if(!evilShip[i2].getIfDead())
{
for(int j2 = 0; j2<evilShip[i2].getkBullets(); j2++)
{
if (evilShip[i2].getIfBulletFree(j2) == false)
{
evilShip[i2].setBulletRectY( evilShip[i2].getBulletRectY(j2) + bulletS,j2);
enemy_bullet_texture.render(gRenderer, evilShip[i2].getBulletRectX(j2), evilShip[i2].getBulletRectY(j2));
evilShip[i2].setBulletDis(evilShip[i2].getBulletDis(j2) + bulletS,j2);
}
}
}
}
//CHECK FOR ANY COLLISION AND FREE BULLET IF FOUND
for (int i = 0; i < kShips; i++)
{
for (int j = 0; j < evilShip[i].getkBullets(); j++)
{
if(!evilShip[i].getIfDead())
{
if (checkCollision(evilShip[i].getBulletPosRect(j), mainShip.getmCollider()))
{
evilShip[i].setIfBulletFree(true,j);
evilShip[i].setBulletDis(0,j);
evilShip[i].setKBullets( evilShip[i].getkBullets() - 1);
evilShip[i].setRectPos(0,0,j);
boolMainShipContact = true;
mainShip.curSetHP(mainShip.curGetHP() - evilShip[i].curGetATK());
kShipContact = i;
score -= rand()%(10 * currentLevel);
}
}
// FREE BULLET IF FOUND OFF SCREEN
if (evilShip[i].getBulletDis(j) > SCREEN_HEIGHT)
{
evilShip[i].setIfBulletFree(true,j);
evilShip[i].setBulletDis(0,j);
evilShip[i].setKBullets(evilShip[i].getkBullets() - 1);
}
}
}
I'm trying to rotate a polygon in place, but it keeps revolving instead.
To rotate, I calculate the center by finding the average location of each vertex. I call the rotation functions and then call a translation using the center to move it to the middle of the screen. It does end up centered, but it still rotates as if it's not. Any ideas as to what I could be doing wrong?
Here's my code:
void Polygon::DrawPolygon()
{
glPushMatrix();
glLoadMatrixf(matrix);
glTranslatef(displace[0], displace[1], displace[2]);
glRotatef(rotation[0], 1, 0, 0);
glRotatef(rotation[1], 0, 1, 0);
glRotatef(rotation[2], 0, 0, 1);
glTranslatef(-displace[0], -displace[1], displace[2]);
displace[0] = 0; displace[1] = 0; displace[2] = 0;
glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
DrawMaterial();
DrawFaces();
ConnectFaces();
glPopMatrix();
}
Here's how I calculate the center:
void Polygon::FindCenter()
{
float x = 0;
float y = 0;
float z = 0;
for(int j = 0; j < 2; j++)
{
for(int i = 0; i < vertexCount; i++)
{
x += vertices[i][0];
y += vertices[i][1];
z += vertices[i][2] + extrusionDistance * j;
}
}
x = x / (vertexCount * 2);
y = y / (vertexCount * 2);
z = z / (vertexCount * 2);
displace[0] = x;
displace[1] = y;
displace[2] = z;
}
Because of the way my extrusion works I don't need to add the x and y for the vertices of both faces, but I did anyway to keep it consistent.
Here is how I draw the shape:
void Polygon::DrawFaces()
{
for(int j = 0; j < 2; j++)
{
glBegin(GL_POLYGON);
for(int i = 0; i < vertexCount; i++)
{
glVertex3f(vertices[i][0], vertices[i][1], j*extrusionDistance);
}
glEnd();
}
}
void Polygon::ConnectFaces()
{
for(int i = 0; i < vertexCount; i++)
{
glBegin(GL_POLYGON);
glVertex3f(vertices[i][0], vertices[i][1], 0);
glVertex3f(vertices[i][0], vertices[i][1], extrusionDistance);
glVertex3f(vertices[(i+1)%vertexCount][0], vertices[(i+1)%vertexCount][1], extrusionDistance);
glVertex3f(vertices[(i+1)%vertexCount][0], vertices[(i+1)%vertexCount][1], 0);
glEnd();
}
}
I see a few things that stand out to me as being odd:
1) You're calling glLoadMatrixf(matrix) before the call to glTranslate() and glRotate(). Depending on what's in the matrix you're loading, that changes things.
2) You're FindCenter() method calculates the center by including the vertex[i][2] in the calculation of z, but when you actually draw the faces in DrawFaces(), you don't include the vertex[i][2] part, just the extrusion * j part. So you're not drawing the same thing that you're calculating the center of.
I had a previous question about a stack overflow error and switch to vectors for my arrays of objects. That question can be referenced here if needed: How to get rid of stack overflow error
My current question is however, how do I speed up the initialization of the vectors. My current method currently takes ~15 seconds. Using arrays instead of vectors it took like a second with a size of arrays small enough that didn't throw the stack overflow error.
Here is how I am initializing it:
in main.cpp I initialize my dungeon object:
dungeon = Dungeon(0, &textureHandler, MIN_X, MAX_Y);
in my dungeon(...) constructor, I initialize my 5x5 vector of rooms and call loadDungeon:
Dungeon::Dungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
currentRoomRow = 0;
currentRoomCol = 0;
for (int r = 0; r < MAX_RM_ROWS; ++r)
{
rooms.push_back(vector<Room>());
for (int c = 0; c < MAX_RM_COLS; ++c)
{
rooms[r].push_back(Room());
}
}
loadDungeon(dungeonID, textureHandler, topLeftX, topLeftY);
}
my Room constructor populates my 30x50 vector of cells (so I can set them up in the loadDungeon function):
Room::Room()
{
for (int r = 0; r < MAX_ROWS; ++r)
{
cells.push_back(vector<Cell>());
for (int c = 0; c < MAX_COLS; ++c)
{
cells[r].push_back(Cell());
}
}
}
My default cell constructor is simple and isn't doing much but I'll post it anyway:
Cell::Cell()
{
x = 0;
y = 0;
width = 16;
height = 16;
solid = false;
texCoords.push_back(0);
texCoords.push_back(0);
texCoords.push_back(1);
texCoords.push_back(0);
texCoords.push_back(1);
texCoords.push_back(1);
texCoords.push_back(0);
texCoords.push_back(1);
}
And lastly my loadDungeon() function will set up the cells. Eventually this will read from a file and load the cells up but for now I would like to optimize this a bit if possible.
void Dungeon::loadDungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
int xOffset = 0;
int yOffset = 0;
for (int r = 0; r < MAX_RM_ROWS; ++r)
{
for (int c = 0; c < MAX_RM_COLS; ++c)
{
for (int cellRow = 0; cellRow < rooms[r][c].getMaxRows(); ++cellRow)
{
xOffset = 0;
for (int cellCol = 0; cellCol < rooms[r][c].getMaxCols(); ++cellCol)
{
rooms[r][c].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("grass"));
xOffset += textureHandler->getSpriteWidth();
}
yOffset += textureHandler->getSpriteHeight();
}
}
}
currentDungeon = dungeonID;
currentRoomRow = 0;
currentRoomCol = 0;
}
So how can I speed this up so it doesn't take ~15 seconds to load up every time. I feel like it shouldn't take 15 seconds to load a simple 2D game.
SOLUTION
Well my solution was to use std::vector::reserve call (rooms.reserve in my code and it ended up working well. I changed my function Dungeon::loadDungeon to Dungeon::loadDefaultDungeon because it now loads off a save file.
Anyway here is the code (I got it down to about 4-5 seconds from ~15+ seconds in debug mode):
Dungeon::Dungeon()
{
rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS);
currentDungeon = 0;
currentRoomRow = 0;
currentRoomCol = 0;
}
void Dungeon::loadDefaultDungeon(TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
int xOffset = 0;
int yOffset = 0;
cerr << "Loading default dungeon..." << endl;
for (int roomRow = 0; roomRow < MAX_RM_ROWS; ++roomRow)
{
for (int roomCol = 0; roomCol < MAX_RM_COLS; ++roomCol)
{
rooms.push_back(Room());
int curRoom = roomRow * MAX_RM_COLS + roomCol;
for (int cellRow = 0; cellRow < rooms[curRoom].getMaxRows(); ++cellRow)
{
for (int cellCol = 0; cellCol < rooms[curRoom].getMaxCols(); ++cellCol)
{
rooms[curRoom].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("default"), "default");
xOffset += textureHandler->getSpriteWidth();
}
yOffset += textureHandler->getSpriteHeight();
xOffset = 0;
}
cerr << " room " << curRoom << " complete" << endl;
}
}
cerr << "default dungeon loaded" << endl;
}
Room::Room()
{
cells.reserve(MAX_ROWS * MAX_COLS);
for (int r = 0; r < MAX_ROWS; ++r)
{
for (int c = 0; c < MAX_COLS; ++c)
{
cells.push_back(Cell());
}
}
}
void Room::setupCell(int row, int col, float x, float y, float width, float height, bool solid, /*std::array<float, 8>*/ vector<float> texCoords, string texName)
{
cells[row * MAX_COLS + col].setup(x, y, width, height, solid, texCoords, texName);
}
void Cell::setup(float x, float y, float width, float height, bool solid, /*std::array<float,8>*/ vector<float> t, string texName)
{
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->solid = solid;
for (int i = 0; i < t.size(); ++i)
this->texCoords.push_back(t[i]);
this->texName = texName;
}
It seems wasteful to have so many dynamic allocations. You can get away with one single allocation by flattening out your vector and accessing it in strides:
std::vector<Room> rooms;
rooms.resize(MAX_RM_ROWS * MAX_RM_COLS);
for (unsigned int i = 0; i != MAX_RM_ROWS; ++i)
{
for (unsigned int j = 0; j != MAX_RM_COLS; ++j)
{
Room & r = rooms[i * MAX_RM_COLS + j];
// use `r` ^^^^^^^^^^^^^^^^^^^-----<< strides!
}
}
Note how resize is performed exactly once, incurring only one single allocation, as well as default-constructing each element. If you'd rather construct each element specifically, use rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS); instead and populate the vector in the loop.
You may also wish to profile with rows and columns swapped and see which is faster.
Since it seems that your vectors have their size defined at compile time, if you can use C++11, you may consider using std::array instead of std::vector. std::array cannot be resized and lacks many of the operations in std::vector, but is much more lightweight and it seems a good fit for what you are doing.
As an example, you could declare cells as:
#include <array>
/* ... */
std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> cells;
UPDATE: since a locally defined std::array allocates its internal array on the stack, the OP will experience a stack overflow due to the considerably large size of the arrays. Still, it is possible to use an std::array (and its benefits compared to using std::vector), by allocating the array on the heap. That can be done by doing something like:
typedef std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> Map;
Map* cells;
/* ... */
cells = new Map();
Even better, smart pointers can be used:
#include <memory>
/* ... */
std::unique_ptr<Map> cells;
cells = std::unique_ptr(new Map());
I am teaching myself OpenGL game programming from tutorials on the net. I want to draw a half torus, such that it can look like a gateway. How can I do this, does any one know the math involved? most tutorials online show how to draw a full torus.
Here's an answer that adapts the OpenGL Redbook torus.c tutorial
Here's their code for drawing a torus:
static void torus(int numc, int numt)
{
int i, j, k;
double s, t, x, y, z, twopi;
twopi = 2 * PI_;
for (i = 0; i < numc; i++) {
glBegin(GL_QUAD_STRIP);
for (j = 0; j <= numt; j++) {
for (k = 1; k >= 0; k--) {
s = (i + k) % numc + 0.5;
t = j % numt;
x = (1+.1*cos(s*twopi/numc))*cos(t*twopi/numt);
y = (1+.1*cos(s*twopi/numc))*sin(t*twopi/numt);
z = .1 * sin(s * twopi / numc);
glVertex3f(x, y, z);
}
}
glEnd();
}
}
What this does is draws a volume of rotation. You can use this same idea, except stop this loop halfway through (i.e.,
for(i = 0; i < numc/2; i++)
)
Set a clip plane appropriately ([0,0,1,0] ought to work, assuming +Z is 'up') and draw a full torus.