Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to handle camera usage? #218

Open
philikran opened this issue May 16, 2019 · 20 comments
Open

how to handle camera usage? #218

philikran opened this issue May 16, 2019 · 20 comments

Comments

@philikran
Copy link

I have a problem when i use sti with a camera like gamera. If i use no special window and display the map in the full window everything is ok. But when i use a defined window where the map should be drawn, the viewport is displaced anywhere at the screen. when i use a static image instead of the sti-map it looks fine. I suppose that the problem is something with the translating of the draw-elements. Maybe because gamera takes a translating and sti takes another translating and in the result the display is shifted to anywhere... something like this. I dont think that there is an issue in the sti-lib but in the way how i use it. Maybe you have an idea how to handle this.

tiletest

The attached zip-file is a love-demo.
Thanks.

tiletest.love.zip

@karai17
Copy link
Owner

karai17 commented May 16, 2019 via email

@philikran
Copy link
Author

philikran commented May 16, 2019

does it work without gamera?

yes, without a camera lib it works correct. and with a camera which uses the complete window it works too.

@karai17
Copy link
Owner

karai17 commented May 16, 2019 via email

@philikran
Copy link
Author

philikran commented May 16, 2019

Ok. Not the answer i hoped for. A reason for using gamera in this project is to work with multiple windows (minmap and also use a splitscreen for coop). Maybe i find another solution for this. Thanks so far.

@karai17
Copy link
Owner

karai17 commented May 16, 2019 via email

@philikran
Copy link
Author

I dont think that as PR is necessary. If i find a solution for this i will give a feedback.

@Bobbyjoness
Copy link
Contributor

Bobbyjoness commented May 17, 2019

It is not really recommended to use a camera lib with STI; I've always found them to be troublesome and they provide very little, if any actual benefit. Just use love.graphics.translate/scale/rotate manually and you should be fine.

I just returned to an old project and just checked just translating does not work. at all. Translating did not affect the map at all like it affected the bump_draw or me drawing my active characters. They don't line up.

With translation:
Screenshot (22)
Without:
Screenshot (23)

@flamendless
Copy link

@karai17 camera libs use scale and transform and such as well.

Sent from my HUAWEI GR5 2017 using FastHub

@karai17
Copy link
Owner

karai17 commented May 22, 2019

@Bobbyjoness Has the bump plugin been updated to match sti's draw function? perhaps there is a discrepency in how map.bump_draw acts.

@flamendless Yes, that is why I said just use those directly. The camera lib wrapper doesn't really add much since it's ultimately just using those main functions anyway.

@karai17
Copy link
Owner

karai17 commented May 22, 2019

Over the past few years I've struggled with getting STI to act properly when the map is moved around or scaled. I've tried several different methods and the only one that seems to have really worked correctly is to just take over the graphics state while rendering the map and anything in it. This means that all transforms done with regards to the map are done by the map. Because of this, it makes using a camera library both unnecessary for typical use cases, and incompatible for edge cases like the one described in this issue. For 99% of STI users, just tossing in the trnasform values into the draw function (like you would in any love.graphics function) works without issue. The map moves where you expect it to, it scales without weird graphical glitches, etc.

That being said, as I stated above, I am open to a PR that keeps STI's current state of rendering (no known graphical glitches) while also being able to hand over control to a camera library. I've tried in the past and could not find a solution, but if someone else can, I am all for it.

@Bobbyjoness
Copy link
Contributor

The reason people use camera libraries is that it abstracts away the code making the code more readable. It also provides convenient tweening methods and other conveniences that would just be a waste of time to reimplement over and over. To solve this problem we can store the translate, scale, and rotation values in variables internal to the library. Then provide methods to edit those values. This should allow for a good camera plugin to be created. As of right now, I do not believe a camera plugin can be created unless it provides its own map draw.

@tpimh
Copy link

tpimh commented Jul 7, 2019

Maybe sti draw function can accept camera object as an argument to handle all the drawing like Editgrid does?

@karai17
Copy link
Owner

karai17 commented Jul 7, 2019

I don't want STI to depend on other libraries, so unless there is a "common camera" implementation I can design against, I don't think having map.draw taking in a camera object would work.

@tpimh
Copy link

tpimh commented Jul 7, 2019

Editgrid doesn't depend on gamera or HUMP camera, but supports both. Maybe this can be added as a plugin?

@karai17
Copy link
Owner

karai17 commented Jul 7, 2019

I am definitely down for more plugins, as long as they are optional.

@sdleffler
Copy link

sdleffler commented Jun 15, 2020

hiya! just wanted to share that I encountered this problem, while using gamera, and I've (temporarily) solved it like this:

