I am sorry to ask such a basic question about bullet. However, I am having an issue. Here is the setup:
I have a world with no gravity. When I press a key, a 1x1x1 box is created in the center of the world. When I right click, a box of size 0.05*0.05*0.05 is create at the camera position, and is 'shot' in the direction you are looking. Here is where the trouble begins.
When a small cube hits a large cube, the interaction seems wrong. You would expcet a box 1/8000th the size of another to have very little effect. Yet the large cube goes flying, as if it been with a cube of its same size. I assumed it is because I created both objects with the same mass.
To confirm this, I apply an upward force of 1 newton (or whatever unit bullet uses). Both objects accelerate at the same rate.
My code for creating objects is as follows:
btMotionState *state = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, 0)));
btConvexHullShape* shape = new btConvexHullShape();
for(unsigned int i = 0; i < vertices.size(); ++i) {
shape->addPoint(toBt(vertices[i]));
}
shape->setMargin(0.01f);
btScalar mass = 1.f;
btVector3 inertia;
shape->calculateLocalInertia(mass, inertia);
shape->setMargin(0.01f);
btRigidBody::btRigidBodyConstructionInfo ci(mass, state, shape, inertia);
body = new btRigidBody(ci);
I expect that the btScalar mass = 1.f; line is the culprit.
Am I expected to calculate the mass of my objects? This is simple enough for a cube, but what about some weird convex shape? Is there any way I can get bullet to do this for me?
Yes, mass is the culprit. Think about your big box being an empty wooden crate and your small box being a solid cube of iron. Huge difference in size but equal mass. Now your physics seem correct, right?
Yes, you are expected to provide the mass. Whether you calculate it or just state "this box weighs 20kg" is up to you. Actually expecting someone else to calculate the mass for you just makes your problem worse, because you'd have to specify materials, material density, and material distribution of your objects besides its geometry. If you want to go that way there are plenty of other tools available to aid you with such calculations. But I'm sure you agree that just stating some mass through trial and error is easier by far.
Related
I'm trying to implement a 'raypicker' for selecting objects within my project. I do not fully understand how to implement this, but I understand conceptually how it should work. I've been trying to learn how to do this, but most tutorials I find go way over my head. My current code is based on one of the recent tutorials I found, here.
After several hours of revisions, I believe the problem I'm having with my raypicker is actually the creation of the ray in the first place. If I substitute/hardcode my near/far planes with a coordinate that would undisputably be located within the region of a triangle, the picker identifies it correctly.
My problem is this: my ray creation doesn't seem to fully take my current "camera" or perspective into account, so camera rotation won't affect where my mouse is.
I believe to remedy this I need something like using gluUnProject() or something, but whenever I used this the x,y,z coordinates returned would be incredibly small,
My current ray creation is a mess. I tried to use methods that others proposed initially, but it seemed like whatever method I tried it never worked with my picker/intersection function.
Here's the code for my ray creation:
void oglWidget::mousePressEvent(QMouseEvent *event)
{
QVector3D nearP = QVector3D(event->x()+camX, -event->y()-camY, -1.0);
QVector3D farP = QVector3D(event->x()+camX, -event->y()-camY, 1.0);
int i = -1;
for (int x = 0; x < tileCount; x++)
{
bool rayInter = intersect(nearP, farP, tiles[x]->vertices);
if (rayInter == true)
i = x;
}
if (i != -1)
{
tiles[i]->showSelection();
}
else
{
for (int x = 0; x < tileCount; x++)
tiles[x]->hideSelection();
}
//tiles[0]->showSelection();
}
To repeat, I used to load up the viewport, model & projection matrices, and unproject the mouse coordinates, but within a 1920x1080 window, all I get is values in the range of -2 to 2 for x y & z for each mouse event, which is why I'm trying this method, but this method doesn't work with camera rotation and zoom.
I don't want to do pixel color picking, because who knows I may need this technique later on, and I'd rather not give up after the amount of effort I put in so far
As you seem to have problems constructing your rays, here's how I would do it. This has not been tested directly. You could do it like this, making sure that all vectors are in the same space. If you use multiple model matrices (or stacks thereof) the calculation needs to be repeated separately with each of them.
use pos = gluUnproject(winx, winy, near, ...) to get the position of the mouse coordinate on the near plane in model space; near being the value given to glFrustum() or gluPerspective()
origin of the ray is the camera position in model space: rayorig = inv(modelmat) * camera_in_worldspace
the direction of the ray is the normalized vector from the position from 1. to the ray origin: raydir = normalize(pos - rayorig)
On the website linked they use two points for the ray and they don't seem to normalize the ray direction vector, so this is optional.
Ok, so this is the beginning of my trail of breadcrumbs.
I was somehow having issues with the QT datatypes for the matrices, and the logic pertaining to matrix transformations.
This particular problem in this question resulted from not actually performing any transformations whatsoever.
Steps to solving this problem were:
Converting mouse coordinates into NDC space (within the range of -1 to 1: x/screen width * 2 - 1, y - height / height * 2 - 1)
grabbing the 4x4 matrix for my view matrix (can be the one used when rendering, or re calculated)
In a new vector, have it equal the inverse view matrix multiplied by the inverse projection matrix.
In order to build the ray, I had to do the following:
Take the previously calculated value for the matrices that were multiplied together. This will be multiplied by a vector 4 (array of 4 spots), where it will hold the previously calculated x and y coordinates, as well as -1, then +1.
Then this vector will be divided by the last spot value of the entire vector
Create another vector 4 which was just like the last, but instead of -1, put "1" .
Once again divide that by its last spot value.
Now the coordinates for the ray have been created at the far and near planes, so it can intersect with anything along it in the scene.
I opened a series of questions (because of great uncertainty with my series of problems), so parts of my problem overlap in them too.
In here, I learned that I needed to take the screen height into consideration for switching the origin of the y axis for a Cartesian system, since windows has the y axis start at the top left. Additionally, retrieval of matrices was redundant, but also wrong since they were never declared "properly".
In here, I learned that unProject wasn't working because I was trying to pull the model and view matrices using OpenGL functions, but I never actually set them in the first place, because I built the matrices by hand. I solved that problem in 2 fold: I did the math manually, and I made all the matrices of the same data type (they were mixed data types earlier, leading to issues as well).
And lastly, in here, I learned that my order of operations was slightly off (need to multiply matrices by a vector, not the reverse), that my near plane needs to be -1, not 0, and that the last value of the vector which would be multiplied with the matrices (value "w") needed to be 1.
Credits goes to those individuals who helped me solve these problems:
srobins of facepunch, in this thread
derhass from here, in this question, and this discussion
Take a look at
http://www.realtimerendering.com/intersections.html
Lot of help in determining intersections between various kinds of geometry
http://geomalgorithms.com/code.html also has some c++ functions one of them serves your purpose
I have a problem that I'm strugling to solve for a few days.
I'm trying to make a bowling game using bullet physics, but the pin shakes, jiggles and moves to the side after I position it and it falls to the floor.
Here is a GIF of what happens:
http://imgur.com/7Mg41sf
Here is how I create a Pin:
btCollisionShape* shape = createShape(pinVertices);
btScalar bodyMass = 1.6f;
btVector3 bodyInertia(0,0,0);
shape->calculateLocalInertia(bodyMass, bodyInertia);
btRigidBody::btRigidBodyConstructionInfo bodyCI = btRigidBody::btRigidBodyConstructionInfo(bodyMass, nullptr, shape, bodyInertia);
bodyCI.m_restitution = 0.7;
bodyCI.m_friction = 0.9f;
_physicsBody = std::unique_ptr<btRigidBody>(new btRigidBody(bodyCI));
_physicsBody->setUserPointer(this);
And here is how I create a floor:
btCollisionShape* shape = createShape(laneVertices);
btScalar bodyMass = 0.0f;
btVector3 bodyInertia(0,0,0);
shape->calculateLocalInertia(bodyMass, bodyInertia);
btRigidBody::btRigidBodyConstructionInfo bodyCI = btRigidBody::btRigidBodyConstructionInfo(bodyMass, nullptr, shape, bodyInertia);
bodyCI.m_restitution = 0.6;
bodyCI.m_friction = 0.5;
_physicsBody = std::unique_ptr<btRigidBody>(new btRigidBody(bodyCI));
_physicsBody->setUserPointer(this);
Right now the floor is a btBoxShape and a pin is a btConvexHullShape, but I've tried using cylinder or cone and they also slide.
Been struggling for few days especially taking into account Bullet Physics website and forum are down.
Looks entirely reasonable to me. A rigid body isn't exactly going to bounce back up, nor is it going to shatter.
You have further issues with the imperfect approximation of reality. The bottom of your pin is probably flat, which means it theoretically hits the floor instantly over multiple points. Furthermore, due to the limited FP accuracy, the pin won't be exactly round, but then that part is realistic.
So the horizontal movements are probably because the small bit of freefall introduced a minor deviation from pure vertical fall. When hitting the ground this component wasn't cancelled, but the friction on moving did eventually bring the pin to a halt. Since the pin had only a small horizontal speed, the friction was not enough to topple the pin.
Perhaps you should set the restitution (bounce) of the pin and floor to something lower (try first with 0.0) This should solve it if the pin is bouncing.
Another thing you could try is to deactivate the pin after creating it. I don't know in Bullet, but in JBullet it's done like this:
body.setActivationState( CollisionObject.WANTS_DEACTIVATION );
This will stop your pin until some other object like the ball or other pin hits it.
I looked at a bunch of similar questions, and I cannot seem to find one that particularly answers my question. I am coding a simple 3d game, and I am trying to allow the player to pick up and move entities around my map. I essentially want to get a velocity vector that will "push" the physics object a distance from the player's eyes, wherever they are looking. Here's an example of this being done in another game (the player is holding a chair entity in front of his eyes).
To do this, I find out the player's eye angles, then get the forward vector from the angles, then calculate the velocity of the object. Here is my working code:
void Player::PickupOtherEntity( Entity& HoldingEntity )
{
QAngle eyeAngles = this->GetPlayerEyeAngles();
Vector3 vecPos = this->GetEyePosition();
Vector3 vecDir = eyeAngles.Forward();
Vector3 holdingEntPos = HoldingEntity.GetLocation();
// update object by holding it a distance away
vecPos.x += vecDir.x * DISTANCE_TO_HOLD;
vecPos.y += vecDir.y * DISTANCE_TO_HOLD;
vecPos.z += vecDir.z * DISTANCE_TO_HOLD;
Vector3 vecVel = vecPos - holdingEntPos;
vecVel = vecVel.Scale(OBJECT_SPEED_TO_MOVE);
// set the entity's velocity as to "push" it to be in front of the player's eyes
// at a distance of DISTANCE_TO_HOLD away
HoldingEntity.SetVelocity(vecVel);
}
All that is great, but I want to convert my math so that I can apply an impulse. Instead of setting a completely new velocity to the object, I want to "add" some velocity to its existing velocity. So supposing I have its current velocity, what kind of math do I need to "add" velocity? This is essentially a game physics question. Thank you!
A very simple implementation could be like this:
velocity(t+delta) = velocity(t) + delta * acceleration(t)
acceleration(t) = force(t) / mass of the object
velocity, acceleration and force are vectors. t, delta and mass scalars.
This only works reasonably well for small and equally spaced deltas. What you are essentially trying to achieve with this is a simulation of bodies using classical mechanics.
An Impulse is technically F∆t for a constant F. Here we might want to assume a∆t instead because mass is irrelevant. If you want to animate an impulse you have to decide what the change in velocity should be and how long it needs to take. It gets complicated real fast.
To be honest an impulse isn't the correct thing to do. Instead it would be preferable to set a constant pick_up_velocity (people don't tend to pick things up using an impulse), and refresh the position each time the object rises up velocity.y, until it reaches the correct level:
while(entPos.y < holdingEntPos.y)
{
entPos.y += pickupVel.y;
//some sort of short delay
}
And as for floating in front of the player's eyes, set an EyeMovementEvent of some sort that also sends the correct change in position to any entity the player is holding.
And if I missed something and that's what you are already doing, remember that when humans apply an impulse, it is generally really high acceleration for a really short time, much less than a frame. You wouldn't see it in-game anyways.
basic Newtonian/D'Alembert physics dictate:
derivate(position)=velocity
derivate(velocity)=acceleration
and also backwards:
integrate(acceleration)=velocity
integrate(velocity)=position
so for your engine you can use:
rectangle summation instead of integration (numerical solution of integral). Define time constant dt [seconds] which is the interval between updates (timer or 1/fps). So the update code (must be periodically called every dt:
vx+=ax*dt;
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt;
y+=vy*dt;
z+=vz*dt;
where:
a{x,y,z} [m/s^2] is actual acceleration (in your case direction vector scaled to a=Force/mass)
v{x,y,z} [m/s] is actual velocity
x,y,z [m] is actual position
These values have to be initialized a,v to zero and x,y,z to init position
all objects/players... have their own variables
full stop is done by v=0; a=0;
driving of objects is done only by change of a
in case of collision mirror v vector by collision normal
and maybe multiply by some k<1.0 (0.95 for example) to account energy loss on impact
You can add gravity or any other force field by adding g vector:
vx+=ax*dt+gx*dt;
vy+=ay*dt+gy*dt;
vz+=az*dt+gz*dt;
also You can add friction and anything else you need
PS. the same goes for angles just use angle/omega/epsilon/I instead of x/a/v/m
to be clear by angles I mean rotation (pitch,yaw,roll) around mass center
I am using OpenGL to create the 3D space.
I have a spaceship which can fire lasers.
Up until now I have had it so that the lasers will simply to deeper into the Z-axis once fired.
But I am attempting to make a proper aiming system with crosshairs so that you can aim and shoot in any direction, but I have not been successfull in trying to update the laser's path.
I have a directional vector based off the lasers end tip and start tip, which is gotten from the aiming.
How should I update the laser's X,Y,Z values (or vectors) properly so that it looks natural?
I think I see.
Let's say you start with the aiming direction as a 3D vector, call it "aimDir". Then in your update loop add all 3 (x, y and z) to the projectile "position". (OK, at the speed of light you wouldn't actually see any movement, but I think I see what you're going for here).
void OnUpdate( float deltaT )
{
// "move" the laser in the aiming direction, scaled by the amount of time elapsed
// since our last update (you probably want another scale factor here to control
// how "fast" the laser appears to move)
Vector3 deltaLaser = deltaT * aimDir; // calc 3d offset for this frame
laserEndpoint += deltaLaser; // add it to the end of the laser
}
then in the render routine draw the laser from the firing point to the new endpoint:
void OnRender()
{
glBegin(GL_LINES);
glVertex3f( gunPos.x, gunPos.Y, gunPos.z );
glVertex3f( laserEndPoint.x, laserEndPoint.y, laserEndPoint.z );
glEnd();
}
I'm taking some liberties because I don't know if you're using glut, sdl or what. But I'm sure you have at least an update function and a render function.
Warning, just drawing a line from the gun to the end of the laser might be disappointing visually, but it will be a critical reference for adding better effects (particle systems, bloom filter, etc.). A quick improvement might be to make the front of the laser (line) a bright color and the back black. And/or make multiple lines like a machine gun. Feel free to experiment ;-)
Also, if the source of the laser is directly in front of the viewer you will just see a dot! So you may want to cheat a bit and fire from just below or to the right of the viewer and then have in fire slightly up or in. Especially if you have one one each side (wing?) that appear to converge as in conventional machine guns.
Hope that's helpful.
Today, I tried to play around Physx and Physx visual debugger and as always, newbies have problems and questions. I'll try to describe my problems as best as I can with my poor english skills.
1) I managed to create a physx scene. added a dynamic actor and manipulated it. I see in Visual Debugger it's motion. It's a standard PxSphereGeometry ball. However, when I add a second ball in the scene, the second one is not visible, but I can see that collision happens. Here's the code and if anyone can point me what's wrong with it I'd be very grateful:
PxMaterial* mMaterial;
mMaterial = mPhysics->createMaterial(0.5f, 0.5f, 0.5f); //static friction, dynamic friction, restitution
if(!mMaterial)
error("createMaterial failed!");
PxVec3 position(0, 50, 0);
PxRigidDynamic* aSphereActor = PxCreateDynamic(*mPhysics, PxTransform(position), PxSphereGeometry(3), *mMaterial, 1.f);
PxRigidDynamic* aTrActor = PxCreateDynamic(*mPhysics, PxTransform(PxVec3(3, 1, 1)), PxSphereGeometry(3), *mMaterial, 1.1f);
if(!aSphereActor)
error("Unable to create sphere actor");
aSphereActor->setMass(1);
aTrActor->setMass(10);
PxRigidStatic* plane = PxCreatePlane(*mPhysics, PxPlane(PxVec3(0,1,0), 0), *mMaterial);
if (!plane)
error("create shape failed!");
mScene->addActor(*plane);
mScene->addActor(*aSphereActor);
mScene->addActor(*aTrActor);
while(true)
{
mScene->simulate(1.0f / 30.0f);
if(!mScene->fetchResults(true))
error("cant fetch result");
Sleep(10);
}
In this scene, aSphereActor collides with aTrActor, but I can't see aTrActor in Visual Debugger, however collision is perfectly visible.
2) Nvidia's documentation is very very poor. It's a torture for newbies like me to find it's way through it. So I wanted to know how can I import a 3d model and add it in the scene. I know there is a Physx plugins for 3ds max, maya etc. Say I have a model exported with this plugin, how can I import it in my app and add it to the scene?
3) During the creation of the scene
sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f);
, what value should I provide to get a real gravity, the one we have on earth
4) I can assign a mass to an actor, however I don't know in which measurement unit the mass is. For example, if I set aSphereActor->setMass(1); will aSphereActor will be 1kg, gram or what?
Thank you very much everyone. I appreciate your help.
First, I am doing my first Physx project this quarter. (read as, I might be making this up)
1)
You don't check aTrActor's creation, but I don't think that's your problem.
Check if aTrActor in your draw/update callback.
2)
Dunno
3)
-9.81 m/s^2 is the acceleration for Earth's gravity.
I'm guessing that the PxVec3 is the gravity with respect to each axis.
So, PxVec3(0.0, -9.81, 0.0) is no x or z acceleration and -9.81 m/s^2 y acceleration.
4)
The answer to #3 would suggest the units are metric.
You can probably throw it all together in standard, but metric > standard imo.
Just looked at the date, this may not help Davita, but hopefully it'll be of some use to someone.