Abysmal performance with vectors in c++ - c++

Disclaimer: i just started programming in c++.
So i made a little program in c++ using SFML where i have 300 particles stored in a vector that are attracted to each other.
The program runs at 17 FPS!! i tried doing the same using a standard array and it didn't run much faster.
The same code in java using the Array libgdx class runs at 5000 FPS.
Using the java.util.Vector class runs at 2000 FPS.
Debugging i found out that most of the delay comes from retrieving an element from the vector.
I know i'm doing something wrong in that regard, so i would be grateful if someone can help me out.
Here is the update loop.
void update(RenderWindow& window) {
window.clear();
for (size_t i = 0, size = particles.size(); i != size; ++i) {
Particle &part = particles[i];
glm::vec2 forceSum;
for (size_t j = 0, size = particles.size(); j != size; ++j) {
Particle &part2 = particles[j];
if (j == i)
continue;
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1);
if (glm::length(v1) < 50)
forceSum += (v2 * -0.1f);
}
part.applyForce(forceSum);
part.update();
shape.setPosition(part.getPos().x, part.getPos().y);
window.draw(shape);
}
window.display();
}
I can show more code if requested.
Thanks in advance!
main.cpp
#include "stdafx.h"
using namespace std;
using namespace sf;
void update(RenderWindow& window);
int t = 0;
CircleShape shape(4);
vector<Particle> particles;
int main() {
ContextSettings settings;
settings.antialiasingLevel = 16;
RenderWindow window(VideoMode(800, 800), "test", Style::Default, settings);
shape.setFillColor(Color(155, 155, 155, 124));
srand(4);
for (int i = 0; i < 300; i++) {
Particle particle = Particle(glm::vec2(rand() % 800, rand() % 800), rand() % 10);
particles.push_back(particle);
}
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
}
update(window);
}
return 0;
}
void update(RenderWindow& window) {
window.clear();
for (size_t i = 0, size = particles.size(); i != size; ++i) {
Particle &part = particles[i];
glm::vec2 forceSum;
for (size_t j = 0, size = particles.size(); j != size; ++j) {
Particle &part2 = particles[j];
if (j == i)
continue;
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1);
if (glm::length(v1) < 50)
forceSum += (v2 * -0.1f);
}
part.applyForce(forceSum);
part.update();
shape.setPosition(part.getPos().x, part.getPos().y);
window.draw(shape);
}
window.display();
}
Particle.h
#pragma once
#include <SFML/Graphics.hpp>
class Particle {
public:
Particle(glm::vec2 pos, float m);
void update();
void applyForce(const glm::vec2 &fce);
void setPos(const glm::vec2 &pos);
void setV(const glm::vec2 &vel);
const glm::vec2 getPos();
const glm::vec2 getV();
private:
float mass;
glm::vec2 acceleration = glm::vec2(0, 0);
glm::vec2 position = glm::vec2(0, 0);
glm::vec2 velocity = glm::vec2(0, 0);
};
Particle.cpp
#include "stdafx.h"
#include "Particle.h"
#include <iostream>
using glm::vec2;
Particle::Particle(vec2 pos, float m) {
mass = m;
position = pos;
}
void Particle::update() {
velocity += acceleration;
if (position.x + velocity.x > 800 || position.x + velocity.x < 0 || position.y + velocity.y > 800 || position.y + velocity.y < 0) {
velocity.x = 0; velocity.y = 0;
}
position += velocity;
}
void Particle::applyForce(const vec2 &fce) {
acceleration = fce/mass;
}
void Particle::setPos(const vec2 &pos) {
position = pos;
}
void Particle::setV(const vec2 &vel) {
velocity = vel;
}
const vec2 Particle::getV() {
return velocity;
}
const vec2 Particle::getPos() {
return position;
}

I've ran a vs studio profiler session on your code.
The app spends 59% of its time calculating square roots. You should optimize your code as follow:
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1); // <- 1 square root computed here
if (glm::length(v1) < 50) // <- another one here
forceSum += (v2 * -0.1f);
Becomes:
glm::vec2 v1 = part.getPos() - part2.getPos();
float l = glm::dot(v1, v1);
if (l < 2500)
{
forceSum += (v1 / sqrt(l));
}
} // end inner loop
part.applyForce(forceSum * .1f);
This will divide the time spent calculating square roots by 2 or more.
With null vec2s this optimization made execution on my machine go from 38s/10000 frames to 7s/10000 frames.
Thanks to BorgLeader and Rabbid76, who helped optimize your code further.

