[SOLVED] - Depth issues with camera rotation

Gizmo199

Member

^^^ The above is a video of the issue I am having ^^^

SOLUTION BELOW

Issue:
So the issue I am having is that I am trying to sort each object via its depth to be either in front or behind objects near them. Much like the isometric / RPG trick:
Code:
depth = -y

What I have tried:
So here are some different methods I have tried, none of which gives me the right success.
Attempt 1:
Code:
var _dir = cos(point_distance(x, y, x+lengthdir_x(height, _camAngle-90), y+lengthdir_y(height, _camAngle-90)));
depth = -floor(_dir)
Attempt 2:
Code:
depth = -(y+sprite_height/4)//-point_direction(x, y, -_camAngle+90, _camAngle+90);
Attempt 3:
Code:
depth = (image_angle/360)-0.5
Attempt 4:
Code:
var _vW = camera_get_view_width(view_camera[0])
var _vH = camera_get_view_height(view_camera[0])
var _vX = camera_get_view_x(view_camera[0])
var _vY = camera_get_view_y(view_camera[0])
depth = point_distance(x, y, _vX+_vW, _vY+_vH)/10;
Attempt 5: (currently in a script)
Code:
var _camAngle = abs(camera_get_view_angle(view_camera[0]));
if (_camAngle<180) & (_camAngle > 0) return -y
if ( _camAngle > 90 && _camAngle < 180 ) return -x
if ( _camAngle > 180 && _camAngle < 360 ) return y-room_height
if ( _camAngle > 270 && _camAngle < 360 ) return x-room_width
-------------------------------------------------------------------------------------------------------
Here is my tree draw event as well:
Code:
var _hCalc = height/image_number
    var _camAngle = camera_get_view_angle(view_camera[0]);
    for ( var i=0; i<image_number; i++ ){
     
        var _sway = (cos(((current_time)/250)+swayOffset)*image_xscale)*i
        var _ang = image_angle-(_sway/2)//-(_camAngle/90);
     
        if ( i == image_number-1 ){
            _ang = (-_sway/4)-_camAngle;
        }
        if ( !sway ){
            _sway = 0;
        }
        var _ldx = lengthdir_x(_sway+(i*_hCalc*image_xscale), -_camAngle+90);
        var _ldy = lengthdir_y(i*_hCalc*image_yscale, _camAngle+90);
        var _midX = camera_get_view_x(view_camera[0])+(camera_get_view_width(view_camera[0])/2)
        var _midY = camera_get_view_y(view_camera[0])+(camera_get_view_height(view_camera[0])/2)
        draw_sprite_ext(sprite_index, i, x+_ldx, y+_ldy, image_xscale, image_yscale, _ang, mainCol[i], image_alpha)
}

Question:
What is the best solution to this problem? I am trying to keep is simple and easy to implement without getting into z buffers and what not. (that stuff kinda goes over my head). I know it would help to keep the different layers at different depths and it would be a little easier to manage but seeing as I am using a for loop to draw each layer at the depth of the object, I have no clue how to really do that. Any help is MUUUCHH appreciated.

Desired Effect:




Thanks in advance! Love this community and hope I can share this little engine design for beginners once I am done! :D
 
Last edited:

TheouAegis

Member
What about just

depth = -y*cos(_camAngle)+x*sin(_camAngle);

Keep in mind the values would swing between positive and negative depths quite easily as the code is right now.
 
Last edited:

Gizmo199

Member
What about just

depth = -y*cos(_camAngle)+x*sin(_camAngle);

Keep in mind the values would swing between positive and negative depths quite easily as the code is right now.
So I tried it out but with some very weird results. Basically it rapidly clips the depth between being correct or the opposite of the desired effect. Hard to explain, but its almost as if odd-numbered angles produce the correct result and even-numbered angles produce the incorrect result. Not saying that that is what is actually happening but if you imagine it that as you turn the camera, every other degree the angle is at it clips below or above. Hope i'm explaining that right. haha. Definitely gonna play with it though. The problem is consistent depth sorting as the camera spins.
 

