• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Question - Code Surfaces && Lighting

P

PixHil Entertainment

Guest
CREATE:
Code:
globalvar global_lighting;
global_lighting = surface_create(room_width, room_height);
STEP:
Code:
surface_set_target(global_lighting);
draw_set_color(c_gray);
draw_rectangle(0, 0, room_width, room_height, false);
surface_reset_target();
DRAW:
Code:
gpu_set_blendenable(true);
gpu_set_blendmode(bm_subtract);
draw_surface(global_lighting, room_width, room_height);
gpu_set_blendmode(bm_normal);
I remember how this use to work in Studio: 1 but i'm having trouble drawing anything to a surface let alone blending it within the scene with Studio 2. Can anyone shed light on this issue?
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
CREATE:
Code:
globalvar global_lighting;
global_lighting = surface_create(room_width, room_height);
STEP:
Code:
surface_set_target(global_lighting);
draw_set_color(c_gray);
draw_rectangle(0, 0, room_width, room_height, false);
surface_reset_target();
DRAW:
Code:
gpu_set_blendenable(true);
gpu_set_blendmode(bm_subtract);
draw_surface(global_lighting, room_width, room_height);
gpu_set_blendmode(bm_normal);
I remember how this use to work in Studio: 1 but i'm having trouble drawing anything to a surface let alone blending it within the scene with Studio 2. Can anyone shed light on this issue?
I bet you this wouldn't work in GMS:1...
you can only use "draw" functions on the [draw event] ;)
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
You should be doing any drawing in the Draw Event. So move that step event code into the draw event. Oh, and I don't' think you need to call the blendenable function either, as (iirc) it's enabled by default.
 
P

PixHil Entertainment

Guest
I bet you this wouldn't work in GMS:1...
you can only use "draw" functions on the [draw event] ;)
I've tried putting it in draw event, still doesn't work.
btw in studio: 1 it works, I couldn't tell you why.

You should be doing any drawing in the Draw Event. So move that step event code into the draw event. Oh, and I don't' think you need to call the blendenable function either, as (iirc) it's enabled by default.
Again, draw event isn't the problem here. you're probably right about the blendmode_enable. however, this still doesn't fix my issue.
It doesn't draw anything to the screen.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Ummm... You are drawing the surface at position room_width/room_height... are you sure that's correct? It's drawing it outside the room...
 
P

PixHil Entertainment

Guest
Ummm... You are drawing the surface at position room_width/room_height... are you sure that's correct? It's drawing it outside the room...
That may be the problem. view_hview isn't a thing anymore, what is the alternative? view_hport[0]?
from my understanding however, drawing from 0 to room_width should draw across from the entire screen so that should be a problem either, I should still be able to see what has been drawn to the surface. however, it's not coming up.
 

csanyk

Member
You should be doing any drawing in the Draw Event. So move that step event code into the draw event. Oh, and I don't' think you need to call the blendenable function either, as (iirc) it's enabled by default.
I know this is the recommended practice in the official documentation. However, I have found that I am able to draw to surfaces (never to the application surface, but to other surfaces) outside of the Draw Event, and it works.

For example, I've successfully used surfaces to have an instance draw its own custom sprite that I procedurally generate during its Create event, using surfaces. Since I only need to generate the sprite one time, it made more sense to try to do it during Create rather than during Draw. And it worked. I don't know that it would work in all circumstances, but it seems to be fine. Maybe there's other issues that aren't as visible that make it a risky or non-optimized practice, but I'm not aware of any.
 

rwkay

GameMaker Staff
GameMaker Dev.
@csanyk - just because it works on one platform does not mean it will work across all targets, we don't recommend you do this as it my break on different targets in non-obvious ways.

Russell
 

Mike

nobody important
GMC Elder
Also, if you lose the surface and have to recreate it, then having the code in the draw event means this will pretty much happen automatically, where as if you create in the create event, it'll be broken unless you destroy and recreate the instance.

We really don't just recommend this stuff for the good of our health :)
 

csanyk

Member
Also, if you lose the surface and have to recreate it, then having the code in the draw event means this will pretty much happen automatically, where as if you create in the create event, it'll be broken unless you destroy and recreate the instance.

We really don't just recommend this stuff for the good of our health :)
Granted.

In my example, I would do all of the following in the Create event:

  1. Create a surface.
  2. Draw to it.
  3. Create sprite from surface.
  4. Destroy surface.
  5. Assign sprite to instance.

I didn't need the surface any longer once the sprite had been generated, so there wasn't a concern about it being lost. It worked, but I only tested it on HTML5, Windows, and Windows YYC builds. I get that it wasn't safe, and wouldn't necessarily work on all build targets, and could break due to some future change to GMS, but at the time it did work.
 

Mike

nobody important
GMC Elder
First.... why bother making a sprite from the surface? Just draw the surface. Never really understood that....

