• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

Windows [Bug Report] Sprite "auto clipped" when it should be visible

Tizzio

Member
Hello,
This issue it's not that easy to explain but i'll try to be as clear as possible.
It's important to mention that this bug is present only in GMS2+
Let's start with this code and try to break it down in smaller parts:
GML:
surface_set_target(surf);
draw_clear(c_white);

if(!keyboard_check(vk_space))
    draw_sprite(spr_test, 0, 0, 0);

//converted from d3d_set_projection_ortho
var mV = matrix_build_lookat(x, y, -16000,  x, y, 0, dsin(rot), dcos(rot), 0);
var mP = matrix_build_projection_ortho(surf_w, surf_h, 1, 32000);
var cam = camera_get_active();
camera_set_view_mat(cam, mV );
camera_set_proj_mat(cam, mP );
camera_apply(cam);

draw_sprite(spr_test, 0, x + cos(current_time/500) * surf_w/2, y + surf_h/2);
surface_reset_target();

In these first 2 lines i set the target surface and clear it.
GML:
surface_set_target(surf);
draw_clear(c_white);
Then it draws a sprite if space is not pressed (helps to debug)
GML:
if(!keyboard_check(vk_space))
    draw_sprite(spr_test, 0, 0, 0);
The following lines comes directly from the converted function d3d_set_projection ortho.
It changes the camera projection to match the surface.
This allows us to draw the successive sprite in World position

GML:
//converted from d3d_set_projection_ortho
var mV = matrix_build_lookat(x, y, -16000,  x, y, 0, dsin(rot), dcos(rot), 0);
var mP = matrix_build_projection_ortho(surf_w, surf_h, 1, 32000);
var cam = camera_get_active();
camera_set_view_mat(cam, mV);
camera_set_proj_mat(cam, mP);
camera_apply(cam);
Then there is a draw_sprite in world position and the surface is reset
GML:
draw_sprite(spr_test, 0, x + cos(current_time/500) * surf_w/2, y + surf_h/2);
surface_reset_target();
When the second sprite drawn reaches a certain X position (this also happens to Y), it disappears.
But, if i remove the first draw_sprite, it will work as expected.

Note: this behaviour was introduced in gms2. It used to work properly in gms1.

I recorded a video showing the bug, and you can also download the project from Google Drive
 
Last edited:

Roldy

Member
You do notice that it doesn't disappear in the left side of the screen. Only when it is drawing to the right side of screen. The video shows the at 26 seconds. Even with the first square drawing, the second square does not disappear when you move left and the left side of the second square never passed to the right side of the screen.

You are 'looking at' x. What instance is 'x' for? The object drawing the surface?

Note that the 'disappearing' second square is oscillating between (x - surfW/2, x + surfW/2);

And when 'x + cos(current_time/500) * surf_w/2' crosses surfW/2 (to the right side of screen), it will dissappear.

Your ortho is setup to be surfw wide and you are looking at x. So the coordinates of the view for x axis would be in range (x - surfW/2, x +surfW/2);

Do you use the same camera ortho for drawing the surface? (Did you reset camera after surface_reset_target()).
 
Last edited:

Tizzio

Member
And when 'x + cos(current_time/500) * surf_w/2' crosses surfW/2 (to the right side of screen), it will dissappear.
This is not correct. The sprite disappears when the sprite bounds are outside of the "clip area" delimited by the view-projection matrix (that area x-max is approx. surf_w + sprite_get_width(spr_test)/2, because the sprite origin is center). This clip is executed inside draw_sprite, hence we cannot do anything. It would be necessary a function to disable this behaviour

The x is just a coordinate used for testing. I posted the project if you want to look at it.
There is only 1 object in the project, with 3 events

Create
GML:
x = 400;
y = 80;
surf_w = 256;
surf_h = 256;
rot = 0;
surf = surface_create(surf_w, surf_h);
Step
GML:
if(!surface_exists(surf))
    surf = surface_create(surf_w, surf_h);

surface_set_target(surf);
draw_clear(c_white);

if(!keyboard_check(vk_space))
    draw_sprite(spr_test, 0, 0, 0);

