Solved OpenGl call error, but why does it reappear when I integrate it into my object-oriented approach?

  animation, c++, opengl, sprite

I have been working on a personal project to learn how to program games in C++ with the SFML library. Currently, I am trying to update a character sprite based on arrow key pressed (pressing the left arrow should make the character turn left by switching to a different sprite, and so on) with the goal of implementing idle and walking animations. Currently, I am getting the following error:

An internal OpenGL call failed in Texture.cpp<98>.
Expression:
    glFlush()
Error description:
    GL_INVALID_OPERATION
    The specified operation is not allowed in the current state.

By stripping away the class files and replacing the methods with functions in Test.cpp, I managed to get rid of the error and make the program work correctly. However, when I tried to integrate that code into my class files, I ended up with the same error, and the sprite not updating properly. Here is the working (but not very object-oriented) code:

#include "Character.h"
#include "Player.h"
#include "Test.h"

std::map<std::pair<State, Direction>, Sprite> setAnimSprites(Texture* texture_, std::map<std::pair<State, Direction>, Sprite> animSprites_);
Sprite updateIdleSprite(Direction dir, std::map<std::pair<State, Direction>, Sprite> animSprites_);

int main()
{
    VideoMode vm(1920, 1080);
    RenderWindow window(vm, "Test", Style::Fullscreen);

    Clock clock;

    while (window.isOpen())
    {
       
        if (Keyboard::isKeyPressed(Keyboard::Escape)) window.close();
       
        Time dt = clock.restart();
      
        Texture texture;
        Image img;
        img.loadFromFile("graphics/eevee.png");
        img.createMaskFromColor(Color(0, 128, 128), 0);
        texture.loadFromImage(img);

        Sprite sprite;
        sprite.setTexture(texture);
        std::map<std::pair<State, Direction>, Sprite> animSprites_;

        animSprites_ = setAnimSprites(&texture, animSprites_);
        sprite = updateIdleSprite(DOWN, animSprites_);

        window.clear();
        window.draw(sprite);
        window.display();
    }

}

std::map<std::pair<State, Direction>, Sprite> setAnimSprites(Texture* texture_, std::map<std::pair<State, Direction>, Sprite> animSprites_)
{
    Direction directions[8] = { DOWN, UP, LEFT, RIGHT, LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP };

    // determine how many IDLE, MOVING, SLEEP sprites how many for each direction
    for (unsigned int i = 0; i < 2; i++) // repeats once for IDLE and once for MOVING
    {
        State currState;
        unsigned int currX, currY;
        if (!i) // boundary?? 
        {
            currState = IDLE; // IDLE on first run
            currX = IDLE_START_X;
            currY = IDLE_START_Y;
        }
        else {
            currState = MOVING; // MOVING on second
            currX = MOVE_START_X;
            currY = MOVE_START_Y;
        }

        // gets row for specific DIRECTION animation for current STATE
        for (unsigned int j = 0; j < 8; j++)
        {
            Sprite row;
            row.setTexture(*texture_);
            row.setTextureRect(IntRect(currX, currY + (SPRITE_SIDE_LEN * j),
                2 * SPRITE_SIDE_LEN, SPRITE_SIDE_LEN)); // SAME as below
            row.setScale(3, 3);
            animSprites_[make_pair(currState, directions[j])] = row;

        }
    }

    Sprite sleepChunk;
    sleepChunk.setTexture(*texture_);
    sleepChunk.setTextureRect(IntRect(SLEEP_START_X,
        SLEEP_START_Y, 3 * SPRITE_SIDE_LEN, SPRITE_SIDE_LEN));
    sleepChunk.setScale(3, 3);
    animSprites_[make_pair(ASLEEP, NONE)] = sleepChunk;

    return animSprites_;
}


Sprite updateIdleSprite(Direction dir, std::map<std::pair<State, Direction>, Sprite> animSprites_) // CURRENTLY JUST FOR IDLE
{
    Sprite s;
    s = animSprites_[make_pair(IDLE, dir)];
    s.setTextureRect(IntRect(s.getTextureRect().left, s.getTextureRect().top, s.getTextureRect().width / 2, s.getTextureRect().height));
    //cout << "top: " << s.getGlobalBounds().top << "left: " << s.getGlobalBounds().left << "height: " << s.getTextureRect().
    return s;
}

And here is the object-oriented, error-producing code: (note: I have a Character base class with an Animation sub-object (containing data members for containing and selecting animation sprites) and a Player derived class))

Test.h (main file header):

#pragma once

#include <SFML/Graphics.hpp>
using namespace sf;

#define SCRN_H (1080)
#define SCRN_W (1920)

enum Direction { LEFT, RIGHT, UP, DOWN, LEFT_UP, RIGHT_UP, LEFT_DOWN, RIGHT_DOWN, NONE };
enum State { IDLE, MOVING, ASLEEP };

Test.cpp (main file):

