PDA

View Full Version : Model not staying rendered? (And trying to get it's world position)



eratic-magician
03-10-2017, 12:55 PM
I'm attempting to fire bullets from my gun in my OpenGL game, by spawning a new bullet model on every left click. However, the bullet is only being rendered while I click, and disappears straight afterwards (because it's being done in an if statement maybe?).

Here's my code (which is inside my main game's do-while loop):



static int lcOldState = GLFW_RELEASE;
int lcNewState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT);
if (lcNewState == GLFW_RELEASE && lcOldState == GLFW_PRESS && bulletCount > 0) {

glm::vec3 bulletPosition = glm::vec3(getPosition().x, getPosition().y, getPosition().z);
glm::quat bulletRotation = glm::normalize(glm::quat(glm::vec3(-1.5708, 0.03054326, 0)));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, bulletTexture);
glUniform1i(TextureID, 0);

glm::mat4 BulletRotationMatrix = glm::toMat4(bulletRotation);
glm::mat4 BulletTranslationMatrix = translate(mat4(), glm::vec3(0.35, 0, -3));
glm::mat4 BulletScaleMatrix = scale(mat4(), glm::vec3(0.1f, 0.1f, 0.1f));
glm::mat4 ModelMatrix2 = BulletTranslationMatrix * BulletRotationMatrix * BulletScaleMatrix;
glm::mat4 MVP2 = ProjectionMatrix * ModelMatrix2;

// Send our transformation to the currently bound shader,
// in the "MVP" uniform
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP2[0][0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix2[0][0]);

// 1st attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer2);
glVertexAttribPointer(
0, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);

// 2nd attribute buffer : UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer2);
glVertexAttribPointer(
1, // attribute
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);

// 3rd attribute buffer : normals
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer2);
glVertexAttribPointer(
2, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);

// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer2);

// Draw the triangles
glDrawElements(GL_TRIANGLES, bulletIndices.size(), GL_UNSIGNED_SHORT, (void*)0);

btDefaultMotionState* bulletMotionstate = new btDefaultMotionState(btTransform(
btQuaternion(bulletRotation.x, bulletRotation.y, bulletRotation.z, bulletRotation.w),
btVector3(bulletPosition.x, bulletPosition.y, bulletPosition.z)
));

btRigidBody::btRigidBodyConstructionInfo bulletRigidBodyCI(bulletMass, bulletMotionstate, bulletCollisionShape, bulletInertia);
btRigidBody *bulletRigidBody = new btRigidBody(bulletRigidBodyCI);

rigidbodies.push_back(bulletRigidBody);
dynamicsWorld->addRigidBody(bulletRigidBody);

btTransform bulletTrans;
bulletRigidBody->getMotionState()->getWorldTransform(bulletTrans);
bulletPosition.x = bulletTrans.getOrigin().getX();
bulletPosition.y = bulletTrans.getOrigin().getY();
bulletPosition.z = bulletTrans.getOrigin().getZ();

bulletCount--;
}
lcOldState = lcNewState;

Also, my other problem is creating my bullet's rigidbody at the bullet's location, since I'm drawing the bullet in camera space (my gun is drawn in camera space, and is therefore attached to the camera like an FPS style gun). I can't draw my bullet in world space because the bullet needs to spawn at the end of my gun model, which could be facing any direction.

Is there a way to get the bullet to stay rendered, and ideally, once it's drawn in camera space then place it in the world space so that it has world coordinates (which I can populate my bulletPosition vector with and create a rigidbody at that point)?

Cheers.

P.S. my getPosition() method just gets the player position for now, I want it to get the bullet's location in world space if possible.

john_connor
03-10-2017, 02:14 PM
if you want to transform the bullets model matrix back to worldspace, multiply it with the inverse view (camera) matrix. another way would be to store all bullets in world space from the beginning, and the camera too

eratic-magician
03-10-2017, 02:27 PM
Where would I do that exactly? Currently my bullet is rendered next to my gun and follows the camera as it moves.

I'm thinking something like this?:


