0

I am developing a 2D physics engine in c++, and one of the main structures I am using is name RigidBody. In order to easily iterate through all of the RigidBody objects in each time step, I attempted to create a global vector of RigidBody object pointers.

vector<RigidBody*> RigidBodies

As suggested in other StackOverflow answers, I declared the global variable in a header file, and defined it in one of the other project .cpp files. However, when I attempt to access member functions or variables, I get bogus values. Below are my source and header files. Could someone please let me know if there is an error with this or if I am doing something fundamentally wrong, as I have been trying to find an error for a few days, and have not been able to figure it out yet.

main.cpp:

#include <iostream>
#include <vector>
#include "polygon.h"
#include "rendering.h"

int test;

std::vector<RigidBody*> RigidBodies;
RigidBody * rigPtr;

void CreateBody() {
  Material m1;  // Settings for ROCK.
  m1.density = 0.6;
  m1.restitution = 0.1;
  float volume = 1;
  Vector p0 = {0,1};
  Vector p1 = {1,1};
  Vector p2 = {1,0};
  Vector p3 = {0,0};
  std::vector<Vector*> Points;
  Points.push_back(&p0);
  Points.push_back(&p1);
  Points.push_back(&p2);
  Points.push_back(&p3);
  //std::cout << Points.at(0)->y << '\n';
  Polygon pol1(Points, m1, volume);
  Polygon * polPtr  = &pol1; 
  //std::cout << pol1.Points.at(0)->y << '\n';
  std::cout << "polygon created" << '\n';
  Vector pos1;
  pos1.x = 10;
  pos1.y = 10;
  RigidBody r1(pos1, polPtr);
  rigPtr = &r1;
  std::cout << "rigid body created" << '\n';
 // std::cout << RigidBodies.at(0)->dt << '\n';

}

// MAIN
int main() {

  test = 3;
  //std::cout << test << '\n';
  test = 6;

  CreateBody();

  RigidBodies.push_back(rigPtr);

  //std::cout << test << '\n';

    unsigned int lastTime = SDL_GetTicks();
    unsigned int currentTime;

    SDL_Renderer* renderer = InitializeRender();

    while(1) {
    currentTime = SDL_GetTicks();
    if (currentTime - lastTime > 33) {
        //RigidBodies.at(0)->Step();
        Render(renderer, RigidBodies);
        lastTime = SDL_GetTicks();
    }
    }
  return(0);
}

rendering.cpp:

#include <iostream>
#include "polygon.h"
#include "rendering.h"



SDL_Renderer* InitializeRender() {
      SDL_Window * window = NULL;
    window = SDL_CreateWindow
    (
        "RIGID BODIES SIM", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        640,
        480,
        SDL_WINDOW_SHOWN
    );

    // Setup renderer
    SDL_Renderer * renderer = NULL;
    renderer =  SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    return(renderer);
}

void Render(SDL_Renderer * renderer, std::vector<RigidBody*> RigidBodies) {
    float scale = 10;  // METERS to PIXELS.
  SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);  // Clear screen.
  SDL_RenderClear(renderer);
  SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);  // Set polygon drawing color (GREEN)
  std::cout << RigidBodies.at(0)->dt << '\n';
  for (int j = 0; j < RigidBodies.size(); j++) {
    RigidBody * rPtr = RigidBodies.at(j);  // Not recognizing rigid body pointer correctly
    //std::cout << rPtr->dt << '\n';
    Polygon * polyPtr = rPtr->p;
    std::cout << "hey1" << '\n';
    int size = polyPtr->Points.size();  // ERROR HERE //
    std::cout << "hey2" << '\n';
  //  std::cout << polyPtr->Points.at(0)->y << '\n';
    for (int i = 0; i < size; i++) {
      std::cout << "hey3" << '\n';
        auto pointPtr1 = polyPtr->Points.at(i);
      int lastIndex = size - 1;
      //std::cout << i+1 << '\n';
        auto pointPtr2 = polyPtr->Points.at((i + 1) % (lastIndex));  // Modulo so last point links back up to first one.
      SDL_RenderDrawLine(renderer, (rPtr->position.x + pointPtr1->x) * scale, SCREEN_HEIGHT 
        - (rPtr->position.y + pointPtr1->y) * scale, (rPtr->position.x + pointPtr2->x) * scale,
        SCREEN_WIDTH - (rPtr->position.y + pointPtr2->y * scale));
    }
  }
  SDL_RenderPresent(renderer);
}

rendering.h:

#include <vector>
#include <SDL2/SDL.h>

#ifndef RENDERING_H
#define RENDERING_H

//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

//SDL_Renderer * renderer; 

SDL_Renderer* InitializeRender();
void Render(SDL_Renderer*, std::vector<RigidBody*>);

#endif

rigid_bodies.cpp

// Joaquin Matias Giorgi [email protected] Impulse-Based Physics Engine 2D //
#include <iostream>
#include <math.h>
#include <SDL2/SDL.h>
#include <vector>
#include "polygon.h"

using namespace std;

vector<RigidBody*> RigidBodies;

