Dynamic cubemap texture? (SOLVED)

Hello everyone,
I started from this example https://github.com/abadonna/defold-skyquad to display a skybox.
Now, I would like to change it at runtime. I have my 6 square textures for each side of my cube. I know how to load them individually but I don’t know how to transform them into a cubemap texture. It remains a rather particular format. Should I send an array of 6 images using a buffer? In which order?
Finally, do you think it’s possible actually to dynamically change or create a “cubemap” texture at runtime?
Thanks for your help.

1 Like

First you need a cubemap resource, which you can create either via the editor or using resource.create_texture (API reference (resource)) in runtime. Then you upload all of the sides of your cubemap to the texture resource via the resource.set_texture function (API reference (resource)). If you create the texture via runtime, you need to assign this to your components via go.set. I think I have some examples how to upload cubemap data, let me see if I can find any.

4 Likes

Here is a small code snippet on how to create a buffer with cubemap data and upload it to a texture resource that already exists.

-- this is a script property, but you can reference
-- a resource from the project by the path to the built resource
-- something like "/main/my-cubemap.texturec"
go.property("cubemap", resource.texture())

local function test_cubemap(self)
	self.x = 16
	self.y = 16
	self.height = 128 - self.x * 2
	self.width = 128 - self.y * 2
	self.buffer = buffer.create(self.width * self.height * 6, { {name=hash("rgb"), type=buffer.VALUE_TYPE_UINT8, count=3} } )
	self.stream = buffer.get_stream(self.buffer, hash("rgb"))

	local psize = self.height * self.width * 3

	for z=1,6 do
		for y=1,self.height do
			for x=1,self.width do
				local index = (y-1) * self.width * 3 + (x-1) * 3 + 1
				index = index + psize * (z-1)

				self.stream[index + 0] = 0xff
				self.stream[index + 1] = 0x00
				self.stream[index + 2] = 0x00
			end
		end
	end

	local header = {
		width        = self.width,
		height       = self.height,
		type         = resource.TEXTURE_TYPE_CUBE_MAP,
		format       = resource.TEXTURE_FORMAT_RGB,
		x            = self.x,
		y            = self.y,
		mipmap       = 1,
	}
	resource.set_texture(self.cubemap, header, self.buffer)
end
5 Likes

thank you very much @jhonny.goransson this will help me a lot :smiley:

2 Likes

Let us know about the results! :wink: This is interesting use of some newest Defold features :orange_heart::blue_heart:

1 Like

Ok, now I have a little problem:

When I run this minimal code

		local cubemap_header = {
			width          = 1024,
			height         = 1024,
			type           = resource.TEXTURE_TYPE_CUBE_MAP,
			format         = resource.TEXTURE_FORMAT_RGB,
			max_mipmaps	   = 0,
		}
		
		local cubemap_texture = resource.create_texture("/mycubemap.texturec", cubemap_header)
		local cubemap_buffer = buffer.create(1024 * 1024 * 6, { {name=hash("rgb"), type=buffer.VALUE_TYPE_UINT8, count=3} } )
		resource.set_texture(cubemap_texture, cubemap_header, cubemap_buffer)
		go.set("/sky#model", "texture0", cubemap_texture)

I get this crashdump:

ERROR:CRASH: CALL STACK:
   
# 0 pc     0x2af824 dmengine _ZN7dmCrashL7HandlerEiP9__siginfoPv+36
# 1 pc     0x2e2dfd libxpc.dylib _sigtramp+29
# 2 pc  0x20d440acc libxslt.1.dylib _Z15glgConvertTo_32I23GLGConverter_BGR8_ARGB8L9GLGMemory0EEvPK15GLGOperationRecPK15GLDPixelModeRec+599
# 3 pc  0x20d4342c6 libxslt.1.dylib glgProcessPixelsWithProcessor+3639
# 4 pc    0xe0be82b dmengine _ZN13GLDTextureRec18uploadTextureLevelEjjjjjjjjjjjjjP12GLDBufferRecPhiiiU13block_pointerFPU30objcproto19MTLCommandBufferSPI11objc_objectvEU13block_pointerFvS4_jEU13block_pointerFvU13block_pointerFvvEESC_PK15GLDPixelModeRecbb+2
# 5 pc    0xe0f7490 dmengine gldModifyTexSubImage+1896
# 6 pc  0x20d653dce libxslt.1.dylib glTexImage2D_Exec+1926
# 7 pc  0x20d631190 libxslt.1.dylib glTexImage2D+55
# 8 pc      0xc9784 dmengine _ZN10dmGraphicsL16OpenGLSetTextureEPNS_7TextureERKNS_13TextureParamsE+3284
# 9 pc      0xcae55 dmengine _ZN10dmGraphicsL23OpenGLDoSetTextureAsyncEPv+213
#10 pc      0xcb206 dmengine _ZN10dmGraphicsL11AsyncThreadEPv+182
#11 pc     0x25b8e5 dmengine _ZN8dmThreadL16ThreadStartProxyEPv+37
#12 pc     0x2cd4e1 libxpc.dylib _pthread_start+125

Am I missing something do you think?

A few comments:

  • If I comment out this line, there is no longer a crash:

    resource.set_texture(cubemap_texture, cubemap_header, cubemap_buffer)
    
  • This snippet code is reduced to its strictest simplification in order to rule out any side effects. So I removed the part that initialized the buffer with a test color first before thinking about using a C function that copies the buffer from a loaded texture to the cubemap buffer (taking care of the 6 faces of the cubemap)

Can you try with a rgba format and 4 components in the buffer?

yeahhhh, you’re right ! :pray: :pray: :pray:
if I replace this line:

 local cubemap_buffer = buffer.create(1024 * 1024 * 6, { {name=hash("rgb"), type=buffer.VALUE_TYPE_UINT8, count=3} } )

by this one:

 local cubemap_buffer = buffer.create(1024 * 1024 * 6, { {name=hash("rgba"), type=buffer.VALUE_TYPE_UINT8, count=4} } )

I no longer have a crash. So, I will be able to work on my C function to quickly copy the buffer of the loaded face to the buffer of the cubemap…

Stay tuned ! :smiley:

1 Like

Great! Though I don’t know why RGB wouldn’t work… I’m guessing it’s a driver issue :confused:

To summarize at this point, my minimal and working code is finally:

local cubemap_header = {
	width          = 1024,
	height         = 1024,
	type           = resource.TEXTURE_TYPE_CUBE_MAP,
	format         = resource.TEXTURE_FORMAT_RGBA, -- notice the mention '###_RGBA'
	max_mipmaps	   = 1,
}

local cubemap_texture = resource.create_texture("/mycubemap.texturec", cubemap_header)
local cubemap_buffer = buffer.create(1024 * 1024 * 6, { {name=hash("rgba"), type=buffer.VALUE_TYPE_UINT8, count=4} } ) -- notice the mentions 'rgba' and '4'
resource.set_texture(cubemap_texture, cubemap_header, cubemap_buffer)
go.set("/sky#model", "texture0", cubemap_texture)

Yeah, I was going to say: “What about max mipmaps”?
At least 1 mipmap is required, otherwise it’s just a header. In the initial example a mipmap count of 0 was used, and could potentially cause issues.

We guard for that in the engine, since that value is optional, so I don’t think it’s that. If I understand the callstack, the error seems to come from an expansion in the driver from RGB → RGBA in the _Z15glgConvertTo_32I23GLGConverter_BGR8_ARGB8L9GLGMemory0EEvPK15GLGOperationRecPK15GLDPixelModeRec

(whatever that means, can’t find any info about that function yet)