Clipping Tilemaps


I want to be able to show three tilemap scenes side-by-side, something along these lines:


I thought clipping nodes would be the way to do this, but it looks like clipping is only available for GUI nodes, and it doesn’t appear to be possible to put a Tilemap in a GUI. Is there some other way that I would be able to achieve this kind of an effect?

1 Like


You’ll need a split screen shader which can cut the screen in those kinds of shapes / preset masks to mask off those shapes. Then you’ll need 3 render predicates / material tags and 3 sets of materials which have those material tags set and to render each scene to its masked off area one after another until the full image is combined.

For a split screen shader like you have illustrated above think circle sectors. That’s basically what you want in the illustration. I don’t know if anyone has made an example of this exact setup for Defold but it is doable.

1 Like


Okay, I think the render predicates and material tags more or less make sense, but I have no idea where I might begin making a split screen shader. As I understand it, the shaders rely on a vertex program and a fragment program? Would it be something like having the vertex program return a material based on which split it’s a part of? I’ve never touched shaders before so I have basically no idea where to start poking.



What you can do is set the fragment program alpha to 0.0 based on if it is inside of the current circle sector or not. Pie chart shaders could give you an idea of how to select a sector of a circle

So for each set of rendering it would have its own constants when drawing each area you want to clip.

local constants_1 = render.constant_buffer()
constants.sector = vmath.vector4(sector_start, sector_end, 1, 1) 
render.draw(self.pred_1, constants_1)

You cannot recycle render constant buffers, use a unique one for each sector.



Okay, I think it looks like the missing piece that took me so long to digest this is that I tried starting at the wrong end. I was looking at the vertex and fragment programs, trying to figure out how they know what they’re working on; if I had started with making the material, I would have seen that the vertex and fragment programs are called by the material.

I think this means that I only need to make one vertex program and one fragment program, and differentiate between the three areas by the vertex and fragment constants on the material itself? I’m not sure exactly how that might relate to what you said about constant buffers, but I think I have enough info now to learn by failing at the very least.

EDIT: And it looks like the scene and sprites are the only places that define the material, so it looks like I will need to pass the correct material to the scene and the sprites contained therein through the collection factories and game objects.



Where is that code snippet intended to be used, exactly?



In the render script. It was referenced by the render file listed in the game.project file. Look at the lines related to defining / drawing the different render predicates / tags. You list the tags of a material at the bottom of the material file.



I’m having trouble getting it to draw if I change the tag, even without trying to change anything.

I set the tag on the material to character_1, added the line

self.character_1_pred = render.predicate({"character_1"})

to init(), and trimmed down your snippet to what seems to be the bare minimum:


and the material doesn’t show up unless I add the tile tag back to it.



To confirm

  1. Make new sprite material, assign it to the
  2. Edit to modify tag (tile -> character_1)
  3. Add tag to top of render script at top and draw it when other tags are drawn

Though I realize you want to be sending 3 camera positions to the render script either with messages or via a shared Lua module. Then I think you could redraw the scene with the same render target giving it different values for its radial clipping material along with the different camera offsets.



Does it need to be told how to draw a new predicate, or should it be enough to just call draw on the predicate?



Okay, so it turns out the new render script works so much better if you actually remember to set the Render in game.project to it.

Time for my nap.



Okay, I give up. I can’t figure out any values to give the constant buffer you use that make anything happen. Is there any kind of tutorial or info on using this that you could point me to? The Defold manuals and API reference don’t really explain any of this at all.



Okay, I’m thinking now that those must be constants provided to the vertex and fragment programs. I’ve figured out how to draw lines on the screen, I think I should be able to work my way up to figuring out the shadertoy sample from there.



Please keep going with trying to get this to work!

I know for sure it’s possible even though I’m not aware if anyone has done this specific thing yet.

Please post notes of specific official manuals you think are not clear enough in relation to the issues you brought up.



Well, for starters, the issue I’m having right now is that, while the API does talk about creating constant buffers, I’m not seeing any mention of how to reference them either there or in the Shader manual, so creating them isn’t doing a whole lot of good without knowing how to read them.

Edit: I’m checking to see if I can find the window size from the fragment program anywhere, but so far no luck.



Also, I think it sounds like they don’t have any inherent meaning other that what you use them for; so I could conceivably code the center coordinates of the circle in the vec4 with the start and end arcs, right? Something like vec4(center_x, center_y, sector_start, sector_end)? Or is there some reason I wouldn’t want to do that?



I’m not seeing any mention of how to reference them

A short manual for render constant buffers and how they related with materials / drawing predicates would be useful. I’ll answer some of what you mention even if it’s clear to you now.

You want to edit the material and fragment program to add the constants you are interested in. See how the tint constant is setup for the builtin sprite material. Unused constants are automatically stripped from shader programs.

You can pass the view size of the window as an additional constant when drawing a predicate. The code sample I posted above is an example of using constants.

local constants_1 = render.constant_buffer()
constants.sector = vmath.vector4(sector_start, sector_end, 1, 1) 
render.draw(self.pred_1, constants_1)

So you could do

local clip_constants = render.constant_buffer()
local window_view_size = vmath.vector4()
window_view_size.x = render.get_window_width()
window_view_size.y = render.get_window_height()
render.draw(self.pred_tiles, clip_constants)

(I’m pretty sure render.get_window_width() etc is for the actual view size of the window but don’t remember for sure atm, DefOS has a get view size function too.)

Then in a “pred_tiles” predicate it would be sent a window_view_size vec4 and you would be able to use it similar to how tint is used in sprites.

You can also update constants directly on for example sprite components but that breaks batching and in this case isn’t useful.

Also, I think it sounds like they don’t have any inherent meaning other that what you use them for

Right, vec4s are just the container being always used for right now. You don’t have to use all of the fields.

Something like vec4(center_x, center_y, sector_start, sector_end)

Yes, you can do that.



Ooh, another missing API reference documentation: are vmath operators a thing?

I see functions for dot() and stuff like that, but can I, for example, add or subtract vectors of the same size? Or divide a vector by a constant, so I don’t have to do fun constructs like:

-- wordy:
local center = vmath.vec4(viewport.x / 2, viewport.y / 2, 0, 0)
-- better:
local center = viewport / 2


Yes, you can do those.

function init(self)
	local viewport = vmath.vector4(800,600,0,0)
	local center = viewport / 2
	center = center - center
	center = center + viewport

DEBUG:SCRIPT: vmath.vector4(400, 300, 0, 0)
DEBUG:SCRIPT: vmath.vector4(0, 0, 0, 0)
DEBUG:SCRIPT: vmath.vector4(800, 600, 0, 0)



Lovely! Thanks for all your help! I’ve still got a bunch of mathy stuff to make sure I’m getting right (I think I’ve identified the important pieces from the shader you linked, but I like to make sure I understand what I’m inputting as much as possible), but I think I’ve got the important foundational stuff laid, at long, exhausting, last.

1 Like