// VECTOR STUFF //
  // Multiplication by scalar quantity.
  Vector Vector::operator*(const float scalar) {
      Vector vout;
      vout.x = this->x * scalar;
      vout.y = this->y * scalar;
      return(vout);
  }
  // Addition overload.
  Vector Vector::operator+=(const Vector vec) {
    Vector vout;
    vout.x = this->x + vec.x;
    vout.y = this->y + vec.y;
    return(vout);
  }

float dot (Vector vec1, Vector vec2) {
    float out = (vec1.x * vec2.x) + (vec1.y * vec2.y);
    return(out);
}

float cross2d (Vector vec1, Vector vec2) {
    // magnitude of perpendicular vector in 3d case.
    float out = (vec1.x * vec2.y) - (vec1.y * vec2.x);
    return(out);
}

// POLYGON Struct Methods //

Polygon::Polygon(vector< Vector* > Points1, Material m1, float volume1) {
    Points = Points1;
    m = m1;
    volume = volume1;
  }

 float Polygon::ComputeMass() {
    float mass = m.density * this->volume;
    return(mass);
 }
 // RIGID BODY Struct Methods //

    RigidBody::RigidBody(Vector position1, Polygon * p1) {

    std::cout << test << '\n';

    position = position1;
    p = p1;
    mass = p1->ComputeMass();
    orientation = 0;
    angularVelocity = 0;
    dt = .033;
    gravity.x = 0;
    gravity.y = -9.8;
    velocity.x = 0;
    velocity.y = 0;
    //RigidBodies.push_back(this); // Push to global vector of all RigidBodies.
    }

  // UPDATE at each iteration.  
    void RigidBody::Step() {
    this->velocity += this->gravity * this->dt;
    this->position += this->velocity * this->dt;
    this->orientation += this->angularVelocity * this->dt;
    }

polygon.h:

#include <vector>

#ifndef POLYGON_H
#define POLYGON_H

struct Vector {
    float x;
    float y;
  // Multiplication by scalar quantity.
  Vector operator*(const float);
  // Addition overload.
  Vector operator+=(const Vector);
};

struct Material {
    float density;
    float restitution;
};

struct Polygon {
  std::vector< Vector* > Points;
  float volume;
  Material m;

  Polygon(std::vector< Vector* >, Material, float);
  float ComputeMass();
};

struct RigidBody {
    float mass;
    float volume;
    float dt;
    // Linear
    Vector position;
    Vector gravity;
    Vector velocity;
    float acceleration;
    // Angular
    float orientation;
    float angularVelocity;
    float torque;
    Polygon * p;
  // Constructor
    RigidBody(Vector, Polygon*);
  // UPDATE at each iteration.  
    void Step();
};

// DECLARATION
extern std::vector<RigidBody*> RigidBodies;  // Global Vector of RigidBody Pointers.


#endif

Makefile:

sim: 
    g++ -std=c++11 main.cpp rigid_bodies.cpp rendering.cpp -I include -L lib -l SDL2-2.0.0
7
  • 2
    Please post an minimal reproducible example - STRONG emphasis on 'minimal' - removing commented out lines would be a good start on minimizing your example (but not sufficient). Then, you need to provide a very specific example of input and expected vs actual output to say what the precise problem you are having is. Commented Aug 31, 2018 at 4:30
  • 3
    In createBody, the code rigPtr = &r1 - r1 is an automatic that ceases to exist the moment the function returns, leaving rigPtr with a dangling pointer. As rigPtr is a global, later dereferencing of said-same thereafter invokes undefined behavior. I stopped reading as soon as I saw that. Commented Aug 31, 2018 at 4:35
  • Welcome to SO. In addition to WhozCraig: A working alternative would be to use instead RigidBodies.push_back(new RigidBody(pos1, polPtr)); rigPtr = RigidBodies.back(); Please, be aware that objects created by new have to be deleted by delete. You may also investigate in std::shared_ptr and std::unique_ptr. Another alternative might be to use a global std::vector<RigidBody> (storing the instances itself) instead. In this case, you should address individual instances via index instead of a pointer (as the latter may change when new instances are added). Commented Aug 31, 2018 at 5:37
  • Read more some C++ reference website. Notice the rule of five. You should use smart pointers (perhaps std::shared_ptr). Learn how to debug small programs and use also valgrind. Compile with all warnings and debug info: g++ -Wall -Wextra -g Commented Aug 31, 2018 at 7:13
  • "As suggested in other StackOverflow answers, I declared the global variable in a header file". Please link to these answers so that we can properly downvote them for giving misleading and dangerous advice. Global variables are bad. On top of that you have defined your variable in a header file, not just declared it, which is even worse. Commented Aug 31, 2018 at 8:02

1 Answer 1

1

In your function CreateBody, RigidBody r1 is created on the stack and ceases to exist when the function returns. Your pointer rigPtr (and the pointers in Points and polPtr) is no longer valid after the end of the function. If you call the CreateBody in a loop you will probably see that you get the same pointers for every call.

The simplest solution is to not use pointers at all, your objects are fairly small and shouldn't be too expensive to copy (or even cheaper to move).

If you really want to use pointers you need to allocate your objects on the heap rather than the stack using new. Note that these objects will then need to the deallocated using delete. A safer solution would be to wrap your pointers in std::shared_ptr or std::unique_ptr which takes care of deleting the objects automatically.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the detailed answer!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.