Negative row and column in terrain following algorithm - c++

I'm trying to do terrain following, and I get a negative camera position in the xz plane. Now I get an out of boundary exception, because the row or the col is negative. How would I transform the cell of the grid to the origin correctly, giving negative camera coordinates.
Here is the two functions
int cGrid::getHeightmapEntry(int row, int col)
{
return m_heightmap[row * 300 + col];
}
float cGrid::getHeight(float x, float z, float _width, float _depth, int _cellSpacing)
{
// Translate on xz-plane by the transformation that takes
// the terrain START point to the origin.
x = ((float)_width / 2.0f) + x;
z = ((float)_depth / 2.0f) - z;
// Scale down by the transformation that makes the
// cellspacing equal to one. This is given by
// 1 / cellspacing since; cellspacing * 1 / cellspacing = 1.
x /= (float)_cellSpacing;
z /= (float)_cellSpacing;
// From now on, we will interpret our positive z-axis as
// going in the 'down' direction, rather than the 'up' direction.
// This allows to extract the row and column simply by 'flooring'
// x and z:
float col = ::floorf(x);
float row = ::floorf(z);
if (row < 0 || col<0)
{
row = 0;
}
// get the heights of the quad we're in:
//
// A B
// *---*
// | / |
// *---*
// C D
float A = getHeightmapEntry(row, col);
float B = getHeightmapEntry(row, col + 1);
float C = getHeightmapEntry(row + 1, col);
float D = getHeightmapEntry(row + 1, col + 1);
//
// Find the triangle we are in:
//
// Translate by the transformation that takes the upper-left
// corner of the cell we are in to the origin. Recall that our
// cellspacing was nomalized to 1. Thus we have a unit square
// at the origin of our +x -> 'right' and +z -> 'down' system.
float dx = x - col;
float dz = z - row;
// Note the below compuations of u and v are unneccessary, we really
// only need the height, but we compute the entire vector to emphasis
// the books discussion.
float height = 0.0f;
if (dz < 1.0f - dx) // upper triangle ABC
{
float uy = B - A; // A->B
float vy = C - A; // A->C
// Linearly interpolate on each vector. The height is the vertex
// height the vectors u and v originate from {A}, plus the heights
// found by interpolating on each vector u and v.
height = A + Lerp(0.0f, uy, dx) + Lerp(0.0f, vy, dz);
}
else // lower triangle DCB
{
float uy = C - D; // D->C
float vy = B - D; // D->B
// Linearly interpolate on each vector. The height is the vertex
// height the vectors u and v originate from {D}, plus the heights
// found by interpolating on each vector u and v.
height = D + Lerp(0.0f, uy, 1.0f - dx) + Lerp(0.0f, vy, 1.0f - dz);
}
return height;
}
float height = m_Grid.getHeight(position.x, position.y, 49 * 300, 49 * 300, 6.1224489795918367f);
if (height != 0)
{
position.y = height + 10.0f;
}
m_Camera.SetPosition(position.x, position.y, position.z);
bool cGrid::readRawFile(std::string fileName, int m, int n)
{
// A height for each vertex
std::vector<BYTE> in(m*n);
std::ifstream inFile(fileName.c_str(), std::ios_base::binary);
if (!inFile)
return false;
inFile.read(
(char*)&in[0], // buffer
in.size());// number of bytes to read into buffer
inFile.close();
// copy BYTE vector to int vector
m_heightmap.resize(n*m);
for (int i = 0; i < in.size(); i++)
m_heightmap[i] = (float)((in[i])/255)*50.0f;
return true;
}
m_Grid.readRawFile("castlehm257.raw", 50, 50);

