Help me understand how to dynamically create atlases (Solved)

I am trying to make a dynamic atlas from images on disk but failing horribly, I have been asking about this on the Discord but I think it’s time to make a forum post about it as I am clearly not smart enough to figure this out!

I start out with collecting the width and height of my images when loading it and saving that in a table also name and the actual buffer.

I then iterate over that and do my thing.

 -- this returns our table with all our needed data.
local pack_data = get_package_data()


local atlas_creation_params = {
	width  = 1024,  -- desired width and height of our atlas
	height = 1024,
	type   = resource.TEXTURE_TYPE_2D,
	format = resource.TEXTURE_FORMAT_RGBA,
}
local texture_id = resource.create_texture("my_atlas_name.texturec", atlas_creation_params)

-- Here we will store our data that we need when setting up the atlas data
local atlas_params = {texture="my_atlas_name.texturec", animations={}, geometries={}}

for i in pairs(pack_data) do
	local img = pack_data[i]
	local set_params = {width=img.w, height=img.h, x=img.w, y= img.y, type=resource.TEXTURE_TYPE_2D, format=resource.TEXTURE_FORMAT_RGBA}
	resource.set_texture(texture_id, set_params, img.buffer)

	-- We add this image data to our atlas parameters
	atlas_params = get_atlas_parameters(img, atlas_params)
end

-- Finally we create the atlas set
resource.create_atlas("my_atlas_name.texturesetc", atlas_params)

I have tried a lot o things for the atlas parameters

local function get_atlas_parameters(image, atlas_params)
	-- Do we need to increment the indices depending on how many images we got? 
	local indices =  {0,1,2,0,2,3}

	table.insert(atlas_params.animations, {
		id          = image.name,
		width       = image.w,
		height      = image.h,
		frame_start = 1,
		frame_end   = 2,
	})
	table.insert(atlas_params.geometries, {
		-- Have also tried to have these be "local" in all combinations
		vertices  = {
			0,   0,
			0,   image.h,
			image.w, image.h,
			image.w, 0
		},
		uvs = {
			image.x,   image.y,
			image.x,   image.y + image.h,
			image.x + image.w, image.y + image.h,
			image.x + image.w, image.y
		},
		indices = indices
	})
	return atlas_params
end

I calculate the x and y in a different location but it’s just putting them on a row.

This doesn’t work for me, I know that the textures are loaded fine because if I only load one image and set the atlas_creation_params width and height to be the same as the image it shows. But if i increase them to a bigger value the image will eventually disappear, making me believe that the atlas lookup and position on the atlas are not correct but I can’t figure out why.

Can anyone see something obvious that’s wrong here?

3 Likes

After digging into this some more I am quite sure the error is when I add the textures to the atlas.

Let’s say I were to add 2 images that are 16x32px next to each other. The first time I would do

set_params = {width=16, height=32, x=0, y=0, type=resource.TEXTURE_TYPE_2D} 

Then the scond time I would just offset it with the previous images width

set_params = {width=16, height=32, x=16, y=0, type=resource.TEXTURE_TYPE_2D} 

I would assume then that when I setup my UVs for my animations in resource.create_atlas they will have the same values as these numbers. Is this assumptions correct or do I need to convert values here somewhere?

Did you and @jhonny.goransson solve the problem via Discord chat?

No, not yet. But he is helping me out there too sent an example which made me noticed one thing I did wrong but the over arching problem is still there.

1 Like

I manage to crash it too.

It’s a bit weird. I have an atlas with an image (image0.png) in it. I have this atlas set to a sprite. I then create a new atlas and set the image with go.set("#sprite1", "image", atlas_id) then I play a flipbook.

This crash happens if I add a new image (image1.png) to the atlas and switch the sprite to that (image1.png). If I don’t switch but keep the (image0.png) it doesn’t crash.

Notably it crashes on the go.set("#sprite1", "image", atlas_id) call and not the play flipbook one.

crash dumps (18.3 KB)

