I am making a game in OpenGL with C++. I have a terrain that has hills and such and I want the character to be able to walk up and down the hill. To do this I have made a function that tries to find the the closest coordinates and return the corresponding y coordinates but its isn't working, my character is just staying at the same height. Here is my function:
float ViewPort::comparePosition(float xPos, float zPos) {
int closestValSoFar = 0;
for (int unit = 0; unit < sizeof(desert_scene_plainVerts)/sizeof(desert_scene_plainVerts[0]); unit++){
int xDifference = terrainxPos[unit] - xPos;
int zDifference = terrainzPos[unit] - zPos;
int combinedDifferece = xDifference + zDifference;
if (unit == 0) {
closestValSoFar = unit;
}
if (combinedDifferece < (terrainxPos[unit-1] - xPos) + (terrainzPos[unit-1] - zPos)) {
closestValSoFar = unit - 1;
}
else {
closestValSoFar = unit;
}
if ((unit - 1) < sizeof(desert_scene_plainVerts)/sizeof(desert_scene_plainVerts[0])) {
return terrainyPos[closestValSoFar];
}
}
return terrainyPos[closestValSoFar];
}
I am calling this and using it with this code:
float yPos = ViewPort::comparePosition(Camera::position.x, Camera::position.z);
Camera::position.y = yPos+1.65;
Does anybody know how I can fix my code?
If I understood you correctly, you're trying to perform terrain clamping by comparing the position of the object and the terrain height at the given position; but the most common way to do this is by performing ray casting from the object's position to the terrain. Then you check if the ray intercepts the terrain and where it happens.
But making a game from openGL is really hard, why don't you try a 3d engine like OGRE?
If you are planning to do it this way, as I do like this simple method, I would simply rewrite the function a little bit. Honestly I think your method can be simplified a little more.
I made a few modifications and placed it here. I changed the adding to Pythagorean Theorem for a slight increase in accuracy, but if it causes a significant loss in performance, I'm sure that the old adding would work nearly as well as the new method.
float ViewPort::comparePosition(float xPos, float zPos) {
int closestValSoFar = 0;
for (int unit = 0; unit < sizeof(desert_scene_plainVerts)/sizeof(desert_scene_plainVerts[0]); unit++){
int xDifference = terrainxPos[unit] - xPos;
int zDifference = terrainzPos[unit] - zPos;
int combinedDifferece = sqrt(xDifference*xDifference + zDifference*zDifference);
if (unit == 0) {
closestValSoFar = unit;
}
else if (combinedDifferece < closesValSoFar) {
closestValSoFar = combinedDifference;
}
}
return terrainyPos[closestValSoFar];
}
Honestly I really liked that old code and this is virtually the same, I simply checked whether the new distance is less than the closestValSoFar instead of the vertex before. I hope this helps!
I appreciate your endeavor to make a game with plain OpenGL, as I too deny engines (for what reason I don't know, I just find them boring. I'm not an artist, I'm a programmer, and engines are into artists and simplifying stuff)! I'd love to see your finished product, sounds cool!
Related
I am making a billiards game. Currently, when one ball collides with another at high speed, the collision is not always calculated correctly. I know what the issue is, but I'm not 100% sure how to fix it.
Say two balls are traveling with these velocities:
More often than not, when the collision is detected, the balls will have some overlap between them that looks like this:
Currently, my physics engine will handle the collision at this moment in time. This will not give the desired result since this is NOT where the balls collide in reality - balls don't go through one another. So, we need back up the balls to where they really collide. That would look like this:
I am looking for an efficient algorithm that would help me do this. Currently, I have a very naive and inefficient method - I move both balls to their locations just before the collision and take very small steps toward the moment of collision. Of course, this is very inefficient. Here is what it looks like:
void CBallCollision::StageCollision()
{
double sumOfRadii = mBall1->GetRadius() + mBall2->GetRadius();
mBall1->SetCenter(mBall1->GetLastLocationOnTable().first, mBall1->GetLastLocationOnTable().second);
mBall2->SetCenter(mBall2->GetLastLocationOnTable().first, mBall2->GetLastLocationOnTable().second);
double timeStep = 0.008;
double tolerance = 0.1 * min(mBall1->GetRadius(), mBall2->GetRadius());
int iter = 0;
while (GetDistance() > sumOfRadii)
{
double xGoal1 = mBall1->GetX() + mBall1->GetVelocityX() * timeStep;
double yGoal1 = mBall1->GetY() + mBall1->GetVelocityY() * timeStep;
pair<double, double> newCoords1 = mBall1->LinearInterpolate(xGoal1, yGoal1);
double xGoal2 = mBall2->GetX() + mBall2->GetVelocityX() * timeStep;
double yGoal2 = mBall2->GetY() + mBall2->GetVelocityY() * timeStep;
pair<double, double> newCoords2 = mBall2->LinearInterpolate(xGoal2, yGoal2);
double dist = (pow(newCoords1.first - newCoords2.first, 2) + pow(newCoords1.second - newCoords2.second, 2));
if (abs(dist - sumOfRadii) > tolerance)
{
timeStep *= 0.5;
}
else
{
mBall1->SetX(newCoords1.first);
mBall1->SetY(newCoords1.second);
mBall2->SetX(newCoords2.first);
mBall2->SetY(newCoords2.second);
}
iter++;
if (iter > 1000)
{
break;
}
}
}
If I don't put an upper bound on the number of iterations, the program crashes. I'm sure there is a much more efficient way of going about this. Any help is appreciated.
I'm using Particle Deposition to try and create some volcano-like mountains procedurally but all I'm getting out of it is pyramid-like structures. Is anyone familiar with the algorithm that might be able to shed some light on what I might be doing wrong. I'm dropping each particle in the same place at the moment. If I don't they spread out in a very thin layer rather than any sort of mountain.
void TerrainClass::ParticalDeposition(int loops){
float height = 0.0;
//for(int k= 0; k <10; k++){
int dropX = mCurrentX = rand()%(m_terrainWidth-80) + 40;
int dropY = mCurrentZ = rand()%(m_terrainHeight-80) + 40;
int radius = 15;
float angle = 0;
int tempthing = 0;
loops = 360;
for(int i = 0; i < loops; i++){
mCurrentX = dropX + radius * cos(angle);
mCurrentZ = dropY + radius * sin(angle);
/*f(i%loops/5 == 0){
dropX -= radius * cos(angle);
dropY += radius * sin(angle);
angle+= 0.005;
mCurrentX = dropX;
mCurrentZ = dropY;
}*/
angle += 360/loops;
//dropX += rand()%5;
//dropY += rand()%5;
//for(int j = 0; j < loops; j++){
float newY = 0;
newY = (1 - (2.0f/loops)*i);
if(newY < 0.0f){
newY = 0.0f;
}
DepositParticle(newY);
//}
}
//}
}
void TerrainClass::DepositParticle(float heightIncrease){
bool posFound = false;
m_lowerList.clear();
while(posFound == false){
int offset = 10;
int jitter;
if(Stable(0.5f)){
m_heightMap[(m_terrainHeight*mCurrentZ)+mCurrentX].y += heightIncrease;
posFound = true;
}else{
if(!m_lowerList.empty()){
int element = rand()%m_lowerList.size();
int lowerIndex = m_lowerList.at(element);
MoveTo(lowerIndex);
}
}
}
}
bool TerrainClass::Stable(float deltaHeight){
int index[9];
float height[9];
index[0] = ((m_terrainHeight*mCurrentZ)+mCurrentX); //the current index
index[1] = ValidIndex((m_terrainHeight*mCurrentZ)+mCurrentX+1) ? (m_terrainHeight*mCurrentZ)+mCurrentX+1 : -1; // if the index to the right is valid index set index[] to index else set index[] to -1
index[2] = ValidIndex((m_terrainHeight*mCurrentZ)+mCurrentX-1) ? (m_terrainHeight*mCurrentZ)+mCurrentX-1 : -1; //to the left
index[3] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX) ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX : -1; // above
index[4] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX) ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX : -1; // bellow
index[5] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX+1) ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX+1: -1; // above to the right
index[6] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX+1) ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX+1: -1; // below to the right
index[7] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX-1) ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX-1: -1; // above to the left
index[8] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX-1) ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX-1: -1; // above to the right
for ( int i = 0; i < 9; i++){
height[i] = (index[i] != -1) ? m_heightMap[index[i]].y : -1;
}
m_lowerList.clear();
for(int i = 1; i < 9; i++){
if(height[i] != -1){
if(height[i] < height[0] - deltaHeight){
m_lowerList.push_back(index[i]);
}
}
}
return m_lowerList.empty();
}
bool TerrainClass::ValidIndex(int index){
return (index > 0 && index < m_terrainWidth*m_terrainHeight) ? true : false;
}
void TerrainClass::MoveTo(int index){
mCurrentX = index%m_terrainWidth;
mCurrentZ = index/m_terrainHeight;
}
Thats all the code thats used.
You should have a look at these two papers:
Fast Hydraulic Erosion Simulation and Visualization on GPU
Fast Hydraulic and Thermal Erosion on the GPU (read the first one first, the second one expands on it)
Don't get scared by the "on GPU", the algorithms work just fine on CPU (albeit slower). The algorithms don't do particle sedimentation per se (but you don't either ;) ) - they instead aggregate the particles into several layers of vector fields.
One important thing about this algorithm is that it erodes already existing heightmaps - for example generated with perlin noise. It fails miserably if the initial height field is completely flat (or even if it has insufficient height variation).
I had implemented this algorithm myself and had mostly success with it (still have more work to do, the algorithms are very hard to balance to give universally great results) - see the image below.
Note that perlin noise with the Thermal weathering component from the second paper may be well enough for you (and might save you a lot of trouble).
You can also find C++ CPU-based implementation of this algorithm in my project (specifically this file, mind the GPL license!) and its simplified description on pages 24-29 of my thesis.
Your particles will need to have some surface friction and/or stickiness (or similar) in their physics model if you want them to not spread out into a single-layer. This is performed in the collision detection and collision response parts of your code when updating your particle simulation.
A simple approach is to make the particles stick (attract each-other). Particles need to have a size too so that they don't simply converge to perfectly overlapping. If you want to make them attract each other, then you need to test the distance between particles.
You might benefit from looking through some of the DirectX SDK examples that use particles, and in particular (pun arf!) there is a great demo (by Simon Green?) in the NVidia GPU Computing SDK that implements sticky particles in CUDA. It includes a ReadMe document describing what they've done. You can see how the particles interact and ignore all the CUDA/GPU stuff if you aren't going for massive particle counts.
Also note that as soon as you use inter-particle forces, then you are checking approximately 0.5*n^2 combinations (pairs) of particles...so you may need to use a simple spatial partitioning scheme or similar to limit forces to nearby groups of particles only.
Good luck!
I'm working on the simple behaviour of billiard balls in a collision with each other. All works normal, but there was a problem when facing a few easy balls is the effect of coupling balls and they're cool with each other. Tell me how to prevent this.
bool MGBilliard::CollisingBall(CCPoint curr_point, CCPoint next_point)
{
float dx = next_point.x - (curr_point.x + dvdt.x);
float dy = next_point.y - (curr_point.y - dvdt.y);
float d = dx*dx+dy*dy;
return d <= BALL_RADIUS * BALL_RADIUS;
}
double MGBilliard::angleCollisionBalls(Ball* current, Ball* next)
{
double na;
double dx = fabs(next->location.x - current->location.x);
double dy = fabs(next->location.y - current->location.y);
na = atan(fabs(dy/dx));
if(atan(fabs(current->location.y/current->location.x)) < atan(fabs(next->location.y/next->location.x)))
na = current->angle - na;
else if(atan(fabs(current->location.y/current->location.x)) > atan(fabs(next->location.y/next->location.x)))
na = current->angle + na;
return na;
}
for(unsigned int i = 0;i<BALL_COUNT;++i)
{
if(vBalls[i]->speed > 0){
vBalls[i]->speed += vBalls[i]->acceleration;
float dsdt = vBalls[i]->speed*dt;
dvdt.x = dsdt*cos(vBalls[i]->angle);
dvdt.y = dsdt*sin(vBalls[i]->angle);
vBalls[i]->location.x += dvdt.x;
vBalls[i]->location.y += dvdt.y;
for(unsigned int j = 1; j < BALL_COUNT; ++j)
{
if(i == j) continue;
if(CollisingBall(vBalls[i]->spriteBall->getPosition(),vBalls[j]->spriteBall->getPosition()))
{
vBalls[j]->speed = 600;
double angle;
angle = angleCollisionBalls(vBalls[i],vBalls[j]);
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
}
}
}
}
There are two straightforward bugs that spring to my attention with a quick look at your code.
Firstly, this:
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
is not the correct way to calculate opposing angles. For example, it will not do what you want it to do when angle is zero (or 180 degrees, for that matter).
Secondly, you iterate over your whole vBalls array multiple times, once with the index i and an inner loop with the index j. This means collisions will be calculated twice, and the speed of both balls would be set to 600! Changing your inner loop to be this:
for(unsigned int j = i + 1; j < BALL_COUNT; ++j)
should prevent this occurring.
There's also a more subtle bug. Your collision detection does not take into account time. Each ball moves a particular distance each iteration of your game loop. This means that if a collision does not occur in one 'tick', a ball could pass straight through another ball and then trigger the collision code on the far side of the ball. You cannot do a simple radius-based collision test in this situation, as if a ball moves more than (BALL_RADIUS * BALL_RADIUS) in a single step, your system will behave strangely or not work at all.
I personally would use vectors to describe speed and direction for each ball rather than angles and speeds, but refactoring your code to do this is a bit outside the scope of this question.
I'm programming in OpenCL using the C++ bindings. I have a problem where on NVidia hardware, my OpenCL code is spontaneously producing very large numbers, and then (on the next run) a "1.#QNaN". My code is pretty much a simple physics simulation using the equation x = vt * .5at^2. The only thing I've noticed odd about it is that the velocities of the particles suddenly shoot up to about 6e+34, which I'm guessing is the maximum floating point value on that machine. However, the velocities/forces before then are quite small, often with values less than 1.
The specific GPU I'm using is the Tesla M2050, with latest drivers. I prototype on my laptop using my AMD Fusion CPU as a platform (it does not have a dedicated GPU) and the problem does not occur there. I am not sure if this is an NVidia driver problem, a problem with my computation, or something else entirely.
Here is the kernel code (Note: I'm reasonably sure mass is always nonzero):
_kernel void update_atom(__global float4 *pos, __global float4 *vel, __global float4 *force,
__global const float *mass, __global const float *radius, const float timestep, const int wall)
{
// Get the index of the current element to be processed
int i = get_global_id(0);
float constMult;
float4 accel;
float4 part;
//Update the position, velocity and force
accel = (float4)(force[i].x/mass[i],
force[i].y/mass[i],
force[i].z/mass[i],
0.0f);
constMult = .5*timestep*timestep;
part = (float4)(constMult*accel.x,
constMult*accel.y,
constMult*accel.z, 0.0f);
pos[i] = (float4)(pos[i].x + vel[i].x*timestep + part.x,
pos[i].y + vel[i].y*timestep + part.y,
pos[i].z + vel[i].z*timestep + part.z,
0.0f);
vel[i] = (float4)(vel[i].x + accel.x*timestep,
vel[i].y + accel.y*timestep,
vel[i].z + accel.z*timestep,
0.0f);
force[i] = (float4)(force[i].x,
force[i].y,
force[i].z,
0.0f);
//Do reflections off the wall
//http://www.3dkingdoms.com/weekly/weekly.php?a=2
float4 norm;
float bouncePos = wall - radius[i];
float bounceNeg = radius[i] - wall;
norm = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
if(pos[i].x >= bouncePos)
{
//Normal is unit YZ vector
pos[i].x = bouncePos;
norm.x = 1.0f;
}
else if(pos[i].x <= bounceNeg)
{
pos[i].x = bounceNeg;
norm.x = -1.0f;
}
if(pos[i].y >= bouncePos)
{
//Normal is unit XZ vector
pos[i].y = bouncePos;
norm.y = 1.0f;
}
else if(pos[i].y <= bounceNeg)
{
//etc
pos[i].y = bounceNeg;
norm.y = -1.0f;
}
if(pos[i].z >= bouncePos)
{
pos[i].z = bouncePos;
norm.z = 1.0f;
}
else if(pos[i].z <= bounceNeg)
{
pos[i].z = bounceNeg;
norm.z = -1.0f;
}
float dot = 2 * (vel[i].x * norm.x + vel[i].y * norm.y + vel[i].z * norm.z);
vel[i].x = vel[i].x - dot * norm.x;
vel[i].y = vel[i].y - dot * norm.y;
vel[i].z = vel[i].z - dot * norm.z;
}
And here's how I store the information into the kernel. PutData just uses the std::vector::push_back function on the positions, velocities and forces of the atoms into the corresponding vectors, and kernel is just a wrapper class I wrote for the OpenCL libraries (you can trust me that I put the right parameters into the right places for the enqueue functions).
void LoadAtoms(int kernelNum, bool blocking)
{
std::vector<cl_float4> atomPos;
std::vector<cl_float4> atomVel;
std::vector<cl_float4> atomForce;
for(int i = 0; i < numParticles; i++)
atomList[i].PutData(atomPos, atomVel, atomForce);
kernel.EnqueueWriteBuffer(kernelNum, posBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomPos[0]);
kernel.EnqueueWriteBuffer(kernelNum, velBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomVel[0]);
kernel.EnqueueWriteBuffer(kernelNum, forceBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomForce[0]);
}
void LoadAtomTypes(int kernelNum, bool blocking)
{
std::vector<cl_float> mass;
std::vector<cl_float> radius;
int type;
for(int i = 0; i < numParticles; i++)
{
type = atomList[i].GetType();
mass.push_back(atomTypes[type].mass);
radius.push_back(atomTypes[type].radius);
}
kernel.EnqueueWriteBuffer(kernelNum, massBuf, blocking, 0, numParticles*sizeof(cl_float), &mass[0]);
kernel.EnqueueWriteBuffer(kernelNum, radiusBuf, blocking, 0, numParticles*sizeof(cl_float), &radius[0]);
}
There is more to my code, as always, but this is what's related to the kernel.
I saw this question, which is similar, but I use cl_float4 everywhere I can so I don't believe it's an issue of alignment. There aren't really any other related questions.
I realize this probably isn't a simple question, but I've run out of ideas until we can get new hardware in the office to test on. Can anyone help me out?
Since no one answered, I suppose I'll just contribute what I've learned so far.
I don't have a definitive conclusion, but at the very least, I figured out why it was doing it so often. Since I'm running this (and other similar kernels) to figure out an estimate for the order of time, I was clearing the lists, resizing them and then re-running the calculations. What I wasn't doing, however, was resizing the buffers. This resulted in some undefined numbers getting pulled by the threads.
However, this doesn't solve the QNaNs I get from running the program over long periods of time. Those simply appear spontaneously. Maybe it's a similar issue that I'm overlooking, but I can't say. If anyone has further input on the issue, it'd be appreciated.
I'm trying to make a platformer game in C++ and I have made a vector of blocks,
and I simply loop through the vector and check for the collision individually:
//Pseudo code
class Block{
...int x
...int y
...int width
...int height
};
class Player{
int x
int y
int width
int height
int hsp //horizontal speed
int vsp //vertical speed
int facing //0 = no direction, -1 = left, 1 = right
...
void loop()
{
if(keyboard_pressed(key_left) { x-=hsp; facing = -1;}
if(keyboard_pressed(key_right) {x+=hsp; facing = 1;}
if(keyboard_pressed(key_up) {y-=vsp;}
if(keyboard_pressed(key_down) {y+=vsp;}
if(keyboard_released(key_left | key_right) {facing = 0;}
for(int i = 0; i < blocks.size(); i++)
{
Block b = blocks.at(i);
check_Collision(b);
}
}
};
As you can see, my player simply moves according to hsp and vsp. Simple enough.
The main portion of my question is in check_Collision(). First I check to see if the player
is on top of the block, and if he is, let him stay there.
Then I check if the player is at the sides of the block.
But for some reason there's a problem. For some reason when I go under the top of the block,
he stays at the top, but then he gets shifted to the left side.
I honestly don't know where to go with this.
The following code only checks for the top and the left side:
check_Collision(){
///////////////////////////////////
var myLeft, myRight, myTop, myBot;
var bLeft, bRight, bTop, bBot;
myLeft = x;
myRight = x + width;
myTop = y;
myBot = y + height;
/////////////////////
bLeft = b.x;
bRight = b.x + b.width;
bTop = b.y;
bBot = b.y + b.height;
//////////////////////////////////
//Check if we are at the top
if(myBot + vsp > bTop+1){
y = bTop - height;
}
//Check if we are at the sides
if(myBot > bTop+2){
if(myRight + hsp > bLeft)
{
x = bLeft - width;
}
}
}
If anyone can point me into some tutorial on 2D box collision that would be great.
The logic you're using doesn't make sense to me. It's not enough just to check that the player is under the block: don't you also need to make sure that the player is standing on it, ie, isn't too far to the right or left of the block? Similarly, in your second check, you've got to make sure that the player isn't jumping over the block (or standing on it). Your if partially checks this, but doesn't take into account the fact that the first check might have modified player position.
Can you assume that the player can't ever walk under the block? Can you assume that the player will never move fast enough to "tunnel" completely through the block (hsp > b.width, etc)?
If the answer to either of these is no, you will need significantly more sophisticated collision detection.