I infer that you’re storing a 50 by 50 matrix inside a 300 by 300 matrix, to represent a grid of 49 by 49 cells. I also infer that m_Grid is an object of type cGrid. Your code appears to contain the following errors:
Argument(2) of call m_Grid.getHeight is not a z value.
Argument(3) of call m_Grid.getHeight is inconsistent with argument(5).
Argument(4) of call m_Grid.getHeight is inconsistent with argument(5).
Implicit cast of literal float to int in argument(5) of call m_Grid.getHeight - the value will be truncated.
Try changing your function call to this:
float height = m_Grid.getHeight(position.x, position.z, 49 * cellspacing, 49 * cellspacing, cellspacing);
-- where cellspacing is as defined in your diagram.
Also, try changing parameter(5) of cGrid::getHeight from int _cellSpacing to float _cellSpacing.
(I have edited this answer a couple of times as my understanding of your code has evolved.)

Related

weird inaccuracy in line rotation - c++

I have programmed a simple dragon curve fractal. It seems to work for the most part, but there is an odd logical error that shifts the rotation of certain lines by one pixel. This wouldn't normally be an issue, but after a few generations, at the right size, the fractal begins to look wonky.
I am using open cv in c++ to generate it, but I'm pretty sure it's a logical error rather than a display error. I have printed the values to the console multiple times and seen for myself that there is a one-digit difference between values that are intended to be the exact same - meaning a line may have a y of 200 at one end and 201 at another.
Here is the full code:
#include<iostream>
#include<cmath>
#include<opencv2/opencv.hpp>
const int width=500;
const int height=500;
const double PI=std::atan(1)*4.0;
struct point{
double x;
double y;
point(double x_,double y_){
x=x_;
y=y_;
}};
cv::Mat img(width,height,CV_8UC3,cv::Scalar(255,255,255));
double deg_to_rad(double degrees){return degrees*PI/180;}
point rotate(int degree, int centx, int centy, int ll) {
double radians = deg_to_rad(degree);
return point(centx + (ll * std::cos(radians)), centy + (ll * std::sin(radians)));
}
void generate(point & r, std::vector < point > & verticies, int rotation = 90) {
int curRotation = 90;
bool start = true;
point center = r;
point rot(0, 0);
std::vector<point> verticiesc(verticies);
for (point i: verticiesc) {
double dx = center.x - i.x;
double dy = center.y - i.y;
//distance from centre
int ll = std::sqrt(dx * dx + dy * dy);
//angle from centre
curRotation = std::atan2(dy, dx) * 180 / PI;
//add 90 degrees of rotation
rot = rotate(curRotation + rotation, center.x, center.y, ll);
verticies.push_back(rot);
//endpoint, where the next centre will be
if (start) {
r = rot;
start = false;
}
}
}
void gen(int gens, int bwidth = 1) {
int ll = 7;
std::vector < point > verticies = {
point(width / 2, height / 2 - ll),
point(width / 2, height / 2)
};
point rot(width / 2, height / 2);
for (int i = 0; i < gens; i++) {
generate(rot, verticies);
}
//draw lines
for (int i = 0; i < verticies.size(); i += 2) {
cv::line(img, cv::Point(verticies[i].x, verticies[i].y), cv::Point(verticies[i + 1].x, verticies[i + 1].y), cv::Scalar(0, 0, 0), 1, 8);
}
}
int main() {
gen(10);
cv::imshow("", img);
cv::waitKey(0);
return 0;
}
First, you use int to store point coordinates - that's a bad idea - you lose all accuracy of point position. Use double or float.
Second, your method for drawing fractals is not too stable numericly. You'd better store original shape and all rotation/translation/scale that indicate where and how to draw scaled copies of the original shape.
Also, I believe this is a bug:
for(point i: verices)
{
...
vertices.push_back(rot);
...
}
Changing size of vertices while inside such a for-loop might cause a crash or UB.
Turns out it was to do with floating-point precision. I changed
x=x_;
y=y_;
to
x=std::round(x_);
y=std::round(y_);
and it works.

Stop spray and stay in its position