Related

How to apply shader to each shapes at their location?

I am playing with SFML and wrote a minimal program that draws a line behind the cursor.
I would like to make this line glow so I wrote a minimal shader that I apply to each line vertex. However, the maximum number of uniforms is about 4096. Instead, I would like to "run" my shader at each vertex position. Is there a better way other than using a global fragment and passing each position as uniforms?
#include <SFML/Graphics.hpp>
#include <boost/circular_buffer.hpp>
#include <iostream>
int main()
{
const float winW = 1000;
const float winH = 1000;
const int tail_length = 100;
sf::RenderWindow window(sf::VideoMode(winW, winH), "SFML");
sf::VertexArray lines(sf::LinesStrip, tail_length);
boost::circular_buffer<sf::Vector2i> tail(tail_length);
sf::Clock clk;
clk.restart();
sf::Vector2i old_mouse_pos = sf::Vector2i(0, 0);
// std::fill(tail.begin(), tail.end(), sf::Mouse::getPosition(window));
sf::Texture tex;
tex.create(winW, winH);
sf::Sprite spr(tex);
sf::Shader shader;
shader.loadFromFile("glow.glsl", sf::Shader::Fragment);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
shader.setUniform("resolution", sf::Vector2f(40, 40));
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
if (old_mouse_pos != mousePos)
tail.push_back(mousePos);
old_mouse_pos = sf::Vector2i(mousePos.x, mousePos.y);
sf::Vector2f v[100];
for (int i = 0; i < tail.size(); i++)
{
v[i] = sf::Vector2f(tail[i].x, tail[i].y- 2* winH / 2);
lines[i].position = sf::Vector2f(tail[i].x, tail[i].y);
lines[i].color = sf::Color(255, 0, 0, i * 255 / tail_length);
}
shader.setUniformArray("tail", v, tail.size());
window.clear();
window.draw(spr, &shader);
window.draw(lines);
window.display();
}
}
Here the shader:
uniform vec2 resolution;
uniform vec2 tail[100];
void main(void) {
vec2 p = gl_FragCoord.xy / resolution.xy;
float d = 0.0;
for (int i = 0; i < 100; i++) {
vec2 u = tail[i];
vec2 q;
q.x = p.x - u.x / resolution.x;
q.y = p.y + u.y / resolution.y;
float dist = pow(1.0/length(q) * 0.1, 1.5);
d += dist;
}
vec3 col = d * vec3(1.0, 0.5, 0.25);
gl_FragColor = vec4(1.0 - exp( -col ), 1.0);
}

How to fill sf::VertexArray of sf::TriangleStrip with color?

