I've faced with The white square problem at SFML C++ while I'm trying to display a textured button. I've got an ImageButton.h which inherits from Button.h. The texture is loaded successfully (checked in debugger). But in the end, I see a white square. How to solve it?
Button.h
#ifndef BUTTON_H
#define BUTTON_H
#include<SFML/Graphics.hpp>
class Button
{
public:
Button();
Button(sf::Vector2f size, sf::Vector2f pos,sf::Color outlineColor, float sizeOutline);
void virtual draw(sf::RenderWindow* w) = 0;
protected:
sf::RectangleShape frame;
};
#endif // !BUTTON_H
Button.cpp
#include "Button.h"
Button::Button()
{
}
Button::Button(sf::Vector2f size, sf::Vector2f pos,sf::Color outlineColor, float sizeOutline)
{
frame.setPosition(pos);
frame.setSize(size);
frame.setOutlineColor(outlineColor);
frame.setOutlineThickness(sizeOutline);
}
ImageButton.h
#ifndef IMAGE_BUTTON_H
#define IMAGE_BUTTON_H
#include"Button.h"
class ImageButton : public Button
{
public:
ImageButton();
ImageButton(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline, std::string path);
void draw(sf::RenderWindow* w);
private:
sf::Texture backTexture;
sf::Sprite background;
};
#endif // !IMAGE_BUTTON_H
ImageButton.cpp
#include "ImageButton.h"
ImageButton::ImageButton()
{
}
ImageButton::ImageButton(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline, std::string path)
: Button(size,pos,outlineColor,sizeOutline)
{
backTexture.loadFromFile(path, sf::IntRect(sf::Vector2i(pos.x,pos.y),sf::Vector2i(size.x,size.y)));
backTexture.setSmooth(true);
background.setTexture(backTexture);
background.setPosition(pos);
}
void ImageButton::draw(sf::RenderWindow* w)
{
w->draw(this->background);
w->draw(this->frame);
}
programm.h
#ifndef PROGRAMM_H
#define PROGRAMM_H
#include<SFML/Graphics.hpp>
#include"ImageButton.h"
class programm
{
public:
programm();
void run();
private:
ImageButton b;
sf::RenderWindow* window;
sf::Event e;
void render();
void update();
};
#endif // !PROGRAMM_H
programm.cpp
#include "programm.h"
programm::programm()
{
this->window = new sf::RenderWindow(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
this->window->setPosition(sf::Vector2i(0, 0));
this->window->setFramerateLimit(60);
this->b = ImageButton(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "images\\putin.png");
}
void programm::run()
{
while (this->window->isOpen())
{
while (this->window->pollEvent(e))
{
update();
}
}
}
void programm::render()
{
this->window->clear();
b.draw(this->window);
this->window->display();
}
void programm::update()
{
switch (e.type)
{
case sf::Event::Closed:
{
this->window->close();
break;
}
default:
break;
}
render();
}
Screenshot
programm::programm()
{
...
this->b = ImageButton(...); //< this line cause the bug
}
You are initializing your image button this->b by assigning from a local ImageButton. Now the sprite will have the texture reference of the local instance's texture, and when the local instance will "die" the texture will be freed. You need to maintain the lifetime of the texture
Solution 1: override the assignment operator and set texture there like this
ImageButton& operator=(const ImageButton& ref) {
backTexture = ref.backTexture;
background = ref.background;
background.setTexture(backTexture);
return *this;
}
Solution 2: Create a TextureManager and make it as your texture API for all your program, maintain your texture lifetime through it.
Solution 3: initialize the image button at programm constructor
programm::programm()
:b (sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "img.jpg")
{
...
}
Another bug
void ImageButton::draw(sf::RenderWindow* w)
{
w->draw(this->background);
w->draw(this->frame);
}
you're drawing your frame after the background, and your frame's fill color by default is white
Solution 1: draw frame first and draw background next
Solution 2: set your frame fill color alpha to 0
Button::Button(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline)
{
frame.setPosition(pos);
frame.setSize(size);
frame.setOutlineColor(outlineColor);
frame.setFillColor(sf::Color(0, 0, 0, 0)); // <----
frame.setOutlineThickness(sizeOutline);
}
The problem you and others (on reddit,
on SO,
and elsewhere)
are facing is caused by the way the constructor and assignment operator of
sf::Sprite are (not) implemented: Since the developers decided to not
implement specialized functions and didn't make them private, the compiler
provides defaults that are not aware of the semantics of the m_texture
pointer and therefore doing a binary copy. This may be not the best approach but
it's documented.
So if you are using a sf::Sprite member in your class without taking
special measures for copying, the compiler will assume default assignment
semantics and therefore invoke the (invalid) assignment semantics of the
sf::Sprite class. The local ImageButton object you create in the
program invokes this default assignment of ImageButton
programm::programm()
{
this->window = new sf::RenderWindow(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
this->window->setPosition(sf::Vector2i(0, 0));
this->window->setFramerateLimit(60);
this->b = ImageButton(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "images\\putin.png");
}
the Sprite in member b takes over the pointer texture from the
Sprite within the local ImageButton whose lifetime is limited to the
scope of the programm constructor. Beyond that, the sprite in your
member b holds a reference to a destroyed texture object which causes
is undefined behaviour (see also sf::Sprite Class Reference (SFML / Learn / 2.4.1 Documentation)):
If the source texture is destroyed and the sprite tries to use it,
the behavior is undefined.
By the way, I'd suggest directly using sf::RenderWindow window in the
program class not via pointer, so you could change your code to something
like this (note that I used button instead of b and shortened inlined
the implementation for the sake of brevity).
class programm
{
public:
programm():
window(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
button(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f),
sf::Color::Yellow, 5.f, "images\\putin.png")
{
window->setPosition(sf::Vector2i(0, 0));
window->setFramerateLimit(60);
}
// ...
private:
// ...
sf::RenderWindow window;
ImageButton button;
};
Another option would be to provide a specialized assignment operator
(thakee nathees shows how)
or copy constructor for your ImageButton class that takes care of the
deep copy of the texture object.
Alternatively, you could think about implementing a wrapper class for
sf::Sprite to fix this issue and use this instead for you application(s).
Related
I'm working on a custom project to learn SFML and I have a problem. I want to do a 'clickable' class where I could add some fancy function, my first goal to write an 'onMouseOver' function which could be nearly the same as in Javascript. This is the class:
template <class object>
class clickable: public object{
private:
sf::Vector2i mouse;
object& aim;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{ target.draw(aim, states); }
public:
clickable(sf::Vector2i mouse,object& aim):mouse(mouse),aim(aim){}
bool onMouseOver(sf::Vector2i mouse) {
float width = aim.getLocalBounds().width;
float height = aim.getLocalBounds().height;
if ((mouse.x >= aim.getPosition().x) && (mouse.x <= aim.getPosition().x + width) && (mouse.y >= aim.getPosition().y) && (mouse.y <= aim.getPosition().y + height))
return true;
else return false;
}
};
And here is my main:
int main()
{
sf::Font font;
if (!font.loadFromFile("arial.ttf")) {
std::cout << "Error!";
}
sf::RenderWindow window(sf::VideoMode(800, 600), "Title", sf::Style::Default);
sf::Vector2i mouse = sf::Mouse::getPosition(window);
vector<sf::Text> objects;
objects.push_back(sf::Text("Test text.", font, 14));
objects[0].setPosition(20, 20);
clickable<sf::Text> stuff(mouse,objects[0]);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
if (event.type == sf::Event::MouseMoved) {
eger = sf::Mouse::getPosition(window);
}
}
window.clear();
window.draw(stuff);
if (stuff.onMouseOver(mouse)) objects[0].setFillColor(sf::Color::Red);
else objects[0].setFillColor(sf::Color::White);
window.display();
}
return 0;}
I tested this code (with different variable names, because I'm from Hungary) and it worked. My problem is that if I want to change the ' objects[0].setFillColor( sf::Color::Red ) ' to 'stuff.setFillColor(sf::Color::Red)' nothing happens on the screen. I assume that the problem is that I want to call an inherited parent-function from the child class, but I am not sure what could I do to solve the problem.
I hope it was understandable and sorry for the possible bad grammar, but I'm not a native speaker, as I mention above. Any help would be very appreciated!
Thanks in advance!
I have a very fast and easy quick fix for your problem. Simply add this line of code in your draw member function of your clickable class, before calling target.draw(aim, states); :
aim.setFillColor(this->getFillColor());
like this :
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
aim.setFillColor(this->getFillColor());
target.draw(aim, states);
}
or you can overload both setFillColor and getFillColor member functions :
void setFillColor(const sf::Color& c) {
aim.setFillColor(c);
}
const sf::Color& getFillColor() {
return aim.getFillColor();
}
Explanations :
You declared a variable stuff like this : clickable<sf::Text> stuff(mouse, objects[0]);
According to your template class definition, that means stuff is a clickable that inherits from sf::Text, so it's a clickable sf::Text, it's also a sf::Text.
Looking at the fields of your clickable class :
a sf::Vector2i mouse;
a object& aim;, the field aim is a reference at a object type, where object is your template parameter
so, your variable stuff holds all the data that a sf::Text object would hold, since stuff is a sf::Text, a sf::Vector2i, and a reference to a sf::Text object, which is not himself. So here, your stuff object can access 2 different sf::Text objects. Himself and the one referenced by aim.
You draw member function code shows that it draw the field aim, so the sf::Text referenced, and not the data held by himself
When, in your code, you do
objects[0].setFillColor( sf::Color::Red );
You call setFillColor method on the object that is referenced by the aim field of the stuff object, whereas that :
stuff.setFillColor(sf::Color::Red);
Changes the stuff object itself. And your draw function will draw the object referenced by the stuff object, but don't know at all modifications applied to the stuff object
So the solution is simple : all modifications applied to your stuff object need to be applied on the referenced object
You can do that when you want to draw your object ( in the draw method ) or when you ask to apply modifications ( when you call setFillColor method for example ).
By the way, Alex is right on the fact that your class is not really well designed. You can also not hold reference on sf::Text object and call the sf::Text draw method for (*this) ( or just don't overload the draw method in the clickable object and do proper copy of the sf::Text in your object )
You have two different objects of type sf::Text:
stuff
stuff's data member called aim
Calling the setFillColor method of stuff will set stuff's fill color, not aim's. But the draw method of stuff defers completely to the draw method of aim:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{ target.draw(aim, states); }
aim is ignorant of stuff's fill color, so window.draw(stuff) will do exactly the same thing as before.
The solution is to redesign your clickable class, and that depends on your intentions. Why do you want to both inherit from the template class and have a data member of that class type?
The simplest thing to do right now is probably to reimplement the setFillColor method of sf::Text as a new method of clickable:
void clickable::setFillColor( const Color & color ) {
aim.setFillColor( color );
}
Thanks you both of you, now I got it I understand! Both of explanations were good. I redesigned the class, for the following stucked programmers here is my solution:
template <class object>
class clickable: public object{
public:
clickable(const string& s, const sf::Font& f, unsigned int size) : object::Text(s, f, size) {}
void cmd_out() {
cout << (string) (*this).getString();
}
void graph_out(float x = 0, float y = 0) {
}
bool onMouseOver(const sf::Vector2i& eger) {
float width = (*this).getLocalBounds().width;
float height = (*this).getLocalBounds().height;
if ((eger.x >= (*this).getPosition().x) && (eger.x <= (*this).getPosition().x + width) && (eger.y >= (*this).getPosition().y) && (eger.y <= (*this).getPosition().y + height))
return true;
else return false;
}
};
Now it's working, but I can only make with this sf::Text clickable objects, but in case if I would need any other, it would be easy to add another contructor based on which is required.
I have a vector of Shapes, Shape being a class I wrote. In the keyDown function, I iterate through this vector of Shapes and update the bool property, background, to true. However, it appears not to be persisting this change.
Main class:
vector<Shape> mTrackedShapes;
void CeilingKinectApp::keyDown( KeyEvent event )
{
// remove all background shapes
if (event.getChar() == 'x') {
for (Shape s : mTrackedShapes) {
s.background = true;
}
}
}
Shape.h
#pragma once
#include "CinderOpenCV.h"
class Shape
{
public:
Shape();
int ID;
double area;
float depth;
cv::Point centroid; // center point of the shape
bool matchFound;
bool moving;
bool background;
cinder::Color color;
int stillness;
float motion;
cv::vector<cv::Point> hull; // stores point representing the hull of the shape
int lastFrameSeen;
};
Shape.cpp
#include "Shape.h"
Shape::Shape() :
centroid(cv::Point()),
ID(-1),
lastFrameSeen(-1),
matchFound(false),
moving(false),
background(false),
stillness(0),
motion(0.0f)
{
}
It registers the keyDown event, and correctly iterates through the vector, but the background property remains false. What am I doing wrong?
Try
for (Shape &s : mTrackedShapes)
Your code would make a copy of the object, and you would change the properties on the copy rather than the one in the vector
I'm currently trying to write a button class. It takes 2 textures, m_texture and m_onHover, and its caption, which should be automatically centered. The function update() takes care of selecting the correct texture.
class button : public sf::Drawable
{
private:
const sf::Texture *m_texture;
const sf::Texture *m_onHover;
sf::Sprite m_sprite;
public:
button();
sf::Text m_caption; // public to allow easy formating, see centerCaption()
bool mouseIsOver() const;
void update();
void setPosition(sf::Vector2f position);
void setPosition(float x, float y);
void centerCaption();
// Access functions
void setTexture(const sf::Texture &texture) { m_texture = &texture; m_sprite.setTexture(*m_texture); }
void setonHoverTexture(const sf::Texture &texture) { m_onHover = &texture; }
void setCaption(sf::String text) { m_caption.setString(text); centerCaption(); }
void setFontSize(unsigned int size) { m_caption.setCharacterSize(size); centerCaption(); }
void setFont(sf::Font& font) { m_caption.setFont(font); }
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
};
bool button::mouseIsOver() const
{
if (m_sprite.getGlobalBounds().contains(sf::Vector2f(sf::Mouse::getPosition()))) // creating a float vector for contains() because getPosition gives int vector
{
return true;
}
else
{
return false;
}
}
Everything seems to be working, but the mouse position at which mouseIsOver() returns true seems to be moved 40 pixels above the sprite. The values in the rect from getGlobalBounds() seem to be correct when printed in the console.
Unluckily I dont have enough reputation to post a screenshot.
The cursor position should be translated to the proper coordinate system. Basically you need to use sf::RenderTarget::mapPixelToCoords (available in sf::Renderwindow by inheritance). For more details, have a look at the documentation and §Coordinates conversions of the official tutorial.
Also, you might want to consider making your button class inherit from sf::Transformable so that you don't have to manage the position/rotation/scale/... yourself. Have a look at Creating a SFML-like entity
I am trying to implement a Class window (with render and window), and a class Draw (of texture), and in order to draw i need to have a references to a render, but sins they are going to be generic classes, i can not marry my draw class to a single render, so i am trying to send a references to draw, of the render in window, so it can draw on the render...
i have something like this...
class SDLWindow{
public:
SDLWindow(std::string Title,int h=480, int w=640, int x=SDL_WINDOWPOS_CENTERED, int y=SDL_WINDOWPOS_CENTERED, Uint32 flag =SDL_WINDOW_SHOWN );
void RendererOptions(Uint8,Uint8,Uint8,Uint8);
void RendererClear();
void RendererPresent();
SDL_Renderer* ref2Window();
~SDLWindow();
private:
SDL_Window* DispWin;
SDL_Renderer* DispRenderer;
};
and i want this to be the function that helps me give a references of the render
SDL_Renderer* SDLWindow::ref2Window(){
return DispRenderer;
}
this is the draw class
class SimpleDrawText{
public:
SimpleDrawText(std::string file,SDL_Renderer& ref, int w=100,int h=100, int x=0, int y=0);
void Draw(SDL_Renderer& ref);
protected:
private:
SDL_Texture* drawTexture; // the new SDL_Texture variable
SDL_Rect source; // the first rectangle
SDL_Rect destination; // another rectangle
int posx;
int posy;
int height;
int width;
};
and i am i get the references something like this
void SimpleDrawText::Draw(SDL_Renderer& ref){
SDL_RenderCopy(&ref, drawTexture, &source, &destination);
}
i am sure, there is something very basic, of pointers and references, but i just can't figure out right now... any help?
void SimpleDrawText::Draw(SDL_Renderer* ref){
SDL_RenderCopy(ref, drawTexture, &source, &destination);
}
This is what you want to do i believe. Hope it helps.
I've been trying to write something that will let me easily manage OpenGL using classes.
I took an approach of having a Drawable class [which shapes/etc would inherit from, and override a draw function], and then using a Controller class to iterate through a table of Drawable classes and draw them all.
The only issue that I've noticed that the draw() method is being called from the Drawable class, instead of the Rectangle class. ??
class Drawable {
public:
void draw();
};
class Rectangle : public Drawable {
public:
void draw();
};
class Controller {
public:
Drawable ents[200];
int ent_count;
void frame();
void append(Drawable item); // this will add an object onto the list
Controller();
};
void Drawable::draw() {
// this is the default drawing function, which should be overridden
}
void Rectangle::draw() {
// gl functions here
}
void Controller::frame() {
for(int i=0;i<ent_count,i++) {
ents[i].draw(); // draw all entities on the list
}
// here, a timer would loop back the frame() function
}
void Controller::append(Drawable item) {
ents[ent_count++]=item;
}
int main(void) {
Controller main_controller; // create a controller
Rectangle rect; // create a rectangle
main_controller.append(rect); // insert rectangle into controller list
main_controller.frame(); // start the frame loop
}
[if there are minor typing errors in that, it is because it was written as a summary of the method.]
This method that I've tried to use has not been very successful, and I'm pretty sure it has to do with inheritance.
Any ideas?
Entire source code:
#include <iostream>
#include <GL/glfw.h>
#include <GL/gl.h>
class Drawable {
public:
int x,y;
void draw();
void frame();
void create();
void destroy();
};
void Drawable::create() {
}
void Drawable::draw() {
}
class Rectangle : public Drawable {
public:
int w,h;
unsigned short r,g,b;
Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b);
void draw();
};
void Rectangle::draw() {
glColor3ub(r,g,b);
glBegin(GL_QUADS);
glVertex2i(x,y);
glVertex2i(x+w,y);
glVertex2i(x+w,y+h);
glVertex2i(x,y+h);
glEnd();
}
Rectangle::Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b) {
this->x=x;
this->y=y;
this->w=w;
this->r=r;
this->g=g;
this->b=b;
}
class Controller {
public:
Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title);
bool running;
int frame_limit;
Drawable entity[200];
int entity_count;
void fev();
void begin();
void bind();
void append(Drawable item);
};
Controller::Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title) {
int fullscreen= (fs ? GLFW_FULLSCREEN : GLFW_WINDOW);
bool window=glfwOpenWindow(w,h,0,0,0,0,10,10,fullscreen);
glfwSetWindowTitle(title);
frame_limit=120;
entity_count=0;
std::cout << (window ? "Successfully initialized a window.\n" : "Error initializing window!\n");
}
void Controller::begin() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,640,480,0,0,5);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0.4f,0.4f,0.4f,1.0f);
running=true;
fev();
}
void Controller::append(Drawable item) {
entity[entity_count++]=item;
}
void Controller::fev() {
glClear(GL_COLOR_BUFFER_BIT);
for (int i=0;i<entity_count;++i) {
entity[i].draw();
}
glfwSwapBuffers();
if (frame_limit>0) {
glfwSleep(1000/frame_limit*0.001);
}
if (running) {
if (glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED)) {
running=false;
}
fev();
} else {
std::cout << "terminated!";
}
}
int main(void) {
glfwInit();
Controller main(640,480,0,false,false,"WindTitle");
Rectangle rect(50,50,50,50,50,50,50);
main.append(rect);
main.begin();
}
I've been trying to write something that will let me easily manage OpenGL using classes.
Newbies often try this. But OpenGL really doesn't translate well into OOP. The problem is, that it's a finite state machine and to map properly to OOP you'd have to to a lot of state tracking between the different classes and instances.
I myself tried at least 3 times to abstract OpenGL into a OOP scheme. It always broke in some way.
Which is not to say that you can not use OOP with OpenGL. You can't just map OpenGL concepts 1:1 into classes.
Regarding your actual problem: Use virtual functions.
As others have mentioned, it would be best to try to use some of the existing wrappers.
That said, you need to use pointers for your list of entities. You are having issues with slicing.
As some of the comments mentioned, you need to make Drawable::draw() virtual so calling draw() on a Drawable will call through to the child implementation. That said, because you are adding your Drawables to a an list of objects, instead of a list of pointers to objects, the objects are being sliced. This means that they are being converted from the child type back into Drawable objects, removing the extra information about specific types.
So instead of this:
Drawable ents[200];
You should do something like this:
std::vector<Drawable*> ents;
or if you have C++11 or Boost:
std::vector<std::shared_ptr<Drawable>> ents;
And your append method would take a reference.
void Controller::append(Drawable &item) {
ents[ent_count++] = &item;
}
or
void Controller::append(Drawable &item) {
ents.push_back(&item);
}
or
void Controller::append(std::shared_ptr<Drawable> item) {
ents.push_back(item);
}
And your render loop would be like this:
for (int i = 0; i < ents.size(); ++i) {
ents[i]->draw();
}
This loop could also be cleaned up to use iterator.
You might want to have a look at Coin3D. It is a large rich set of C++ class wrappers around OpenGL. You clould either look at how they do things of just use it. There are libraries for integration with various windowing systems (Qt, Windows, Cocoa, ...).