// #include loads up library files, the order can matter
// generally load glut.h last
#include <stdio.h> // this library is for standard input and output
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "glut.h"// this library is for glut the OpenGL Utility Toolkit
//this defines a constant for the array size
#define SPRAYSIZE 500
// the properties of a spray particle are defined in a struct
struct sprayParticle {
float x = 0; // current position x
float y = 0; // current position y
float startx = 0; // birth position x
float starty = 0; // birth position y
int startTime; // a birthtime in frames when it will be born
int startRange = 100; // the maximum time at which a birth can happen
bool started = false; // tracks whether the particle has benn born or not
float speed = 0.1;
float radius;
float startxd = 0; // starting direction vector x value
float startyd = 0; // startingdirection vestor y value
float xd = 0; // current direction vector x value
float yd = 0; // current direction vector x value
float alpha = 1.0; // transparency
};
int winWidth = 1000, winHeight = 1000;
int counter = 0;
time_t t;
sprayParticle spray[SPRAYSIZE];
float angle = 90; // the angle of the spray: 0 degrees is to the left,
// 90 degrees straight up, 180 to the right etc
float sprayWidth = 30;// the width of the spray in degrees
float sprayCenterX, sprayCenterY;
//variables for spray colour, set once per spray
float fr = 1; float fg = 1; float fb = 1;
// the gravity vector
float gx = 0;
float gy = -0.0005;
// the position of thepartcle ystem emitter, wher the rocket should be drawn
float rocketstartx = winWidth - 100, rocketstarty = winHeight - 100;
void init() {
glClearColor(0.0, 0.0, 0.0, 0.0); // set what colour you want the background to be
glMatrixMode(GL_PROJECTION); // set the matrix mode, we will look at this later
gluOrtho2D(0.0, winWidth, 0.0, winHeight);
}
void circle(double radius, double xc, double yc) {
int i;
double angle = 2 * 3.1415 / 20; // circle is drawn using 20 line.
double circle_xy[40][2];
circle_xy[0][0] = radius + xc;
circle_xy[0][1] = yc;
glBegin(GL_POLYGON);
for (i = 1; i < 20; i++) {
circle_xy[i][0] = radius * cos(i *angle) + xc;
circle_xy[i][1] = radius * sin(i * angle) + yc;
glVertex2f(circle_xy[i - 1][0], circle_xy[i - 1][1]);
glVertex2f(circle_xy[i][0], circle_xy[i][1]);
}
glEnd();
}
void normalise(int i) {
float mag;
mag = sqrt((spray[i].xd*spray[i].xd) + (spray[i].yd*spray[i].yd));
spray[i].xd = spray[i].xd / mag;
spray[i].yd = spray[i].yd / mag;
}
// we calculate the direction vector of the current particle from the global
variable angle and spread
void setDirectionVector(int i) {
float minAngle, maxAngle, range, newangle;
double newAngleInRadians; // variable
int rangeInt;
minAngle = angle - (sprayWidth / 2.0); // calc the minimum angle the particle could move along
maxAngle = angle + (sprayWidth / 2.0); // calc the maximum angle
range = maxAngle - minAngle;
rangeInt = (int)(range*100.0);
newangle = minAngle + ((float)(rand() % rangeInt) / 100.0); // generate a random angle between mi and max angles
newAngleInRadians = (double)(newangle / 360.0)*(2 * 3.1415); // convert it to radians
spray[i].xd = (float)cos(newAngleInRadians);// calc the diection vector x value
spray[i].yd = (float)sin(newAngleInRadians);// calc the diection vector y value
}
void initspray() {
for (int i = 0; i < SPRAYSIZE; i++) {
spray[i].x = winWidth / 2; // set current start x position
spray[i].y = 100;// set current start y position
spray[i].startx = spray[i].x; spray[i].starty = spray[i].y;// set start x and y position
spray[i].speed = 0.1 + (float)(rand() % 150) / 1000.0;// speed is 0.1 to 0.25
spray[i].startTime = rand() % spray[i].startRange;// set birth time
spray[i].radius = (float)(rand() % 15); // random radius
setDirectionVector(i);// set the current direction vector
spray[i].startxd = spray[i].xd; spray[i].startyd = spray[i].yd; // set start direction vector to current
}
// set colour of spray
fr = 0.5 + (float)(rand() % 500) / 1000.0;
fg = 0.5 + (float)(rand() % 500) / 1000.0;
fb = 0.5 + (float)(rand() % 500) / 1000.0;
}
void drawsprayParticle(int i) {
glLineWidth(2);
if (!spray[i].started) {
if (counter == spray[i].startTime) {
spray[i].started = true;
}
}
if (spray[i].started) {
glColor4f(fr, fg, fb, spray[i].alpha); // white particiles
circle(spray[i].radius, spray[i].x, spray[i].y);
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
// this produces a direction vector that is a little longer than 1
spray[i].yd = spray[i].yd + gy;
// so the normalise the vector to make length 1
normalise(i);
// reduce transparency
spray[i].alpha -= 0.00015;
}
if (spray[i].x<0 || spray[i].x>winWidth + 500 || spray[i].y<0 || spray[i].y>winHeight) {
spray[i].x = spray[i].startx; spray[i].y = spray[i].starty; //rocketstartx
spray[i].xd = spray[i].startxd; spray[i].yd = spray[i].startyd;
spray[i].alpha = 1.0;
}
}
void drawspray() {
// draw each spray particle
for (int i = 0; i < SPRAYSIZE; i++) {
drawsprayParticle(i);
}
// increment rocket position
rocketstartx += 0.2;
// if the rocket is oof the screen more nad 500 pixels to the right the rest it to 0
if (rocketstartx > winWidth + 500) { rocketstartx = 0; }
counter++;
}
// This is the display function it is called when ever you want to draw something
void display() {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen using the background colour
glColor3f(1.0, 1.0, 1.0); // set colour to white
drawspray();
glFlush(); // force all drawing to finish
}
// This is the idle function it is called whenever the program is idle
void idle() {
display();
}
// As with many programming languages the main() function is the entry point for execution of the program
int main(int argc, char** argv) {
srand((unsigned)time(&t));
// initialise first spray work
initspray();
glutInit(&argc, argv); //perform the GLUT initialization
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); // more initialisation
glutInitWindowSize(winWidth, winHeight); // set window position
glutInitWindowPosition(0, 0); // set window size
glutCreateWindow("Fire"); // create a display with a given caption for the title bar
glEnable(GL_BLEND); //Enable blending.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
init(); // call init function defined above
glutIdleFunc(idle); // define what function to call when the program is idle
glutDisplayFunc(display); // define what function to call to draw
glutMainLoop();
// this line exits the program
return 0;
}
The original code above normally creates a fountain that sprays particles all over the screen, but I have changed the size and range of the spray, so I can create a flame. The problem is that I can't stop the spread of the particles and it keeps moving up. I want it to stay in its position.
This is how it works:
This is how I want it to work:
With the formula
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
the distance of the points to it origin linear increases. You have to decrease the speed by time to smoothly approach to a limit position.
e.g.
spray[i].speed *= 0.9992f;
Of course you have to rest the speed (spray[i].speed), when the point is "restarted" at its origin. If the speed of the point or the alpha value of the point falls below a threshold, then the point has to be "restarted", too:
void drawsprayParticle(int i) {
glLineWidth(2);
if (!spray[i].started) {
if (counter == spray[i].startTime) {
spray[i].started = true;
}
}
if (spray[i].started) {
glColor4f(fr, fg, fb, spray[i].alpha); // white particles
circle(spray[i].radius, spray[i].x, spray[i].y);
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
// this produces a direction vector that is a little longer than 1
spray[i].yd = spray[i].yd + gy;
// so the normalize the vector to make length 1
normalise(i);
// reduce transparency
spray[i].alpha -= 0.0003;
spray[i].speed *= 0.9992f;
}
if ( spray[i].x<0 || spray[i].x>winWidth + 500 ||
spray[i].y<0 || spray[i].y>winHeight ||
spray[i].alpha < 1.0f/256.0f ||
spray[i].speed < 0.04f ) {
spray[i].x = spray[i].startx; spray[i].y = spray[i].starty;
spray[i].xd = spray[i].startxd; spray[i].yd = spray[i].startyd;
spray[i].alpha = 1.0;
spray[i].speed = 0.1 + (float)(rand() % 150) / 1000.0;
}
}
This is an empirical algorithm, so you have to play around with the values, to get the effect of your need.
Additionally I increase the start range: int startRange = 1300;
Preview:

