Glm rotate object by time - opengl

I have an object which I want to rotate on key hold using this function
if (key == GLFW_KEY_S && action == GLFW_REPEAT) {
timer = glfwGetTime();
}
Which sends timer to this rotation
auto rotateMat = rotate(mat4{}, timer * 0.4f, {0, 1, 0});
But problem is, I hold key S pressed and the object is rotating but when I release it, time is changing of course, since glfwGetTime() gets real time. Then I press S again and hold it to rotate, but it starts rotation from different object angle as when it stopped. Any ideas how to fix it?
EDIT:
I have fixed it by using
timer += 0.1;
But when I press S and hold it, it has a delay about 1 second until the object starts rotating. It was same with using real glfwGetTime(). How can I have no delay?

You should track if the key is pressed:
if (action == GLFM_PRESS) {
keysPressed[key] = true;
}
if (action == GLFM_RELEASE) {
keysPressed[key] = false;
}
In the render loop:
now = glfwGetTime();
double delta = now - lastTime;
lastTime = now;
if (keysPressed[GLFW_KEY_S]) {
timer += delta;
}
if (keysPressed[GLFW_KEY_A]) {
timer -= delta;
}
auto rotateMat = rotate(mat4{}, timer * 0.4f, {0, 1, 0});

Related

OpenGL how to move the camera upward and then come back on the Grid

I want to implement a conventional FPS control. By pressing the spacebar the camera moves upward and then comes down back on the grid, simulating the jumping action. Right now, I am only able to move the camera upward for a distance but I don't know how to let the camera come down.
This is my camera class:
Camera::Camera(glm::vec3 cameraPosition, glm::vec3 cameraFront, glm::vec3 cameraUp){
position = glm::vec3(cameraPosition);
front = glm::vec3(cameraFront);
up = glm::vec3(cameraUp);
// Set to predefined defaults
yawAngle = -90.0f;
pitchAngle = 0.0f;
fieldOfViewAngle = 45.0f;
}
Camera::~Camera() {}
void Camera::panCamera(float yaw){
yawAngle += yaw;
updateCamera();}
void Camera::tiltCamera(float pitch){
pitchAngle += pitch;
// Ensure pitch is inbounds for both + and - values to prevent irregular behavior
pitchAngle > 89.0f ? pitchAngle = 89.0f : NULL;
pitchAngle < -89.0f ? pitchAngle = -89.0f : NULL;
updateCamera();}
void Camera::zoomCamera(float zoom){
if (fieldOfViewAngle >= MIN_ZOOM && fieldOfViewAngle <= MAX_ZOOM){
fieldOfViewAngle -= zoom * 0.1;}
// Limit zoom values to prevent irregular behavior
fieldOfViewAngle <= MIN_ZOOM ? fieldOfViewAngle = MIN_ZOOM : NULL;
fieldOfViewAngle >= MAX_ZOOM ? fieldOfViewAngle = MAX_ZOOM : NULL;}
glm::mat4 Camera::calculateViewMatrix(){
return glm::lookAt(position, position + front, up);}
void Camera::updateCamera(){
front.x = cos(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));
front.y = sin(glm::radians(pitchAngle));
front.z = sin(glm::radians(yawAngle)) * cos(glm::radians(pitchAngle));
front = glm::normalize(front);}
void Camera::moveForward(float speed){
position += speed * front;}
void Camera::moveBackward(float speed){
position -= speed * front;}
void Camera::moveLeft(float speed){
position -= glm::normalize(glm::cross(front, up)) * speed;}
void Camera::moveRight(float speed){
position += glm::normalize(glm::cross(front, up)) * speed;}
void Camera::moveUpward(float speed){
position.y += speed;}
This is how I implement it in main.cpp:
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
{
camera.moveUpward(cameraSpeed);
}
Can someone help me?
You can simulate a jump by applying an acceleration to the y-position. You'll need to add 3 new attributes to your struct: acceleration (vec3), velocity (vec3), and onGround (boolean).
void Camera::jump() {
// Apply a upwards acceleration of 10 units. You can experiment with
// this value or make it physically correct and calculate the
// best value, but this requires the rest of your program to
// be in well-defined units.
this->onGround = false;
this->acceleration.y = 10.0f;
}
void Camera::update() {
// Your other update code
// The acceleration should decrease with gravity.
// Eventually it'll become negative.
this->acceleration.y += GRAVITY; // Define 'GRAVITY' to a negative constant.
// Only update if we're not on the ground.
if (!this->onGround) {
// Add the acceleration to the velocity and the velocity to
// the position.
this->velocity.y += this->acceleration.y
this->position.y += this->velocity.y;
if (this->position.y <= 0) {
// When you're on the ground, reset the variables.
this->onGround = true;
this->acceleration.y = 0;
this->velocity.y = 0;
this->position.y = 0;
}
}
}
And in your event handling you'll call the jump method.
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
{
camera.jump();
}
However, this code probably shouldn't be on the camera, but instead on a Physics object of some sort. But it'll work.

