I want my game engine to be nice and tidy and rather than coding a bunch of redundant lines, I would rather include functions that do this for me.
void draw::image(){
SDL_Surface *bmp = SDL_LoadBMP("C:\\Users\\Joe\\Documents\\Visual Studio 2013\\Projects\\SDL_APP1\\map1.bmp");
SDL_Texture*bmptx;
SDL_Renderer * renderer = SDL_CreateRenderer(_window, -1, 0);
bmptx = SDL_CreateTextureFromSurface(renderer, bmp);
SDL_FreeSurface(bmp);
SDL_RenderCopy(renderer, bmptx, NULL, NULL);
SDL_RenderPresent(renderer);
}
void draw::text(string q, float x, float y, float w, float h){
SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255);
SDL_RenderDrawLine(renderer, x, y, w, h);
SDL_RenderPresent(renderer);
}
My problem is that I don't know how to format the text() function so that it draws to the same renderer as the image() function. I want it so that in my main loop, I can call both the text and the image functions and they will draw to the same renderer at the same time. This way I wont have to directly code these into my main loop. Side note I am using SDL 2.
You would want to pass a pointer to the renderer you previously created (probably in main()) to each of your drawing functions. Creating a new renderer for every object you draw would be very inefficient.
void draw::image(SDL_Renderer *renderer)
{
// ...
}
void draw::text(SDL_Renderer *renderer, string q, float x, float y, float w, float h)
{
SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255);
// ...
}
Then, within your main() after you've created the window, you'd create your renderer:
renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
and then pass it to your drawing functions. Assuming your methods are static, then:
draw::image(renderer);
or if you have an object on the stack:
myDrawObject.image(renderer);
or if you have a pointer to one of your draw objects:
myDrawPtr->image(renderer);
Also, you probably need to think about this approach a bit more. Presumably you want to draw these things every frame, so with your current approach you are requiring SDL to load the BMP file from disk (or disk cache) and convert it to whatever internal format it requires for the texture, every single frame. It's unlikely this will give you acceptable performance, especially when you want to render multiple images per frame.
More than likely, a better approach will be to store some of these resources in member variables within your draw class. But this is basic C++ stuff, so you might want to do some more reading and tutorials around the subject of classes.
Related
Are there layers to sdl or something?
by layers I mean like in photoshop we have multiple layer and can draw on one without effecting the other,
for example if I had a main_layer , a background_layer & an enemy_layer where the main player reandering (like moving the character by user), a static background rendering & enemies rendering can take place respectively?
instead of having to clear the entire screen then placing everything back again over and over? i.e. changing a single thing without effecting the other? can someone point me in the right direction?
You can implement your own layer system using render targets.
Create a texture render target for each layer.
Draw to a layer's render target to update it.
Every frame, draw each layer to the screen. You still need to clear the final frame beforehand.
It's worth noting that there is a point of diminishing return here. If a layer only contains a few sprites, it's probably cheaper to draw each sprite directly to the screen every frame even if they don't move.
Example:
// Given a renderer
SDL_Renderer *renderer = ...;
// *** Creating the layer ***
SDL_Texture *my_layer = SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET,
screen_width, screen_height);
// To make transparency work (for non-base layers):
SDL_SetTextureBlendMode(my_layer, SDL_BLENDMODE_BLEND);
// *** Drawing TO the layer ***
SDL_SetRenderTarget(renderer, my_layer);
// For non-base layers, you want to make sure you clear to *transparent* pixels.
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
// ... Draw to the layer ...
// *** Drawing the layer to the screen / window ***
SDL_SetRenderTarget(renderer, NULL);
SDL_RenderCopy(renderer, my_layer, NULL, NULL);
You can take this a bit further by creating layers that are larger than screen_width x screen_height and use the srcrect parameter of SDL_RenderCopy() to scroll the layer. With a few background layers, that can be used to get efficient and neat-looking old-school parallax effects.
You will also probably want to encapsulate the notion of a layer into some Layer class in C++. Here's a rough starting point:
class Layer {
public:
Layer(SDL_Renderer *renderer, int w, int h)
: texture_(SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h),
, display_{0, 0, 0, 0}
{
SDL_GetRendererOutputSize(renderer, &display_.w, &display_.h);
w = std::min(w, display_.w);
h = std::min(h, display_.h);
max_scroll_x = w - display_.w;
max_scroll_y = h - display_.h;
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
}
Layer(Layer&& rhs)
: texture_(rhs.texture_)
, display_(rhs.display)
, max_scroll_x(rhs.max_scroll_x)
, max_scroll_y(rhs.max_scroll_y) {
rhs.texture_ = nullptr;
}
Layer& operator=(const Layer& rhs) {
if(texture_) {SDL_DestroyTexture(texture_);}
texture_ = rhs.texture_;
display_ = rhs.display_;
max_scroll_x = rhs.max_scroll_x;
max_scroll_y = rhs.max_scroll_y;
rhs.texture_ = nullptr;
)
Layer(const Layer& rhs) = delete;
Layer& operator=(const Layer& rhs) = delete;
~Layer() {
if(texture_) {SDL_DestroyTexture(texture_);}
}
// Subsequent draw calls will target this layer
void makeCurrent(SDL_Renderer* renderer) {
SDL_SetRenderTarget(renderer, texture_);
}
// Draws the layer to the currently active render target
void commit(SDL_Renderer* renderer, const SDL_Rect * dstrect=nullptr) {
SDL_RenderCopy(renderer, texture_, &display_, dstrect);
}
// Changes the offset of the layer
void scrollTo(int x, int y) {
display_.x = std::clamp(x, 0, max_scroll_x);
display_.y = std::clamp(y, 0, max_scroll_y);
}
private:
SDL_Texture* texture_;
SDL_Rect display_;
int max_scroll_x;
int max_scroll_y;
};
I want to create an entity with Qt3D that has a custom image as texture. I came across the QPaintedTextureImage (link leads to Qt 5.9 version for details. Here ist doc for 5.8), which can be written with a QPainter but I don't understand how.
First, this is how I imagine the entity could look like:
[EDIT]: code is edited and works now!
planeEntity = new Qt3DCore::QEntity(rootEntity);
planeMesh = new Qt3DExtras::QPlaneMesh;
planeMesh->setWidth(2);
planeMesh->setHeight(2);
image = new TextureImage; //see below
image->setSize(QSize(100,100));
painter = new QPainter;
image->paint(painter)
planeMaterial = new Qt3DExtras::QDiffuseMapMaterial;
planeMaterial->diffuse()->addTextureImage(image);
planeEntity->addComponent(planeMesh);
planeEntity->addComponent(planeMaterial);
TextureImage is the subclassed QPaintedTextureImage with paint function:
class TextureImage : public Qt3DRender::QPaintedTextureImage
{
public:
void paint(QPainter* painter);
};
What does the QPainter, passed to paint function, need to do in the implementation of paint if I just want to draw a big circle to the planeEntity?
[Edit] Implementation:
void TextureImage::paint(QPainter* painter)
{
//hardcoded values because there was no device()->width/heigth
painter->fillRect(0, 0, 100, 100, QColor(255, 255, 255));
/* Set pen and brush to whatever you want. */
painter->setPen(QPen(QBrush(QColor(255, 0, 255)) ,10));
painter->setBrush(QColor(0, 0, 255));
/*
* Draw a circle (or an ellipse -- the outcome depends very much on
* the aspect ratio of the bounding rectangle amongst other things).
*/
painter->drawEllipse(0, 0, 100, 100);
}
The short answer is... use QPainter exactly the same way you would normally.
void TextureImage::paint (QPainter* painter)
{
int w = painter->device()->width();
int h = painter->device()->height();
/* Clear to white. */
painter->fillRect(0, 0, w, h, QColor(255, 255, 255));
/* Set pen and brush to whatever you want. */
painter->setPen(QPen(QBrush(QColor(0, 0, 0)) ,10));
painter->setBrush(QColor(0, 0, 255));
/*
* Draw a circle (or an ellipse -- the outcome depends very much on
* the aspect ratio of the bounding rectangle amongst other things).
*/
painter->drawEllipse(0, 0, w, h);
}
However, note that you really shouldn't invoke the paint method directly. Instead use update which will cause Qt to schedule a repaint, initialize a QPainter and invoke your overridden paint method with a pointer to that painter.
It might be simpler to dynamically load the image you need in QML.
I had to do it not so long ago and opened a question on SO for it:
Qt3D dynamic texture
I am currently developing a small game in SDL in CodeBlocks and it seems i got into a little bit of trouble with surface and texture management. My current progress is getting to move a texture on-screen using the arrow keys. However, i have noticed that when loading a relatively large image, FPS drops drastically and thus the texture i move on-screen moves a lot slower.
My draw function looks like this :
const char* assets[5]={"assets/textures/0.tga","assets/textures/1.tga",
"assets/textures/2.tga","assets/textures/3.png","assets/textures/4.png"};
SDL_Surface* tex[5];
void DrawImage(int i, SDL_Surface* &dest, SDL_Rect &rect)
{
if(tex[i]==NULL) tex[i] = IMG_Load(assets[i]);
SDL_BlitSurface(tex[i], NULL, dest, &rect);
}
with the fourth texture in the array being the large one. If i issue any of the following :
DrawImage(0, screenSurface, rect);
DrawImage(1, screenSurface, rect);
DrawImage(2, screenSurface, rect);
DrawImage(4, screenSurface, rect);
everything runs smooth. However, if i issue:
DrawImage(3, screenSurface, rect);
and keep the texture in the window's bounds, everything runs in slow motion. Moving the texture outside the window's bounds makes everything run normal again.
The main loop looks like this :
bool running = true;
while(running)
{
SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0, 0, 0));
DrawImage(3, screenSurface, rect);
MovePlayer(rect, state);
MoveCursor(NULL, NULL, screenSurface);
SDL_PumpEvents();
SDL_UpdateWindowSurface(window);
if (state[SDL_SCANCODE_ESCAPE])
{
SDL_DestroyWindow(window);
running = false;
}
}
Is there any way to overcome this problem?
Thanks in advance.
Look into SDL_Renderer to make use of hardware rendering. SDL_UpdateWindowSurface uses software rendering and can be very slow.
I'm trying to display a texture onto the screen but all I'm getting is a black window.
No SDL Errors are being reported. There's a good chance that I'm missing something stupid, but I can't see it. Hopefully another set of eyes will help. Feel free to ask for more code/info.
main.cpp
SDL_Window * window;
SDL_Renderer * renderer;
SDL_Texture * grass;
SDL_Rect g_dst;
SDL_Event event;
Game app;
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("tmp", 100, 100, 640, 480, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
g_dst.x = g_dst.y = 0;
g_dst.w = 640;
g_dst.h = 480;
grass = IMG_LoadTexture(renderer, "grass.bmp");
while (app.isRunning()) {
app.pollEvents(&event);
app.render_init();
app.render(grass, NULL, &g_dst);
app.render_end();
}
//SDL_Quit() is handled by the Game class' destructor
Game.cpp
//Only functions used for rendering are shown
void render_init(Uint8 red=0, Uint8 green=0, Uint8 blue=0, Uint8 alpha=255)
{
SDL_SetRenderDrawColor(renderer, red, green, blue, alpha);
SDL_RenderClear(renderer);
}
void render(SDL_Texture * texture, SDL_Rect * src, SDL_Rect * dest) {
SDL_RenderCopy(renderer, texture, src, dest);
}
void render_end() { SDL_RenderPresent(renderer); }
First of all, you're initializing everything? please don't do that frequently, mind you that you're also initializing MANY unnecessary stuffs like for game controllers, etc. if the app gets bigger then the efficiency and the possibility of this app running at a smoot speed is at stake.
I also noticed that you are declaring variables in the .cpp file, do that in the header file and just recall the header to the cpp file that will be using it.
You want to render the grass right? and render it as much as the screens size.
(I'll just assume that you used this in the game.cpp part, which is the very first file, thus, not regarding any classes made)
int winWidth = 680; //The reason for this is just in case you make the window resizable
int winHeight = 480; //then the texture would also resize along with the window
SDL_Window *window = window = SDL_CreateWindow("The Space Project", 100, 100, winWidth, winHeight, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = NULL; //I've set this to NULL so that we can know if
the reason as to why your image is not rendering is because the renderer is not properly working.
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
if(renderer == NULL)
{
cout >> "Renderer is not working" >> endl;
//This shows a line at the command prompt that your renderer doesn't have any output, thus, only having a NULL as an equivalent
}
SDL_Texture* grass= NULL;
grass= IMG_LoadTexture(renderer, "grass.bmp"); //As you can see, I've set the grass to Null again
if(grass == NULL)
{
cout >> "Grass have failed to initialize" >> endl;
/*I don't normally do this but it's very important if you really need trouble shooting guides
but this time, were here to check IF the grass.bmp entered the SDL_Texture grass, so if the system can't find the .bmp file then it would show this error
since the grass (SDL_Texture) still doesn't have anything inside it (NULL)*/
}
SDL_Rect grass_rect;
grass_rect.x = 0;
grass_rect.y = 0;
grass_rect.w = winWidth;
grass_rect.h = winHeight;
//Loop part, I'll skip some of it
while (!quit && mainEvent->type != SDL_QUIT) //!quit is just an imaginary Boolean I've typed)
{
SDL_PollEvent(mainEvent); //Let's say you created the event already
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, grass, NULL, &grass_rect);
//The NULL part is also similar to a rect but it's a limiting type, we didn't assign anything to it
since I assumed that you wanted the whole image to be rendered
SDL_RenderPresent(renderer);
}
I revised the code and made it more efficient since your code called for useless extras which might result in lower performance.
I also notice that you tried calling for color changes?
use this
SDL_SetTextureColorMod(texture, red-value, green-value, blue-value);
and put it in the loops part under the render present of the same texture.
SDL_SetTextureColorMod(grass, 250, 250, 250);
Doing this would set all color values to 250, thus, having a white color, this change your texture color to white.
You're also wasting space on making the app.is running(), you could easily replace it with a boolean, which consumes much less space or you could omit it if you don't have an exit button inside the application and just make your loop read the SDL_QUIT, this saves space for the file, mind the efficiency.
If this still doesn't work then try replacing the image your using, make a simple one on paint name it something like "grass.png" or anything then try it again.
Don't forget to put the file in the proper folder, in the DEBUG folder if you haven't specified a folder, and also put it in the app folder so it would also read it when it executes as an .exe file and not as part of the debug command.
I have a self coded rectangle (not using QRect for educational purposes), which looks like so:
class Block {
private: // also has getters and setters for this stuff
int m_x;
int m_y;
uint m_width;
uint m_height;
QColor m_color;
public:
Block(int x = 0, int y = 0, uint w = 64, uint h = 64);
Block(const QColor &color, int x = 0, int y = 0, uint w = 64, uint h = 64);
void paint(QPainter &painter) const
{
painter.fillRect(m_x, m_y, m_width, m_height, m_color);
}
};
Now I'd like to add a support for images, so the block can either have a color or an image (if both provided, image will be used).
The problem is, there are too many classes to represent images (QPixmap, QImage, QIcon) and I have no idea which one should I use.
What are the differences, which one is best suited for simply drawing a resource image into a rectangle?
If you want to display the image on screen, use QPixmap. If you want to modify image, load or save it to file, use QImage.
QIcon is based on QPixmap and provides ability to choose one of many pixmaps based on requested size and state. QIcon is probably not what you want.
From the documentation:
Qt provides four classes for handling image data: QImage, QPixmap, QBitmap and QPicture. QImage is designed and optimized for I/O, and for direct pixel access and manipulation, while QPixmap is designed and optimized for showing images on screen. QBitmap is only a convenience class that inherits QPixmap, ensuring a depth of 1. Finally, the QPicture class is a paint device that records and replays QPainter commands.
The QIcon class provides scalable icons in different modes and states. A QIcon can generate smaller, larger, active, and disabled pixmaps from the set of pixmaps it is given. Such pixmaps are used by Qt widgets to show an icon representing a particular action.