Code:
// create event
mysurf=-1;

// draw event
if( !surface_exists( mysurf) ){
    mysurf = surface_create(w,h);
    surface_set_target(mysurf);
    draw_clear_alpha(0,0);
   
    // set shaders...draw stuff...go nuts!

    surface_reset_target();
}

// draw surface here as a sprite....
Not complicated, and will work across all targets,

If you try and draw in the create events - especially if its the first frame of the game, it may be very fragile as it's not been through a draw cycle yet, and views...cameras... buffers aren't setup yet (depending on targets). I've also never understood creating a sprite from a surface. It's just wasting time and memory. You already have a drawable image - just draw it.
 

csanyk

Member
First.... why bother making a sprite from the surface? Just draw the surface. Never really understood that....

Code:
// create event
mysurf=-1;

// draw event
if( !surface_exists( mysurf) ){
    mysurf = surface_create(w,h);
    surface_set_target(mysurf);
    draw_clear_alpha(0,0);
  
    // set shaders...draw stuff...go nuts!

    surface_reset_target();
}

// draw surface here as a sprite....
Not complicated, and will work across all targets,

If you try and draw in the create events - especially if its the first frame of the game, it may be very fragile as it's not been through a draw cycle yet, and views...cameras... buffers aren't setup yet (depending on targets). I've also never understood creating a sprite from a surface. It's just wasting time and memory. You already have a drawable image - just draw it.
Sprites support sub-images, animation, etc. and don't run the risk of being lost; surfaces don't. In my case, I had a 2-subimage animation to put into the sprite. I suppose I could have created 2 surfaces and cycled between them with code, but since sprites already do that for free I figured why reinvent it.

I guess if I were to do it over again, I would have had a sprite-generator object that would do its drawing in the Draw event where it properly belongs, and have it spew out sprites as needed, which could be assigned to instances, and then destroy itself when more new sprites were no longer needed. The instances that consume the sprites could then just do the default draw. In general I prefer objects that will have many instances have as simple a Draw as possible, so doing a one-time sprite creation in the Draw Event where it would be called every step, or an if-check performed every step, seems like an unnecessary complication to me.
 

Mike

nobody important
GMC Elder
Yes... I guess. However, personally I'd do that myself. I'd also try and use sub-sections of a surface so it acts as a texture page, rather than a single sprite per page which is what you'll be getting just now. Then draw sub-sections of that surface depending on frame. Placing images on a simple grid means it's easy to work out location and draw the correct frame and means it's not a lot of management.
 
A

Ampersand

Guest
I bet you this wouldn't work in GMS:1...
you can only use "draw" functions on the [draw event] ;)
This is incorrect. My current project runs about a thousand particles and objects at the high stress points, and I only call a single "draw_surface" from my controller. Draw actions can be performed anywhere so long as the target surface is not the application_surface. Even then I'm almost certain you can draw to the application surface anywhere, so long as you set the target and the application_surface_draw_enable is true.
 
R

renex

Guest
I don't know how these things work, but a way of making a surface nonvolatile would save a lot of time and trouble, even if that meant losing write access to it. I don't use surfaces as much simply because they are fragile... wouldn't want to have to bake a complex minimap or something like that everytime the surface decides to die on you.
 
A

Ampersand

Guest
I don't know how these things work, but a way of making a surface nonvolatile would save a lot of time and trouble, even if that meant losing write access to it. I don't use surfaces as much simply because they are fragile... wouldn't want to have to bake a complex minimap or something like that everytime the surface decides to die on you.
Every time a surface is drawn, check if it exists. If not, init surface then draw. If you're losing surfaces so often this check effects performance, you're using surfaces wrong. I did this and included a debug message for when a surface has to be recreated, and I've never had a surface "disappear" on me.

My graphics buffer on my current project is two static surfaces and three dynamic surfaces layered then shaded. It performs better than when I was just handling drawing the "default" way.
 
R

renex

Guest
If you're losing surfaces so often
Android loses surfaces very frequently and you can't even know because instead of going invalid it just goes black.

Moreover, I'm making an image editor so all my surfaces are critical.

However, backgrounds and texture pages are nonvolatile - this is why I turn important runtime graphics into bgs. Which brings me to my original point.
 
A

Ampersand

Guest
Drawing backgrounds faster in your experience? I have a few layers of static surfaces that have to be mixed for shaders, would changing them into a background when I initialize the surface be faster for draw calls?
 
R

renex

Guest
would changing them into a background when I initialize the surface be faster for draw calls?
Probably not, but it allows me to bake complex runtime graphics into a permanent medium for usage during the game. So things like procedural textures, minimaps, static shadow maps and the such only have to be built once. I also make use of dynamic resources for games with skinning function, where the user can supply their own texture for some ingame objects. Doing this with a surface would involve rereading the file when the surface is lost and that's not a good approach.
 
Top