UE4 C++: Can't move Actor in circular path

I am using Unreal Engine 4.25. I am trying to move an Actor, which is a sphere, in a circular path. However the sphere only moves back and forth in a straight line.
Here is MyActor.cpp code:
#include "MyActor.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
RootComponent = Root;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->AttachTo(Root);
Dimensions = FVector(30, 0, 0);
AxisVector = FVector(0.01, 0.01, 0);
Location = FVector(0.1, 0.1, 0.1);
Multiplier = 50.f;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Updates the angle of the object
AngleAxis += DeltaTime * Multiplier;
if (AngleAxis >= 360.0f)
{
AngleAxis = 0;
}
//Rotates around axis
FVector RotateValue = Dimensions.RotateAngleAxis(AngleAxis, AxisVector);
Location.X += RotateValue.X;
Location.Y += RotateValue.Y;
Location.Z += RotateValue.Z;
SetActorLocation(Location, false, 0, ETeleportType::None);
}
Please let me know if I need to add any more information.
Disclaimer: I am completely new to UE4. My apologies if it is a dumb question.

Opengl, simple animation

I'm trying to make simple animation in Opengl. My agenda is to move object in one direction. I'm doing this by using glm:translate:
int iModelViewLoc = glGetUniformLocation(program, "modelViewMatrix");
mModelView = glm::translate(mModelView,
glm::vec3(dxb1, 0.0, -12.41989));
glUniformMatrix4fv(iModelViewLoc, 1, GL_FALSE, glm::value_ptr(mModelView));
brama1->Draw();
And my function to animate this is by function which are activated by pressing key and:
for (int i = 0; i < 116; i++)
{
dxb1 = dxb1 + 0.05;
}
But I dont see animation, just first point of move and the last one. How can I change this to see animation?
you have to create an animation loop, and on every frame update the value. Even better, if on every frame, you calculate the desired position based on time. if you use glut:
int oldTimeSinceStart = 0;
while( ... )
{
int timeSinceStart = glutGet(GLUT_ELAPSED_TIME);
int deltaTime = timeSinceStart - oldTimeSinceStart;
oldTimeSinceStart = timeSinceStart;
//your part
dxb1 = dxb1 + deltaTime * 0.05;
}

Strange behaviour updating sprite position