I would like to increase the drawing performance by using a single sf::VertexArray to display thousands of circles. To make sure it works, I wrote this example:
#include <SFML/Graphics.hpp>
#include <iostream>
#define WIDTH 100
#define HEIGHT 100
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE",
sf::Style::Close);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
sf::CircleShape circle(50);
circle.setPosition(100,100);
size_t count = circle.getPointCount();
sf::VertexArray objects(sf::TriangleStrip, count);
for (int i = 0; i < count; i++) {
objects.append(circle.getPoint(i));
}
for (int i = 0; i < objects.getVertexCount(); i++) {
objects[i].color = sf::Color::Blue;
}
window.clear();
window.draw(objects);
window.display();
}
}
However, the color only applies to the line, not the fill:
Is it possible to fill the shape with a single color?
You are right, displaying more than 1000 circles with a window.draw between each of them result in poor performances. The vertexArray is indeed a good solution.
You used sf::TriangleStrip which does not work as you expected. Your verticles are indeed painted, but they are only on the edge of the circle. You need to use either sf::TriangleFan or sf::Triangles if you want more than one circle per array.
Here an example. I was able to display 513'000 verticles at 60 fps (each circle is about 30 verticles, which is about 10'000 circles.
#include <SFML/Graphics.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#define WIDTH 800
#define HEIGHT 800
using namespace std;
template <typename T>
std::string to_string_with_precision(const T a_value, const int n = 6)
{
std::ostringstream out;
out.precision(n);
out << std::fixed << a_value;
return out.str();
}
void addCircle(sf::VertexArray &array, sf::Vector2f position, float radius, sf::Color color) {
size_t count = 30;
for (int i = 0; i < count; i += 1) {
sf::Vertex v0;
v0.position = sf::Vector2f(position.x, position.y);
v0.color = color;
array.append(v0);
sf::Vertex v1;
float angle = i * 2 * M_PI / count;
v1.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v1.color = color;
array.append(v1);
sf::Vertex v2;
angle = (i + 1) * 2 * M_PI / count;
v2.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v2.color = color;
array.append(v2);
}
}
#define FRAMERATE 120
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE", sf::Style::Close);
// Fps text
sf::Font font;
if (!font.loadFromFile("../assets/collegiate.ttf")) {
std::cout << "Error loading font" << std::endl;
}
sf::Text fps;
fps.setFont(font);
fps.setPosition(10, 10);
fps.setCharacterSize(16);
fps.setFillColor(sf::Color::White);
// Time management
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time timePerFrame = sf::seconds(1.f / FRAMERATE);
sf::Time timeSinceLastDraw = sf::Time::Zero;
window.setFramerateLimit(FRAMERATE);
float vr = 0;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
size_t count = 15;
sf::Color color = sf::Color::Red;
color.a = 50;
sf::Color color2 = sf::Color::Blue;
color2.a = 20;
sf::VertexArray objects(sf::Triangles);
for (int k = 0; k < 5; k++) {
sf::Color c = k % 2 ? color : color2;
for (int j = 20; j < 400; j += 5) {
for (int i = 0; i < count; i++) {
float angle = i * 2 * M_PI / count;
angle += (i % 2 ? vr : -vr) * k * (k % 2 ? 1 : -1);
addCircle(objects, sf::Vector2f(WIDTH / 2 + cos(angle) * j, HEIGHT / 2 + sin(angle) * j), j / 5, c);
}
}
}
timeSinceLastDraw = clock.restart();
timeSinceLastUpdate += timeSinceLastDraw;
double timeFps = 1.f / timeSinceLastDraw.asSeconds();
fps.setString("verticles: " + to_string_with_precision(objects.getVertexCount(), 0) + " fps: " + to_string_with_precision(timeFps, 0));
while (timeSinceLastUpdate > timePerFrame) {
timeSinceLastUpdate -= timePerFrame;
vr += 0.01;
}
window.clear();
window.draw(objects);
window.draw(fps);
window.display();
}
}

Gravity and collision detection in SFML

