For a school project, my group is using OpenCV to capture video. From these (top-down) images, positions of objects are extracted and turned into a list of Points. Those Points then get triangulated using http://code.google.com/p/poly2tri/ (to overcome the problem of possible non-convex objects). Then, using the coordinates of the triangulated ground pane, we draw the objects in 3D using freeglut. (Side and Top panes are calculated using the ground pane coordinates). The problem we have is that when we delete our old list of Points, the application randomly crashes. Sometimes after 1 second, sometimes after 30 seconds, sometimes after a few minutes. The error we get is "Access violation writing location 0xCCCCCCCC"
Our code:
void WorldLayoutBuilder::update()
{
pointList.clear();
// Capture image
<code to capture image and get countours>
for(size_t i = 0; i < contours.size(); i++)
{
if(contours[i].size() > 50)
{
approxPolyDP(contours[i], approxShape, cv::arcLength(cv::Mat(contours[i]), true)*0.04, true);
drawContours(drawing, contours, i, cv::Scalar(255, 0, 0), 0);
std::vector<Point> newObject;
for(size_t j = 0; j < contours[i].size(); j++)
{
cv::Point newPoint = contours[i][j];
newObject.push_back(Point((float) newPoint.x / 100, 0.0f,(float) newPoint.y / 100));
}
pointList.push_back(newObject);
}
}
ObjectCreator3D::createObjects(&pointList);
contours.clear();
<code to release images, etc>
}
This captures an image, retrieves coordinates of objects, and then calls ObjectCreator3D::createObjects():
void ObjectCreator3D::createObjects(std::list<std::vector<Point>>* inputList)
{
std::list<WorldObject>* tempObjects = new std::list<WorldObject>;
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
DataStorage::getInstance()->setObjects(tempObjects);
}
All objects are turned into WorldObjects:
#include <list>
#include <iostream>
#include <GL/glut.h>
#include <GL/freeglut.h>
#include <time.h>
#include "WorldObject.h"
#include "Point.h"
//Constant height - adjustable/randomized solution is partially implemented in the constructor.
const float WorldObject::HEIGHT = 5.0f;
template <class C> void FreeClear(C & cntr)
{
for(typename C::iterator it = cntr.begin(); it != cntr.end(); ++it)
{
delete * it;
}
cntr.clear();
}
WorldObject::WorldObject(std::vector<Point>* pointList)
{
//TODO, when we have time. Seems difficult because height will change each update...
/*srand (time(NULL));
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
//point.setY(fGeneratedY);
point.setY(HEIGHT);
this->pointList = pointList;
}
WorldObject::~WorldObject()
{
//Cleanup
delete cdt;
FreeClear(polyPoints);
}
/*
Author Tim Cocu & Bas Rops
Function for drawing the WorldObject
*/
void WorldObject::draw()
{
glPushMatrix();
glColor3f(0.8f, 0.8f, 0.8f);
//Calculate our bottom pane
calculateTriangles();
//BOTTOM PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, -1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)0.0f, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)0.0f, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)0.0f, (GLfloat)c.y);
glEnd();
}
//TOP PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, 1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)HEIGHT, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)HEIGHT, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)HEIGHT, (GLfloat)c.y);
glEnd();
}
glColor3f(1.0f, 1.0f, 1.0f);
//SIDE PANES
for(std::size_t iPaneCounter = 0; iPaneCounter < pointList->size(); iPaneCounter++)
{
Point firstPoint = (*pointList)[iPaneCounter];
Point secondPoint (0.0f, 0.0f, 0.0f);
if(iPaneCounter + 1 < pointList->size())
secondPoint.set((*pointList)[iPaneCounter + 1].getX(), (*pointList)[iPaneCounter + 1].getY(), (*pointList)[iPaneCounter + 1].getZ() );
else
secondPoint.set((*pointList)[0].getX(), (*pointList)[0].getY(), (*pointList)[0].getZ());
glBegin(GL_POLYGON);
float fNormalX = (firstPoint.getY() * secondPoint.getZ()) - (firstPoint.getZ() * secondPoint.getY());
float fNormalY = -((secondPoint.getZ() * firstPoint.getX()) - (secondPoint.getX() * firstPoint.getZ()));
float fNormalZ = (firstPoint.getX() * secondPoint.getY()) - (firstPoint.getY() * secondPoint.getX());
glNormal3f(fNormalX, fNormalY, fNormalZ);
glVertex3f(firstPoint.getX(), 0.0f, firstPoint.getZ());
glVertex3f(secondPoint.getX(), 0.0f, secondPoint.getZ());
glVertex3f(secondPoint.getX(), secondPoint.getY(), secondPoint.getZ());
glVertex3f(firstPoint.getX(), firstPoint.getY(), firstPoint.getZ());
glEnd();
}
}
/*
Calculates triangles that make a ground or top pane. Used for calculating possible non-convex objects
*/
void WorldObject::calculateTriangles()
{
//Empty the polyPoints list
if(polyPoints.size() > 0)
FreeClear(polyPoints);
//Convert our Points to p2t::Points
for(std::size_t iBottomIndex = 0; iBottomIndex < pointList->size(); iBottomIndex++)
polyPoints.push_back(new p2t::Point((*pointList)[iBottomIndex].getX(), (*pointList)[iBottomIndex].getZ()));
if(cdt == nullptr)
//Create CDT (Constrained Delaunay Triangulation) and add primary polyPoints
//NOTE: polyPoints must be a simple polygon. The polyPoints' points constitute constrained edges. No repeating points are allowed!
cdt = new p2t::CDT(polyPoints);
//Turn our polyPoints into p2t::Triangles
cdt->Triangulate();
//Set the triangles to use for drawing
calculatedTriangles = cdt->GetTriangles();
}
/*
Retrieve a pointer to a list of Points
*/
std::vector<Point>* WorldObject::getPoints()
{
return pointList;
}
/*
Retrieve a pointer to a list of p2t::Triangles
*/
std::vector<p2t::Triangle*> WorldObject::getCalculatedTriangles()
{
return calculatedTriangles;
}
When all WorldObjects are created, they are stored in DataStorage, DataStorage::getInstance()->setObjects() is called:
void DataStorage::setObjects(std::list<WorldObject>* objectList)
{
delete this->objectList;
this->objectList = objectList;
}
The application seems to crash on delete this->objectList; in setObjects(), so we think the application is trying to delete things he can't delete.
Any help would be greatly appreciated, we've been on this for a few days already
Here, you pass a pointer to an object owned by the list to the constructor of WorldObject:
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
In WorldObject you store the pointer:
//Default Constructor
WorldObject::WorldObject(std::vector<Point>* pointList)
{
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
point.setY(HEIGHT);
this->pointList = pointList;
}
Which means WorldObject::pointList is only valid so long as the std::list which you constructed your WorldObjects from is still around. (After that, the result is undefined -- it could work, it could crash, it could format your hard drive and leak your identity to Texas).
If you insist on working with raw pointers, you as programmer are responsible for checking and keeping track of the lifetime of every single pointer. This is error prone and will cause random crashes that you will find difficult to track down.
Stop using raw pointers. Instead, if an object owns a resource, store it in a std::unique_ptr<>. If you want the same resource to be shared by multiple objects, use std::shared_ptr and std::weak_ptr, unless the lifetime of all but one of these objects is much, much shorter than the others in a guaranteed way.
Related
I am currently working on a game and I want to know if there is any way of handling with the elements i am drawing . For example : if i draw in a loop 100 cubes , how can i show / hide the cube number 15 or 63 or n ... I thought that initializing elements in a list would work , but i didn't find any property of it that could help.
GLuint cube;
cube = glGenLists(1);
glNewList(cube,GL_COMPILE);
for(int i = -30; i < 3; i++) {
for(int j = -30; j < 3; j++) {
glPushMatrix();
glTranslatef(i*2.0,0,j * 2.0);
Dcube();
glPopMatrix();
}
}
glEndList();
//something like : glDeleteList(cube); but that only works with entire list not with individual objects..
You have a display list, very good. So now you're back to using your regular language primitives to simply call that function.
std::array<bool, 100> cubes;
std::fill(cubes.begin(), cubes.end(), true);
cubes[15] = false;
cubes[63] = false;
for (bool drawCube : cubes) {
if (drawCube) {
// move a bit, perhaps using glTranslate
glCallList(cube);
}
}
OpenGL isn't your statekeeper. It just draws what you tell it to, you're responsible for keeping your objects.
I have an Entity.h like this:
using namespace physx;
class Entity
{
public:
Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id);
virtual ~Entity(void);
virtual void update(Ogre::Real dt);
virtual void init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity=PxVec3(0, 0, 0));
protected:
Ogre::Entity* mOgreEntity = NULL;
Ogre::SceneNode* mOgreNode = NULL;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDimensions;
std::string mMesh;
std::string mId;
PxRigidDynamic* mActor;
PxMaterial* mMaterial;
};
And here is my Entity source:
#include "Entity.h"
Entity::Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id)
{
mDimensions = dims;
mPosition = pos;
mMesh = mesh;
mId = id;
mActor = NULL;
mMaterial = NULL;
}
Entity::~Entity(void)
{
}
void Entity::update(Ogre::Real dt)
{
PxVec3 pos = mActor->getGlobalPose().p;
Ogre::Real r = 0;
mOgreNode->setPosition(Ogre::Vector3(pos.x + r, pos.y + r, pos.z + r));
}
void Entity::init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity)
{
// Create an Entity
mOgreEntity = sceneMgr->createEntity(mId, mMesh);
mOgreEntity->setCastShadows(true);
// Create a SceneNode and attach the Entity to it
mOgreNode = sceneMgr->getRootSceneNode()->createChildSceneNode(mId + "Node");
Ogre::AxisAlignedBox box = mOgreEntity->getBoundingBox();
Ogre::Vector3 realSizes = box.getSize();
mOgreNode->setPosition(mPosition);
mOgreNode->attachObject(mOgreEntity);
Ogre::Vector3 scaler = Ogre::Vector3(mDimensions.x / realSizes.x, mDimensions.y / realSizes.y, mDimensions.z / realSizes.z);
mOgreNode->scale(scaler);
mMaterial = physics->createMaterial(1.5f, 1.5f, 1.0f);
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
} else {
// geometry = NULL;
}
PxTransform transform = PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z));
mActor = PxCreateDynamic(*physics, transform, *geometry, *mMaterial, PxReal(.1));
// if(!mActor) {
// MessageBox( NULL, "no actor", "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
// return;
// }
mActor->setLinearVelocity(velocity);
// And add the actor to a scene:
scene->addActor(*mActor);
}
Now, if I create a single entity and initialize it works. Even wirh a second entity var it works as well. Now with an array:
Entity *mEntities[20];
for(int i = 0 ; i < 20 ; i++ ){
ostringstream nameStream;
nameStream << "Sphere_" << i;
string name = nameStream.str();
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6), Ogre::Vector3(i*5, i*4.5, i*6), "sphere.mesh", name);
sphere->init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
mEntities[i] = sphere;
}
I got Access violation. W/ the just-in-time debugger, it turned out that mActorwas null as well as mMaterial
EDIT:
This code does not work either:
mEntity = Entity(Ogre::Vector3(50.0f, 50.0f, 50.0f), Ogre::Vector3(50.0f, 40.5f, 60.0f), "sphere.mesh", "sphere");
mEntity.init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
1)
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6),
Ogre::Vector3(i*5, i*4.5, i*6),
"sphere.mesh",
"Sphere_"+i);
Look at the "Sphere_"+i
If the i is larger then length of ”Sphere_” you are passing pointer to some random memory. I assume that you wanted to create a string with i at the end.
Use sprintf or std::string for that.
2)
If you change the loop range from 20 to let's say 3 it will probably work. The problem is that your names will be:
Sphere_, phere_, here_
Because by doing "Sphere_"+i you are not adding integer to the string.
This is "equal" to:
char *string = "String";
string += 3;
3)
This code will generate string that you need:
std::ostringstream newStringStream;
newStringStream << "Sphere_" << i;
std::string newString = newStringStream.str();
Here is another issue:
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
geometry = &PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
The problem with this is that you are assigning to geometry the address of a temporary value. Once that line of code is completed, that temporary is gone.
The probable fix for this is to do this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
geometry = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
//...
mActor = PxCreateDynamic(*physics, transform, geometry, *mMaterial, PxReal(.1));
Now geometry is no longer a pointer, and you're assigning geometry to the value returned, (not address-of the value returned).
I am reading the documentation here:
http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/apireference/files/classPxSphereGeometry.html
So PxSphereGeometry(x) is a constructor call. So you need to assign the return value to a PxShpereGeometry, not a PxSphereGeometry*.
Edit: Your latest changes also do not have the desired effect:
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
}
The g is local to the if() block. You assign the address of this g to geometry. Then when that block exits, g is gone, and now you have geometry pointing to something that no longer exists.
The difference between your edited code and the answer I gave is that my answer assigns the return value to an existing object. So I created a duplicate of the return value. What your doing in the edited code is not creating a duplicate, but pointing to a local object, which as explained, won't exist after it leaves scope.
So if you were to write code that follows the pattern of your edited code, and have it be valid, the change would look like this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = g;
}
However, this does extraneous work. The original answer is sufficient.
I tried the alternate way to create a rigid body and it worked!!!!
mActor = physics->createRigidDynamic(PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z)));
PxShape* shape = mActor->createShape(PxSphereGeometry(mDimensions.x / 2), *mMaterial);
PxRigidBodyExt::updateMassAndInertia(*mActor, 0.4f);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have programmed a simple top-down car driving game that resembles the first GTA, on the GameBoyAdvance. I have used only vector graphics for doing so, and the GBA doesn't handle it very well; basically with 5 pedestrian instances it lags.
I don't have much experience in optimizing code, so I would like to know if there are some tweaks I could make to my code in order to make it run faster, not depending on the fact that it runs on a GBA.
The collision testing I use is SAT (separating axis theorem) as I've found it to be the easisest one for collision check with vector graphics; the game is very simple itself.
Here is the code:
/*
GTA Vector City
Author: Alberto Taiuti
Version: 2.0
*/
#include "Global.h"
#include <string.h>
#include <cstdio>
#include "font.h"
#include "CVector2D.h"
#include "CCar.h"
#include "CPed.h"
#include <vector>
#include <memory>
/* GLOBAL VARIABLES */
void CheckCollisionsRect(CRect *test_a, CRect *test_b);
std::vector<CVector2D> PrepVectors(CRect *shape);
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis);
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point);
/* MAIN */
// The entry point for the game
int main()
{
// Frame counter
uint32_t frames = 0;
// Previous & current buttons states
static uint16_t prev_buttons = 0, cur_buttons = 0;
// Put the display into bitmap mode 3, and enable background 2.
REG_DISPCNT = MODE4 | BG2_ENABLE;
// Set up the palette.
SetPaletteBG(BLACK, RGB(0, 0, 0)); // black
SetPaletteBG(WHITE, RGB(31, 31, 31)); // white
SetPaletteBG(GREY, RGB(15, 15, 15)); // grey
SetPaletteBG(RED, RGB(31, 0, 0)); // red
SetPaletteBG(GREEN, RGB(0, 31, 0)); // green
SetPaletteBG(BLUE, RGB(0, 0, 31)); // blue
// Create car instance
CCar *car = new CCar(50,50);
// Create a building
/*CRect *test_b = new CRect(100.0f, 100.0f, 30, 30);
CRect *test_c = new CRect(120.0f, 120.0f, 30, 30);
CRect *test_d = new CRect(30.0f, 30.0f, 30, 30);*/
// Pedestrian instances
int ped_number = 10; // Number of pedestrians
std::vector<CPed*> peds; // Ped. entities container (made of smart pointers)
typedef std::vector<CPed*>::iterator p_itor; // Iterator
for(int i = 1; i <= ped_number; i++)
{
peds.push_back(new CPed(i, RED, 2.0f));
}
// Check whether the game is over
bool end = false;
// Main loop
while (!end)
{
// Flip the screen
FlipBuffers();
//Clear the screen
ClearScreen8(BLACK);
// Update frame counter
frames ++;
// Get the current state of the buttons.
cur_buttons = REG_KEYINPUT;
// Handle Input
car->HandleInput(prev_buttons, cur_buttons);
// Logic
car->Update();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Update();
}
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
/*CheckCollisionsRect(car->shape, test_b);
CheckCollisionsRect(car->shape, test_c);
CheckCollisionsRect(car->shape, test_d);
CheckCollisionRectVSPoint(car->shape, test_ped->pos);*/
// Render
car->Draw();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Draw();
}
/*test_b->DrawFrame8(GREEN);
test_c->DrawFrame8(WHITE);
test_d->DrawFrame8(RED);
test_ped->Draw();*/
prev_buttons = cur_buttons;
// VSync
WaitVSync();
}
// Free memory
delete car;
//delete test_b; delete test_c; delete test_d;
//delete test_ped;
for(p_itor itor = peds.begin(); itor != peds.end(); itor ++)// Delete pedestrians
{
peds.erase(itor);
}
return 0;
}
void CheckCollisionsRect(CRect *test_a, CRect *test_b)
{
// If the two shapes are close enough, check for collision, otherways skip and save calculations to the CPU
//if((pow((test_a->points[0]->x - test_b->points[0]->x), 2) + pow((test_a->points[0]->y - test_b->points[0]->y), 2)) < 25.0f)
{
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
std::vector<CVector2D> normals_b = test_b->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
std::vector<CVector2D> vect_test_b = PrepVectors(test_b);
// Get the min and max vectors for each shape for each projection (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]); //
CVector2D result_P2 = GetMinMaxShape(vect_test_b, normals_a[1]); //
// If the two objects are not colliding
if(result_P1.y < result_P2.x || result_P2.y < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]); // First axis couple
CVector2D result_Q2 = GetMinMaxShape(vect_test_b, normals_a[0]); //
if(result_Q1.y < result_Q2.x || result_Q2.y < result_Q1.x)
{
return;
}
CVector2D result_R1 = GetMinMaxShape(vect_test_a, normals_b[1]); //
CVector2D result_R2 = GetMinMaxShape(vect_test_b, normals_b[1]); //
if(result_R1.y < result_R2.x || result_R2.y < result_R1.x)
{
return;
}
CVector2D result_S1 = GetMinMaxShape(vect_test_a, normals_b[0]); // Second axis couple
CVector2D result_S2 = GetMinMaxShape(vect_test_b, normals_b[0]); //
if(result_S1.y < result_S2.x || result_S2.y < result_S1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
}
// Check for collision between an OOBB and a point
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point)
{
// Prepare the normals for the shape
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create a container for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
// Get projections for the OOBB (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]);
float result_point = point->DotProduct(normals_a[1]);
// If the two objects are not colliding on this axis
if(result_P1.y < result_point || result_point < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]);
result_point = point->DotProduct(normals_a[0]);
// If the two objects are not colliding on this axis
if(result_Q1.y < result_point || result_point < result_Q1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
// Returns a container with projection vectors for a given shape
std::vector<CVector2D> PrepVectors(CRect *shape)
{
std::vector<CVector2D> vect;
// Create vectors for projection and load them into the arrays
for( uint16_t i=0; i < 5; i++)
{
// Get global position of vectors and then add them to the array
vect.push_back(shape->GetVectorGlobal(i));
}
return vect;
}
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis)
{
// Set initial minimum and maximum for shape's projection vectors
float min_proj = vect_shape[1].DotProduct(axis);
float max_proj = vect_shape[1].DotProduct(axis);
// Calculate max and min projection vectors by iterating along all of the corners
for(uint16_t i = 2; i < vect_shape.size(); i ++)
{
float current_proj = vect_shape[i].DotProduct(axis);
// Select minimum projection on axis
if(current_proj < min_proj) // If current projection is smaller than the minimum one
min_proj = current_proj;
// Select maximum projection on axis
if(current_proj > max_proj) // If current projection is greater than the minimum one
max_proj = current_proj;
}
return (CVector2D(min_proj, max_proj)); // Return a vector2D as it is a handy way for returning a couple of values
}
Many thanks in advance to everyone and sorry for the messy code!
I gave it a really quick reading so I may have overlooked something. Well, there are obvious tips for improving performance such as passing vectors to functions by reference. Using prefix incrementation instead of postfix is also a good habit. These two rules are definitely nothing like 'premature optimization the, root of ...'. Do not delete pedestrians one by one but use std::vector::clear(). And If you claim you use smart pointers, you shoud, because it seems you have memory leak because you did not delete the pedestrian pointers. And use const keyword whereever possible. Once you make the obvious correction, and the speed is still not satisfactory, then you need to use profiler.
And read something about optimization, here for example: http://www.agner.org/optimize/optimizing_cpp.pdf
One thing leaps out at me (apart from the continuous passing of vectors by value rather than reference, which will be incredibly costly!)
In you collision detection, you're seeing if the car hits each pedestrian
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
Then, in the collision detector, you're repeating a lot of the same processing on the car shape every time:-
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
.. etc...
You should rework that loop to create the normals etc for the car just once, and then reuse the results for each check against a pedestrian.
This is how I position my torus (satellite) upon a sphere, and then rotate it around the sphere:
int satellite_1_1_step = 0;
int &r_satellite_1_1_step = satellite_1_1_step;
float satellite_1_1_divider = 300;
float satellite_1_1_theta = 6.5;
float satellite_1_1_phi = 1;
float satellite_1_1_theta_increment = 20/satellite_1_1_divider;
float satellite_1_1_phi_increment = 20/satellite_1_1_divider;
void satellite_1_1 ()
{
float satellite_1_1_theta_math = (satellite_1_1_theta-(satellite_1_1_theta_increment * r_satellite_1_1_step))/10.0*M_PI;
float satellite_1_1_phi_math = (satellite_1_1_phi-(satellite_1_1_phi_increment * r_satellite_1_1_step))/10.0*2*M_PI;
r_satellite_1_1_x = radius_exodus_pos * sin(satellite_1_1_theta_math) * cos(satellite_1_1_phi_math);
r_satellite_1_1_y = radius_exodus_pos * sin(satellite_1_1_theta_math) * sin(satellite_1_1_phi_math);
r_satellite_1_1_z = radius_exodus_pos * cos(satellite_1_1_theta_math);
glPushMatrix();
glTranslatef(r_satellite_1_1_x,r_satellite_1_1_y,r_satellite_1_1_z);
glColor3f(1,0,0);
glutSolidTorus(0.04, 0.2, 10, 100);
glEnd();
glPopMatrix();
}
This is how I update and increment its position:
void satellite_1_1_increment()
{
if (r_satellite_1_1_step < satellite_1_1_divider)
{
++(r_satellite_1_1_step);
}
if (r_satellite_1_1_step >= satellite_1_1_divider)
{
r_satellite_1_1_step = 1;
}
}
So, my torus (satellite) moves around the sphere, ending back up in its starting position, and continues over again - which is great. However, the path it takes wobbles around the poles (I think) along the way - rather than simply circumnavigating the sphere.
Is there an improvement that can be made to my math which will cause the satellite to circumnavigate the sphere in a more circular path?
The first issue I see is this:
void satellite_1_1_increment()
{
if (r_satellite_1_1_step < satellite_1_1_divider)
{
++(r_satellite_1_1_step);
}
if (r_satellite_1_1_step >= satellite_1_1_divider)
{
r_satellite_1_1_step = 1;
}
}
What happens at the edge case when the step is incremented by the first test such that it satisfies the second test? It is immediately reset, thus missing the value. I think you want it written like this to avoid that problem:
void satellite_1_1_increment()
{
if (r_satellite_1_1_step >= satellite_1_1_divider)
r_satellite_1_1_step = 1;
else ++r_satellite_1_1_step;
}
Is 1 the correct reset value? Maybe it should be 0?
Changed the first two lines of:
void satellite_1_1 ()
float satellite_1_1_theta_math = (satellite_1_1_theta+(satellite_1_1_theta_increment* r_satellite_1_1_step))*M_PI;
float satellite_1_1_phi_math = (satellite_1_1_phi-(satellite_1_1_phi_increment* r_satellite_1_1_step))*M_PI/360;
Now the satellite orbits 360 degrees along the equator. Adding a glRotatef after my glPushMatrix lets me fine tune its axis.
Thanks again wallyk. - kropcke
I have a very rudimentary camera which generates 3 vectors for use with gluLookAt(...) the problem is I'm not sure if this is correct, I adapted code from something my lecturer showed us (I think he got it from somewhere).
This actually works until you spin the mouse round in circles than camera starts to rotate around the z-axis. Which shouldn't happen as the mouse coords are only attached to the pitch and yaw not the roll.
Camera
// Camera.hpp
#ifndef MOOT_CAMERA_INCLUDE_HPP
#define MOOT_CAMERA_INCLUDE_HPP
#include <GL/gl.h>
#include <GL/glu.h>
#include <boost/utility.hpp>
#include <Moot/Platform.hpp>
#include <Moot/Vector3D.hpp>
namespace Moot
{
class Camera : public boost::noncopyable
{
protected:
Vec3f m_position, m_up, m_right, m_forward, m_viewPoint;
uint16_t m_height, m_width;
public:
Camera()
{
m_forward = Vec3f(0.0f, 0.0f, -1.0f);
m_right = Vec3f(1.0f, 0.0f, 0.0f);
m_up = Vec3f(0.0f, 1.0f, 0.0f);
}
void setup(uint16_t setHeight, uint16_t setWidth)
{
m_height = setHeight;
m_width = setWidth;
}
void move(float distance)
{
m_position += (m_forward * distance);
}
void addPitch(float setPitch)
{
m_forward = (m_forward * cos(setPitch) + (m_up * sin(setPitch)));
m_forward.setNormal();
// Cross Product
m_up = (m_forward / m_right) * -1;
}
void addYaw(float setYaw)
{
m_forward = ((m_forward * cos(setYaw)) - (m_right * sin(setYaw)));
m_forward.setNormal();
// Cross Product
m_right = m_forward / m_up;
}
void addRoll(float setRoll)
{
m_right = (m_right * cos(setRoll) + (m_up * sin(setRoll)));
m_right.setNormal();
// Cross Product
m_up = (m_forward / m_right) * -1;
}
virtual void apply() = 0;
}; // Camera
} // Moot
#endif
Snippet from update cycle
// Mouse movement
m_camera.addPitch((float)input().mouseDeltaY() * 0.001);
m_camera.addYaw((float)input().mouseDeltaX() * 0.001);
apply() in the camera class is defined in an inherited class, which is called from the draw function of the game loop.
void apply()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40.0,(GLdouble)m_width/(GLdouble)m_height,0.5,20.0);
m_viewPoint = m_position + m_forward;
gluLookAt( m_position.getX(), m_position.getY(), m_position.getZ(),
m_viewPoint.getX(), m_viewPoint.getY(), m_viewPoint.getZ(),
m_up.getX(), m_up.getY(), m_up.getZ());
}
Don't accumulate the transforms in your vectors, store the angles and generate the vectors on-the-fly.
EDIT: Floating-point stability. Compare the output of a and b:
#include <iostream>
using namespace std;
int main()
{
const float small = 0.00001;
const unsigned int times = 100000;
float a = 0.0f;
for( unsigned int i = 0; i < times; ++i )
{
a += small;
}
cout << a << endl;
float b = 0.0f;
b = small * times;
cout << b << endl;
return 0;
}
Output:
1.00099
1
I am not sure where to start, as you are posting only small snippets, not enough to fully reproduce the problem.
In your methods you update all parameters, and your parameters are depending on previous values. I am not sure what exactly you call, because you posted that you call only these two :
m_camera.addPitch((float)input().mouseDeltaY() * 0.001);
m_camera.addYaw((float)input().mouseDeltaX() * 0.001);
You should somehow break that circle by adding new parameters, and the output should depend on the input (for example, m_position shouldn't depend on m_forward).
You should also initialize all variables in the constructor, and I see you are initializing only m_forward, m_right and m_up (by the way, use initialization list).
You might want to reconsider your approach in favor of using quaternion rotations as described in this paper. This has the advantage of representing all of your accumulated rotations as a single rotation about a single vector (only need to keep track of a single quaternion) which you can apply to the canonical orientation vectors (up, norm and right) describing the camera orientation. Furthermore, since you're using C++, you can use the Boost quaternion class to manage the math of most of it.