Yeah that's true.Copy it then draw the copy. It's just like that. Basically your trying to draw it on itself and it refuses.
Ah yes that's done already.GMS automatically draws and sort of "lock" the application surface. You can't fool around the draw pipeline with it unless you deactivate that automatic drawing.
If ever you want to be totally free with the application surface and do the hell you want with it and draw it as your see fit, you can use this function:
application_surface_draw_enable()
This function is meant to "unlock" / remove all that is automatic about the application surface (drawing, resizing, etc)
Not sure what you mean.If it wasn't clear... surface_copy ()
I believe there are differences to how the memory is handled.Not sure what you mean.
Isn't copying a surface to a surface the same as drawing a surface inside a surface?
No, it works. I do it all the time. For instance a water reflection effect, a glass refraction effects. I want those drawn at a certain depth so objects in the foreground aren't effected, so I copy the surface, draw the copy right there in the normal draw event and then everything drawn after is unaffected.Not sure what you mean.
Isn't copying a surface to a surface the same as drawing a surface inside a surface?
If you need to mess with the application_surface what I like to do is keep that to the Post Draw event. I don't think you can write the application_surface to itself in the draw event, but I may be mistaken.
I believe there are differences to how the memory is handled.
I think the issue is that you're trying to read from the application_surface while it is "open" for writing.
Yeah, I had plenty of issues with exactly this even when disabling the application_surface drawing. This is why I recommended doing it in Post Draw -- it won't affect your GUI layer and won't give you trouble.Just to be clear, I got application_surface_draw_enable(false); at the start of every room.
Anyway, the problem is, I'm trying to draw my surface where my application surface is on (I'm using multiple shaders so I got multiple surfaces).
But it only draws when I'm drawing it on the draw GUI, how can I draw the end result on the normal draw event? (or post draw of whatever, it all doesn't work)
Yeah, I had plenty of issues with exactly this even when disabling the application_surface drawing. This is why I recommended doing it in Post Draw -- it won't affect your GUI layer and won't give you trouble.
aaaah but that's the problem, Post-Draw isn't working for me, when I'm drawing my application_surface-surface there it doesn't show anything.Post drawn is MEANT to be an event where you apply shaders and stuff to the application surface and then you draw it on screen.
And to answer a previous question of yours Kyon, the GUI layer and the application_surface are basically 2 different layers with both their draw events and GUI layer is always on top. They are also independant from each other in matter of resizing.
Also even without vSync on, the post-draw event suddenly worked. But it works exactly like Draw GUI. So a lot of my code won't work because I use x/y's that match the camera and stuff.Sorry for the silly question but do you draw anything on it in the normal draw events?
Also, how do you draw it after? You might draw it outside the screen or something
Another tidbits, there's a known bug on certain Win10 builds... do you have V-sync On? If not, turn it on and you might see the application surface when you draw it.
Also make sure you're grabbing the window offsets. Lemme grab some code from a project real quick...aaaah but that's the problem, Post-Draw isn't working for me, when I'm drawing my application_surface-surface there it doesn't show anything.
What am I missing haha
//Prep Surface Draw
draw_enable_alphablend(0) ;
var offset = application_get_position() ;
//Set Shader
if(shader_on) {
shader_set(sha_example);
//set shader uniforms
}
//Draw Screen
draw_surface(application_surface, offset[0], offset[1]) ;
//Reset alpha blend
draw_enable_alphablend(1) ;
//Reset Shader
if(shader_on) {
shader_reset() ;
}
surface_set_target( application_surface );
Thanks for that explanation!If you disable the application surface drawing automatically you need to understand what each of the draw events are actually doing. That section of the manual can be a little confusing, so let's take a look:
The pre-draw event deals with the 'back buffer', or the location in memory in which what you actually see drawn on the screen is stored. Basically, your window is blank until you draw something to the back buffer. And what you draw there stays until you write over it. By default this is when the application surface is cleared. Interestingly, if we were to use:
We won't see anything drawn to the screen. It's not listed here, but the surface stack is also reset before the normal draw phase begins. We would have to put that into the draw_begin event if we wanted to mimic how the program normally operates. The draw events happen as expected, but something to note is that these events happen for each layer. Draw begin is run for each object on each layer, then draw step, then draw end. This can cause some sorting anomalies if you aren't paying attention to it but should generally have the expected results. For example, if you had two layers "A" and "B", and you had an object on "A" that has a draw end event, it will draw over objects on layer "B" despite layer B being 'higher'. But, for the most part it should behave as expected. Lastly, the post-draw event. This is the only time you can draw to the actual screen. It doesn't matter how much you've done during every other draw event, if you have application surface rendering turned off you'll never see any of it if you don't draw here. This is a good step to use 'compositing', if you have multiple layers you want to combine together, though it should be mentioned the application_surface seems to have a unique property: it always blends at an alpha of 1. It also, when drawn automatically, tries to fit to the room then scale to the window.Code:surface_set_target( application_surface );
However, looking at this chart we can see why the application surface can be drawn during the GUI steps. At that point it's already been drawn to the back buffer, and you are using it to draw to the GUI layer. Now, personally I think the GUI layer confuses things a bit, but the idea is pretty much the same except there is no post-draw for it. The major 'feature' is simply that all coordinates are absolute. When you draw to the application surface everything is relative to it's position in the room. Since the GUI is a static resolution, this isn't the case. There's also the minor benefit that the GUI can be in a different resolution than the game but to be completely honest the GUI doesn't do anything you can't accomplish in the normal draw pipeline. I don't know if any of this helps, but hopefully I covered something you wanted to know.
var _layers = layer_get_all();
for ( var _i = 0; _i < array_length_1d( _layers ); _i++ ) {
var _layer = _layers[ array_length_1d( _layers ) - _i - 1 ];
var _type = string_copy( layer_get_name( _layer ), 1, 6 );
if _type == "TilesF" { //floor
layer_script_begin( _layer, scr_render_floor );
layer_script_end( _layer, scr_render_reset );
} else if _type == "TilesS" { //shadow
layer_script_begin( _layer, scr_render_shadow );
layer_script_end( _layer, scr_render_reset );
} else if _type == "TilesO" { //overlay
layer_script_begin( _layer, scr_render_overlay );
layer_script_end( _layer, scr_render_reset );
} else if _type == "TilesR" { //roof
layer_script_begin( _layer, scr_render_roof );
layer_script_end( _layer, scr_render_reset );
}
}
///@func scr_render_overlay();
if event_type == ev_draw {
surface_set_target( world.surfaces[ world.surfaceOverlay ] );
}
///@func scr_render_reset();
if event_type == ev_draw {
surface_reset_target();
shader_reset();
}
if update {
surface_set_target( surface );
for ( var _i = 0; _i < array_length_1d( surfaces ); _i++ ) {
if surfaceHide[ _i ] { continue }
if _i == surfaceRoof && flags & world_flags.inside { continue } // skip roof if should be hidden
if surfaceType[ _i ] == 0 {
if colorFlash == undefined { pal_swap( palSource, pal( palIndex ) ) } // set palette swap if color transition is happening, otherwise use default coloring
else { pal_swap( colorSource, colorFlash ) }
if flags & world_flags.wrap {
draw_surface_tiled( surfaces[ _i ], camera.x, camera.y ); // if this surface should wrap, tile it
} else {
draw_surface( surfaces[ _i ], camera.x, camera.y ); // otherwise draw the surface
}
shader_reset(); // reset shader
} else if surfaces[ _i ] != undefined {
with ( surfaces[ _i ] ) { event_perform( ev_draw, 0 ) } // draws instance layers
}
}
surface_reset_target();
}
draw_surface( surface, 0, 0 );
COMPOSITING
UI
Roof
Overlay
INSTANCE
Shadow
Floor
yikes.@Kyon - Ah, okay, I see what you want. So, there are two ways to accomplish this but the most obvious one is 'compositing'. Think about the screen like a deck of cards. By default GameMaker draws everything to a single card, which is then displayed to the player. However, we could instead draw a lot of things to different cards, and make ourselves a stack. Then we would draw that stack, from the bottom to the top, to the screen. Personally I use this probably more than I should. However, there is a way to handle this you may not have considered. This is going to take a wild swing in complexity, so please bear with me for a moment! You need a persistent 'scene' object that will set up your rooms to handle this compositing, the important part is during the room start event:
I apologize for not cleaning this up more, it's an excerpt from the render I wrote for my Final Fantasy project. Basically, each room is made up of a series of layers. Because I wasn't sure how many layers any particular scene would have, I just gave them generic names followed by a number. What this does is attach a script to each layer based on it's naming scheme, please refer to the manual for more information about these specific commands, that allows you to run code just before the draw event is called for objects on that layer. The scripts themselves are quite simple:Code:var _layers = layer_get_all(); for ( var _i = 0; _i < array_length_1d( _layers ); _i++ ) { var _layer = _layers[ array_length_1d( _layers ) - _i - 1 ]; var _type = string_copy( layer_get_name( _layer ), 1, 6 ); if _type == "TilesF" { //floor layer_script_begin( _layer, scr_render_floor ); layer_script_end( _layer, scr_render_reset ); } else if _type == "TilesS" { //shadow layer_script_begin( _layer, scr_render_shadow ); layer_script_end( _layer, scr_render_reset ); } else if _type == "TilesO" { //overlay layer_script_begin( _layer, scr_render_overlay ); layer_script_end( _layer, scr_render_reset ); } else if _type == "TilesR" { //roof layer_script_begin( _layer, scr_render_roof ); layer_script_end( _layer, scr_render_reset ); } }
Code:///@func scr_render_overlay(); if event_type == ev_draw { surface_set_target( world.surfaces[ world.surfaceOverlay ] ); }
The short version is that each layer is given a surface to draw to. The floor layers draw to the floor surface, the shadow layers draw to the shadow surface. Then when it comes time to finally draw everything to the screen, I have an object that is higher than anything else, thus is drawn last, that performs this:Code:///@func scr_render_reset(); if event_type == ev_draw { surface_reset_target(); shader_reset(); }
This is a LOT to unpack but you have asked a complex question so buckle in! I've left this code mostly unmodified so I can show you how I solved some parts of the question you have. As I say above, I've broken down my room into a series of layers that are all drawn to a number of surfaces. I have a floor layer, a shadow layer, an overlay layer and a roof layer. When it comes time to draw everything to the screen there are two layers that have been left out: the UI and the INSTANCE layers. The UI has all of what you'd expect, dialogue boxes, menus, cursors, etc... and the INSTANCE layer is all the characters on screen. If we go back to my card analogy above the game is stacked like this:Code:if update { surface_set_target( surface ); for ( var _i = 0; _i < array_length_1d( surfaces ); _i++ ) { if surfaceHide[ _i ] { continue } if _i == surfaceRoof && flags & world_flags.inside { continue } // skip roof if should be hidden if surfaceType[ _i ] == 0 { if colorFlash == undefined { pal_swap( palSource, pal( palIndex ) ) } // set palette swap if color transition is happening, otherwise use default coloring else { pal_swap( colorSource, colorFlash ) } if flags & world_flags.wrap { draw_surface_tiled( surfaces[ _i ], camera.x, camera.y ); // if this surface should wrap, tile it } else { draw_surface( surfaces[ _i ], camera.x, camera.y ); // otherwise draw the surface } shader_reset(); // reset shader } else if surfaces[ _i ] != undefined { with ( surfaces[ _i ] ) { event_perform( ev_draw, 0 ) } // draws instance layers } } surface_reset_target(); } draw_surface( surface, 0, 0 );
The block of code above runs through that stack and draws each one of those layers to a final 'composited' image, that is ready to be used during the post-draw step. This allows each layer to be run through their own shader and effects to ensure the final image looks just like what I need it to. Once it is sent to the back buffer I can now apply any 'full screen' post-processing, such as a CRT shader. The point being, and hopefully what resonates with your question, is that I only apply effects to the layers that need them. The UI is not colored by the world, nor are the characters. And everything is put together into a final image at the end. There is one more thing that I will talk about very briefly here at the end, and that is I also only draw my layers once for those that don't change. I noticed that there was a lot of pointless redrawing of things on the screen that aren't changing, so to make it so maps of any size could be drawn more quickly, the layers themselves are only drawn once, then hidden, and are only redrawn if something changes or the surface is lost from VRAM. While this is beyond your question's scope, it should show that the GM draw pipeline has a lot of potential once you really dig into it and take control of how things are drawn to the screen.Code:COMPOSITING UI Roof Overlay INSTANCE Shadow Floor
Anyways, that is a LOT of information and I wouldn't be surprised if you have questions so feel free to ask them and I'll do my best to help out.
X ((msgX-cam_x)/(zoom))*displayratio Y ((msgY-cam_y)/(zoom))*displayratio