I'm trying to build a simple 2D application on the Windows Console using C++ to display various primitives so I started with the most basic one: a triangle. I can display the triangle's outline and vertices correctly but I'm having problems with filling it. I have come across some rasterization algorithms presented here: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html and decided I would go for the first approach. I copied the code for the flat bottom triangle case, however this doesn't appear to work correctly on my application and I have no idea what's the cause of the problem.
My result:
main()
screen.fillFlatBottomTriangle(30,10, 10, 140/4, 256/2, 140/4, block_char, FG_CYAN);
screen.drawTriangle(30,10, 10, 140/4, 256/2, 140/4, block_char, BG_DARK_GREY);
screen.drawPixel(30,10,block_char,FG_RED);
screen.drawPixel(10,140/4,block_char,FG_GREEN);
screen.drawPixel(256/2,140/4,block_char,FG_MAGENTA);
fillFlatBottomTriangle
//http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html
void Screen::fillFlatBottomTriangle(int x1, int y1, int x2, int y2, int x3, int y3, short character, short color)
{
float invslope1 = (x2 - x1) / (y2 - y1);
float invslope2 = (x3 - x1) / (y3 - y1);
float curx1 = x1;
float curx2 = x1;
for (int scanlineY = y1; scanlineY <= y2; scanlineY++)
{
this->drawLine((int)curx1, scanlineY, (int)curx2, scanlineY, character, color);
curx1 += invslope1;
curx2 += invslope2;
}
}
drawLine
// https://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm)
void Screen::drawLine(int x1, int y1, int x2, int y2, short character, short color)
{
float delta_x = x2 - x1;
float delta_y = y2 - y1;
float step;
if (abs(delta_x) >= abs(delta_y))
step = abs(delta_x);
else
step = abs(delta_y);
delta_x = delta_x / step;
delta_y = delta_y / step;
float x = x1;
float y = y1;
for (int i = 1; i <= step; i++)
{
this->drawPixel((int)x, (int)y, character, color);
x += delta_x;
y += delta_y;
}
}
DrawTriangle
void Screen::drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, short character, short color)
{
this->drawLine(x1,y1, x2, y2, character, color);
this->drawLine(x1,y1, x3, y3, character, color);
this->drawLine(x2,y2, x3, y3, character, color);
}
See this part: (x2 - x1) / (y2 - y1)
Since all variables here are integers, this is integer division. That is, result of the division is being rounded towards 0. Assigning the result to a float doesn't change that.
To make this float division, you should convert at least one of the operands to float: (x2 - x1) / (float) (y2 - y1)
Your current code rounds one of the inverse slopes to 0, resulting in the vertical line at the left side. x values at the other side also don't increase fast enough, since that part was also rounded below.
Related
Hey I want to draw something when a form pops up. I noticed the OnPaint even but it doesn't show my drawing. When I add it to a button it does show what I wanted to draw.
This is what I draw:
void TSelectElementForm::DrawLinesInLayout()
{
EraseLayOut(); //draws a new square over the previous lines
int X_origin, Y_origin;
int X1, Y1, X2, Y2, X3, Y3, X4, Y4;
int VectorSize;
int scale;
X_origin = Layout->Left +5;
Y_origin = Layout->Top +5;
Canvas->Pen->Color=clRed;
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin,Y_origin+10);
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin+10,Y_origin);
VectorSize = ElementVector[Index].vectorLP.size();
scale = CalculateScale();
for(int i =0; i < VectorSize; i++)
{
int LPX = ElementVector[Index].vectorLP[i].X;
int LPY = ElementVector[Index].vectorLP[i].Y;
int BeginX = ElementVector[Index].el_beginX;
Canvas->Pen->Color=clLime;
X1 = X_origin + ((LPX - BeginX) / scale);
Y1 = Y_origin + (LPY / scale);
if(i == 0)
{
Canvas->MoveTo(X1, Y1);
}
else
{
Canvas->LineTo(X1, Y1);
}
}
}
So when I call this method in a button it works, but when I call this in a OnShow event or OnPaint event it doesn't draw anything.
The strange thing is, when I simply just create a new project with a drawLine() method(that draws 1 line) and a OnPaint event It DOES work. How is that possible?
So, simply 2 questions:
Is this method to long to draw it before my form pops up?
How can I get this drawn on my form right away when it pops up?
I am working on moving a small rectangle around a screen using the thumbstick on the xbox controller. I have it perfect for the mouse Input but I seem to have run into a problem with the controller. The square appears on the screen at (0,0) the deadzone for the controller. When I move the thumbstick the square move a specific amount and no further, when I lift my finger off the stick it goes back to (0,0). The code is below, its pretty simple code but cant get it to work. Thanks for any help.
Here is the first part.
void Graphics::rectangle(int x, int y)
{
{
r1.x1 = x;
r1.y1 = y;
r1.x2 = r1.x1 + 50;
r1.y2 = r1.y1 + 50;
}
}
Here is the second part.
LONG x = input->getGamepadThumbRX(0);
LONG y = input->getGamepadThumbRY(0);
float x1 = x/32767;
float y1 = y/32767;
if(x1 > 0 && x1 < 0.2f || x1 < 0 && x1 > -0.2f){
x1 = 0;
}
if(y1 > 0 && y1 < 0.2f || y1 < 0 && y1 > -0.2f){
y1 = 0;
}
float factor = 10;
int dx = int(factor * x1);
int dy = int(factor * y1);
graphics->rectangle(dx,dy);
I finally got a solution to this problem. I added the variables xNew and yNew that take the values of x and y and add them on to the existing values. This allowed me to move the square around the screen. xNew and yNew are initialize at the top of the class below is the code for the input from controller and the result in rectangle.
`
void Graphics::rectangle(int x, int y)
{
xNew += x;
yNew += -y;
{
r1.x1 = xNew;
r1.y1 = yNew;
r1.x2 = r1.x1 + 50;
r1.y2 = r1.y1 + 50;
}
}
void Game::update()
{
LONG x = input->getGamepadThumbRX(0);
LONG y = input->getGamepadThumbRY(0);
float x1 = x/32767;
float y1 = y/32767;
float factor = 10;
int dx = int(factor * x1);
int dy = int(factor * y1);
graphics->rectangle(dx,dy);
}`
The problem is the mouse is a "Relative Movement" Input and the thumb-stick is a "Absolute Movement" input. What you need to do is when you are working with the thumb-sticks your final line needs to be something like
graphics->rectangle(xOld + dx, yOld + dy);
where xOld and yOld is the location of the square before you applied the function.
Might be a little late, but I do have a very easy-to-use C++ XInput wrapper. Have a look here
It literally takes a few lines of code and you have full support for XInput controllers in your app :)
I have homework, ASCII line plot drawer. I must print graph into to file. All algoritms of Bresenham's line algoritm have function SetPixel ( x, y ); in loops. This function must print pixel by x and y. NCurses library is idealy solution for print on windows console, but I must print into file.txt. I think that Ncurses only print on window console. My question: How to implement SetPixel function for print into file in this code? :
void Line( const float x1, const float y1, const float x2, const float y2, const Color& color )
{
// Bresenham's line algorithm
const bool steep = (fabs(y2 - y1) > fabs(x2 - x1));
if(steep)
{
std::swap(x1, y1);
std::swap(x2, y2);
}
if(x1 > x2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
const float dx = x2 - x1;
const float dy = fabs(y2 - y1);
float error = dx / 2.0f;
const int ystep = (y1 < y2) ? 1 : -1;
int y = (int)y1;
const int maxX = (int)x2;
for(int x=(int)x1; x<maxX; x++)
{
if(steep)
{
SetPixel(y,x, color);
}
else
{
SetPixel(x,y, color);
}
error -= dy;
if(error < 0)
{
y += ystep;
error += dx;
}
}
}
To save this to a file, you will need to do some initial calculations before writing data to a file. I suggest that you create a data structure (perhaps an array) to keep track of each "pixel". For example, you can declare
char graph[100][100];
Each element of graph is either a space or a 'X'. Use Bresenham's line algoritm to calculate the elements in graph which should be set to 'X' and then write the array to a file.
First make an instance of a dynamic structure, preferably std::vector. I suggest to separate x and y for ease, e.g std::vector<int> x_points, y_points. Then, from your for loop body, record all coordinates i.e that (x,y). Then create a function that writes all the data from your vector into a file.
You don't need NCurses to save ASCII, just create a plain text file and save the output of the Bresenham's algorithm in there. I suggest you use a different implementation of the algorithm aswell.
How can i draw a 2D line between two given points using SDL c++ library. I don't want to use any other external libraries like SDL_draw or SDL_gfx .
Up-to-date answer for the coders who are struggling with the same issue.
In SDL2, there are a couple of functions in SDL_Render.h to achive this without implementing your own line drawing engine or using an external library.
You likely want to use:
int SDL_RenderDrawLine( SDL_Renderer* renderer, int x1, int y1, int x2, int y2 );
Where renderer is the renderer you created before, and x1 & y1 are for the beginning, and x2 & y2 for the ending.
There is also an alternative function where you could draw a line with multiple points right away, instead of calling the mentioned function several times:
int SDL_RenderDrawPoints( SDL_Renderer* renderer, const SDL_Point* points, int count );
Where renderer is the renderer you created before, points is a fixed-array of the known points, and count the amount of points in that fixed-array.
All mentioned functions give a -1 back when error, and 0 on success.
Rosetta Code has some examples:
void Line( float x1, float y1, float x2, float y2, const Color& color )
{
// Bresenham's line algorithm
const bool steep = (fabs(y2 - y1) > fabs(x2 - x1));
if(steep)
{
std::swap(x1, y1);
std::swap(x2, y2);
}
if(x1 > x2)
{
std::swap(x1, x2);
std::swap(y1, y2);
}
const float dx = x2 - x1;
const float dy = fabs(y2 - y1);
float error = dx / 2.0f;
const int ystep = (y1 < y2) ? 1 : -1;
int y = (int)y1;
const int maxX = (int)x2;
for(int x=(int)x1; x<maxX; x++)
{
if(steep)
{
SetPixel(y,x, color);
}
else
{
SetPixel(x,y, color);
}
error -= dy;
if(error < 0)
{
y += ystep;
error += dx;
}
}
}
You can use any of the line drawing algorithms.
Some common and easy ones are:
Digital Differential Analyzer (DDA)
Bresenham's line algorithm
Xiaolin Wu's line algorithm
Im working on my OpenGL task, and next stage is loading models and producing shadows using shadow volumes algorithm. I do it in 3 stages -
setConnectivity - finding
neighbours of each triangle and
storing their indices in neigh
parameter of each triangle,
markVisible(float* lp) - if lp
represents vector of light's
position, it marks triangles as
visible = true or visible =
false depending on dot production
of its normal vector and light
position,
markSilhoutte(float *lp) - marking silhouette edges and building the volume itself, extending silhouette to infinity(100 units is enough) in the direction opposite to light.
I checked all stages, and can definitely say that its all ok with first two, so the problem is in third function, which i included in my question. I use the algorithm introduced in this tutorial: http://www.3dcodingtutorial.com/Shadows/Shadow-Volumes.html
Briefly, edge is included in silhouette if it belongs to the visible triangle and non-visible triangle at the same time.
Here is a pair of screenshots to show you whats wrong:
http://prntscr.com/17dmg , http://prntscr.com/17dmq
As you can see, green sphere represents light's position, and these ugly green-blue polygons are faces of "shadow volume". You can also see, that im applying this function to the model of cube, and one of volume's side is missing(its not closed, but i should be). Can someone suggest whats wrong with my code and how can i fix it? Here goes the code i promised to include(variables names are self-explanatory, i suppose, but if you dont think so i can add description for each of them):
void Model::markSilhouette(float* lp){
glBegin(GL_QUADS);
for ( int i = 0; i < m_numMeshes; i++ )
{
for ( int t = 0; t < m_pMeshes[i].m_numTriangles; t++ )
{
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[t];
Triangle* pTri = &m_pTriangles[triangleIndex];
if (pTri->visible){
for(int j=0;j<3;j++){
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[pTri->neigh[j]-1];
Triangle* pTrk = &m_pTriangles[triangleIndex];
if(!pTrk->visible){
int p1j=pTri->m_vertexIndices[j];
int p2j=pTri->m_vertexIndices[(j+1)%3];
float* v1=m_pVertices[p1j].m_location;
float* v2=m_pVertices[p2j].m_location;
float x1=m_pVertices[p1j].m_location[0];
float y1=m_pVertices[p1j].m_location[1];
float z1=m_pVertices[p1j].m_location[2];
float x2=m_pVertices[p2j].m_location[0];
float y2=m_pVertices[p2j].m_location[1];
float z2=m_pVertices[p2j].m_location[2];
t=100;
float xl1=(x1-lp[0])*t;
float yl1=(y1-lp[1])*t;
float zl1=(z1-lp[2])*t;
float xl2=(x2-lp[0])*t;
float yl2=(y2-lp[1])*t;
float zl2=(z2-lp[2])*t;
glColor3f(0,0,1);
glVertex3f(x1 + xl1,
y1 + yl1,
z1 + zl1);
glVertex3f(x1,
y1,
z1);
glColor3f(0,1,0);
glVertex3f(x2 + xl2,
y2 + yl2,
z2 + zl2);
glVertex3f(x2,
y2,
z2);
}
}
}
}
}
glEnd();
}
I've found it. It looks like if you dont see an obvious algorithm mistake for a few days, then you've made a f*cking stupid mistake.
My triangle index variable is called t. Guess what? My extending vector length is also called t, and they are in the same scope, and i set t=100 after FIRST visible triangle :D So now volumes look like this:
outside http://prntscr.com/17l3n
inside http://prntscr.com/17l40
And it looks good for all light positions(acceptable by shadow volumes aglorithm, of course). So the working code for drawing a shadow volume is the following:
void Model::markSilouette(float* lp){
glDisable(GL_LIGHTING);
glPointSize(4.0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_FILL);
glBegin(GL_QUADS);
for ( int i = 0; i < m_numMeshes; i++ )
{
for ( int t = 0; t < m_pMeshes[i].m_numTriangles; t++ )
{
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[t];
Triangle* pTri = &m_pTriangles[triangleIndex];
if (pTri->visible){
for(int j=0;j<3;j++){
Triangle* pTrk;
if(pTri->neigh[j]){
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[pTri->neigh[j]-1];
pTrk = &m_pTriangles[triangleIndex];
}
if((!pTri->neigh[j]) || !pTrk->visible){
int p1j=pTri->m_vertexIndices[j];
int p2j=pTri->m_vertexIndices[(j+1)%3];
float* v1=m_pVertices[p1j].m_location;
float* v2=m_pVertices[p2j].m_location;
float x1=m_pVertices[p1j].m_location[0];
float y1=m_pVertices[p1j].m_location[1];
float z1=m_pVertices[p1j].m_location[2];
float x2=m_pVertices[p2j].m_location[0];
float y2=m_pVertices[p2j].m_location[1];
float z2=m_pVertices[p2j].m_location[2];
float f=100; // THE PROBLEM WAS HERE
float xl1=(x1-lp[0])*f;
float yl1=(y1-lp[1])*f;
float zl1=(z1-lp[2])*f;
float xl2=(x2-lp[0])*f;
float yl2=(y2-lp[1])*f;
float zl2=(z2-lp[2])*f;
glColor3f(0,0,0);
glVertex3f(x1 + xl1,
y1 + yl1,
z1 + zl1);
glVertex3f(x1,
y1,
z1);
glVertex3f(x2,
y2,
z2);
glVertex3f(x2 + xl2,
y2 + yl2,
z2 + zl2);
}
}
}
}
}
glEnd();
}
I think everything is ok, you are just rendering volume without depth test =)