I have been watching OneLoneCoder video about circle collision resolve
and use the code from git.
But the code is for 2D case, i.e. circles, and I need a solution for spheres. So I tried to add Z-component to equations but getting failures.
So I am testing the next way: I create two spheres, one above other. Lower is situated a heavy sphere, upper a tiny. The higher sphere is falling. In reality tiny should bounce up, but it just stuck, and they are falling as a single body for a while, but then the higher sphere is bouncing up. Also the sphere per each jump gets higher(see gif):
Here is the code:
/*
vertex - VERTEX **vertex=new VERTEX*[100];
vc - vertex count (how many vertices are currently created)
*/
void Update()
{
auto DoCirclesOverlap = [](float x1, float y1, float z1, float r1, float x2, float y2, float z2, float r2)
{
return ((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2) + (z1 - z2)*(z1 - z2)) <= (r1 + r2)*(r1 + r2);
};
vector<pair<VERTEX*, VERTEX*>> vecCollidingPairs;
// Update vertex[i] Positions
for (int i = 0;i < vc;i++)
{
// Update vertex[i] physics
vertex[i]->x += vertex[i]->vx * TIME_QUANT_CONSTANT;
vertex[i]->y += vertex[i]->vy * TIME_QUANT_CONSTANT;
vertex[i]->z += vertex[i]->vz * TIME_QUANT_CONSTANT;
}
// Static collisions, i.e. overlap
for (int i(0);i < vc;i++)
{
for(int j(0);j < vc;j++)
{
if (vertex[i]->index != vertex[j]->index)
{
if (DoCirclesOverlap(vertex[i]->x, vertex[i]->y, vertex[i]->z, vertex[i]->size, vertex[j]->x, vertex[j]->y, vertex[j]->z, vertex[j]->size))
{
// Collision has occured
vecCollidingPairs.push_back({ vertex[i], vertex[j] });
// Distance between vertex[i] centers
float fDistance = sqrtf((vertex[i]->x - vertex[j]->x)*(vertex[i]->x - vertex[j]->x) + (vertex[i]->y - vertex[j]->y)*(vertex[i]->y - vertex[j]->y) + (vertex[i]->y - vertex[j]->y)*(vertex[i]->y - vertex[j]->y));
// Calculate displacement required
float fOverlap = 0.5f * (fDistance - vertex[i]->size - vertex[j]->size);
// Displace Current vertex[i] away from collision
vertex[i]->x -= fOverlap * (vertex[i]->x - vertex[j]->x) / fDistance;
vertex[i]->y -= fOverlap * (vertex[i]->y - vertex[j]->y) / fDistance;
vertex[i]->z -= fOverlap * (vertex[i]->z - vertex[j]->z) / fDistance;
// Displace vertex[j] vertex[i] away from collision
vertex[j]->x += fOverlap * (vertex[i]->x - vertex[j]->x) / fDistance;
vertex[j]->y += fOverlap * (vertex[i]->y - vertex[j]->y) / fDistance;
vertex[j]->z += fOverlap * (vertex[i]->z - vertex[j]->z) / fDistance;
}
}
}
}
// Now work out dynamic collisions
for (auto c : vecCollidingPairs)
{
VERTEX *b1 = c.first;
VERTEX *b2 = c.second;
// Distance between vertex[i]s
float fDistance = sqrtf((b1->x - b2->x)*(b1->x - b2->x) + (b1->y - b2->y)*(b1->y - b2->y) + (b1->z - b2->z)*(b1->z - b2->z));
// Normal
float nx = (b2->x - b1->x) / fDistance;
float ny = (b2->y - b1->y) / fDistance;
float nz = (b2->z - b1->z) / fDistance;
// Tangent
float tx = -ny;
float ty = nx;
float tz = nz;
// Dot Product Tangent
float dpTan1 = b1->vx * tx + b1->vy * ty + b1->vz * tz;
float dpTan2 = b2->vx * tx + b2->vy * ty + b2->vz * tz;
// Dot Product Normal
float dpNorm1 = b1->vx * nx + b1->vy * ny + b1->vz * nz;
float dpNorm2 = b2->vx * nx + b2->vy * ny + b2->vz * nz;
// Conservation of momentum in 1D
float m1 = (dpNorm1 * (b1->mass - b2->mass) + 2.0f * b2->mass * dpNorm2) / (b1->mass + b2->mass);
float m2 = (dpNorm2 * (b2->mass - b1->mass) + 2.0f * b1->mass * dpNorm1) / (b1->mass + b2->mass);
// Update ball velocities
b1->vx = tx * dpTan1 + nx * m1;
b1->vy = ty * dpTan1 + ny * m1;
b1->vz = tz * dpTan1 + nz * m1;
b2->vx = tx * dpTan2 + nx * m2;
b2->vy = ty * dpTan2 + ny * m2;
b2->vz = tz * dpTan2 + nz * m2;
}
}
What is the problem?
Thanks for #Tyyppi_77 from Stack GameDev, where I posted the same question, that pointed that the issue was on line
float fDistance = sqrtf((vertex[i]->x - vertex[j]->x)*(vertex[i]->x - vertex[j]->x) + (vertex[i]->y - vertex[j]->y)*(vertex[i]->y - vertex[j]->y) + (vertex[i]->y - vertex[j]->y)*(vertex[i]->y - vertex[j]->y));
And the correct one is
float fDistance = sqrtf((vertex[i]->x - vertex[j]->x)*(vertex[i]->x - vertex[j]->x) + (vertex[i]->y - vertex[j]->y)*(vertex[i]->y - vertex[j]->y) + (vertex[i]->z - vertex[j]->z)*(vertex[i]->z - vertex[j]->z));
It just solved almost all of issues. The only terrible thing is, because of static collision, the lower and much more heavier sphere is a little bit shifts down while it is hit by the sphere above. Do You know how it can be solved?
Related
My current implementation looks like this:
if (shapesCollide) {
if (velocity.y > 0) entity.position.y = other.position.y - entity.size.y;
else entity.position.y = other.position.y + other.size.y;
velocity.y = 0;
if (velocity.x > 0) entity.position.x = other.position.x - entity.size.x;
else entity.position.x = other.position.x + other.size.x;
velocity.x = 0;
}
However, this leads to weird handling when movement is happening on both axes - for example, having entity moving downward to the left of object, and then moving it to collide with object, will correctly resolve the horizontal collision, but will break the vertical movement.
I previously simply went
if (shapesCollide) {
position = oldPosition;
velocity = { 0, 0 };
}
But this lead to another multi-axis issue: if I have my entity resting atop the object, it will be unable to move, as the gravity-induced movement will constantly cancel out both velocities. I also tried considering both axes separately, but this lead to issues whenever the collision only occurs when both velocities are taken into account.
What is the best solution to resolving collision on two axes?
I assume that the entities can be considered to be more or less round and that size is the radius of the entities?
We probably need a little vector math to resolve this. (I don't know the square-root function in c++, so be aware at sqrt.) Try replacing your code inside if(shapesCollide) with this and see how it works for you.
float rEntity = sqrt(entity.size.x * entity.size.x + entity.size.y * entity.size.y);
float rOther = sqrt(other.size.x * other.size.x + other.size.y * other.size.y);
float midX = (entity.position.x + other.position.x) / 2.0;
float midY = (entity.position.y + other.position.y) / 2.0;
float dx = entity.position.x - midX;
float dy = entity.position.y - midY;
float D = sqrt(dx * dx + dy * dy);
rEntity and rOther are the radii of the objects, and midX and midY are their center coordinates. dx and dy are the distances to the center from the entity.
Then do:
entity.position.x = midX + dx * rEntity / D;
entity.position.y = midY + dy * rEntity / D;
other.position.x = midX - dx * rOther / D;
other.position.y = midY - dy * rOther / D;
You should probably check that D is not 0, and if it is, just set dx = 1, dy = 0, D = 1 or something like that.
You should also still do:
velocity.x = 0;
velocity.y = 0;
if you want the entities to stop.
For more accurate modelling, you could also try the following:
float rEntity = sqrt(entity.size.x * entity.size.x + entity.size.y * entity.size.y);
float rOther = sqrt(other.size.x * other.size.x + other.size.y * other.size.y);
float midX = (entity.position.x * rOther + other.position.x * rEntity) / (rEntity + rOther);
float midY = (entity.position.y * rOther + other.position.y * rEntity) / (rEntity + rOther);
float dxEntity = entity.position.x - midX;
float dyEntity = entity.position.y - midY;
float dEntity = sqrt(dxEntity * dxEntity + dyEntity * dyEntity);
float dxOther = other.position.x - midX;
float dyOther = other.position.y - midY;
float dOther = sqrt(dxOther * dxOther + dyOther * dyOther);
entity.position.x = midX + dxEntity * rEntity / dEntity;
entity.position.y = midY + dyEntity * rEntity / dEntity;
other.position.x = midX + dxOther * rOther / dOther;
other.position.y = midY + dyOther * rOther / dOther;
which finds the midpoints when the radii are taken into account. But I won't guarantee that that works. Also, the signs on the last additions are important.
I hope this helps (and works). Let me know if something is unclear.
I am trying to make a red circle follow the path of a semi-circle using the DDA algorithm in OpenGL. I almost have it, though the circle is slightly offset on its X-axis, which increases as the angle of the semi-circle increases.
Any assistance would be greatly appreciated! Here's my code:
scrPt movecircle (scrPt p1, scrPt p2)
{
scrPt circlePos;
float angle, x = p1.x, y = p1.y, vectorX, vectorY;
// Get tahe x distance between the two points
int dx = p2.x - p1.x, steps;
// Get the y distance between the two points
int dy = p2.y - p1.y;
// Get the length between the points
float length = sqrt(dx*dx + dy*dy);
if (fabs (dx) > fabs (dy))
steps = fabs (dx);
else
steps = fabs (dy);
// calculate the direction
float xIncrement = float (dx) / float (steps);
float yIncrement = float (dy) / float (steps);
if (nextPos == 0)
{
for(int i = 0; i < steps; i++)
{
glClear(GL_COLOR_BUFFER_BIT);
angle = PI * i / steps;
vectorX = x + (length / 2) * cos(angle + theta);
vectorY = y + dy / 2 + (length / 2) * sin(angle + theta);
circlePos.x = round(vectorX - length / 2);
circlePos.y = round(vectorY);
drawCircle (circlePos.x, circlePos.y);
drawArch();
glFlush();
usleep(3000);
}
}
else
{
for (int i = 0; i < steps; i++)
{
glClear(GL_COLOR_BUFFER_BIT);
drawCircle (round(x),round(y));
glFlush();
usleep(3000);
x += xIncrement;
y += yIncrement;
}
}
return circlePos;
}
There were a couple of errors in the for-loop that were causing the issue. I needed to change
this:
vectorX = x + (length / 2) * cos(angle + theta);
to this:
vectorX = x + (dx / 2) + (length / 2) * cos(angle + theta);
and this:
circlePos.x = round(vectorX - (length / 2));
to this:
circlePos.x = round(vectorX);
I have that piece of code that is responsible for lighting a pyramid.
float Geometric3D::calculateLight(int vert1, int vert2, int vert3) {
float ax = tabX[vert2] - tabX[vert1];
float ay = tabY[vert2] - tabY[vert1];
float az = tabZ[vert2] - tabZ[vert1];
float bx = tabX[vert3] - tabX[vert1];
float by = tabY[vert3] - tabY[vert1];
float bz = tabZ[vert3] - tabZ[vert1];
float Nx = (ay * bz) - (az * by);
float Ny = (az * bx) - (ax * bz);;
float Nz = (ax * by) - (ay * bx);;
float Lx = -300.0f;
float Ly = -300.0f;
float Lz = -1000.0f;
float lenN = sqrtf((Nx * Nx) + (Ny * Ny) + (Nz * Nz));
float lenL = sqrtf((Lx * Lx) + (Ly * Ly) + (Lz * Lz));
float res = ((Nx * Lx) + (Ny * Ly) + (Nz * Lz)) / (lenN * lenL);
if (res < 0.0f)
res = -res;
return res;
}
I cannot understand calculations at the end. Can someone explain me the maths that is behind them? I know that firstly program calculates two vectors of a plane to compute the normal of it (which goes for vector N). Vector L stand for lighting but what happens next? Why do we calculate length of normal and light then multiply it and divide by their sizes?
Here is the code for an oval drawing method I am working on. I am applying the Bresenham method to plot its co-ordinates, and taking advantage of the ellipse's symmetrical properties to draw the same pixel in four different places.
void cRenderClass::plotEllipse(int xCentre, int yCentre, int width, int height, float angle, float xScale, float yScale)
{
if ((height == width) && (abs(xScale - yScale) < 0.005))
plotCircle(xCentre, yCentre, width, xScale);
std::vector<std::vector <float>> rotate;
if (angle > 360.0f)
{
angle -= 180.0f;
}
rotate = maths.rotateMatrix(angle, 'z');
//rotate[0][0] = cos(angle)
//rotate[0][1] = sin(angle)
float theta = atan2(-height*rotate[0][1], width*rotate[0][0]);
if (angle > 90.0f && angle < 180.0f)
{
theta += PI;
}
//add scalation in at a later date
float xShear = (width * (cos(theta) * rotate[0][0])) - (height * (sin(theta) * rotate[0][1]));
float yShear = (width * (cos(theta) * rotate[0][1])) + (height * (sin(theta) * rotate[0][0]));
float widthAxis = abs(sqrt(((rotate[0][0] * width) * (rotate[0][0] * width)) + ((rotate[0][1] * height) * (rotate[0][1] * height))));
float heightAxis = (width * height) / widthAxis;
int aSquared = widthAxis * widthAxis;
int fourASquared = 4*aSquared;
int bSquared = heightAxis * heightAxis;
int fourBSquared = 4*bSquared;
x0 = 0;
y0 = heightAxis;
int sigma = (bSquared * 2) + (aSquared * (1 - (2 * heightAxis)));
while ((bSquared * x0) <= (aSquared * y0))
{
drawPixel(xCentre + x0, yCentre + ((floor((x0 * yShear) / xShear)) + y0));
drawPixel(xCentre - x0, yCentre + ((floor((x0 * yShear) / xShear)) + y0));
drawPixel(xCentre + x0, yCentre + ((floor((x0 * yShear) / xShear)) - y0));
drawPixel(xCentre - x0, yCentre + ((floor((x0 * yShear) / xShear)) - y0));
if (sigma >= 0)
{
sigma += (fourASquared * (1 - y0));
y0--;
}
sigma += (bSquared * ((4 * x0) + 6));
x0++;
}
x0 = widthAxis;
y0 = 0;
sigma = (aSquared * 2) + (bSquared * (1 - (2 * widthAxis)));
while ((aSquared * y0) <= (bSquared * x0))
{
drawPixel(xCentre + x0, yCentre + ((floor((x0 * yShear) / xShear)) + y0));
drawPixel(xCentre - x0, yCentre + ((floor((x0 * yShear) / xShear)) + y0));
drawPixel(xCentre + x0, yCentre + ((floor((x0 * yShear) / xShear)) - y0));
drawPixel(xCentre - x0, yCentre + ((floor((x0 * yShear) / xShear)) - y0));
if (sigma >= 0)
{
sigma += (fourBSquared * (1 - x0));
x0--;
}
sigma += (aSquared * (4 * y0) + 6);
y0++;
}
//the above algorithm hasn't been quite completed
//there are still a few things I want to enquire Andy about
//before I move on
//this other algorithm definitely works
//however
//it is computationally expensive
//and the line drawing isn't as refined as the first one
//only use this as a last resort
/* std::vector<std::vector <float>> rotate;
rotate = maths.rotateMatrix(angle, 'z');
float s = rotate[0][1];
float c = rotate[0][0];
float ratio = (float)height / (float)width;
float px, py, xNew, yNew;
for (int theta = 0; theta <= 360; theta++)
{
px = (xCentre + (cos(maths.degToRad(theta)) * (width / 2))) - xCentre;
py = (yCentre - (ratio * (sin(maths.degToRad(theta)) * (width / 2)))) - yCentre;
x0 = (px * c) - (py * s);
y0 = (px * s) + (py * c);
drawPixel(x0 + xCentre, y0 + yCentre);
}*/
}
Here's the problem. When testing the rotation matrix on my oval drawing function, I expect it to draw an ellipse at a slant from its original horizontal position as signified by 'angle'. Instead, it makes a heart shape. This is sweet, but not the result I want.
I have managed to get the other algorithm (as seen in the bottom part of that code sample) working successfully, but it takes more time to compute, and doesn't draw lines quite as nicely. I only plan to use that if I can't get this Bresenham one working.
Can anyone help?
I'm trying to rotate image with interpolation, but it's too slow for real time for big images.
the code something like:
for(int y=0;y<dst_h;++y)
{
for(int x=0;x<dst_w;++x)
{
//do inverse transform
fPoint pt(Transform(Point(x, y)));
//in coor of src
int x1= (int)floor(pt.x);
int y1= (int)floor(pt.y);
int x2= x1+1;
int y2= y1+1;
if((x1>=0&&x1<src_w&&y1>=0&&y1<src_h)&&(x2>=0&&x2<src_w&&y2>=0&&y2<src_h))
{
Mask[y][x]= 1; //show pixel
float dx1= pt.x-x1;
float dx2= 1-dx1;
float dy1= pt.y-y1;
float dy2= 1-dy1;
//bilinear
pd[x].blue= (dy2*(ps[y1*src_w+x1].blue*dx2+ps[y1*src_w+x2].blue*dx1)+
dy1*(ps[y2*src_w+x1].blue*dx2+ps[y2*src_w+x2].blue*dx1));
pd[x].green= (dy2*(ps[y1*src_w+x1].green*dx2+ps[y1*src_w+x2].green*dx1)+
dy1*(ps[y2*src_w+x1].green*dx2+ps[y2*src_w+x2].green*dx1));
pd[x].red= (dy2*(ps[y1*src_w+x1].red*dx2+ps[y1*src_w+x2].red*dx1)+
dy1*(ps[y2*src_w+x1].red*dx2+ps[y2*src_w+x2].red*dx1));
//nearest neighbour
//pd[x]= ps[((int)pt.y)*src_w+(int)pt.x];
}
else
Mask[y][x]= 0; //transparent pixel
}
pd+= dst_w;
}
How I can speed up this code, I try to parallelize this code but it seems there is no speed up because of memory access pattern (?).
The key is to do most of your computations as ints. The only thing that is necessary to do as a float is the weighting. See here for a good resource.
From that same resource:
int px = (int)x; // floor of x
int py = (int)y; // floor of y
const int stride = img->width;
const Pixel* p0 = img->data + px + py * stride; // pointer to first pixel
// load the four neighboring pixels
const Pixel& p1 = p0[0 + 0 * stride];
const Pixel& p2 = p0[1 + 0 * stride];
const Pixel& p3 = p0[0 + 1 * stride];
const Pixel& p4 = p0[1 + 1 * stride];
// Calculate the weights for each pixel
float fx = x - px;
float fy = y - py;
float fx1 = 1.0f - fx;
float fy1 = 1.0f - fy;
int w1 = fx1 * fy1 * 256.0f;
int w2 = fx * fy1 * 256.0f;
int w3 = fx1 * fy * 256.0f;
int w4 = fx * fy * 256.0f;
// Calculate the weighted sum of pixels (for each color channel)
int outr = p1.r * w1 + p2.r * w2 + p3.r * w3 + p4.r * w4;
int outg = p1.g * w1 + p2.g * w2 + p3.g * w3 + p4.g * w4;
int outb = p1.b * w1 + p2.b * w2 + p3.b * w3 + p4.b * w4;
int outa = p1.a * w1 + p2.a * w2 + p3.a * w3 + p4.a * w4;
wow you are doing a lot inside most inner loop like:
1.float to int conversions
can do all on floats ...
they are these days pretty fast
the conversion is what is killing you
also you are mixing float and ints together (if i see it right) which is the same ...
2.transform(x,y)
any unnecessary call makes heap trashing and slow things down
instead add 2 variables xx,yy and interpolate them insde your for loops
3.if ....
why to heck are you adding if ?
limit the for ranges before loop and not inside ...
the background can be filled with other fors before or later