(pipeline.camera contains my rendering pipeline's gamera camera instance)

function TiledMapRenderer:draw(pipeline)
    pipeline:setZ(0)
    local camera = pipeline.camera

    camera:draw(function(l, t, w, h)
        for _, e in ipairs(self.entities) do
            local map = e[TiledMapComponent].map
            for _, layer in ipairs(map.layers) do
                if layer.visible and layer.opacity > 0 then
                    map:drawLayer(layer)
                end
            end

            do
                local collision = map.box2d_collision
                for _, obj in ipairs(collision) do
                    local points = {obj.body:getWorldPoints(obj.shape:getPoints())}
                    local shape_type = obj.shape:getType()
        
                    if shape_type == "edge" or shape_type == "chain" then
                        love.graphics.line(points)
                    elseif shape_type == "polygon" then
                        love.graphics.polygon("line", points)
                    else
                        error("sti box2d plugin does not support "..shape_type.." shapes")
                    end
                end
            end
        end
    end)
end

it's a hack and I plan to fix it up later but it shows how you can force STI to work with a camera if you're as pissed off and stubborn as I am about it. I just copy/pasted the rendering code from Map.draw and box2d_draw in since at this stage that's what I care about; anyways, point is since you have drawLayer and Map.draw is pretty much trivial you can just drop the code right in and have it work. I haven't seen any issues with tearing but I need to see if they do crop up and whether or not I can get the fix in Map.draw to work with that as well. My rendering pipeline is... a bit weird, so if they do I'm certain I can get something to work with it, but again my use case is atypical, so if I find something and share it I'm not sure that particular solution will be portable. Anyways here's a solution if you still need one in a pinch; it might be worth exposing a Map.rawDraw function that works like this and is used internally by Map.draw, perhaps? Anyhow, cheers!

@karai17
Copy link
Owner

karai17 commented Jun 16, 2020

I wonder if there is a way to go about it the other way: Writing an STI plugin that works with a gamera object instead of injecting code into the gamera object?

@sdleffler
Copy link

Hmm, I think it should be doable. I think I'll take a stab at it today if I can grok the plugin API well enough.

@sdleffler
Copy link

Oh, I should mention: injecting code into the gamera instance is how gamera works; there's no way around that one, short of modifying gamera to return an instance. It does love.graphics.push/pop inside there around the closure. Kinda like how love.graphics.stencil works.

@sdleffler
Copy link

Alright! I have a little gamera-enabled proof of concept over in my fork here. To test it, I added gamera.lua temporarily to that repo and modified the main.lua file to use the gamera plugin. Current issues with it are that since it doesn't do the same pixel-rounding technique as STI's Map.draw, it doesn't quite match up with box2d_draw in the objects.lua test (and the other tests besides "ortho" and "ortho_inf" appear to be currently broken since the image directory has changed since they were written.)

Anyhow, I've found that there are a number of drawbacks to using a plugin as they currently exist over directly modifying STI to add a few more hooks to support custom coordinate transformations. The biggest one is that if you use this and then you use, say, box2d_draw to debug your colliders, the results are not necessarily going to match up, because gamera does more than just translate and scale; it can also rotate.

To give STI the ability to support custom coordinate transformations, it should be as simple as allowing plugins/the user to specify some sort of function to override the origin/translate/scale calls inside Map.draw. This could be done as a pair of functions begin/end to push and then pop/undo the transform, or it could be done with a function that takes in a closure, and performs the transform before calling the closure and pops it after the closure returns (like how gamera works). The default implementation could be lifted straight from Map.draw as it is now, so it wouldn't change the current behavior and would be backwards compatible.

Unfortunately, the big issue with rendering something through gamera is that since the camera rotates, the AABB of the visible section of the screen may be larger than the screen itself. This means that the size of the canvas used internally by STI would need to be made larger to support that, specifically made square, with sides as long as the longest diagonal of the screen. With the canvas resized to support rotation, the current STI method of rendering at scale=1, rounding the unscaled offset to an integer, and then scaling up would still work, and rotating could be added at the end of that.

If custom coordinate transformation hooks were added then there would also need to be hooks added to modify convertPixelToTile and convertTileToPixel... Or, the custom transform hooks would have to be graphics-nonspecific enough to implement a pre-transform to fix the inputs to the convertXXXToYYY functions. In the little test I made, converting the mouse position to gamera's world coordinates w/ gamera.toWorld and then feeding the resulting point into convertPixelToTile produced the correct result; but, I have the feeling that might not be sufficient once the camera starts rotating/scaling/such. I will need to test that.

With custom transform hooks, it would become possible to write a gamera_install_hooks function in a gamera plugin, which would overwrite those functions in the Map object with ones which reference a camera object and use it to calculate those transforms instead. This would be easiest with the closure variant because of how gamera works, and I think that'd end up flexible, but it would also be possible to do it directly using the coordinate transform that gamera tracks. The biggest issue I see with this is that if multiple plugins want to overwrite the same functions you're going to have some weird stuff happen, so I can totally understand if this isn't an acceptable solution.

If STI were given these hooks, then the bump.lua and box2d plugins as they currently exist could be updated to work with or without a camera by accessing these hooks, and other camera plugins could be made to overwrite those hooks and those plugins would work with those cameras. Hopefully. But again this doesn't address the tearing fixes that currently exist in Map.draw; since I haven't experienced them, I can't try to fix them in my little gamera draw function as of now. If I had a bit more information on what those issues were it might be more doable to deal with them. Otherwise, I could recreate the rough flow of that code by using gamera's coordinate transform tracking stuff, and get the same effect... I might try that in the next few days.

Sorry for rambling, this got a bit long. Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants