love2d / love

LÖVE is an awesome 2D game framework for Lua.
https://love2d.org
Other
4.84k stars 388 forks source link

Add love.graphics.getTransform() #1641

Closed codecat closed 2 years ago

codecat commented 3 years ago

This would allow to automatically build a Transform from the current transform set using love.graphics.translate etc.

This might also need an option to only get the top stack transformation or the full transformation of the entire stack.

geniuszxy commented 3 years ago

Why not use transform:translate and replaceTransform?

codecat commented 3 years ago

Because Transform by itself does not provide a stack like love.graphics.push() does. I can of course replicate the stack manually by implementing a new stack by hand in pure Lua, worst case scenario, but I would much rather use the actual functions provided by Love.

slime73 commented 3 years ago

One of the reasons I didn't add this to begin with is I didn't really want to encourage creating new love objects every frame. I also felt like love.graphics.transformPoint and inverseTransformPoint would probably cover most use cases people would want to retrieve the Transform for.

What sort of use cases did you have in mind?

codecat commented 3 years ago

My use case for this was in a GUI, where I'd stack transforms for each individual widget. I haven't worked on this project for a while so I don't remember the specifics off the top of my head.

slime73 commented 2 years ago

I think I'll close this for now but I'm open to revisiting it if people have some good+specific use cases.

lolbinarycat commented 2 years ago

here's a use case: i have a level editor. i want to add an object where the mouse clicked. i can't use inverseTransformPoint because the coordinate transformations are only active during the draw step. if i had getTransform, i could just save the transformation during the draw step. as is, it seems like i basically need to hack together some sort of event queue to perform the translation on the draw step, which feels ugly and wrong. there are some other possible solutions, but all of them feel flimsy and likely to go wrong.

basically, any time you want to translate a mouse event to global coordinates, you have to deal with this problem. i don't know if there's something i'm missing, but there doesn't seem to be an easy way to handle this with the current functions.

if you don't want people creating new objects every frame, you could require them to pass in an existing Transform object that would be replaced by the one on the top of the stack (sort of the opposite of love.graphics.replaceTransform).

thegrb93 commented 2 years ago

you could require them to pass in an existing Transform object that would be replaced by the one on the top of the stack

Or do something like Transform:copyContext() --Sets the transform to the current context's transform

steveRoll-git commented 2 years ago

Or do something like Transform:copyContext() --Sets the transform to the current context's transform

That's pretty much just Transform:setTransform(love.graphics.getTransform()), which makes more sense imo

slime73 commented 2 years ago

if i had getTransform, i could just save the transformation during the draw step.

Or do something like Transform:copyContext() --Sets the transform to the current context's transform

These will have to use the previous frame's transform if they're in the current frame's event or update code, right? That seems suboptimal. love.graphics' immediate transform API is fundamentally in conflict with code like you're describing that wants to perform transformations outside of that immediate context. I don't believe they're really reconcilable.

Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call Transform:transformPoint/inverseTransformPoint whenever you want, such as your mouse example, and you can call love.graphics.applyTransform or replaceTransform to use that transformation during drawing.

Basically you can treat something other than love.graphics immediate mode APIs as the ground-truth, and use that other thing everywhere including in drawing code.

thegrb93 commented 2 years ago

Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call Transform:transformPoint/inverseTransformPoint whenever you want, such as your mouse example, and you can call love.graphics.applyTransform or replaceTransform to use that transformation during drawing.

This usually how I do it

That's pretty much just Transform:setTransform(love.graphics.getTransform()), which makes more sense imo

Mine avoids creating an new object and an additional C call which slime was against.

lolbinarycat commented 2 years ago

if i had getTransform, i could just save the transformation during the draw step.

Or do something like Transform:copyContext() --Sets the transform to the current context's transform

These will have to use the previous frame's transform if they're in the current frame's event or update code, right? That seems suboptimal. love.graphics' immediate transform API is fundamentally in conflict with code like you're describing that wants to perform transformations outside of that immediate context. I don't believe they're really reconcilable.

yes, like every other bit of code, it can only refer to things that happened in the past. this really isn't a problem, or if it is, it's not one that is really solvable. i don't know what "fundamental conflict" there is, other than the fact that there's no way to save the current transformation.

Instead, if you keep your own Transform object and apply the transformations you want to that (or a plain Lua version of the same thing, which many camera libraries do for you), you would be able to call Transform:transformPoint/inverseTransformPoint whenever you want, such as your mouse example, and you can call love.graphics.applyTransform or replaceTransform to use that transformation during drawing.

Basically you can treat something other than love.graphics immediate mode APIs as the ground-truth, and use that other thing everywhere including in drawing code.

ok so, basically. instead of adding this quite basic operation, you want me to basically reimplement love2d's coordinate stack, except with the function i want, and refactor my code around that??

why even have the transformation stack then?

the current system seems to be setting up people for failure by providing a thing that looks like it does what they want, until later on they realize there is no good way to handle mouse events within their code.

slime73 commented 2 years ago

Conceptually, graphics operations happen as a result of game logic and events. They're at the end of the pipeline: they're affected by game logic, rather than the other way around. Most of love.graphics is built around this idea, including the fact that love.draw is separate from love.update and doesn't contain dt, for example (which encourages people to keep game logic earlier in the pipeline than graphics operations).

why even have the transformation stack then?

It's very useful as a way for game logic to drive graphics.

Camera systems tend to affect both game logic and graphics operations. love.graphics' transform APIs aren't a camera system. There are a few popular camera libraries that probably do something like you want (and aren't too complicated internally), if you're curious: https://github.com/love2d-community/awesome-love2d#camera

yes, like every other bit of code, it can only refer to things that happened in the past. this really isn't a problem

It's a problem because it's using the result of game logic that happened before the current frame's input events and game logic. For example if I press a button to teleport a far distance, I'd expect the game code after that button press to be based on that new position. But if the game uses the previous frame's data instead of the current frame's, everything will be out of sync for a frame. This isn't an issue with approaches that don't rely on end-of-pipeline data like you would with love.graphics transforms.