INFO:ENGINE: Defold Engine 1.4.2 (8cd3a63)
INFO:DLIB: Initialized Remotery (ws://127.0.0.1:17815/rmt)
INFO:ENGINE: Loading data from: http://127.0.0.1:52009/build
INFO:ENGINE: Initialised sound device 'default'
INFO:CRASH: Successfully wrote Crashdump to file: C:\Users\Jerakin\AppData\Roaming\Defold/_crash
ERROR:CRASH: CALL STACK:

ERROR:CRASH:  0 0x7FF6A515F620 dmCrash::GenerateCallstack D:\a\defold\defold\engine\crash\src\backtrace_win32.cpp:144

ERROR:CRASH:  1 0x7FF6A5237EA4 _seh_filter_exe minkernel\crts\ucrt\src\appcrt\misc\exception_filter.cpp:219

ERROR:CRASH:  2 0x7FF6A527BEB4 `__scrt_common_main_seh'::`1'::filt$0 d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:304

ERROR:CRASH:  3 0x7FF6A51CC2C4 __C_specific_handler d:\agent\_work\4\s\src\vctools\crt\vcruntime\src\eh\riscchandler.cpp:290

ERROR:CRASH:  4 0x7FFB9E272300 __chkstk <unknown>:0

ERROR:CRASH:  5 0x7FFB9E221070 RtlRaiseException <unknown>:0

ERROR:CRASH:  6 0x7FFB9E270F20 KiUserExceptionDispatcher <unknown>:0

ERROR:CRASH:  7 0x7FF6A4EA2120 dmGameSystem::UpdateVertexAndIndexCount D:\a\defold\defold\engine\gamesys\src\gamesys\components\comp_sprite.cpp:1008

ERROR:CRASH:  8 0x7FF6A4E9CDF0 dmGameSystem::CompSpriteRender D:\a\defold\defold\engine\gamesys\src\gamesys\components\comp_sprite.cpp:1126

ERROR:CRASH:  9 0x7FF6A4E050A0 dmGameObject::Render D:\a\defold\defold\engine\gameobject\src\gameobject\gameobject.cpp:2679

ERROR:CRASH: 10 0x7FF6A4DE4DF0 dmEngine::StepFrame D:\a\defold\defold\engine\engine\src\engine.cpp:1590

ERROR:CRASH: 11 0x7FF6A4DE5A90 dmEngineUpdate D:\a\defold\defold\engine\engine\src\engine.cpp:2098

ERROR:CRASH: 12 0x7FF6A4DE5CD0 dmEngine::RunLoop D:\a\defold\defold\engine\engine\src\engine_loop.cpp:83

ERROR:CRASH: 13 0x7FF6A4DDE3C0 engine_main D:\a\defold\defold\engine\engine\src\engine_main.cpp:148

ERROR:CRASH: 14 0x7FF6A51C74E4 __scrt_common_main_seh d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288

ERROR:CRASH: 15 0x7FFB9C7B7600 BaseThreadInitThunk <unknown>:0

ERROR:CRASH: 16 0x7FFB9E222680 RtlUserThreadStart <unknown>:0

ERROR:CRASH: 

INFO:CRASH: Successfully wrote Crashdump to file: C:\Users\Jerakin\AppData\Roaming\Defold/_crash
ERROR:CRASH: CALL STACK:

ERROR:CRASH:  0 0x7FF6A4EA2120 dmGameSystem::UpdateVertexAndIndexCount D:\a\defold\defold\engine\gamesys\src\gamesys\components\comp_sprite.cpp:1008

ERROR:CRASH:  1 0x7FF6A4E9CDF0 dmGameSystem::CompSpriteRender D:\a\defold\defold\engine\gamesys\src\gamesys\components\comp_sprite.cpp:1126

ERROR:CRASH:  2 0x7FF6A4E050A0 dmGameObject::Render D:\a\defold\defold\engine\gameobject\src\gameobject\gameobject.cpp:2679

ERROR:CRASH:  3 0x7FF6A4DE4DF0 dmEngine::StepFrame D:\a\defold\defold\engine\engine\src\engine.cpp:1590

ERROR:CRASH:  4 0x7FF6A4DE5A90 dmEngineUpdate D:\a\defold\defold\engine\engine\src\engine.cpp:2098

ERROR:CRASH:  5 0x7FF6A4DE5CD0 dmEngine::RunLoop D:\a\defold\defold\engine\engine\src\engine_loop.cpp:83

ERROR:CRASH:  6 0x7FF6A4DDE3C0 engine_main D:\a\defold\defold\engine\engine\src\engine_main.cpp:148

ERROR:CRASH:  7 0x7FF6A51C74E4 __scrt_common_main_seh d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288

ERROR:CRASH:  8 0x7FFB9C7B7600 BaseThreadInitThunk <unknown>:0

ERROR:CRASH:  9 0x7FFB9E222680 RtlUserThreadStart <unknown>:0

ERROR:CRASH: 

INFO:CRASH: Successfully wrote MiniDump to file: C:\Users\Jerakin\AppData\Roaming\Defold/_crash.dmp

Could you share the repro for this? It’s likely that your indices are incorrect, but I want to make sure it’s not an engine issue

2 Likes

All my images are square, doesn’t that mean that they should all be {0,1,2,0,2,3}

To me it kind of feels like the 0, 0 for set_texture is bottom left. But for uv it’s top left or something like that

I set the whole texture as an image and it does look like what I expected at least. So the images are added correctly
2023-02-23 23_46_10-defold-dynamic-atlas

I guess this is what happens when you stare yourself blind at something… I forgot to update the frame number for the images. :man_facepalming:

To clarify, in my code above the only logical issue is


	table.insert(atlas_params.animations, {
		id          = image.name,
		width       = image.w,
		height      = image.h,
		frame_start = 1,
		frame_end   = 2,
	})

As each of my images are set as it’s own animation I also need to update the frame or it will only play the first frame (with diffrent w/h).

That means that the fix is something like this


	table.insert(atlas_params.animations, {
		id          = image.name,
		width       = image.w,
		height      = image.h,
		frame_start = #atlas_params.animations + 1,
		frame_end   = #atlas_params.animations + 2,
	})
6 Likes