c++ triangle rasterizer z buffer issue - c++

I'm quite new to c++ and coding in general, so excuse what may be strange looking c++. I've been making my own 3d engine for the past while and have run into an issue with the z buffer. All the pixels work out great usually, but there are certain perspectives where the z buffer doesn't seem to be working.
I think it has something to do with floating point rounding, but I'm not experienced enough to say. Ill include the code and a picture of the issue if anyone is cool enough to have a look. Thanks a bunch if so!
void Triangle::draw(Screen screen, Camera& camera, float* zbuffer) {
//drawTriangle(screen, camera, sP1, sP2, sP3, color, mP1, mP2, mP3, zbuffer);
draw3(screen, camera, zbuffer);
}
void Triangle::draw3(Screen screen, Camera& camera, float* zbuffer) {
//check if the triangle is visible on screen
if (sP1.x < 0 && sP2.x < 0 && sP3.x < 0 ||
sP1.y < 0 && sP2.y < 0 && sP3.y < 0 ||
sP1.x > Screen::SCREEN_WIDTH && sP2.x > Screen::SCREEN_WIDTH && sP3.x > Screen::SCREEN_WIDTH ||
sP1.y > Screen::SCREEN_HEIGHT && sP2.y > Screen::SCREEN_HEIGHT && sP3.y > Screen::SCREEN_HEIGHT) {
return;
}
//sort points based on height
Point2D temp2d; Point3D temp3d;
if (sP1.y > sP2.y) { temp2d = sP1; sP1 = sP2; sP2 = temp2d; temp3d = mP1; mP1 = mP2; mP2 = temp3d; }
if (sP1.y > sP3.y) { temp2d = sP1; sP1 = sP3; sP3 = temp2d; temp3d = mP1; mP1 = mP3; mP3 = temp3d; }
if (sP2.y > sP3.y) { temp2d = sP2; sP2 = sP3; sP3 = temp2d; temp3d = mP2; mP2 = mP3; mP3 = temp3d; }
//dont draw triangle with no area
if (sP1.y == sP3.y) { return; }
float longSlope = (sP1.x - sP3.x) / (sP3.y - sP1.y);
//if the triangle has a flat top, dont calculate the short sloap
float shortSlope = 0;
float bottomSloap = 0;
if (sP1.y != sP2.y) { shortSlope = (sP1.x - sP2.x) / (sP2.y - sP1.y); }
if (sP2.y != sP3.y) { bottomSloap = (sP2.x - sP3.x) / (sP3.y - sP2.y); }
//start at the peak of the triangle and work down
float leftX = sP1.x;
float rightX = sP1.x;
float bisectX;
float leftSlope = longSlope;
float rightSlope = shortSlope;
float end = sP2.y;
bool swap = false;
int y = sP1.y;
if (sP1.y > 0) { y = sP1.y + 1; }
double totalArea = areaOfTriangle(sP1, sP2, sP3), areaP1P2P4, areaP1P3P4, areaP2P3P4;
float ratioP1, ratioP2, ratioP3;
float mP4Dist;
//make sure leftSlope corrosponds to the left most sloap
bisectX = sP1.x - (sP1.x - sP3.x) / (sP3.y - sP1.y) * (sP2.y - sP1.y);
if (sP2.x < bisectX) {
leftSlope = shortSlope;
rightSlope = longSlope;
swap = true;
}
if (sP1.y == sP2.y) { y += 1; }
if (sP1.y != sP2.y) {
for (y; y < sP2.y; ++y) {
for (int x = leftX; x < rightX - 1; x++) {
sP4.x = x;
sP4.y = y;
//find bary coords
areaP1P2P4 = areaOfTriangle(sP1, sP2, sP4);
areaP1P3P4 = areaOfTriangle(sP1, sP3, sP4);
areaP2P3P4 = areaOfTriangle(sP2, sP3, sP4);
ratioP1 = areaP2P3P4 / totalArea;
ratioP2 = areaP1P3P4 / totalArea;
ratioP3 = areaP1P2P4 / totalArea;
mP4.x = mP1.x * ratioP1 + mP2.x * ratioP2 + mP3.x * ratioP3;
mP4.y = mP1.y * ratioP1 + mP2.y * ratioP2 + mP3.y * ratioP3;
mP4.z = mP1.z * ratioP1 + mP2.z * ratioP2 + mP3.z * ratioP3;
mP4Dist = getLineDist3d(mP4, camera.map);
if (x > 0 && x < Screen::SCREEN_WIDTH && y > 0 && y < Screen::SCREEN_HEIGHT) {
if (mP4Dist < screen.m_zbuffer[x + y * Screen::SCREEN_WIDTH]) {
screen.setPixel(x, y, color);
screen.setZBuffer(x, y, mP4Dist);
}
}
}
leftX -= leftSlope;
rightX -= rightSlope;
}
}
if (swap) { leftSlope = bottomSloap; leftX = sP2.x; }
else { rightSlope = bottomSloap; rightX = sP2.x; }
if (sP2.y != sP3.y) {
for (y; y < sP3.y; y++) {
for (int x = leftX; x < rightX - 1; x++) {
sP4.x = x;
sP4.y = y;
//find bary coords
areaP1P2P4 = areaOfTriangle(sP1, sP2, sP4);
areaP1P3P4 = areaOfTriangle(sP1, sP3, sP4);
areaP2P3P4 = areaOfTriangle(sP2, sP3, sP4);
ratioP1 = areaP2P3P4 / totalArea;
ratioP2 = areaP1P3P4 / totalArea;
ratioP3 = areaP1P2P4 / totalArea;
mP4.x = mP1.x * ratioP1 + mP2.x * ratioP2 + mP3.x * ratioP3;
mP4.y = mP1.y * ratioP1 + mP2.y * ratioP2 + mP3.y * ratioP3;
mP4.z = mP1.z * ratioP1 + mP2.z * ratioP2 + mP3.z * ratioP3;
mP4Dist = getLineDist3d(mP4, camera.map);
if (x > 0 && x < Screen::SCREEN_WIDTH && y > 0 && y < Screen::SCREEN_HEIGHT) {
if (mP4Dist < screen.m_zbuffer[x + y * Screen::SCREEN_WIDTH]) {
screen.setPixel(x, y, color);
screen.setZBuffer(x, y, mP4Dist);
}
}
}
leftX -= leftSlope;
rightX -= rightSlope;
}
}
}
float areaOfTriangle(Point2D p1, Point2D p2, Point2D p3) {
//black box Heron's Formula finds area of triangle based on 3 points
float area = 0.5 * abs((p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)));
return area;
}
red triangle clipping into brown triangle