I'm coding a simple roguelike game in C++ using SDL library, and I have some problems moving my character on the screen. Each time a frame needs to be rendered, I update the position of the sprite using the update() function, which does nothing if the player is standing still. To issue the movement command, and thus starting the animation, I use the step() function called only once per each player movement from one tile to another. Upon receiving the "up" command, the game behaves fine and the character moves smoothly in one second to the new position. However, when the "down" command is given, he moves at about half the speed, and obviously after one second has passed, he is instantly "teleported" to the final position, with a sudden flicker. The code for the movement is basically identical, but for the fact that in one case the delta movement is summed to the y position, in the other case is subtracted. Maybe the fact that the position is an integer and the delta is a double is causing problems? Does sum and subract behave differently (maybe different rounding)? Here is the relevant code (sorry for the length):
void Player::step(Player::Direction dir)
{
if(m_status != STANDING) // no animation while standing
return;
switch(dir)
{
case UP:
if(m_currMap->tileAt(m_xPos, m_yPos - m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
// if next tile is not a wall, set up animation
m_status = WALKING_UP;
m_yDelta = m_currMap->tileHeight(); // sprite have to move by a tile
m_yVel = m_currMap->tileHeight() / 1000.0f; // in one second
m_yNext = m_yPos - m_currMap->tileHeight(); // store final destination
}
break;
case DOWN:
if(m_currMap->tileAt(m_xPos, m_yPos + m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
m_status = WALKING_DOWN;
m_yDelta = m_currMap->tileHeight();
m_yVel = m_currMap->tileHeight() / 1000.0f;
m_yNext = m_yPos + m_currMap->tileHeight();
}
break;
//...
default:
break;
}
m_animTimer = SDL_GetTicks();
}
void Player::update()
{
m_animTimer = SDL_GetTicks() - m_animTimer; // get the ms passed since last update
switch(m_status)
{
case WALKING_UP:
m_yPos -= m_yVel * m_animTimer; // update position
m_yDelta -= m_yVel * m_animTimer; // update the remaining space
break;
case WALKING_DOWN:
m_yPos += m_yVel * m_animTimer;
m_yDelta -= m_yVel * m_animTimer;
break;
//...
default:
break;
}
if(m_xDelta <= 0 && m_yDelta <= 0) // if i'm done moving
{
m_xPos = m_xNext; // adjust position
m_yPos = m_yNext;
m_status = STANDING; // and stop
}
else
m_animTimer = SDL_GetTicks(); // else update timer
}
EDIT: I removed some variables and only left the elapsed time, the speed and the final position. Now it moves without flickering, but the down and right movements are visibly slower than the up and left ones. Still wonder why...
EDIT 2: Ok, I figured out why this is happening. As I supposed in the first place, there is a different rounding from double to integer when it comes to sum and subtraction. If I perform a cast like this:
m_xPos += (int)(m_xVel * m_animTimer);
the animation speed is the same, and the problem is solved.
Consider the following:
#include <iostream>
void main()
{
int a = 1, b = 1;
a += 0.1f;
b -= 0.1f;
std::cout << a << std::endl;
std::cout << b << std::endl;
}
During the implicit conversion of float to int when a and b are assigned, everything past the decimal point will be truncated and not rounded. The result of this program is:
1
0
You've said that m_yPos is an integer and m_yVel is a double. Consider what happens in Player::update if the result of m_yVel * m_animTimer is less than 1. In the UP case, the result will be that your sprite moves down one pixel, but in the DOWN case, your sprite won't move at all, because if you add less than one to an integer, nothing will happen. Try storing your positions as doubles and only converting them to integers when you need to pass them to the drawing functions.
A trick you can do to ensure rounding instead of truncation during conversion is to always add 0.5 to the floating point value during assignment to an integer.
For example:
double d1 = 1.2;
double d2 = 1.6;
int x = d1 + 0.5;
int y = d2 + 0.5;
In this case, x will become 1, while y will become 2.
I'd rather not do incremental calculations. This is simpler, will give correct results even if you go back in time, doesn't suffer from lost of precision, and will be just as fast, if not faster, on modern hardware:
void Player::step(Player::Direction dir)
{
// ...
case UP:
if(m_currMap->tileAt(m_xPos, m_yPos - m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
// if next tile is not a wall, set up animation
m_status = WALKING_UP;
m_yStart = m_yPos;
m_yDelta = -m_currMap->tileHeight(); // sprite have to move by a tile
m_tStart = SDL_GetTicks(); // Started now
m_tDelta = 1000.0f; // in one second
}
break;
case DOWN:
if(m_currMap->tileAt(m_xPos, m_yPos + m_currMap->tileHeight())->m_type == Tile::FLOOR)
{
m_status = WALKING_DOWN;
m_yStart = m_yPos;
m_yDelta = m_currMap->tileHeight();
m_tStart = SDL_GetTicks(); // Started now
m_tDelta = 1000.0f; // in one second
}
break;
// ...
}
void Player::update()
{
auto tDelta = SDL_GetTicks() - m_tStart;
switch(m_status)
{
case WALKING_UP:
case WALKING_DOWN:
m_yPos = m_yStart + m_yDelta*tDelta/m_tDelta; // update position
break;
default:
break;
}
if(tDelta >= m_tDelta) // if i'm done moving
{
m_xPos = m_xStart + m_xDelta; // adjust position
m_yPos = m_yStart + m_yDelta;
m_status = STANDING; // and stop
}
}

My sequence of CCActions slow down after changing action type

I am creating a sequence of actions as play back to recorded gestures. If I only pan the sequence it plays at full speed. If I only rotate, it plays at full speed. If I only scale it plays at full speed. If, however, I pan then rotate, rotate plays back about half speed. If I rotate then pan, pan runs at about half speed. Here is the play function. RecordData is just an object with the time, position, rotation, scale information.
-(void) Play
{
int count = [m_MoveTos count];
if (count < 2)
{
return;
}
[m_Actor stopAllActions];
[m_Actions removeAllObjects];
RecordData* start = [m_MoveTos objectAtIndex:0];
m_Actor.position = start.Pos;
m_Actor.rotation = start.Rotate;
m_Actor.scale = start.Scale;
NSLog(#"Starting with %d nodes.", [m_MoveTos count]);
NSLog(#"Actor Start Position:%f, %f Rotation:%f, Scale:%f", m_Actor.position.x, m_Actor.position.y, m_Actor.rotation, m_Actor.scale);
float lastTime = 0;
RecordData* lastData = start;
//Skip start
for(int i = 1; i < count; ++i)
{
bool delay = true;
RecordData* rData = [m_MoveTos objectAtIndex:i];
float delta = rData.Time - lastTime;
if(rData.Pos.x != lastData.Pos.x || rData.Pos.y != lastData.Pos.y)
{
CCAction* moveAction = [CCMoveTo actionWithDuration:delta position:rData.Pos];
[m_Actions addObject:moveAction];
delay = false;
}
if (rData.Rotate != lastData.Rotate)
{
// Put this in Record data
float degrees = rData.Rotate;
int sign = degrees/ABS(degrees);
degrees = ABS(degrees);
while (degrees > 360)
{
degrees -= 360;
}
degrees *= sign;
NSLog(#"Rotation %f", degrees);
CCAction* rotAction = [CCRotateTo actionWithDuration:delta angle:degrees];
[m_Actions addObject:rotAction];
delay = false;
}
if (rData.Scale != lastData.Scale)
{
CCAction* scaleAction = [CCScaleTo actionWithDuration:delta scale:rData.Scale];
[m_Actions addObject:scaleAction];
delay = false;
}
if(delay)
{
CCActionInterval* delayTime = [CCDelayTime actionWithDuration:delta];
[m_Actions addObject:delayTime];
}
delay = true;
lastTime = rData.Time;
}
if([m_Actions count] < 2)
{
return;
}
CCAction* seq = [CCSequence actionsWithArray:[m_Actions getNSArray]];
[m_Actor runAction:seq];
}
Is there something I'm missing?
Thanks,
Jim
Mixing action types seems to be the problem. I now have a different action sequence for position, rotation, and scale. It runs fine now.