//converted from d3d_set_projection_ortho
var mV = matrix_build_lookat(x, y, -16000,  x, y, 0, dsin(rot), dcos(rot), 0);
var mP = matrix_build_projection_ortho(surf_w, surf_h, 1, 32000);
var cam = camera_get_active();
camera_set_view_mat(cam, mV );
camera_set_proj_mat(cam, mP );
camera_apply(cam);

draw_sprite(spr_test, 0, x + cos(current_time/500) * surf_w/2, y + surf_h/2);
surface_reset_target();



//movement

if(keyboard_check(vk_right))
    x++;
else if(keyboard_check(vk_left))
    x--;

if(keyboard_check(vk_up))
    y--;
else if(keyboard_check(vk_down))
    y++;

else if(keyboard_check(ord("R")))
    rot += 5;

DRAW GUI
GML:
if(!surface_exists(surf))
    surf = surface_create(surf_w, surf_h);
draw_surface(surf, x, y);

draw_text(10, 10, "Hold Space to remove the first draw\n"+
"Expected: The moving square should not disappear when something is drawn before\n" +
"Real: The moving square disappears depending on position when something is drawn before\n"+
"you can use arrow keys to move around\n"+
"square.x = " + string(x + cos(current_time/500)*surf_w/2));
 
Last edited:

Roldy

Member
This is not correct. The sprite disappears when the sprite bounds are outside of the "clip area" delimited by the view-projection matrix (that area x-max is approx. surf_w + sprite_get_width(spr_test)/2, because the sprite origin is center). This clip is executed inside draw_sprite, hence we cannot do anything. It would be necessary a function to disable this behaviour
Not sure what you mean. This behavior is clearly visible in both your project and the video.

However, let me be a little clearer.

This behavior is the same as it was in 2.2.5. The same project has the same behavior regardless of 2.2.5 or 2.3.

It might be a bug, or just a matter of expectation, but I would expect this behavior. Imagine you were not drawing to a surface but Instead drawing during the Draw Event with the exact same code (minus the surface target). You wouldn't even see anything remotely the same because how the view just changed. But if you put a camera_set_default(cam) (or saved off the buffer) then it would 'fix' it.

So in the case of the surface if you just reset the surface between the two different views/cameras then it will 'fix' it.

And it does. In your code right after the first draw sprite if you reset the surface then it will work like you think it 'should;' i.e. the second camera doesn't use the scenes view for clipping).

GML:
if(!keyboard_check(vk_space))
    draw_sprite(spr_test, 0, 0, 0);

surface_reset_target();         // reset surface and then just choose it again so
surface_set_target(surf);       // so it can figure out clips

// do the rest. Set ortho, draw occilating sprite etc...

Like I said it is actually questionable which way it should work. You are basically trying to get two views/cameras rendered like one. If drawing to a surface you would need to reset, if drawing the main scene in the Draw Event you would need to reset cameras and preserve the first 'view' that was rendered.


It is a little confusing and I can see an argument for it to work the way it does or the way you expected it to.

If there was an exposed function like 'Begin/End Scene' then it would be obvious how it should work. But that is handled under the hood. But you can effectively 'Begin/End Scene' by resetting and re-targeting the surface.

EDIT:
Here is an existing bug report (Jan 2018) on this: https://bugs.yoyogames.com/view.php?id=28629

Look at the comments. They basically say the same thing. If you wanted to do this you would need to save of the camera or re-target the surface.

Since this bug report hasn't been fixed or closed I am guessing they are trying to decide if it should work like it does, or should it work differently.
 
Last edited:

Tizzio

Member
Thanks for your time. I also had this problem drawing sprites or tiles in a 3d projection(only ortho, perspective works), so this issue is like an optimization by design. But we agree that a function or simply a toggle in the editor settings would be useful to prevent unwanted behaviours.

EDIT: in the link you posted, there is an interesting statement

If there is currently a user matrix in use, all "auto" clipping should be disable, as we have no idea what they're setting.
i will try to use the matrix_set functions instead of camera_*

EDIT 2: Nothing to do, i tried also with
GML:
//var cam = camera_get_active();
//camera_set_view_mat(cam, mV );
//camera_set_proj_mat(cam, mP );
//camera_apply(cam);
matrix_set(matrix_view, mV);
matrix_set(matrix_projection, mP);
 
Last edited:
Top