Related

How to repair the spacing between blocks when I rotate them?

Hello I'm currently trying to rotate blocks that are within a square. But when I start to rotate them it starts to create weird spaces between blocks that I don't want. Could you help me to fix the problem of spaces beetween blocks? Here are some code and screenshots how does it look.
https://imgur.com/a/BLuO7FF
I have already checked if all angles and radiuses are calculated correctly and I don't see any problem there.
World.h
#pragma once
#include <SFML/Graphics.hpp>
class World
{
public:
World(sf::Vector2f Wpos);
float AngleToRadian(int angle);
void RotateWorld();
void draw(sf::RenderWindow &window);
sf::Texture tx;
sf::Sprite** Block;
sf::Vector2f Pos;
sf::Vector2i Size;
float** radius;
float** angle;
};
World.cpp
#include "World.h"
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#define PI 3.14159
World::World(sf::Vector2f Wpos)
{
Pos = Wpos;
Size = sf::Vector2i(10, 10);
Block = new sf::Sprite*[Size.y];
radius = new float*[Size.y];
angle = new float*[Size.y];
for (int i = 0; i < Size.y; i++)
{
Block[i] = new sf::Sprite[Size.x];
radius[i] = new float[Size.x];
angle[i] = new float[Size.x];
}
tx.loadFromFile("Img/Block.png");
sf::Vector2i off(Size.x * tx.getSize().x / 2, Size.y * tx.getSize().y / 2); //tx size is 32px x 32px
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(tx.getSize().x / 2, tx.getSize().y / 2);
Block[y][x].setPosition(x*tx.getSize().x + Wpos.x - off.x + Block[y][x].getOrigin().x, y*tx.getSize().y + Wpos.y - off.y + Block[y][x].getOrigin().y);
radius[y][x] = sqrt(pow(Pos.x - Block[y][x].getPosition().x, 2) + pow(Pos.y - Block[y][x].getPosition().y, 2));
angle[y][x] = (atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI;
if ((atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI < 0)
{
angle[y][x] += 360;
}
//angle[y][x] = round(angle[y][x]);
/*radius[y][x] = round(radius[y][x]);*/
}
}
}
void World::RotateWorld()
{
float dx = 0, dy = 0;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]++;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(-1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]--;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
}
I expected it to rotate withouth any spaces between. I would be really thankfull if someone would help me.
I would try with setting the origin of the sf::Sprite using it's getGlobalBounds() method instead of the sf::Texture size getter.
The difference seems minor and something like that might be the case.
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(Block[y][x].getGlobalBouds().width / 2, Block[y][x].getGlobalBouds().height / 2);
Block[y][x].setPosition(x*Block[y][x].getGlobalBouds().width + Wpos.x - off.x + Block[y][x].getOrigin().x, y*Block[y][x].getGlobalBouds().height + Wpos.y - off.y + Block[y][x].getOrigin().y);

2D collisions check between 3 and more objects

i've got a problem on my collision Check Algorithm.
The problem is when i try to resolve collisions between 3 objects, 1 of them is still not colliding and not resolving collisions, here's the code:
void check_collisions(engine_t* engine)
{
for (int i = 0; i < engine->actor_count; i++)
{
actor_t* first = (actor_t*)engine->collision_pairs->data[i];
collider_t* a = (collider_t*)get_component_by_name(first, "collider");
for(int j = 0; j < engine->actor_count; j++)
{
actor_t* second = (actor_t*)engine->collision_pairs->data[j];
if(second == first)
continue;
collider_t* b = (collider_t*)get_component_by_name(second, "collider");
hit_state_t hit = aabb(a, b);
resolve_collisions(a, b, hit.normal);
}
}
}
The problem is that when for example: i have A, B, C
A could collide with B and C at the same frame time, it seems that when more object are colliding the first one (first) will not be calculated anymore.. any idea?
void resolve_collisions(collider_t* a, collider_t* b, vec2_t normal)
{
//Stop rigidbody
vec2_t position = a->owner->transform.position;
vec2_t position2 = b->owner->transform.position;
rigid_body_t* rb = (rigid_body_t*)get_component_by_name(a->owner, "rigid_body");
// if(!rb) { SDL_Log("rigid_body not while resolving collisions"); return; }
//hit from dx
if (normal.x > 0.0f && position.x < b->owner->transform.position.x + b->size.x)
{
rb->velocity.x = 0.0f;
// SDL_Log("collided dx");
position.x = (b->owner->transform.position.x + b->size.x) + 0.7f;
}
//hit from sx
if (normal.x < 0.0f && position.x + a->size.x > b->owner->transform.position.x)
{
rb->velocity.x = 0.0f;
float offset = b->size.x - a->size.x;
float offset2 = a->size.x - b->size.x;
// SDL_Log("collided sx");
position.x = (b->owner->transform.position.x - b->size.x) + offset;
position2.x = (a->owner->transform.position.x - a->size.x) + offset2;
}
//hit from top
if (normal.y < 0.0f && position.y + a->size.y > b->owner->transform.position.y)
{
rb->velocity.y = 0.0f;
float offset = b->size.y - a->size.y;
position.y = (b->owner->transform.position.y - b->size.y) + offset;
}
//hit from bottom
if (normal.y > 0.0f && position.y < b->owner->transform.position.y + b->size.y)
{
rb->velocity.y = 0.0f;
// SDL_Log("collided bottom");
position.y = (b->owner->transform.position.y + b->size.y) + 0.7f;
}
//change pos
a->owner->transform.position = position;
}
any help would be much appreciated!
-Thanks

Sobel Edge Detection without a buffer

For an embedded design I am attempting to implement sobel's edge detection on a board without the use of a buffer. i.e. I am reading and writing directly from the screen. I can however, store about one or two imge width full of data to be referenced later. This is due to limitations set forth by the board. However I have fallen into some issue. All that I recieve is noise regardless if I attempt to do sobel or another edge detection algorithm. The code is below, does anyone have any suggestions
Version 1
void sobelEdgeDetection2() {
int GX[3][3];
int GY[3][3];
int sumX[3];
int sumY[3];
int SUM[3];
int piX = 0;
int piY = 0;
//uint8_t R, G, B = 0;
int I, J = 0;
//UnpackedColour pixVal;
uint16_t *buffer;
// allocate space for even scan lines and odd scan lines
buffer = new uint16_t[_gl->getWidth()];
//buffer for previous line
uint16_t *bufT;
// allocate space for even scan lines and odd scan lines
bufT = new uint16_t[_gl->getWidth()];
// Masks //////////////////////////////////////
//X//
GX[0][0] = -1;
GX[0][1] = 0;
GX[0][2] = 1;
GX[1][0] = -2;
GX[1][1] = 0;
GX[1][2] = 2;
GX[2][0] = -1;
GX[2][1] = 0;
GX[2][2] = 1;
//Y//
GY[0][0] = 1;
GY[0][1] = 2;
GY[0][2] = 1;
GY[1][0] = 0;
GY[1][1] = 0;
GY[1][2] = 0;
GY[2][0] = -1;
GY[2][1] = -2;
GY[2][2] = -1;
for (int Y = 0; Y < _gl->getHeight(); Y++) {
for (int X = 0; X < _gl->getWidth(); X++) {
sumX[0] = sumX[1] = sumX[2] = 0;
sumY[0] = sumY[1] = sumY[2] = 0;
if (Y == 0 || Y == _gl->getHeight() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else if (X == 0 || X == _gl->getWidth() - 1) {
SUM[0] = SUM[1] = SUM[2] = 0;
} else {
for (I = -1; I <= 1; I++) {
for (J = -1; J <= 1; J++) {
piX = J + X;
piY = I + Y;
pixel16 pix = getPixel(piX, piY);
uint8_t Red = pix.Red;
uint8_t Green = pix.Green;
uint8_t Blue = pix.Blue;
sumX[0] += (Red) * GX[J + 1][I + 1];
sumX[1] += (Green) * GX[J + 1][I + 1];
sumX[2] += (Blue) * GX[J + 1][I + 1];
sumY[0] += (Red) * GY[J + 1][I + 1];
sumY[1] += (Green) * GY[J + 1][I + 1];
sumY[2] += (Blue) * GY[J + 1][I + 1];
}
}
SUM[0] = abs(sumX[0]) + abs(sumY[0]);
SUM[1] = abs(sumX[1]) + abs(sumY[1]);
SUM[2] = abs(sumX[2]) + abs(sumY[2]);
}
if (SUM[0] > 255)
SUM[0] = 255;
if (SUM[0] < 0)
SUM[0] = 0;
if (SUM[1] > 255)
SUM[1] = 255;
if (SUM[1] < 0)
SUM[1] = 0;
if (SUM[2] > 255)
SUM[2] = 255;
if (SUM[2] < 0)
SUM[2] = 0;
int newPixel[3];
newPixel[0] = (255 - ((unsigned char) (SUM[0])));
newPixel[1] = (255 - ((unsigned char) (SUM[1])));
newPixel[2] = (255 - ((unsigned char) (SUM[2])));
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
buffer[X] = packColour(pix).packed565;
}
//Need to move cursor back
// draw it
this->paintRow(Point(0, Y), buffer, _gl->getWidth());
}
delete[] buffer;
}
Version2
/**
* https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/image-processing/edge_detection.html
* 1 Iterate over every pixel in the image
* 2 Apply the x gradient kernel
* 3 Apply the y gradient kernel
* 4 Find the length of the gradient using pythagoras' theorem
* 5 Normalise the gradient length to the range 0-255
* 6 Set the pixels to the new values
*/
void sobelEdgeDetection4() {
UnpackedColour colour;
for (int x = 1; x < _gl->getWidth() - 1; x++) {
for (int y = 1; y < _gl->getHeight() - 1; y++) {
// initialise Gx and Gy to 0
int Gx = 0;
int Gy = 0;
unsigned int intensity = 0;
// Left column
pixel16 pixel = this->getPixel(x - 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += -intensity;
pixel = this->getPixel(x - 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -2 * intensity;
pixel = this->getPixel(x - 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += -intensity;
Gy += +intensity;
// middle column
pixel = this->getPixel(x, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += -2 * intensity;
pixel = this->getPixel(x, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gy += +2 * intensity;
// right column
pixel = this->getPixel(x + 1, y - 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += -intensity;
pixel = this->getPixel(x + 1, y);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +2 * intensity;
pixel = this->getPixel(x + 1, y + 1);
intensity = pixel.Red + pixel.Green + pixel.Blue;
Gx += +intensity;
Gy += +intensity;
// calculate the gradient length
unsigned int length = (unsigned int) sqrt(
(float) (Gx * Gx) + (float) (Gy * Gy));
// normalise the length to 0 to 255
length = length / 17;
// draw the pixel on the edge image
pixel16 pixel2(length,length,length);
this->setPixel(x, y, pixel2);
}
}
}
Version 3
// sobel map for the x axis
const double _SOBEL_Gx[3][3] = { { -1.0, +0.0, +1.0 }, { -2.0, +0.0, +2.0 },
{ -1.0, +0.0, +1.0 } };
// sobel map for the y axis
const double _SOBEL_Gy[3][3] = { { +1.0, +2.0, +1.0 }, { +0.0, +0.0, +0.0 },
{ -1.0, -2.0, -1.0 } };
double get_sobel_gradient(int width, int height, int x, int y) {
double sobel_gradient_x = 0, sobel_gradient_y = 0;
int mx = 0, my = 0, sx = 0, sy = 0;
for (mx = x; mx < x + 3; mx++) {
sy = 0;
for (my = y; my < y + 3; my++) {
if (mx < width && my < height) {
//int r, g, b, idx;
int idx = (mx + width * my) * 3;
pixel16 pixVal = this->getPixel(idx);
//r = pixVal.Red;
//g = pixVal.Green;
//b = pixVal.Blue;
UnpackedColour col = this->packColour(pixVal);
sobel_gradient_x += col.packed565 * _SOBEL_Gx[sx][sy];
sobel_gradient_y += col.packed565 * _SOBEL_Gy[sx][sy];
}
sy++;
}
sx++;
}
return abs(sobel_gradient_x) + abs(sobel_gradient_y);
}
void sobelEdgeDetection3() {
double threshold = 50000.0;
UnpackedColour colour;
for (int y = 0; y < _gl->getHeight(); y++) {
for (int x = 0; x < _gl->getWidth(); x++) {
if (get_sobel_gradient(_gl->getWidth(), _gl->getHeight(), x, y)
>= threshold) {
colour.packed565 = 0x0000; //set white
} else {
colour.packed565 = 0xFFFF; //set black
}
this->setPixel(x, y, colour);
}
}
}
For Version 1, after you allocate 2 buffers (just use buffer and bufT), create 2 pointers to point to the current and previous rows, like this:
uint16_t *currentRow = buffer;
uint16_t *prevRow = bufT;
Inside the row loop, write to currentRow instead of buffer:
pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
currentRow[X] = packColour(pix).packed565;
Because the Sobel filter reads from the previous row, you can't overwrite a row until after you have finished calculating the filtered values for the row after it. So at the end of the loop, where you are currently calling paintRow(), draw the previous row (if one exists), and then swap the buffers so that the current becomes the previous, and the previous becomes the new current row (to be overwritten on the next pass through the loop). On the last row the current row is also drawn, because otherwise it won't be since the outer loop is about to terminate.
if(Y > 0) // draw the previous row if this is not the first row:
this->paintRow(Point(0, Y-1), prevRow, _gl->getWidth());
if(Y == _gl->getHeight()-1) // draw the current row if it is the last:
this->paintRow(Point(0, Y), currentRow, _gl->getWidth());
// swap row pointers:
uint16_t *temp = prevRow;
prevRow = currentRow;
currentRow = temp;
The same strategy should work for the other versions.

Convertion of window coordinates to screen coordinates is not working

This function is not working to convert the coordinates of window to my screen.But in most of the sites it is same as this code.
My coordinates are: x-axis -8 to 8 and y-axis -3.3 to 3.3
void handleMouseclick(int button, int state, int x, int y) {
if (state == GLUT_DOWN)
{
if (button == GLUT_LEFT_BUTTON){
int w = glutGet(GLUT_SCREEN_WIDTH);
int h = glutGet(GLUT_SCREEN_HEIGHT);
//float striker_x = x*1.0 ;
//float striker_y = y * 1.0;
w = 1300;
h = 715;
float x1 = x / float(w) * 16 + -8;
float y1 = y / float(h) * 6.6 + -3.3;
//striker_x = ((1.0 / float(1300)) * x) - 1;
//striker_y = ((1.0 / float(715)) * -y) + 1;
// striker_x = ((8.0 / float(1300)) * striker_x);
//striker_y = (((3.3 / float(715)) * -striker_y) + 0.5)*2;
printf("x_s = %f\n",x1);
printf("x = %d\n",x);
printf("y_s = %f\n",y1);
target_x= x1;
target_y= y1;
}
else if (button == GLUT_RIGHT_BUTTON){
float theta = 0.0f;
theta -= 15;
}
}
}

C++ triangle rasterization

I'm trying to fix this triangle rasterizer, but cannot make it work correctly. For some reason it only draws half of the triangles.
void DrawTriangle(Point2D p0, Point2D p1, Point2D p2)
{
Point2D Top, Middle, Bottom;
bool MiddleIsLeft;
if (p0.y < p1.y) // case: 1, 2, 5
{
if (p0.y < p2.y) // case: 1, 2
{
if (p1.y < p2.y) // case: 1
{
Top = p0;
Middle = p1;
Bottom = p2;
MiddleIsLeft = true;
}
else // case: 2
{
Top = p0;
Middle = p2;
Bottom = p1;
MiddleIsLeft = false;
}
}
else // case: 5
{
Top = p2;
Middle = p0;
Bottom = p1;
MiddleIsLeft = true;
}
}
else // case: 3, 4, 6
{
if (p0.y < p2.y) // case: 4
{
Top = p1;
Middle = p0;
Bottom = p2;
MiddleIsLeft = false;
}
else // case: 3, 6
{
if (p1.y < p2.y) // case: 3
{
Top = p1;
Middle = p2;
Bottom = p0;
MiddleIsLeft = true;
}
else // case 6
{
Top = p2;
Middle = p1;
Bottom = p0;
MiddleIsLeft = false;
}
}
}
float xLeft, xRight;
xLeft = xRight = Top.x;
float mLeft, mRight;
// Region 1
if(MiddleIsLeft)
{
mLeft = (Top.x - Middle.x) / (Top.y - Middle.y);
mRight = (Top.x - Bottom.x) / (Top.y - Bottom.y);
}
else
{
mLeft = (Top.x - Bottom.x) / (Top.y - Bottom.y);
mRight = (Middle.x - Top.x) / (Middle.y - Top.y);
}
int finalY;
float Tleft, Tright;
for (int y = ceil(Top.y); y < (int)Middle.y; y++)
{
Tleft=float(Top.y-y)/(Top.y-Middle.y);
Tright=float(Top.y-y)/(Top.y-Bottom.y);
for (int x = ceil(xLeft); x <= ceil(xRight) - 1 ; x++)
{
FrameBuffer::SetPixel(x, y, p0.r,p0.g,p0.b);
}
xLeft += mLeft;
xRight += mRight;
finalY = y;
}
// Region 2
if (MiddleIsLeft)
{
mLeft = (Bottom.x - Middle.x) / (Bottom.y - Middle.y);
}
else
{
mRight = (Middle.x - Bottom.x) / (Middle.y - Bottom.y);
}
for (int y = Middle.y; y <= ceil(Bottom.y) - 1; y++)
{
Tleft=float(Bottom.y-y)/(Bottom.y-Middle.y);
Tright=float(Top.y-y)/(Top.y-Bottom.y);
for (int x = ceil(xLeft); x <= ceil(xRight) - 1; x++)
{
FrameBuffer::SetPixel(x, y, p0.r,p0.g,p0.b);
}
xLeft += mLeft;
xRight += mRight;
}
}
Here is what happens when I use it to draw shapes.
When I disable the second region, all those weird triangles disappear.
The wireframe mode works perfect, so this eliminates all the other possibilities other than the triangle rasterizer.
I kind of got lost in your implementation, but here's what I do (I have a slightly more complex version for arbitrary convex polygons, not just triangles) and I think apart from the Bresenham's algorithm it's very simple (actually the algorithm is simple too):
#include <stddef.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#define SCREEN_HEIGHT 22
#define SCREEN_WIDTH 78
// Simulated frame buffer
char Screen[SCREEN_HEIGHT][SCREEN_WIDTH];
void SetPixel(long x, long y, char color)
{
if ((x < 0) || (x >= SCREEN_WIDTH) ||
(y < 0) || (y >= SCREEN_HEIGHT))
{
return;
}
Screen[y][x] = color;
}
void Visualize(void)
{
long x, y;
for (y = 0; y < SCREEN_HEIGHT; y++)
{
for (x = 0; x < SCREEN_WIDTH; x++)
{
printf("%c", Screen[y][x]);
}
printf("\n");
}
}
typedef struct
{
long x, y;
unsigned char color;
} Point2D;
// min X and max X for every horizontal line within the triangle
long ContourX[SCREEN_HEIGHT][2];
#define ABS(x) ((x >= 0) ? x : -x)
// Scans a side of a triangle setting min X and max X in ContourX[][]
// (using the Bresenham's line drawing algorithm).
void ScanLine(long x1, long y1, long x2, long y2)
{
long sx, sy, dx1, dy1, dx2, dy2, x, y, m, n, k, cnt;
sx = x2 - x1;
sy = y2 - y1;
if (sx > 0) dx1 = 1;
else if (sx < 0) dx1 = -1;
else dx1 = 0;
if (sy > 0) dy1 = 1;
else if (sy < 0) dy1 = -1;
else dy1 = 0;
m = ABS(sx);
n = ABS(sy);
dx2 = dx1;
dy2 = 0;
if (m < n)
{
m = ABS(sy);
n = ABS(sx);
dx2 = 0;
dy2 = dy1;
}
x = x1; y = y1;
cnt = m + 1;
k = n / 2;
while (cnt--)
{
if ((y >= 0) && (y < SCREEN_HEIGHT))
{
if (x < ContourX[y][0]) ContourX[y][0] = x;
if (x > ContourX[y][1]) ContourX[y][1] = x;
}
k += n;
if (k < m)
{
x += dx2;
y += dy2;
}
else
{
k -= m;
x += dx1;
y += dy1;
}
}
}
void DrawTriangle(Point2D p0, Point2D p1, Point2D p2)
{
int y;
for (y = 0; y < SCREEN_HEIGHT; y++)
{
ContourX[y][0] = LONG_MAX; // min X
ContourX[y][1] = LONG_MIN; // max X
}
ScanLine(p0.x, p0.y, p1.x, p1.y);
ScanLine(p1.x, p1.y, p2.x, p2.y);
ScanLine(p2.x, p2.y, p0.x, p0.y);
for (y = 0; y < SCREEN_HEIGHT; y++)
{
if (ContourX[y][1] >= ContourX[y][0])
{
long x = ContourX[y][0];
long len = 1 + ContourX[y][1] - ContourX[y][0];
// Can draw a horizontal line instead of individual pixels here
while (len--)
{
SetPixel(x++, y, p0.color);
}
}
}
}
int main(void)
{
Point2D p0, p1, p2;
// clear the screen
memset(Screen, ' ', sizeof(Screen));
// generate random triangle coordinates
srand((unsigned)time(NULL));
p0.x = rand() % SCREEN_WIDTH;
p0.y = rand() % SCREEN_HEIGHT;
p1.x = rand() % SCREEN_WIDTH;
p1.y = rand() % SCREEN_HEIGHT;
p2.x = rand() % SCREEN_WIDTH;
p2.y = rand() % SCREEN_HEIGHT;
// draw the triangle
p0.color = '1';
DrawTriangle(p0, p1, p2);
// also draw the triangle's vertices
SetPixel(p0.x, p0.y, '*');
SetPixel(p1.x, p1.y, '*');
SetPixel(p2.x, p2.y, '*');
Visualize();
return 0;
}
Output:
*111111
1111111111111
111111111111111111
1111111111111111111111
111111111111111111111111111
11111111111111111111111111111111
111111111111111111111111111111111111
11111111111111111111111111111111111111111
111111111111111111111111111111111111111*
11111111111111111111111111111111111
1111111111111111111111111111111
111111111111111111111111111
11111111111111111111111
1111111111111111111
11111111111111
11111111111
1111111
1*
The original code will only work properly with triangles that have counter-clockwise winding because of the if-else statements on top that determines whether middle is left or right. It could be that the triangles which aren't drawing have the wrong winding.
This stack overflow shows how to Determine winding of a 2D triangles after triangulation
The original code is fast because it doesn't save the points of the line in a temporary memory buffer. Seems a bit over-complicated even given that, but that's another problem.
The following code is in your implementation:
if (p0.y < p1.y) // case: 1, 2, 5
{
if (p0.y < p2.y) // case: 1, 2
{
if (p1.y < p2.y) // case: 1
{
Top = p0;
Middle = p1;
Bottom = p2;
MiddleIsLeft = true;
}
else // case: 2
{
Top = p0;
Middle = p2;
Bottom = p1;
MiddleIsLeft = false;
}
}
This else statement means that p2.y (or Middle) can equal p1.y (or Bottom). If this is true, then when region 2 runs
if (MiddleIsLeft)
{
mLeft = (Bottom.x - Middle.x) / (Bottom.y - Middle.y);
}
else
{
mRight = (Middle.x - Bottom.x) / (Middle.y - Bottom.y);
}
That else line will commit division by zero, which is not possible.