Perlin Noise getting wrong values in Y axis (C++)

Issue
I'm trying to implement the Perlin Noise algorithm in 2D with a single octave with a size of 16x16. I'm using this as heightmap data for a terrain, however it only seems to work in one axis. Whenever the sample point moves to a new Y section in the Perlin Noise grid, the gradient is very different from what I expect (for example, it often flips from 0.98 to -0.97, which is a very sudden change).
This image shows the staggered terrain in the z direction (which is the y axis in the 2D Perlin Noise grid)
Code
I've put the code that calculates which sample point to use at the end since it's quite long and I believe it's not where the issue is, but essentially I scale down the terrain to match the Perlin Noise grid (16x16) and then sample through all the points.
Gradient At Point
So the code that calculates out the gradient at a sample point is the following:
// Find the gradient at a certain sample point
float PerlinNoise::gradientAt(Vector2 point)
{
// Decimal part of float
float relativeX = point.x - (int)point.x;
float relativeY = point.y - (int)point.y;
Vector2 relativePoint = Vector2(relativeX, relativeY);
vector<float> weights(4);
// Find the weights of the 4 surrounding points
weights = surroundingWeights(point);
float fadeX = fadeFunction(relativePoint.x);
float fadeY = fadeFunction(relativePoint.y);
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
return lerpC;
}
Surrounding Weights of Point
I believe the issue is somewhere here, in the function that calculates the weights for the 4 surrounding points of a sample point, but I can't seem to figure out what is wrong since all the values seem sensible in the function when stepping through it.
// Find the surrounding weight of a point
vector<float> PerlinNoise::surroundingWeights(Vector2 point){
// Produces correct values
vector<Vector2> surroundingPoints = surroundingPointsOf(point);
vector<float> weights;
for (unsigned i = 0; i < surroundingPoints.size(); ++i) {
// The corner to the sample point
Vector2 cornerToPoint = surroundingPoints[i].toVector(point);
// Getting the seeded vector from the grid
float x = surroundingPoints[i].x;
float y = surroundingPoints[i].y;
Vector2 seededVector = baseGrid[x][y];
// Dot product between the seededVector and corner to the sample point vector
float dotProduct = cornerToPoint.dot(seededVector);
weights.push_back(dotProduct);
}
return weights;
}
OpenGL Setup and Sample Point
Setting up the heightmap and getting the sample point. Variables 'wrongA' and 'wrongA' is an example of when the gradient flips and changes suddenly.
void HeightMap::GenerateRandomTerrain() {
int perlinGridSize = 16;
PerlinNoise perlin_noise = PerlinNoise(perlinGridSize, perlinGridSize);
numVertices = RAW_WIDTH * RAW_HEIGHT;
numIndices = (RAW_WIDTH - 1) * (RAW_HEIGHT - 1) * 6;
vertices = new Vector3[numVertices];
textureCoords = new Vector2[numVertices];
indices = new GLuint[numIndices];
float perlinScale = RAW_HEIGHT/ (float) (perlinGridSize -1);
float height = 50;
float wrongA = perlin_noise.gradientAt(Vector2(0, 68.0f / perlinScale));
float wrongB = perlin_noise.gradientAt(Vector2(0, 69.0f / perlinScale));
for (int x = 0; x < RAW_WIDTH; ++x) {
for (int z = 0; z < RAW_HEIGHT; ++z) {
int offset = (x* RAW_WIDTH) + z;
float xVal = (float)x / perlinScale;
float yVal = (float)z / perlinScale;
float noise = perlin_noise.gradientAt(Vector2( xVal , yVal));
vertices[offset] = Vector3(x * HEIGHTMAP_X, noise * height, z * HEIGHTMAP_Z);
textureCoords[offset] = Vector2(x * HEIGHTMAP_TEX_X, z * HEIGHTMAP_TEX_Z);
}
}
numIndices = 0;
for (int x = 0; x < RAW_WIDTH - 1; ++x) {
for (int z = 0; z < RAW_HEIGHT - 1; ++z) {
int a = (x * (RAW_WIDTH)) + z;
int b = ((x + 1)* (RAW_WIDTH)) + z;
int c = ((x + 1)* (RAW_WIDTH)) + (z + 1);
int d = (x * (RAW_WIDTH)) + (z + 1);
indices[numIndices++] = c;
indices[numIndices++] = b;
indices[numIndices++] = a;
indices[numIndices++] = a;
indices[numIndices++] = d;
indices[numIndices++] = c;
}
}
BufferData();
}
Turned out the issue was in the interpolation stage:
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
I had the interpolation in the y axis the wrong way around, so it should have been:
lerp(lerpB, lerpA, fadeY)
Instead of:
lerp(lerpA, lerpB, fadeY)