int main()
{

    // setting up window code //
    
    Player player = Player(SCRN_W / 2, SCRN_H / 3);

    Clock clock;

    // game loop
    while (window.isOpen())
    {

        Event event;

        while (window.pollEvent(event))
        {
            // --- I believe the error is happening somewhere in this block --- //
            if (event.type == Event::KeyPressed)
            {
                player.setState(MOVING);
                // updates orientation and direction vector based on arrow key presses
                player.updateDirection();
                // updates current sprite to be drawn
                player.getAnimation().updateIdleSprite(player.getOrientation());
            }
            if (event.type == Event::KeyReleased)
            {
                player.setState(IDLE);
                // sets direction vector to (0,0) to stop movement
                player.zeroDirection();
            }
            // ---------------------------------------------------------------- //
        }

        Time dt = clock.restart(); // update time

        window.clear();
        window.draw(player.getAnimation().getSprite());

        window.display();
    }
}

Character.h:

#pragma once

#include "Test.h"
#include "Animation.h"
#include <map>
using namespace std;

class Character {
private:
    std::string imgRef_;
    Animation animation_;
    Direction orientation_ = DOWN; // direction character faces  (default is DOWN)
    State state_ = IDLE; // IDLE, MOVING, ASLEEP
    Vector2f directionVect_ = { 0 , 0 }; // vector interpretation of orientation_
    float speed_ = 0;

public:
    //https://stackoverflow.com/questions/14693745/string-as-parameter
    Character(float x_start, float y_start, std::string imgPath);

    void setOrientation(Direction new_orient);
    Direction getOrientation();
    State getState();
    void setState(State new_state);
    Animation getAnimation();
};

Character.cpp:


#include "Test.h"
#include "Character.h"


Character::Character(float x_start, float y_start, std::string imgPath) {
    updatePosition(x_start, y_start);
    imgRef_ = imgPath;
    animation_.setTexture(imgPath);
    animation_.getSprite().setPosition(position_);
}

// set_... get_... method definition (just simple returns or assignments) //

Player.h:

#pragma once

#include "Test.h"
#include "Character.h"

class Player : public Character
{
private:
    std::string text_loc = "graphics/eevee.png";

public:
    Player(float new_x, float new_y);
    void updateDirection();
};

Player.cpp:


#include "Character.h"
#include "Player.h"

Player::Player(float new_x, float new_y) : Character(new_x, new_y, text_loc)
{
    Character::getAnimation().setTexture(text_loc);
};

// updates orientation_ and direction vector based on key presses
void Player::updateDirection()
{
    if (Keyboard::isKeyPressed(Keyboard::Left))
    {
        setOrientation(LEFT);
        Character::set_directionVect(-1.00, Character::get_directionVect().y);
    }

    if (Keyboard::isKeyPressed(Keyboard::Right))
    {
        setOrientation(RIGHT);
        Character::set_directionVect(1.00, Character::get_directionVect().y);
    }

    if (Keyboard::isKeyPressed(Keyboard::Up))
    {
        setOrientation(UP);
        Character::set_directionVect(Character::get_directionVect().x, -1.00);
    }

    if (Keyboard::isKeyPressed(Keyboard::Down))
    {
        setOrientation(DOWN);
        Character::set_directionVect(Character::get_directionVect().x, 1.00);
    }
}

Animation.h:

#pragma once

#include "Test.h"
#include <map>
using namespace std;

class Animation
{
private:
    Texture texture_;
    Image textBack_; // updated to hold masked spritesheet
    Sprite sprite_;

    // stores sequence of sprites for each animation,
    // paired with keys for state and direction
    std::map<std::pair<State, Direction>, Sprite> animSprites_;

public:
    Animation();
    void setTexture(std::string imgPath);
    Texture getTexture();

    Sprite getSprite();
    void updateIdleSprite(Direction dir);
    void set_animSprites(Texture* texture);
};

Animation.cpp:


#include "Animation.h"

Animation::Animation()
{
    set_animSprites(&texture_);
    updateIdleSprite(RIGHT); // this command actually works
}

// sets the current sprite given STATE and DIRECTION
// (currently just for IDLE state)
void Animation::updateIdleSprite(Direction dir)
{
    sprite_ = animSprites_[make_pair(IDLE, dir)];
    sprite_.setTextureRect(IntRect
                                (sprite_.getTextureRect().left,
                                 sprite_.getTextureRect().top,
                                 sprite_.getTextureRect().width / 2,
                                 sprite_.getTextureRect().height));
}

I think the updateIdleSprite is working since I see the sprite facing right due to the updateIdleSprite(RIGHT) call in the Animation constructor when I run the program. I confirmed that the updateDirection method in player is correctly updating the Character’s orientation_ as well.

Also, I apologize for the length of the code included in this post- I am working on creating a shorter version.

Source: Windows Questions C++

LEAVE A COMMENT