// Like in my given code
glm::mat4 ModelMatrix2 = BulletTranslationMatrix * BulletRotationMatrix * BulletScaleMatrix;
glm::mat4 MVP2 = ProjectionMatrix * ModelMatrix2;
MVP2 = inverse(ViewMatrix) * ModelMatrix2; // Bullet moves around strangely

I can't seem to get the bullet to stay at the point it's drawn, rather than continuing to follow the camera.

john_connor
03-11-2017, 02:07 AM
this is just a suggestion:

using namespace std;
using namespace glm;

struct Bullet {
float LifeTime = 3; // for example
vec3 Position, Velocity;
quat Rotation{1, 0, 0, 0};
};

struct Camera {
vec3 Position;
quat Rotation{1, 0, 0, 0}; // no rotation by default
float ZNear{0.1f}, ZFar{100.0f}; // clipping planes
float FieldOfView{radians(45.0f)};
};

struct Scene {
Camera camera;
list<Bullet> bullets;
// other stuff, like objects, environmental infos, etc ...
};


you have a global variable "scene", in it you describe the whole world around you
"you" are the "camera", it determines what part of that scene you'll see
there are also a bunch (std::list<>) of "bullet"s in the scene


make a "simulator" class and a "graphics" class, the simulator manages all the stuff in the "scene" WITHOUT rendering, the "graphics" then renders the "scene"

struct Simulator {
void AnimateScene(Scene& scene, float frametime);
};

struct Graphics {
void RenderScene(Scene& scene);
};


each frame, you call "AnimateScene(...)" and "void RenderScene(...)" in which you pass you global variable "scene". in "AnimateScene(...)", you advance each bullet, like that:

for (auto& bullet : scene.bullets)
{
bullet.LifeTime -= frametime;
if (bullet.LifeTime > 0)
{
bullet.Position += bullet.Velocity * frametime;
bullet.Velocity *= 0.99f; // slow down the bullet as it travells trough the air
bullet.Rotation = rotate(bullet.Rotation, 0.1f, vec3(0, 0, 1)); // roll the bullet a bit
}
else
... remove bullet from list ...
}

in "RenderScene(...)" you rebuild the model-to-world-matrix again, dependend on the new bullet properties
mat4 ModelToWorld = translate(bullet.Position) * toMat4(bullet.Rotation); // no scaling needed


another way is to keep the bullets matrices as property and do the simulation part with matrix math, dont know whats "better"
to build the "World-to-Camera" matrix as well as the "Projection" matrix, use the scene's camera properties
mat4 WorldToCamera = lookAt(scene.camera.Position, rotate(scene.camera.Rotation, vec3(0, 0, 1)), rotate(scene.camera.Rotation, vec3(0, 1, 0)));

float aspectratio = (float)window.width / window.height;
mat4 Projection = perspective(scene.camera.FieldOfView, aspectratio, scene.camera.ZNear, scene.camera.ZFar);

or keep the view / projection matrices as camera properties and do the simulation part with matrix math, dont know whats "better"

(i prefer the "easiest" way, which isnt matrix math, at least not for me ^^)

in you main loop (in main.cpp), you have all your input callbacks functions for GLFW, like;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_E && action == GLFW_PRESS)
scene.bullets.push_back(Bullet(scene.camera.positi on, scene.camera.rotation, ... etc ...));

}

the task of the callback is to just append a new bullet to the scene, advancing and drawing it is done somewhere else. if you want to check if it hits another object, the "simulator" is the right place to do that as well since it has access to ALL items in the "scene"

if you use for example "OpenAL" for sound, the simulator could also handle the "listener" and all the "source"s

eratic-magician
03-11-2017, 11:24 AM
Thanks for the extensive post, I'm using Bullet to handle collisions and such, and I already have a camera set up. I think that rebuilding my code to resemble what you've provided would still leave me with a similar problem. I've now got the bullets to stay rendered by taking the rendering out of the if statement that handles mouse clicks, and just putting them into a for loop that is incremented on each click.

Unfortunately, I still have the issue of getting the bullet (once drawn) to go back from camera space and remain positioned in world space. I think your first post was more of what I needed, but I appreciate the time you put into your suggestion. Hopefully it will help someone that's looking to do something similar but doesn't yet have a good structure.