Texture mapping so close but not quite right

I am building a raytracer and my texture mapping isn't quite right. Its very close though. I build a cup in blender and did a UV unwrap to display a texture. I exported the object and loaded it into my raytracer with the same texture. Here are two pictures:
As you can see the textures look very close, but something is off. If you look at the bottom of the cup on the sides you can see they aren't the same, but the textures are all aligned correctly so it does look somewhat right. The way the textures are calculated is by using barycentric coordinates.
Vect n = getTriangleNormal();
Vect ba = B.add(A.negative()).negative();
Vect ca = C.add(A.negative()).negative();
Vect ap = A.add(point.negative()).negative();
Vect bp = B.add(point.negative()).negative();
Vect cp = C.add(point.negative()).negative();
double areaABC = n.dotProduct(ba.crossProduct(ca));
double areaPBC = n.dotProduct(bp.crossProduct(cp));
double areaPCA = n.dotProduct(cp.crossProduct(ap));
if(areaABC < 0){areaABC = -areaABC;}
if(areaPBC < 0){areaPBC = -areaPBC;}
if(areaPCA < 0){areaPCA = -areaPCA;}
double u = areaPBC / areaABC ; // alpha
double v = areaPCA / areaABC ; // beta
double w = 1.0f - u - v ; // gamma
Then to find the color I take the new point and map it onto the image
Vect uv = (textA.mult(u)).add(textB.mult(v)).add(textC.mult(w));
int width = texture ->columns();
int height = texture ->rows();
double x = width * (uv.getX()) ; x = (int) x;
double y = height * (1-uv.getY()) ; y = (int) y;
//vector<unsigned int> c = texture -> getPixel(x,y);
//return Color(c[0]/255.0,c[1]/255.0,c[2]/255.0,0);
int row = y;
int column = x;
Magick::PixelPacket *pixels = texture->getPixels(0, 0, width, height);
Magick::Color color = pixels[width * row + column];
double range = pow(2, texture -> modulusDepth());
double r = color.redQuantum()/range ;
double g = color.greenQuantum()/range ;
double b = color.blueQuantum()/range ;
return Color(r, g, b, 0);

