3d-tilemap example (tilemap to mesh)

Hi there!

Here is my code example for creating a mesh from a tilemap in runtime:

An example is well commented, hope it helps someone to open the wonderful world of Defold meshes :slight_smile:

Try it here: HTML 5 demo

How to use:

  1. Copy ‘mtool.lua’ module to your project.
  2. Create a standard tilemap.
  3. Add to scene a game object with a mesh component. We will use it as target later.
  4. In your code require mtool: local mtool = require "src.mtool"
  5. Fill the options table:
local options = {
		tilemap = "level#level", -- Tilemap URL.
		layer = "layer1", -- Tilemap layer name.
		w = 16, -- Horizontal number of tiles in tileset (texture atlas).
		h = 16, -- Vertical number of tiles in tileset (texture atlas).
		tilesize = 16, -- Size of tile in pixels (it will be use as scale factor when a script generates quads).
		
		-- Note: The same image that using as tilesource image we are using as a texture for generated mesh.
		-- Each tile in tilemap layer will represented as box.
		-- 
		-- Each box contains 6 edges (faces). We can replacement their textures by setting tile number for each edge.
		-- '1' is front edge, '2' - top, etc.
		--    [2]
		-- [4][1][5][6]
		--    [3]
		-- For example we want see a mixed grass tile (N) on front edge and pure green tile (M) on top edge.
		-- So, mapping it as: [N] = {N, M, N, N, N, N}
		-- 
		
		tile_info = {
			[2] = {2, 18, 34, 1, 1, 1},
			[3] = {3, 19, 35, 1, 1, 1},
			[4] = {4, 20, 36, 1, 1, 1},
			[6] = {6, 19, 35, 1, 1, 1},
			[51] = {51, 49, 49, 49, 49, 49},
			[53] = {53, 49, 49, 49, 49, 49},
		},

		-- You also can setup function 'tile_info_fn' for dynamic setting tile number. It may useful for generating levels in runtime.
		-- tile_info_fn = function(tileno, x, y)
		--  	if tileno == 1 then return {1, 4, 1, 1, 1, 1} end
		-- end

		optimize = false, -- If 'true' a script will culling the hidden by neighbors faces (edges 2,3,4,5).
		optimize_back = false -- If 'true' a script will culling back face (6) for all tiles.

		-- Also you may setup a texture atlas and setup for some tile numbers UV coordinates directly way.
		-- It's optionaly and made for further commits.
		-- All units are in pixels. Top-left corner is 0,0.

		-- For example:
		-- texture = {
		-- 	width = 512,
		-- 	height = 512,
		-- 	[2] = {x = 128, y = 0, w = 128, h = 256},
		-- }
	}
  1. Create a mesh from tilemap:
	self.grid = mtool.create_mesh_from_tilemap("go#mesh", options)
  1. Hide origin tilemap.
	msg.post(options.tilemap, "disable")

An example contains utility camera contoller script and uses rendercam extension.
Custom render script is modified rendercam render script where fixed render ordering 3d models and 2d objects. In example you may see that sprites are drawing in the same space as mesh tilemap and their can overlap each other.

If you do not need to see light source and want to do flat colors then remove “Diffuse light calculations” sections in the mesh fragment shader (assets/material/mesh.fp). In this case it should looks like

void main()
{
    // Pre-multiply alpha since all runtime textures already are
    vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
    vec4 color = texture2D(tex0, var_texcoord0.xy)*tint_pm;
    gl_FragColor = vec4(color.rgb*var_color0.rgb, color.a*var_color0.a);
}

Happy Defolding!

23 Likes

This is really great! Thank you for sharing.

5 Likes

How did you?! :flushed: Looking soo cool! :star_struck:

Thanks for sharing this, this looks like a great way to do a classic brawler game like Super Smash Brothers or something along those lines. Would it be cool to adapt this example for use in a game I started working on? This would be a great way to render the levels since I’m working on a top down shooter style game.

2 Likes

Another great optimization is for the mesh components. They will no longer upload their vertex buffer each frame, but only if the buffer is modified. The Native Extension api got the new
https://github.com/defold/defold/issues/5167

Seems, this changing of mesh component in the latest release has broken some cool mesh behavior. I mean able to change vertex properties (color, position, uv) in the realtime. In the demo above that feature was used for changing a quad color in the generated before mesh component. (Click on tile within a 3d tilemap). If build a demo with a new engine version, nothing will happen.

How to let know the mesh component need to change his state (upload the buffer) now? @Mathias_Westerdahl

Ah, that’s unfortunate and it’s not intentional.

From C++ you’ve have to use the new special function dmBuffer::UpdateContentVersion(dmBuffer::HBuffer).

From Lua, we should be able to detect accesses and writes to the buffer.
The (C++) implementation for updating a buffer stream from Lua is here.

What type is the mesh.color0 and how do you update the underlying mesh stream?

As I understand the type declare in buffer.create method here:


Then this color table getting as buffer stream local color0 = buffer.get_stream(buf, "color0") and stored in mesh object (this is just a table with fields like buffer, streams etc).
So, in the code I just change values in this streams and can see changes immediately.

May be we need for special method in Lua to call c++ update buffer function?

1 Like
local color0 = buffer.get_stream(buf, "color0")
color0[0] = ...

Using this should have worked, so I need to debug that.
And once it works, there would be no need for a Lua version of the buffer update function.

3 Likes

I’d be nice!
For example this Mesh Component little demo doesn’t work too.

3 Likes

Fix is coming in 1.2.182 (PR is in review)

4 Likes

Checked in beta 182. Works correct. Thanks!

5 Likes

Another example:

14 Likes

This is absolutely fantastic! It looks like a really nice workflow. I’d like to share more about this in a future blog post.

6 Likes