NightFrost

Member
Yea, it is a little bit weird that while GML normally works with degrees everywhere, those angle functions take radians, and you need to use the d* ones to handle degrees. They should have given degrees to those and created rsin etc for the exceptions when someone wants to work with radians instead.
 
Yea, it is a little bit weird that while GML normally works with degrees everywhere, those angle functions take radians, and you need to use the d* ones to handle degrees. They should have given degrees to those and created rsin etc for the exceptions when someone wants to work with radians instead.
But it's standard practice for cos/sin functions to work with radians, not degrees. If they were to do it the other way around things would get very confusing for people when they want to transition to another language or engine. You have to remember that a lot of the other functions that take degrees are shorter methods of achieving a result you would normally get with cos/sin i.e 'lengthdir_x(length, angle)' is just 'cos(degtorad(angle)) * length'

You could also use the function degtorad in standard cos/sin functions
 

Gizmo199

Member
So I have found a solution but I am kind of worried as to how well it will perform with multiple 3d objects in the room.

video of solution:

How this solution works:
So I basically create new layers for each sprite and set the layer depths to match the height/number of images the sprite has and draw them accordingly. So here are the 3 scripts used to "model" the sprite:

f3d_model_create()
Code:
///@param sprite
///@param x
///@param y
///@param height

modelSpr = argument0;
modelX = argument1;
modelY = argument2;
modelHeight = argument3;
modelIndex = sprite_get_number(modelSpr);

for ( var i=0; i<modelIndex; i++ ){
    // Depth layers
    lId[i] = layer_create(-((modelHeight*((image_yscale+image_xscale)/2))/modelIndex)*i);
   
    // Create the sprite
    lSpr[i] = layer_sprite_create(lId[i], modelX, modelY, modelSpr);
    layer_sprite_speed(lSpr[i], 0)
    layer_sprite_index(lSpr[i], i)
}

f3d_model_render()
Code:
///@param camera
///@param x
///@param y
///@param angle
///@param blend
///@param alpha

var _cam = argument0
var _camAngle = camera_get_view_angle(_cam);

var _mX = argument1
var _mY = argument2
var _ang = argument3
var _blend = argument4
var _alp = argument5

var _hCalc = modelHeight/modelIndex

for ( var i=0; i<modelIndex; i++ ){
  
    var _ldx = lengthdir_x(i*_hCalc*image_xscale, -_camAngle+90);
    var _ldy = lengthdir_y(i*_hCalc*image_yscale, _camAngle+90);

    layer_sprite_x(lSpr[i], _mX+_ldx);
    layer_sprite_y(lSpr[i], _mY+_ldy);
    layer_sprite_xscale(lSpr[i], image_xscale);
    layer_sprite_yscale(lSpr[i], image_yscale);
    layer_sprite_angle(lSpr[i], _ang);
    layer_sprite_blend(lSpr[i], _blend)
    layer_sprite_alpha(lSpr[i], _alp);
}

f3d_model_destroy()
Code:
for ( var i=0; i<modelIndex; i++ ){
    layer_destroy(lId[i]);  
}

Do you all think that creating layers like this will lag the game? I can't really find much about how efficient or not layers are. I know you can have a total of 32,000 layers so I am assuming it's optimized but IDK if all 32,000 can handle a sprite each and still generate strong performance. Thoughts?

Thank you again @TheStolenBattenberg @NightFrost @flyingsaucerinvasion @TheouAegis for the help!
 
Draw calls and texture swaps (done once per different sprite) are generally what cause slow down in games. If we assume that each 32,000 sprites are being drawn per object in the room then yes you are likely to get a lot of slow down, as this would be 32,000 multiplied by the count of objects with sprites.
 

Gizmo199