Separating Axis Theorem is driving me nuts!

i am working on an implementation of the Separting Axis Theorem for use in 2D games. It kind of works but just kind of.
I use it like this:
bool penetration = sat(c1, c2) && sat(c2, c1);
Where c1 and c2 are of type Convex, defined as:
class Convex
{
public:
float tx, ty;
public:
std::vector<Point> p;
void translate(float x, float y) {
tx = x;
ty = y;
}
};
(Point is a structure of float x, float y)
The points are typed in clockwise.
My current code (ignore Qt debug):
bool sat(Convex c1, Convex c2, QPainter *debug)
{
//Debug
QColor col[] = {QColor(255, 0, 0), QColor(0, 255, 0), QColor(0, 0, 255), QColor(0, 0, 0)};
bool ret = true;
int c1_faces = c1.p.size();
int c2_faces = c2.p.size();
//For every face in c1
for(int i = 0; i < c1_faces; i++)
{
//Grab a face (face x, face y)
float fx = c1.p[i].x - c1.p[(i + 1) % c1_faces].x;
float fy = c1.p[i].y - c1.p[(i + 1) % c1_faces].y;
//Create a perpendicular axis to project on (axis x, axis y)
float ax = -fy, ay = fx;
//Normalize the axis
float len_v = sqrt(ax * ax + ay * ay);
ax /= len_v;
ay /= len_v;
//Debug graphics (ignore)
debug->setPen(col[i]);
//Draw the face
debug->drawLine(QLineF(c1.tx + c1.p[i].x, c1.ty + c1.p[i].y, c1.p[(i + 1) % c1_faces].x + c1.tx, c1.p[(i + 1) % c1_faces].y + c1.ty));
//Draw the axis
debug->save();
debug->translate(c1.p[i].x, c1.p[i].y);
debug->drawLine(QLineF(c1.tx, c1.ty, ax * 100 + c1.tx, ay * 100 + c1.ty));
debug->drawEllipse(QPointF(ax * 100 + c1.tx, ay * 100 + c1.ty), 10, 10);
debug->restore();
//Carve out the min and max values
float c1_min = FLT_MAX, c1_max = FLT_MIN;
float c2_min = FLT_MAX, c2_max = FLT_MIN;
//Project every point in c1 on the axis and store min and max
for(int j = 0; j < c1_faces; j++)
{
float c1_proj = (ax * (c1.p[j].x + c1.tx) + ay * (c1.p[j].y + c1.ty)) / (ax * ax + ay * ay);
c1_min = min(c1_proj, c1_min);
c1_max = max(c1_proj, c1_max);
}
//Project every point in c2 on the axis and store min and max
for(int j = 0; j < c2_faces; j++)
{
float c2_proj = (ax * (c2.p[j].x + c2.tx) + ay * (c2.p[j].y + c2.ty)) / (ax * ax + ay * ay);
c2_min = min(c2_proj, c2_min);
c2_max = max(c2_proj, c2_max);
}
//Return if the projections do not overlap
if(!(c1_max >= c2_min && c1_min <= c2_max))
ret = false; //return false;
}
return ret; //return true;
}
What am i doing wrong? It registers collision perfectly but is over sensitive on one edge (in my test using a triangle and a diamond):
//Triangle
push_back(Point(0, -150));
push_back(Point(0, 50));
push_back(Point(-100, 100));
//Diamond
push_back(Point(0, -100));
push_back(Point(100, 0));
push_back(Point(0, 100));
push_back(Point(-100, 0));
I am getting this mega-adhd over this, please help me out :)
http://u8999827.fsdata.se/sat.png
OK, I was wrong the first time. Looking at your picture of a failure case it is obvious a separating axis exists and is one of the normals (the normal to the long edge of the triangle). The projection is correct, however, your bounds are not.
I think the error is here:
float c1_min = FLT_MAX, c1_max = FLT_MIN;
float c2_min = FLT_MAX, c2_max = FLT_MIN;
FLT_MIN is the smallest normal positive number representable by a float, not the most negative number. In fact you need:
float c1_min = FLT_MAX, c1_max = -FLT_MAX;
float c2_min = FLT_MAX, c2_max = -FLT_MAX;
or even better for C++
float c1_min = std::numeric_limits<float>::max(), c1_max = -c1_min;
float c2_min = std::numeric_limits<float>::max(), c2_max = -c2_min;
because you're probably seeing negative projections onto the axis.