I'm using glScissor() in my application and it works properly, but I met a problem:
I have my Window object for which drawing area is specified by glScissor() and inside this area, I'm drawing my ListView object for which the drawing area should also be specified with glScissor() as I don't want to draw it all.
In a code I could represent it as:
Window::draw()
{
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw some components...
mListView.draw(); // mListView is an object of ListView type
glDisable(GL_SCISSOR_TEST);
}
ListView::draw()
{
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
glDisable(GL_SCISSOR_TEST);
}
But of course in this situation enable/disable calls are wrong:
glEnable(GL_SCISSOR_TEST);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
If I remove those internal glEnable/glDisable calls (the ones in ListView), I would anyway end up with two glScissor() calls which also seems to be wrong.
EDIT
I would like to somehow achieve both scissor effects, I mean - that Window should draw only in it's scissored area and internal ListView also only in it's scissored area.
As you can see in a picture, with red rectangle I have marked scissor area for Window which WORKS, and with a blue rectangle I marked an area on which I'd like to draw my ListView. That's why I was trying to use nested scissors, but I know it was useless. So basically my question is, what could be the best approach to achieve this?
Since OpenGL is a state machine and the scissor rect, like any other state, is overwritten when calling glScissor the next time, you have to properly restore the window's scissor rect after drawing the list view. This can either be done by just letting the window manage it:
Window::draw()
{
// draw some components with their own scissors
mListView.draw(); // mListView is an object of ListView type
glScissor(x, y, width, height);
glEnable(GL_SCISSOR_TEST);
// draw other stuff using window's scissor
glDisable(GL_SCISSOR_TEST);
}
But it might be more flexible to let the individual components restore the scissor state themselves, especially if used in such a hierarchical manner. For this you can either use the deprecated glPush/PopAttrib functions to save and restore the scissor rect:
ListView::draw()
{
glPushAttrib(GL_SCISSOR_BIT);
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
glDisable(GL_SCISSOR_TEST);
glPopAttrib();
}
Or you save and restore the scissor state yourself:
ListView::draw()
{
// save
int rect[4];
bool on = glIsEnabled(GL_SCISSOR_TEST);
glGetIntegerv(GL_SCISSOR_BOX, rect);
glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);
// Draw a chosen part of ListView here
// restore
glScissor(rect[0], rect[1], rect[2], rect[3]);
if(!on)
glDisable(GL_SCISSOR_TEST);
}
This can of course be automated with a nice RAII wrapper, but that is free for you to excercise.
edited: General case.
Every time you set the scissor state with glScissor, you set the scissor state. It does not nest, and it does not stack, so you cannot "subscissor" with nested calls to glScissor. You'll have to manually compute the rectangular intersection of your ListView and your Window bounding rects, and then scissor to that when drawing ListView.
In the general case, you'll be manually maintaining a stack of scissor rectangles. As you draw each sub-element, you intersect the sub-element's bounding rect against the current top of the stack, and use that as the scissor for that sub-element. Push the new subrect onto the stack when painting children, and pop it when returning back up the hierarchy.
If you're painting other contents to Window, you'll also have to make sure to handle overdraw correctly; either by setting the Z ordering and enabling depth buffering, or by disabling depth buffering and painting your contents back-to-front. Scissoring won't help you mask the Window contents behind the ListView, since scissors can only be rectangular regions.
OpenGL is a state machine. You can call glScissor, glEnable and glDisable as often as you'd like to. They don't act like opening-closing braces that must be matched. If your calls "fold" like this, that's no problem. Just don't expect the one scissor to merge with the other one; it will merely change/overwrite the previous setting.
Related
Background:
Sorry for my English . So I am in a slightly unique situation in the scenario. I am working on a project that involves using a DLL proxy to intercept DirectX9 calls and dnd control drawing of a game.They are things that are statically draw and I want to be able to draw them in another part of the screen.
The Question:
I am wanting to be able to save pixels in a rect on the screen and then draw exact rect somewhere else on the screen. So if I can grab the pixels at x100, y100, w30, h30 and copy that that to another location on the screen that would be great.
This is the code that I have so far which I assume is making a texture from a memory rect.
HRESULT myIDirect3DDevice9::EndScene(void)
{
GetInput();
// Draw anything you want before the scene is shown to the user
m_pIDirect3DDevice9->GetBackBuffer(iSwapChain, iBackBuffer, Type, ppBackBuffer);
LPDIRECT3DTEXTURE9 textureMap;
D3DXCreateTexture(m_pIDirect3DDevice9, 100, 100, D3DX_DEFAULT, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &textureMap);
m_pIDirect3DDevice9->SetTexture(0, textureMap);
// SP_DX9_draw_text_overlay();
return(m_pIDirect3DDevice9->EndScene());
}
Project is based off this:
Library_Wrappers
Other notes:
I want to avoid DLL injection to accomplish this.
I am trying to make simple square where you could paint with mouse. Problem is, whenever draw signal is happens, cairo surface seems to be cleared entirely. I understand this because after first queue_draw() white background is gone and I see my GTK theme color (which is grey).
I thought I could save surface or context, but you can't just create empty surface in cairo, and I can't create it using this->get_window()->create_cairo_surface() (where this is object of class inherited from Gtk::DrawingArea) because when constructor is called, widget isn't attached to any window yet, so it is a null pointer. I mean, I could create some public function called you_are_added_to_window_create_cairo_surface() but I'd really like not to do this.
So I really don't know what to do and what I don't understand about cairo.
How do I preserve, or save 'canvas' current state, so whatever is actually being drawn is just applied on existing drawing?
Here is callback function of my class:
bool MyDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context> & cr) {
/* clear and fill background with white in the beginning */
if (first_draw) {
cr->save();
cr->set_source_rgb(255.0, 255.0, 255.0);
cr->paint();
cr->restore();
first_draw = false;
}
cr->save();
cr->set_source_rgb(0.0, 0.0, 0.0);
cr->begin_new_path();
while (!dots_queue.empty()) {
auto dot = dots_queue.front();
cr->line_to(dot.first, dot.second);
dots_queue.pop();
}
cr->close_path();
cr->stroke();
cr->restore();
return false;
}
Remove first_draw and instead of dots_queue.pop(), just iterate over the dots_queue and redraw all of them each time.
The draw function is not meant for "I want to add some drawing". Instead, it is "hey, the windowing system has no idea what should be drawn here, please fill this with content". That's why the cairo surface is cleared.
So while storing all actions works, it's really not ok if you are trying to have your program save your drawings, you will have to use second surface to save everything on.
My solution combines both answers of Uli Schlachter.
First, I have structure, in which I store last drawing action, since last Button Press, and until Button Release. This allows me to show things such as lines in real time, while keeping canvas clean of it.
Second, I store everything drawn on canvas on a surface, which is created like that:
// this - is object of class, derived from DrawingArea
auto allocation = this->get_allocation();
this->surface = Cairo::ImageSurface::create(
Cairo::Format::FORMAT_ARGB32,
allocation.get_width(),
allocation.get_height()
);
Then, on each draw signal, I restore it like that:
cr->save();
cr->set_source(surface, 0.0, 0.0);
cr->paint();
cr->restore();
Whenever I want to save surface, i.e. apply drawing on to canvas, I do the following:
Cairo::RefPtr<Cairo::Context> t_context = Cairo::Context::create(surface);
t_context->set_source(cr->get_target(), -allocation.get_x(), -allocation.get_y());
t_context->paint();
Here is the important moment. Without adjusting for the allocation coordinates, your canvas is going to slide away on each surface save and restore.
With that, I can easily keep my drawings on canvas, load canvas from file (because I am using ImageSurface), or save it to the file.
I wanted to implement alpha blending within my Texture class. It works almost completely. I use the following functions for manipulating the alpha value:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, alpha);
The only problem I have is that the textures that have been manipulated seem to reset to the normal alpha value of 255 when I resize or maximize the window. I checked the alpha value and recognized that it is still the value I manipulated it to be before. So the value is not 255. Why is the renderer rendering it as if the alpha value was 255 then?
Information about how and when I use these functions:
Within the main game loop I change the alpha value of the texture with a public method of my Texture class:
Texture::setAlphaValue(int alpha)
There the private alpha variable of the Texture class is changed.
Within the Draw method of my Texture class the texture is drawn and I call
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, alpha);
before
SDL_RenderCopyEx(renderer, texture, &sourceRectangle, &destinationRectangle, 0, 0, SDL_Flip);
Information about how I resize the window:
I basically just set the window mode to a resizable window in my SDL initialization. Then handling it like any normal window is possible:
SDL_CreateWindow(window_Title, x_Position, y_Position, window_Width, window_Height, SDL_WINDOW_RESIZABLE);
My primary loop area:
This is the main game loop:
void Game::Render()
{
// set color and draw window
SDL_SetRenderDrawColor(renderer, windowColor.R(), windowColor.G(), windowColor.B(), 0);
SDL_RenderClear(renderer);
texture.setAlphaValue(100);
texture.Draw(SDL_FLIP_NONE);
// present/draw renderer
SDL_RenderPresent(renderer);
}
Test my project:
I also uploaded my alpha-blending test project to dropbox. In this project I simplified everything, there isn't even a texture class anymore. So the code is really simple, but the bug is still there. Here is the link to the Visual Studio project: http://www.dropbox.com/s/zaipm8751n71cq7/Alpha.rar
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
You should directly change the alpha in this area.
example: alpha = 100;
SDL_SetTextureAlphaMod(texture, alpha); //remember that alpha is an int
SDL_RenderCopy(renderer, texture, NULL, &rect);
P.S. If you're going for a fade-out/fade-in effect, resizing will temporarily pausa alpha changes (in-case you used SDL_GetTicks() and made a float to slowly reduce/increase alpha as time goes by. This is because windows pauses the rendering inside the program but once you stop resizing, it resumes.
Another P.S. Since you're resizing the window make sure to assign the w and h values not as numbers but as products or dynamic numbers(Multiplication is faster than division but you can also use division).
Assigning static numbers would cause the window to resize but the textures inside won't change size.
Happy Coding :)
This has been a reported bug in the SDL library. It is fixed for some time now: https://bugzilla.libsdl.org/show_bug.cgi?id=2202, https://github.com/libsdl-org/SDL/issues/1085
i would like to change the screen size so that sprites will disappear before they reach the real screen edges.
BUT i still want my background to stay on all of the screen size.
Imagine a paper on my screen so i want to game to exist only on that paper, and around that paper there still will be some background.
so, how do i set my CCSprites to move in and out from that paper and slowly disappear when coming to its edges ?
my sprites are moves with : (i need to put some code to get published cause site "standard" )
id moveclouds1 = [CCMoveTo actionWithDuration:30 position:ccp(420,380)];
thanks.
you can use glscissor for that
simply subclass a CCLayer to make your "paper screen". Then add you sprites inside this layer.
on this layer override the visit method
- (void) visit
{
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(x,y, width, height); //here put the position and the size of the paper/screen
[super visit];
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
}
a sprite reaching the border of the paper/screen will be scissored off.
REMEMBER: glScissor will use PIXEL values not points, so it`s your job to use double values for retina display ( CC_CONTENT_SCALE_FACTOR() can come in handy )
In my mousefunc i call a function bspline. It works like this:
With your mouse you can put controllpoints and according to these points the bspline is drawn.So if you have drawn three points a curve between those points is displayed. By adding another point the old curve disappears and a new one appears. This new one lies now between the four points.This works just fine. BUT: This bspline curve is only displayed in one viewport.This viewport has a black border. This border disappears when my bspline is redrawn. This happens because of calling glutPostredisplay. Because in my glutDisplayFunc i call glClear(GL_COLOR_BUFFER_BIT). So it is the natural thing to happen. If i delete the glClear(GL_COLOR_BUFFER_BIT) in my displayfunc the border stays but the old curves stay too. Even if i say that the border should be redrawn nothing happens. I cant think of an alternative. Would appreciate it if you could help me...
In OpenGL the usual approach is to rerender the whole scene whenever some part of it changes. In your case changing the control points of the B-Spline should trigger a redisplay of the scene instead of perform drawing operations in the mouseclick handler function.
OpenGL has no geometry persistency, it just draws primitves to a pixelbased framebuffer. And as such you must use it.
To clarify, some pseudocode:
BSpline *b_spline;
void on_mouseclick(int x, int y)
{
float x_, y_;
transform_screen_to_scene(x,y, &x_, &y_);
bspline_add_control_point(b_spline, x_, y_);
trigger_redisplay();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
setup_viewport_and_projection();
bspline_draw(b_spline);
swap_buffers();
}