Member
Draw calls and texture swaps (done once per different sprite) are generally what cause slow down in games. If we assume that each 32,000 sprites are being drawn per object in the room then yes you are likely to get a lot of slow down, as this would be 32,000 multiplied by the count of objects with sprites.
Is this done for non-draw-phase calls? So for instance the "f3d_sprite_render" could be applied to the step event instead of the draw event. Are texture swaps only applied during the draw phase? if not would it be more efficient to direct each objects "draw" phase to a single rendering object, so only one object is drawing everything? I know texture pages would help keep everything in check as well but just curious. I know that YOYO made that optimized draw pipeline shader that allows for thousands of sprites to be drawn without much lag but not sure if thats a better route or not
 
I'm not entirely sure how it works in GMS2, but drawing multiple times in one object won't reduce the draw call count, as you're still doing the draw operation multiple times per frame. If you want to cut down draw calls, you can batch multiple sprites into a vertex buffer yourself, but this would mean you'd have to rebuild the buffers every frame for depth effect to be applied. Since this is done on the CPU and GM doesn't allow multithreading it's not going to be any faster.

To make sure we're clear, anything that results in pixels being displayed on the screen (or any function that modifies how those pixels are displayed i.e texture changes, colour changes etc) is a draw call. This means using a step event instead is likely not going to work.
 

Gizmo199

Member
I'm not entirely sure how it works in GMS2, but drawing multiple times in one object won't reduce the draw call count, as you're still doing the draw operation multiple times per frame. If you want to cut down draw calls, you can batch multiple sprites into a vertex buffer yourself, but this would mean you'd have to rebuild the buffers every frame for depth effect to be applied. Since this is done on the CPU and GM doesn't allow multithreading it's not going to be any faster.

To make sure we're clear, anything that results in pixels being displayed on the screen (or any function that modifies how those pixels are displayed i.e texture changes, colour changes etc) is a draw call. This means using a step event instead is likely not going to work.
Ah okay, I got you. Yeah, So I guess just billboarding sprites that don't necessarily need to be volumetric in nature is probably the way to reduce the sprite count. That's a shame that a buffer wouldn't really increase performance. Thanks for the insight! :D
 
If you wanted to do this effect with a vertex buffer, you would not need to rebuild it every frame. As long as the visual state of the objects in the buffer don't change that is.

The 3d effect can be reproduced with a shader. You could even use the shader to animate the tree swaying
 

Gizmo199

Member
If you wanted to do this effect with a vertex buffer, you would not need to rebuild it every frame. As long as the visual state of the objects in the buffer don't change that is.

The 3d effect can be reproduced with a shader. You could even use the shader to animate the tree swaying
I definitely am interested in this idea! do you have any sources/links that could point me in the right direction!? :D
 
It would be difficult to explain the vertex buffer idea. So I made a crude example for you. It's a bit complicated, but it's very fast. I am concerned about the possibility of there being depth conflicts under certain conditions.

https://www.dropbox.com/s/gpneakgfbv7w3aw/rotate_view_fake_3d_vertex_buffer.gmz?dl=0

The spriets are sorted according to their height (above ground), so we wont need to have the contents of the vertex buffer write to the depth buffer.

I used GMS1, so if you are using GMS2, a few functions in the draw event will need to be replaced with their GMS2 counterparts.
 
Last edited:

Gizmo199

Member
It would be difficult to explain the vertex buffer idea. So I made a crude example for you. It's a bit complicated, but it's very fast. I am concerned about the possibility of there being depth conflicts under certain conditions.

https://www.dropbox.com/s/gpneakgfbv7w3aw/rotate_view_fake_3d_vertex_buffer.gmz?dl=0

The spriets are sorted according to their height (above ground), so we wont need to have the contents of the vertex buffer write to the depth buffer.

I used GMS1, so if you are using GMS2, a few functions in the draw event will need to be replaced with their GMS2 counterparts.
This is fabulous! I only get to check it out for a sec tonight but I will dive in and take it in tomorrow! Great work! Thank you again!
 
Top