Trying to implement a rope, but it freeze
Posted: Wed May 16, 2018 10:55 am
Greetings.
I'm trying to implement a rope.
I'm using a simplified model:
a kinematic obj on the top <- a P2P constraint <- a mass obj in the middle <- a P2P constraint <- a tail obj to connect payload
So I wrote this
The problem is that, when the program starts, then I move the top kinematic obj immediately, things works. However, if I wait for a few seconds, then the dynamic objs wouldn't move anymore, only the kinematic obj could be moved by me.
And if I move the top obj when it still work, then I have to keep moving my hand to keep things work. If I stop, then some time latter, dynamic objs would freeze either.
It feels like the system would freeze if things calm down once.
I'm quite confused. Could anyone give me some hint?
All of the code is below. Using libSDL and GLEW.
The load_obj.h is loading a box.obj file, a size 1x1x1 box.
vmath.h is from the OpenGL redbook, which would generate rotation and scaling matrix: https://github.com/openglredbook/exampl ... de/vmath.h
Vertex Shader:
Fragment Shader:
main.cpp:
I'm trying to implement a rope.
I'm using a simplified model:
a kinematic obj on the top <- a P2P constraint <- a mass obj in the middle <- a P2P constraint <- a tail obj to connect payload
So I wrote this
Code: Select all
typedef struct {
btRigidBody* body;
GLfloat size;
} draw_box;
//for OpenGL.
//things in it would be drew on the screen
std::vector<draw_box> draw;
btRigidBody* rope_top = nullptr;
btRigidBody* rope_tail = nullptr;
btVector3 rope_pos(0, 9, 0);
btScalar rope_length = 8.f;
btScalar rope_fat(0.2f);
{
btScalar fat(rope_fat);
{
//kinematic obj on top, so I could move the rope
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y(), rope_pos.z()));
btScalar mass(0.f);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape);
rope_top = body;
world->addRigidBody(body);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
btRigidBody* rope_middle = nullptr;
{
//mass obj at the middle
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y() - rope_length / 2, rope_pos.z()));
btScalar mass(1.f);
btVector3 inertia(0.f, 0.f, 0.f);
shape->calculateLocalInertia(mass, inertia);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape, inertia);
rope_middle = body;
world->addRigidBody(body);
btTypedConstraint* pivot = new btPoint2PointConstraint(*rope_top, *body, btVector3(0.f, -fat * 2, 0.f), btVector3(0.f, rope_length / 2 - fat * 2, 0.f));
world->addConstraint(pivot);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
{
//tail obj to connect payload
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y() - rope_length, rope_pos.z()));
btScalar mass(1.f);
btVector3 inertia(0.f, 0.f, 0.f);
shape->calculateLocalInertia(mass, inertia);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape, inertia);
rope_tail = body;
world->addRigidBody(body);
btTypedConstraint* pivot = new btPoint2PointConstraint(*rope_middle, *body, btVector3(0.f, -fat * 2, 0.f), btVector3(0.f, rope_length / 2 - fat * 2, 0.f));
world->addConstraint(pivot);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
}
And if I move the top obj when it still work, then I have to keep moving my hand to keep things work. If I stop, then some time latter, dynamic objs would freeze either.
It feels like the system would freeze if things calm down once.
I'm quite confused. Could anyone give me some hint?
All of the code is below. Using libSDL and GLEW.
The load_obj.h is loading a box.obj file, a size 1x1x1 box.
vmath.h is from the OpenGL redbook, which would generate rotation and scaling matrix: https://github.com/openglredbook/exampl ... de/vmath.h
Vertex Shader:
Code: Select all
#version 330 core
uniform mat4 change;
in vec4 in_position;
out vec4 fragment_color;
void
main()
{
gl_Position = change * in_position;
fragment_color = vec4(1.0, 1.0, 1.0, 1.0);
}
Code: Select all
#version 330 core
in vec4 fragment_color;
out vec4 out_color;
void
main()
{
out_color = fragment_color;
}
main.cpp:
Code: Select all
#include <iostream>
#include <vector>
#include <SDL.h>
#include <GL/glew.h>
#include <SDL_opengl.h>
#include <GL/GLU.h>
#include <btBulletDynamicsCommon.h>
#include "load_shader.h"
#include "load_obj.h"
#include "vmath.h"
int main(int, char**) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) {
std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window *win = SDL_CreateWindow("Greetings", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
if (win == nullptr) {
std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_GLContext gl_context = SDL_GL_CreateContext(win);
if (gl_context == nullptr) {
std::cerr << "SDL_GL_CreateContext Error: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(win);
SDL_Quit();
return 1;
}
glewExperimental = GL_TRUE;
GLenum glewError = glewInit();
if (glewError != GLEW_OK) {
std::cerr << "glewInit Error: " << glewGetErrorString(glewError) << std::endl;
SDL_DestroyWindow(win);
SDL_Quit();
return 1;
}
// try adaptive vsync first (-1)
// when failed, try normal vsync (1)
if (SDL_GL_SetSwapInterval(-1) < 0 && SDL_GL_SetSwapInterval(1) < 0) {
std::cerr << "SDL_GL_SetSwapInterval Error: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(win);
SDL_Quit();
return 1;
}
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
GLuint vertex_shader = load_shader(GL_VERTEX_SHADER, "vertex_shader.glsl");
GLuint fragment_shader = load_shader(GL_FRAGMENT_SHADER, "fragment_shader.glsl");
GLuint gpu_program = glCreateProgram();
glAttachShader(gpu_program, vertex_shader);
glAttachShader(gpu_program, fragment_shader);
glLinkProgram(gpu_program);
GLint link_result;
glGetProgramiv(gpu_program, GL_LINK_STATUS, &link_result);
if (link_result != GL_TRUE)
{
GLchar error_log[1024];
glGetProgramInfoLog(gpu_program, 1024, nullptr, error_log);
std::cerr << "failed to link GPU program: " << error_log << std::endl;
return 1;
}
GLuint in_position = glGetAttribLocation(gpu_program, "in_position");
GLuint change = glGetUniformLocation(gpu_program, "change");
Model box; // from load_obj.h, just loading a 1x1x1 box here
if (!box.load("box.obj"))
{
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glDeleteProgram(gpu_program);
SDL_DestroyWindow(win);
SDL_Quit();
return 1;
}
GLuint gpu_vao = 0;
glGenVertexArrays(1, &gpu_vao);
glBindVertexArray(gpu_vao);
glBindBuffer(GL_ARRAY_BUFFER, box.my_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, box.my_element_buffer);
glVertexAttribPointer(in_position, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnableVertexAttribArray(in_position);
btDefaultCollisionConfiguration* collision_config = new btDefaultCollisionConfiguration();
btCollisionDispatcher* collision_dispatcher = new btCollisionDispatcher(collision_config);
btBroadphaseInterface* overlapping_pair_cache = new btDbvtBroadphase();
btSequentialImpulseConstraintSolver* constraint_solver = new btSequentialImpulseConstraintSolver();
btDiscreteDynamicsWorld* world = new btDiscreteDynamicsWorld(collision_dispatcher, overlapping_pair_cache, constraint_solver, collision_config);
world->setGravity(btVector3(0, -10, 0));
btAlignedObjectArray<btCollisionShape*> collision_shapes;
{
//ground
btCollisionShape* ground = new btBoxShape(btVector3(btScalar(50), btScalar(1), btScalar(50)));
collision_shapes.push_back(ground);
btTransform groundTransform;
groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(0, -10, 0));
btScalar mass(0.);
//rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0, 0, 0);
if (isDynamic)
ground->calculateLocalInertia(mass, localInertia);
//using motionstate is optional, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, myMotionState, ground, localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
body->setContactStiffnessAndDamping(300, 100);
//add the body to the dynamics world
world->addRigidBody(body);
}
typedef struct {
btRigidBody* body;
GLfloat size;
} draw_box;
std::vector<draw_box> draw; //things in it would be draw on the screen
btRigidBody* rope_top = nullptr;
btRigidBody* rope_tail = nullptr;
btVector3 rope_pos(0, 9, 0);
btScalar rope_length = 8.f;
btScalar rope_fat(0.2f);
{
//rope
btScalar fat(rope_fat);
{
//top obj, so I could move the rope
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y(), rope_pos.z()));
btScalar mass(0.f);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape);
rope_top = body;
world->addRigidBody(body);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
btRigidBody* rope_middle = nullptr;
{
//mass at the middle
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y() - rope_length / 2, rope_pos.z()));
btScalar mass(1.f);
btVector3 inertia(0.f, 0.f, 0.f);
shape->calculateLocalInertia(mass, inertia);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape, inertia);
rope_middle = body;
world->addRigidBody(body);
btTypedConstraint* pivot = new btPoint2PointConstraint(*rope_top, *body, btVector3(0.f, -fat * 2, 0.f), btVector3(0.f, rope_length / 2 - fat * 2, 0.f));
world->addConstraint(pivot);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
{
//tail obj to connect payload
btCollisionShape* shape = new btSphereShape(fat);
collision_shapes.push_back(shape);
btTransform start_pos;
start_pos.setIdentity();
start_pos.setOrigin(btVector3(rope_pos.x(), rope_pos.y() - rope_length, rope_pos.z()));
btScalar mass(1.f);
btVector3 inertia(0.f, 0.f, 0.f);
shape->calculateLocalInertia(mass, inertia);
btMotionState* motion = new btDefaultMotionState(start_pos);
btRigidBody* body = new btRigidBody(mass, motion, shape, inertia);
rope_tail = body;
world->addRigidBody(body);
btTypedConstraint* pivot = new btPoint2PointConstraint(*rope_middle, *body, btVector3(0.f, -fat * 2, 0.f), btVector3(0.f, rope_length / 2 - fat * 2, 0.f));
world->addConstraint(pivot);
draw_box my_draw;
my_draw.body = body;
my_draw.size = fat;
draw.push_back(my_draw);
}
}
bool fin = false;
unsigned int last_render_time = SDL_GetTicks();
btScalar top_posX(rope_pos.x());
btScalar top_posY(rope_pos.y());
unsigned int fps = 0;
unsigned int fps_timer = SDL_GetTicks();
while (!fin) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
fin = true;
break;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) {
fin = true;
break;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP) {
top_posY += 0.2f;
continue;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN) {
top_posY -= 0.2f;
continue;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT) {
top_posX += 0.2f;
continue;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT) {
top_posX -= 0.2f;
continue;
}
}
btTransform move_rope;
move_rope.setIdentity();
move_rope.setOrigin(btVector3(top_posX, top_posY, rope_pos.z()));
rope_top->setWorldTransform(move_rope);
unsigned int now = SDL_GetTicks();
if (now - fps_timer > 1000)
{
std::cout << "fps: " << fps << std::endl;
fps_timer = now;
fps = 0;
}
if (now - last_render_time < 1000 / 30)
{
SDL_Delay(1);
continue;
}
world->stepSimulation((now - last_render_time) / 1000.f, (now - last_render_time) / 1000.f / (1.f / 60.f) + 2, btScalar(1.) / btScalar(60.));
static const GLfloat black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, black);
for (unsigned int i = 0; i < draw.size(); ++i)
{
btRigidBody* body = draw[i].body;
btTransform trans;
if (body->isStaticOrKinematicObject())
{
trans = body->getWorldTransform();
}
else
{
body->getMotionState()->getWorldTransform(trans);
}
vmath::mat4 physic;
trans.getOpenGLMatrix(physic);
GLfloat world_size_factor = 0.1f;
physic[3][0] *= world_size_factor;
physic[3][1] *= world_size_factor;
physic[3][2] *= world_size_factor;
glUseProgram(gpu_program);
glUniformMatrix4fv(change, 1, GL_FALSE, physic* vmath::scale(world_size_factor) * vmath::scale(draw[i].size));
glDrawElements(GL_TRIANGLES, box.my_size, GL_UNSIGNED_INT, NULL);
}
SDL_GL_SwapWindow(win);
last_render_time = SDL_GetTicks();
++fps;
}
while (world->getNumConstraints() > 0)
{
btTypedConstraint* ptr = world->getConstraint(0);
world->removeConstraint(ptr);
delete ptr;
}
while (world->getNumCollisionObjects() > 0)
{
btCollisionObject* obj = world->getCollisionObjectArray()[0];
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
{
delete body->getMotionState();
}
world->removeCollisionObject(obj);
delete obj;
}
for (int i = 0; i < collision_shapes.size(); ++i)
{
delete collision_shapes[i];
}
delete world;
delete constraint_solver;
delete overlapping_pair_cache;
delete collision_dispatcher;
delete collision_config;
glDeleteVertexArrays(1, &gpu_vao);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glDeleteProgram(gpu_program);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}