CoolDotty / Shader-Stacker

The GPU accelerated version of Sprite Stacking. A top-down 2.5D trick for the Godot Engine.
https://dotty.cool/Shader-Stacker/
Mozilla Public License 2.0
55 stars 7 forks source link

Rework cam_pitch #1

Closed CoolDotty closed 2 years ago

CoolDotty commented 3 years ago

Right now cam_pitch works by doubling up on layers and increasing the resolution height of the viewport. Basically, what can be known as the Moppin_ method. It should probably squish the layers instead, like the Azuzota method does.

CoolDotty commented 3 years ago

Blocked by #2

CoolDotty commented 3 years ago

So, yeah. I'm stuck on this for now. Luckily with the fps improvements in #5 setting a cam_pitch greater than 1 wont murder your framerate.

Sousio commented 3 years ago

Hi Karl! I really appreciate your work on this sprite stacker. Trying to implement the camera pitch, I studied davidpesce's cpu's stacker, I dug as well into your stacker and also the Securas' shader's stacker (though the latter didn't work for me! :( ), but ended up trying to convert the Azuzota's snippet from GameMaker to Godot. It takes me a lot of time, as I'd got no idea about the way the GM works!

I just realized that you're experienced in GM too and may help me on this. But the first and foremost question I've got to ask: Why did you prefer "canvas_item_add_texture_rect_region" over "canvas_item_add_polygon"?? Using the polygon, it looks easy to implement the Azuzota's logic (which involves 4 corner drawing) into godot, and so the camera pitch will be easy to achieve. This is my first thoughts! Have you tried that with some issues?

CoolDotty commented 3 years ago

I'll give it a try. It didn't occur to me that I could just use the polygon function as a direct port of the 4 corner rect lol. Just taking a look the function only take a Texture so I'll need to figure out how to get each layer as an individual texture. Hopefully by still using a sheet and not needing a separate file for each layer.

CoolDotty commented 3 years ago

Looks like I'll need to rework the code to use Atlas Textures

Sousio commented 3 years ago

I forgot to mention that the draw_polygon is considered slower than draw_rect and sprite rendering, though that should not be the case with Vulkan backend. Anyway in this stage you should expect the FPS to drop, but consider it as the loophole to achieve the minimal viable product. I think it would be easier also to store the texture layers inside an array and pass them to draw function. Once established, the stacker can be optimized to Godot's sense by: 1) let the shader define the texture layers' UVs; or implement the Atlas Texture. 2) Try to replace the 4 corner's draw function (which is the way GM works) wit sth specific to Godot. I just realized that the transformation's shearing can be the simpler and faster way to go. As there would be no rare cases where 4 corners be needed to form a trapezoid like GM's draw_sprite_pos does. Instead all the use cases will be in the form of Parallelogram; hence easy to achieve by transformations. The other advantage is that it does not need to draw every frame like what draw_polygon and draw_rect does, and just controls the sprite's transformations. It looks super efficient, though I don't know what's it's equivalent in visual server.

CoolDotty commented 3 years ago

Oh my god you're right. I've been assuming the sprites scale off axis but the skew. I'll poke around with transforms to get it to happen.

Now there's a question to be asked about what to do with hitboxes, since skewing the sprites is gonna desync them.

Sousio commented 3 years ago

Now there's a question to be asked about what to do with hitboxes, since skewing the sprites is gonna desync them.

I don’t think of the hitboxes and CollisionShapes to be a great concern; as most of the times, the hitboxes do not cover the whole area of the sprites, and it can be best to define a small area that always be covered by the whole sprite. If not, it’s still possible to change the CollisionShape2d’s polygon dynamically (just like how draw_polygon works), though not desirable.

I've been assuming the sprites scale off axis but the skew. I'll poke around with transforms to get it to happen.

At first I tried to modify your code to get integrated with Transforms for every single texture_rect_region drawn, but I was unable to skew every single texture/polygon separately. The scale property of draw_set_transform seems not to be enough to achieve the needed effect. But the VS.canvas_item_set_transform or draw_set_transform_matrix should be the way to go, however I didn’t dig into how to use them. If not functional, then maybe again the Davidpesce’s sprite rendering be the proper base for integrating the Transforms, if more desirable than draw_polygon below.


                                                         Polygon stacking

After all, I’ve finished porting the Azuzota's snippet from GameMaker to Godot and found that it works like a charm! The gist will be updated to include a better scale support, multiple row& column support, describing comments, and some other improvements and cleanups to get ready for publishing in Asset Store (after contacting the Azuzota)… However it may not be desirable for you right now because:

1.I’ve stripped out the camera_rotation feature, as it was out of the scope of my use case. You can add that feature easily.

  1. The snippet can work with either either draw_polygon() or VisualServer.canvas_item_add_polygon(). However the benchmarking shows no privilege over the sprite rendering. It is expected that a “full shader” or a “texture renderer with Trasform modification” approach outperform easily the mentioned approaches.
  2. The UV calculations for each layer may introduce some artifacts. This is related to the precision uncertainty involved with float numbers. On the other side, defining a Rect2 in VS.canvas_item_add_texture_rect_region or in sprite rendering involves the integers, so is more reliable on calculations. This can be overcame later with some conversions.
CoolDotty commented 3 years ago

Thanks! The code works awesome! I'm going to be working on fulling implementing the code with rotation and stuff. I've placed a commit for you (f797d0d1b2d98f96b148f06f11e227c6a4b2f64f) and I'll cut a new release with it once I add in some transforms to handle camera rotation and stuff.

CoolDotty commented 3 years ago

Oh yeah I've also shot Azuzota a message on twitter. We'll see if they reply or not. I can't imagine there will be any issue though, since the code was released for free on itch and not as a paid asset in the GameMaker marketplace.

CoolDotty commented 3 years ago

I'm almost through. Just trying to figure out what the last transform would be. Basically I need to take every canvas item's Y value relative to the viewport and scale their translation by an amount related to the camera pitch. Without effecting the sprite size. I'm hoping I can do this globally, but I'm only able to imagine math that involves knowing the position of the given canvas item. It's a real toughie.

Old on the left. New on the right. Camera is rotated 200 degrees. image

CoolDotty commented 3 years ago

Ugh, finally. I figured out how to squish things properly. What I was looking for was to apply the canvas transform and then translate by half of that Y value to get the same squishing effect.

var squish = get_canvas_transform().basis_xform(global_position).y * _perspectiveTop

This operations doesn't incorporate the canvas translation, so I just need to do that next. Then some hacking to get Godot's sprite culling to cooperate. Stuff is coming soon!

Sousio commented 3 years ago

Looks very promising!

CoolDotty commented 2 years ago

Fixed in 3.0.0