This is my first attempt with SFML and game development, and I'm having issues with the collision and gravity.
I'm making a 2D platformer game using a tilemap system.
Collision seems to be working(slightly) but is very choppy and I just can't seem to get gravity to work properly. I've tried a few different tutorials but I cant get anything working and I'm at a bit of a loss right now.
If someone could point out what I'm missing here it'd be greatly appreciated!!
Heres the code I'm using for these elements:
In PlayerSprite class(attempt at gravity)
PlayerSprite::PlayerSprite(const sf::Vector2f& size) : AnimatedSprite(size)
{
playerPos = (sf::Vector2f(300.0f, 400.0f));
dead = false;
jumpHeight = 5.f;
scale = 50.f;
accelGravity = 0.5f;
maxGravity = 5.f;
velocity.x = 2.0f;
velocity.y = 2.0f;
playerTexture.loadFromFile("gfx/spritemansheet.png");
setSize(sf::Vector2f(48, 48));
setPosition(playerPos);
setTexture(&playerTexture);
}
PlayerSprite::~PlayerSprite()
{
}
void PlayerSprite::update(float dt)
{
onGround = false;
if (input->isKeyDown(sf::Keyboard::A)) {
input->setKeyUp(sf::Keyboard::A);
playerPos.x -= (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &walkBack;
}
if (input->isKeyDown(sf::Keyboard::D)) {
input->setKeyUp(sf::Keyboard::D);
playerPos.x += (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &walk;
}
if (input->isKeyDown(sf::Keyboard::W) ) {
input->setKeyUp(sf::Keyboard::Space);
playerPos.x += (dt * step) * 5;
setPosition(playerPos);
//currentAnimation = &jump;
velocity.y = -5.f * -1;
}
if (onGround == false) {
velocity.y += accelGravity;
if (velocity.y > maxGravity) {
velocity.y = maxGravity;
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
input->setMouseLeftUp(sf::Mouse::Left);
currentAnimation = &attack;
}
}
void PlayerSprite::setInput(Input* newInp)
{
input = newInp;
}
void PlayerSprite::collisionResponse(Sprite* sp)
{
if (velocity.x > 0) {
velocity.x = 0.f;
}
if (velocity.x < 0) {
velocity.x = 0.f;
}
if (velocity.y > 0) {
velocity.y = 0.f;
onGround = true;
}
if (velocity.y < 0) {
velocity.y = 0.f;
}
setPosition(getPosition().x, sp->getPosition().y - getSize().y);
}
Collision Detection in Game.cpp
void Game::update(float dt)
{
player.update(dt);
manager.update(dt);
std::vector<Tile>* world = worldMap.getScene();
for (int i = 0; i < (int)world->size(); i++) {
if ((*world)[i].isAlive()) {
if (checkGroundBounding(&player, &(*world)[i])) {
player.collisionResponse(&(*world)[i]);
}
}
}
void Game::render() {
beginDraw();
window->draw(bg);
window->draw(player);
manager.render(window);
worldMap.render(window);
endDraw();
}
bool Game::checkGroundBounding(PlayerSprite* b1, Tile* b2)
{
//get radius of sprites
float r1 = b1->getSize().x / 2;
float r2 = b2->getSize().x / 2;
float xposb1 = b1->getPosition().x + r1;
float xposb2 = b2->getPosition().x + r2;
float yposb1 = b1->getPosition().y + r1;
float yposb2 = b2->getPosition().y + r2;
if (pow(xposb2 - xposb1, 2) + pow(yposb2 - yposb1, 2) < pow(r1 + r2, 2)) {
return true;
}
return false;
}
you're collision is super wonky, simply set a oldPos variable to your player position, then do your movement code but only for the X axis, if collision is detected, set the position to oldPos, then set oldPos to the current position again, and repeat for the Y axis:
Pseudocode example:
//X axis
oldPos = position
if(leftKey):
position.x -= speed
if(rightKey):
position.x += speed
if(collision):
position = oldPos
//Y axis
oldPos = position
if(upKey):
position.y -= speed
if(downKey):
position.y += speed
if(collision):
position = oldPos
also you can replace your tile iteration with:
for(Tile t : *world){
t->doStuff();
}
I also don't recommend declaring your tile vector in your update function unless you hate performance

How to write an intersection function for any kind of shapes

I have to write a function, which detects intersection and returns true or false.
I have Shape.cpp file, and rectangle.cpp, circle.cpp files inherits of it. I tried to calculate it, but i failed. There is no error, but when my program starts, it crashes. MY Question is why it crashes? is my way wrongs? Here is circle.cpp file.
bool Circ::intersects(Shape* pshape)
{
Rect *p1 = dynamic_cast<Rect*>(pshape);
Circ *p2 = dynamic_cast<Circ*>(pshape);
if(p1)
{
float circleDistance_x = abs(p2->getPos().x - p1->getPos().x);
float circleDistance_y = abs(p2->getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + p2->getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + p2->getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= p2->getRad()^2);
}
return false;
}
This is not the code all i want to write. But when it fails, i stopped to write.
and my Shapes.h file
#ifndef _SHAPES_H
#define _SHAPES_H
struct Point2d
{
float x, y;
};
struct Point3d
{
float r, g, b;
};
class Shape
{
protected:
bool m_bMarked;
Point3d m_col;
Point2d m_veldir;
Point2d m_pos;
float m_vel;
public:
Shape(Point2d& pos, Point2d& veldir, float vel, Point3d& col)
:m_pos(pos),m_veldir(veldir),m_vel(vel),m_col(col)
{
m_bMarked = false;
}
virtual ~Shape() {}
virtual void draw() = 0;
virtual bool intersects(Shape*) = 0;
inline void move() { m_pos.x += m_veldir.x*m_vel; m_pos.y += m_veldir.y*m_vel; }
inline void invert_xdir() { m_veldir.x *= -1; }
inline void invert_ydir() { m_veldir.y *= -1; }
inline void MarkShape() { m_bMarked = true; }
inline void UnMarkShape() { m_bMarked = false; }
inline bool isMarked() { return m_bMarked; }
inline void increase_vel() { m_vel += 0.01f; }
inline void decrease_vel() { m_vel -= 0.01f; }
};
#endif
And finally my ShapesMain.cpp file
#include <time.h>
#include <GL/glut.h>
#include <cmath>
#include "Rectangle.h"
#include "Circle.h"
// YOU CAN CHANGE THE NUMBER OF SHAPES
#define SHAPE_COUNT 20
// YOU CAN MODIFY WINDOW SIZE BY CHANGING THESE
// YOU MAY ALSO VIEW WINDOW IN FULL SCREEN
#define WINDOWX 500
#define WINDOWY 500
// UNCOMMENT THE LINE BELOW TO STOP MOVING SHAPES
//#define NO_MOTION
// CHANGE THESE DIMENSIONS HOWEVER YOU LIKE
#define MAX_SHAPE_DIM 70
#define MIN_SHAPE_DIM 10
float g_windowWidth = WINDOWX;
float g_windowHeight = WINDOWY;
Shape* g_shapeList[SHAPE_COUNT];
int g_numShapes = 0;
bool g_bShowIntersection = true;
//------------------------------------
void Initialize()
{
srand ( time(NULL) );
// delete previous shapes, if there is any
if (g_numShapes > 0)
{
for (int i = 0; i < g_numShapes; i++)
delete g_shapeList[i];
}
// create a new shape repository
do {
g_numShapes = rand() % SHAPE_COUNT; // number of shapes are randomly determined
} while (g_numShapes < 5); // we dont want to have less than 5 shapes
int rect_count = g_numShapes * (rand() % 10 / 10.0f);
int circle_count = g_numShapes - rect_count;
int half_wind_x = 3* g_windowWidth / 4;
int half_wind_y = 3* g_windowHeight / 4;
int max_dim = MAX_SHAPE_DIM; // max dim. of any shape
int min_dim = MIN_SHAPE_DIM; // min dim. of any shape
int quad_wind = g_windowWidth / 4;
for (int i= 0; i<g_numShapes; i++)
{
float x, y;
float v1, v2;
// set positions
do {
x = rand() % half_wind_x;
} while (x <= quad_wind);
do {
y = rand() % half_wind_y;
} while (y <= quad_wind);
Point2d pos = { x,y };
// set velocity directions
do{
v1 = rand() % 10 / 10.0f;
v2 = rand() % 10 / 10.0f;
} while (v1 == 0 || v2 == 0);
v1 *= (rand() % 2) ? -1 : 1;
v2 *= (rand() % 2) ? -1 : 1;
float vnorm = sqrt(v1*v1 + v2*v2);
Point2d veldir = { v1 / vnorm, v2 / vnorm };
// set velocity
float vel;
do {
vel = rand() % 2 / 10.0f;
} while (vel == 0);
#ifdef NO_MOTION
vel = 0.0f;
#endif
//set color
float R = rand()%100/100.0f;
float G = rand()%100/100.0f;
float B = rand()%100/100.0f;
Point3d color = { R,G,B };
// construct objects
if (i < rect_count)
{
float wx;
float wy;
do {
wx = rand() % quad_wind;
} while (wx < min_dim || wx>max_dim);
do {
wy = rand() % quad_wind;
} while (wy < min_dim || wy>max_dim);
Point2d size = { wx, wy };
Rect* pRect = new Rect(pos, size, veldir, vel, color);
g_shapeList[i] = pRect;
}
else
{
float rad;
do {
rad = rand() % quad_wind;
} while (rad < min_dim || rad>max_dim);
Circ* pCirc = new Circ(pos, rad, veldir, vel, color);
g_shapeList[i] = pCirc;
}
}
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
//-------------------------------------
// This function handles the intersections of shapes.
// if the user is not interested in marking intersections
// s/he can set bMarkIntersections to false..in this case
// no intersection test is performed
void MarkObjects(bool bMarkIntersections)
{
if (bMarkIntersections == false)
{
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->UnMarkShape();
}
else
{
// reset the states of all shapes as unmarked
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->UnMarkShape();
for (int i = 0; i < g_numShapes; i++)
{
for (int j = i+1; j < g_numShapes; j++)
{
if (g_shapeList[i]->intersects(g_shapeList[j]))
{
g_shapeList[i]->MarkShape();
g_shapeList[j]->MarkShape();
}
}
}
}
}
//------------------------------------
void UpdateData()
{
// create viewport bounding rectangles to keep the shapes within the viewport
Point2d Winpos = { -1.0,0.0 };
Point2d Winsize = { 1.0 , g_windowHeight };
Point2d Winveldir = { 0,0 }; // dummy veldir
float Winvel = 0.0f; //not moving
Point3d Wincol = { 0,0,0 }; // dummy color
Rect WindowRectLeft(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = 0.0; Winpos.y = -1.0;
Winsize.x = g_windowWidth; Winsize.y = 1.0;
Rect WindowRectBottom(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = g_windowWidth; Winpos.y = 0.0;
Winsize.x = 1; Winsize.y = g_windowHeight;
Rect WindowRectRight(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = 0.0; Winpos.y = g_windowHeight;
Winsize.x = g_windowWidth; Winsize.y = 1.0f;
Rect WindowRectUp(Winpos, Winsize, Winveldir, Winvel, Wincol);
for (int i = 0; i < g_numShapes; i++)
{
// move the shape
g_shapeList[i]->move();
// if it bounces to the window walls, invert its veldir
if (g_shapeList[i]->intersects(&WindowRectLeft) ||
g_shapeList[i]->intersects(&WindowRectRight))
g_shapeList[i]->invert_xdir();
if (g_shapeList[i]->intersects(&WindowRectBottom) ||
g_shapeList[i]->intersects(&WindowRectUp))
g_shapeList[i]->invert_ydir();
}
}
//------------------------------------
void ChangeSize(GLsizei w, GLsizei h)
{
if(h == 0)
h = 1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
g_windowHeight = h;
g_windowWidth = w;
glOrtho(0, g_windowWidth, 0, g_windowHeight , 1.0f, -1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//------------------------------------
void processNormalKeys(unsigned char key, int x, int y)
{
if (key == 'q') // PRESS 'q' to terminate the application
exit(0);
if(key=='r') // PRESS 'r' ket to reset the shapes
Initialize();
if (key == 's') // toggle between showing the intersections or not
g_bShowIntersection = g_bShowIntersection ? false: true;
}
//------------------------------------
void processSpecialKeys(int key, int x, int y)
{
switch(key) {
case GLUT_KEY_LEFT :
break;
case GLUT_KEY_RIGHT :
break;
case GLUT_KEY_UP:
// PRESSING UP ARROW KEY INCREASES THE SHAPE VELOCITIES
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->increase_vel();
break;
case GLUT_KEY_DOWN:
// PRESSING DOWN ARROW KEY DECREASES THE SHAPE VELOCITIES
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->decrease_vel();
break;
}
}
//-------------------------------------
void display() {
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
UpdateData();
MarkObjects(g_bShowIntersection);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
for (int i= 0; i<g_numShapes; i++)
g_shapeList[i]->draw();
glutSwapBuffers();
}
//------------------------------------
int main(int argc, char* argv[])
{
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );
glutInitWindowPosition(100,100);
glutInitWindowSize(WINDOWX, WINDOWY);
glutCreateWindow("COM102B - PA4");
// Register callback handler for window re-paint
glutDisplayFunc(display);
glutReshapeFunc(ChangeSize);
glutIdleFunc(display);
glutKeyboardFunc(processNormalKeys);
glutSpecialFunc(processSpecialKeys);
Initialize();
glutMainLoop(); // Enter infinitely event-processing loop
return 0;
}
Your problem is in these lines:
Rect *p1 = dynamic_cast<Rect*>(pshape);
Circ *p2 = dynamic_cast<Circ*>(pshape);
Unless you had inherited Rect from Circ or vice versa this is what makes your program crash, you can't cast your pShape to Circ if it is a Rect, so when you pass a Rect object to your function it will correctly cast to Rect* but it will fail with Circ* returning nullptr, so then when you try to access methods from p2 it will crash becouse you are accessing to invalid memory (0x00000000) :
if(p1)
{
float circleDistance_x = abs(p2->getPos().x - p1->getPos().x);
float circleDistance_y = abs(p2->getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + p2->getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + p2->getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= p2->getRad()^2);
}
So, you could simply just cast the first p1 pointer since the method is from circle, it's obvious that it was called from a Circ Object so there's no need for p2 pointer.
Rect *p1 = dynamic_cast<Rect*>(pshape);
if(p1)
{
float circleDistance_x = abs(getPos().x - p1->getPos().x);
float circleDistance_y = abs(getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= getRad()^2);
}
Also on the line:
return (cornerDistance_sq <= getRad()^2)
i think you are trying to get the radius square but this wont do it, what it is actually doing is
(cornerDistance_sq <= getRad()) ^ 2
becouse <= has greater precedence to ^, plus ^ is not a square operator it is a bitwise operator. So what you actually want is :
return cornerDistance_sq <= getRad() * getRad();

Implementing rasterization and depth buffer in c++

I am trying to implement the rasterization method in cc+. I am trying to implement an interpolation function that handles the interpolation between the x,y and z vertices. That way I can save the inverse of z in a depth buffer.
At this point I get only the vertices drawn on the rendered image. Can someone see what is wrong with my code? I have posted the full code so you can see the whole program.
Many thanks in advance.
EDIT
I saw that I had made an error in vertexshader by writing pixel.zinv = 1 / vPrime.z instead of p.zinv = 1/ vPrime.z. Now nothing renders, just a black screen.
EDIT 2
My check to see if a pixel should be painted was wrong.
if (depthBuffer[row[i].x][row[i].y] < row[i].zinv)
is correct. Now I get little pieces of color.
#include <iostream>
#include <glm/glm.hpp>
#include <SDL.h>
#include "SDLauxiliary.h"
#include "TestModel.h"
using namespace std;
using glm::vec2;
using glm::vec3;
using glm::ivec2;
using glm::mat3;
using glm::max;
// ----------------------------------------------------------------------------
// GLOBAL VARIABLES
int cc = 0;
const int SCREEN_WIDTH = 500;
const int SCREEN_HEIGHT = 500;
SDL_Surface* screen;
int t;
vector<Triangle> triangles;
vec3 cameraPos(0, 0, -3.001);
float f = 500;
double yaw = 0;
vec3 c1(cos(yaw), 0, -sin(yaw));
vec3 c2(0, 1, 0);
vec3 c3(sin(yaw), 0, cos(yaw));
glm::mat3 R(c1, c2, c3);
float translation = 0.1; // use this to set translation increment
const float PI = 3.1415927;
vec3 currentColor;
float depthBuffer[SCREEN_HEIGHT][SCREEN_WIDTH];
// ----------------------------------------------------------------------------
// STUCTURES
struct Pixel
{
int x;
int y;
float zinv;
}pixel;
// ----------------------------------------------------------------------------
// FUNCTIONS
void Update();
void Draw();
void VertexShader(const vec3& v, Pixel& p);
void Interpolate(ivec2 a, ivec2 b, vector<ivec2>& result);
void DrawLineSDL(SDL_Surface* surface, ivec2 a, ivec2 b, vec3 color);
void DrawPolygonEdges(const vector<vec3>& vertices);
void ComputePolygonRows(const vector<Pixel>& vertexPixels, vector<Pixel>& leftPixels, vector<Pixel>& rightPixels);
void DrawPolygonRows(const vector<Pixel>& leftPixels, const vector<Pixel>& rightPixels);
void DrawPolygon(const vector<vec3>& vertices);
void Interpolate2(Pixel a, Pixel b, vector<Pixel>& result);
int main(int argc, char* argv[])
{
LoadTestModel(triangles);
screen = InitializeSDL(SCREEN_WIDTH, SCREEN_HEIGHT);
t = SDL_GetTicks(); // Set start value for timer.
while (NoQuitMessageSDL())
{
Draw();
}
//Draw();
//cin.get();
SDL_SaveBMP(screen, "screenshot.bmp");
return 0;
}
void Draw()
{
SDL_FillRect(screen, 0, 0);
if (SDL_MUSTLOCK(screen))
SDL_LockSurface(screen);
for (int y = 0; y<SCREEN_HEIGHT; ++y)
for (int x = 0; x<SCREEN_WIDTH; ++x)
depthBuffer[y][x] = 0;
for (int i = 0; i<triangles.size(); ++i)
{
currentColor = triangles[i].color;
vector<vec3> vertices(3);
int aa = 24;
vertices[0] = triangles[i].v0;
vertices[1] = triangles[i].v1;
vertices[2] = triangles[i].v2;
DrawPolygon(vertices);
}
if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
SDL_UpdateRect(screen, 0, 0, 0, 0);
}
void VertexShader(const vec3& v, Pixel& p)
{
vec3 vPrime = (v - cameraPos)*R;
p.zinv = 1 / vPrime.z;
p.x = f * vPrime.x / vPrime.z + SCREEN_WIDTH / 2;
p.y = f * vPrime.y / vPrime.z + SCREEN_HEIGHT / 2;
//cout << p.x << " this is it " << p.y << endl;
depthBuffer[p.x][p.y] = pixel.zinv;
}
void ComputePolygonRows(const vector<Pixel>& vertexPixels,
vector<Pixel>& leftPixels, vector<Pixel>& rightPixels)
{
// Find y-min,max for the 3 vertices
vec3 vp(vertexPixels[0].y, vertexPixels[1].y, vertexPixels[2].y);
Pixel start; Pixel end; Pixel middle;
int yMin = 1000;
int yMax = -1000;
int w=0; int s=0;
for (int k = 0; k < vertexPixels.size(); ++k)
{
if (vp[k] <= yMin)
{
yMin = vp[k];
end = vertexPixels[k];
w = k;
}
}
for (int k = 0; k < vertexPixels.size(); ++k)
{
if (vp[k] >= yMax)
{
yMax = vp[k];
start = vertexPixels[k];
s = k;
}
}
for (int k = 0; k < vertexPixels.size(); ++k)
{
if (vertexPixels[k].y != start.y
&& vertexPixels[k].y != end.y)
{
middle = vertexPixels[k];
}
if (w!= k && s!= k)
{
middle = vertexPixels[k];
}
}
int ROWS = yMax - yMin + 1;
leftPixels.resize(ROWS);
rightPixels.resize(ROWS);
for (int i = 0; i<ROWS; ++i)
{
leftPixels[i].x = +numeric_limits<int>::max();
rightPixels[i].x = -numeric_limits<int>::max();
}
int pixels1 = glm::abs(start.y - end.y) + 1;
vector<Pixel> line1(pixels1);
Interpolate2(end, start, line1);
int pixels2 = glm::abs(end.y - middle.y) + 1;
vector<Pixel> line2(pixels2);
Interpolate2(end, middle, line2);
int pixels3 = glm::abs(middle.y - start.y) + 1;
vector<Pixel> line3(pixels3);
Interpolate2(middle, start, line3);
vector<Pixel> side1(ROWS);
for (int i = 0; i < line2.size(); ++i)
{
side1[i] = line2[i];
}
for (int i = 0; i < line3.size(); ++i)
{
side1[line2.size()+i-1] = line3[i];
}
for (int i = 0; i < ROWS; ++i)
{
if (line1[i].x < leftPixels[i].x)
{
leftPixels[i] = line1[i];
}
if (line1[i].x > rightPixels[i].x)
{
rightPixels[i] = line1[i];
}
if (side1[i].x < leftPixels[i].x)
{
leftPixels[i] = side1[i];
}
if (side1[i].x > rightPixels[i].x)
{
rightPixels[i] = side1[i];
}
}
}
void DrawPolygonRows(const vector<Pixel>& leftPixels, const vector<Pixel>& rightPixels)
{
//cout << cc++ << endl;
for (int k = 0; k < leftPixels.size(); ++k)
{
int pixels = glm::abs(leftPixels[k].x - rightPixels[k].x) + 1;
vector<Pixel> row(pixels);
Interpolate2(leftPixels[k], rightPixels[k], row);
for (int i = 0; i < pixels; ++i)
{
if (depthBuffer[row[i].x][row[i].y] < row[i].zinv)
{
PutPixelSDL(screen, row[i].x, row[i].y, currentColor);
depthBuffer[row[i].x][row[i].y] = row[i].zinv;
}
}
}
}
void DrawPolygon(const vector<vec3>& vertices)
{
int V = vertices.size();
vector<Pixel> vertexPixels(V);
for (int i = 0; i<V; ++i)
VertexShader(vertices[i], vertexPixels[i]);
vector<Pixel> leftPixels;
vector<Pixel> rightPixels;
ComputePolygonRows(vertexPixels, leftPixels, rightPixels);
DrawPolygonRows(leftPixels, rightPixels);
}
void Interpolate2(Pixel a, Pixel b, vector<Pixel>& result)
{
int N = result.size();
float stepx = (b.x - a.x) / float(glm::max(N - 1, 1));
float stepy = (b.y - a.y) / float(glm::max(N - 1, 1));
float stepz = (b.zinv - a.zinv) / float(glm::max(N - 1, 1));
float currentx = a.x;
float currenty = a.y;
float currentz = a.zinv;
for (int i = 0; i<N; ++i)
{
result[i].x = currentx;
result[i].y = currenty;
result[i].zinv = currentz;
currentx = a.x;
currenty = a.y;
currentz = a.zinv;
currentx += stepx;
currenty += stepy;
currentz += stepz;
}
}
The last loop in the last function seems incorrect to me. You define currentx outside the loop. Then, define a local variable inside the loop with the same name and use it later in the loop. I'd suggest not using the same name for variable inside the loop and outside it to make it more readable. Also, using global variables make the code difficult to read too, since I prefer to look at a function as a separate entity for analysis.