I'm planning on making a simple game where you deform the terrain of stages to move a little ball around. With a little research, I have stumbled on SDFs and as destiny has it, I found a recent library for Playdate by robga with some very helpful examples (https://github.com/pdstuff/PlaydateSDF/tree/main), which provided me with a foundation for the collision and basic physics with smooth slopes that was easy to port to Love.
On the CPU side of things, things seem to be going well, I think I can handle the adjustments and optimizations to physics and collision I need. However, I have a big problem which is rendering the SDFs. On CPU, it is very slow, but with shaders I think it can be done while maintaining good performance.
The problem is, I need to evaluate the SDFs both on CPU for collision and on the GPU for rendering. For simple scenes, I can hardcode everything I need on both sides and it works, but scaling this seems like a nightmare.
I will start with a problem I'm having which I imagine the solution to be simple and then describe a much bigger thing to be solved.
So, I have 0 experience with shaders. While I managed to set up the rendering shader matching the rendered SDF with the one used on CPU for collision, I'm getting mixed up on the coordinates. To get things working, I'm scaling every argument passed to the SDF function by the screen height and for the SDF position, I'm offsetting the position like so:
Code: Select all
//Code for the shader,
float height = love_ScreenSize.y;
vec2 p = screen_coords/height;
vec2 pos = (400,300) //Position the the SDF should be drawn on screen
//sdCircle is a function that defines the SDF for a circle, taking a vec2 for position and a float for radius
float d = sdCircle(pos - p, 30/height);
So for the second, big problem. Let's imagine a scenario for a very simple game where each of its stages is defined by the union a fixed number of circle SDFs (i.e the min of all the SDFs). I can define the function for a circle SDF on both Lua and GLSL, a mapping function for the union and pass the arguments for each circle to the Love function that takes care of collisions and send also them to the shader. As a proof of concept, this should work well.
But if we go just a bit deeper, like adding an arbitrary number of SDFs for different types of primitives per scene, I'd need to define on a shader the function for every possible primitive, send to it information about how many and which primitives to use and the arguments for each SDF. If I want to compose a scene using different operations between SDFs or send the results of a SDF to another one to add distortions and stuff, I'd need either to parse a whole lot of data inside a shader in a huge mapping function.
It seems to me that the best way to go about this is coming up with a syntax that can handle defining primitives and operations between them, to be parsed directly for CPU collision and injecting shader text file with the definitions for the function I will be using and the mapping function to be used, avoiding the need to iterate through a bunch of data sent to a shader and evaluating nested functions accordingly.
I'm I overthinking this or approaching it from the wrong angle? Any suggestions on how to handle this would be welcome. I probably could use some other method of slopes and terrain deformation, but I've grown fond of SDFs in the little time I had with them. I want to see this working in some capacity.
I'm attaching to this a simple example of what I'm working on. It's a ball rolling around a horseshoe. Collisions handled on the CPU and rendering on a shader. You can see my jank implementation of the shader on main.lua and the collision and physics on collision.lua and Ball.lua.
sdf.lua defines the horseshoe SDF and vec2.lua is just a lib for vector operations, these can be ignored.