I'm loading a stack of PNG files from a zip file that represent 3D volumetric data. I'd like to generate mipmaps for this Texture3D, but it's not clear to me how to do this. The ImageTexture3D.create() method simply has a flag for mipmaps with no explanation. If I set the flag to false and just pass in the list of images I loaded, the texture creation works. If I set it to true, then if fails:

E 0:00:02:0260   main.gd:51 @ load_image_from_zip(): Missing Images
  <C++ Error>    Method/function failed.
  <C++ Source>   servers/rendering/renderer_rd/storage_rd/texture_storage.cpp:976 @ texture_3d_initialize()
  <Stack Trace>  main.gd:51 @ load_image_from_zip()
                 main.gd:57 @ _ready()

From the error, I'm presuming it wants me to generate the mipmaps myself and add them to the end of the list, but when I try that, it still fails:

E 0:00:02:0230   main.gd:51 @ load_image_from_zip(): Image size mismatch
  <C++ Error>    Method/function failed.
  <C++ Source>   servers/rendering/renderer_rd/storage_rd/texture_storage.cpp:976 @ texture_3d_initialize()
  <Stack Trace>  main.gd:51 @ load_image_from_zip()
                 main.gd:57 @ _ready()

What does this method expect and how can I create my mipmaps?

func add_mipmaps(img_width:int, img_height:int, img_depth:int, img_format:int, img_list:Array[Image]):
	
	var new_width:int = ceil(img_width / 2.0)
	var new_height:int = ceil(img_height / 2.0)
	var new_depth:int = ceil(img_depth / 2.0)
	
	for i in new_depth:
		var image:Image = Image.create(new_width, new_height, false, img_format)
		img_list.append(image)
	
	pass


func load_image_from_zip(path:String)->Texture3D:
	var reader:ZIPReader = ZIPReader.new()
	var err := reader.open(path)
	if err != OK:
		return null
	
	var img_width:int = -1
	var img_height:int = -1
	var img_format:int
	var img_list:Array[Image]
	
	for filename in reader.get_files():
		if filename.ends_with(".png"):
			var buf:PackedByteArray = reader.read_file(filename)
			
			var image:Image = Image.new()
			image.load_png_from_buffer(buf)
			var cur_width:int = image.get_width()
			var cur_height:int = image.get_height()
			var cur_format:int = image.get_format()
			
			if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format):
				img_width = cur_width
				img_height = cur_height
				img_format = cur_format
				img_list.append(image)
	
	reader.close()
	
	add_mipmaps(img_width, img_height, img_list.size(), img_format, img_list)

	var tex:ImageTexture3D = ImageTexture3D.new()
	tex.create(img_format, img_width, img_height, img_list.size(), true, img_list)
#	tex.create(img_format, img_width, img_height, img_list.size(), false, img_list)
	return tex

I was able to figure it out. After diving into the Godot source, I came up with this method for generating the extra mipmaps.

func add_mipmaps(img_width:int, img_height:int, img_depth:int, img_format:int, parent_images:Array[Image], img_list:Array[Image]):
	
	if img_width == 1 && img_height == 1 && img_depth == 1:
		return
	
	var mip_width:int = max(1, img_width >> 1)
	var mip_height:int = max(1, img_height >> 1)
	var mip_depth:int = max(1, img_depth >> 1)
	
	var mip_images:Array[Image]
	
	for z_idx in mip_depth:
		var image:Image = Image.create(mip_width, mip_height, false, img_format)
		
		img_list.append(image)
		mip_images.append(image)
		
		for y_idx in mip_height:
			for x_idx in mip_width:
				var color:Color

				for zz in 2:
					var src_image:Image = parent_images[min(z_idx * 2 + zz, img_depth - 1)]

					for yy in 2:
						for xx in 2:
							color += src_image.get_pixel(min(x_idx * 2 + xx, img_width - 1), min(y_idx * 2 + yy, img_height - 1))

				color /= 8
				image.set_pixel(x_idx, y_idx, color)
		
	add_mipmaps(mip_width, mip_height, mip_depth, img_format, mip_images, img_list)


func load_image_from_zip(path:String)->Texture3D:
	var reader:ZIPReader = ZIPReader.new()
	var err := reader.open(path)
	if err != OK:
		return null
	
	var img_width:int = -1
	var img_height:int = -1
	var img_format:int
	var img_list:Array[Image]
	
	for filename in reader.get_files():
		if filename.ends_with(".png"):
			var buf:PackedByteArray = reader.read_file(filename)
			
			var image:Image = Image.new()
			image.load_png_from_buffer(buf)
			var cur_width:int = image.get_width()
			var cur_height:int = image.get_height()
			var cur_format:int = image.get_format()
			
			if img_width == -1 || (img_width == cur_width && img_height == cur_height && img_format == cur_format):
				print("loading image ", img_list.size())
				img_width = cur_width
				img_height = cur_height
				img_format = cur_format
				img_list.append(image)
				
	var img_depth:int = img_list.size()
	print("num images ", img_list.size())
	
	reader.close()
	
	add_mipmaps(img_width, img_height, img_list.size(), img_format, img_list, img_list)

	var tex:ImageTexture3D = ImageTexture3D.new()
	tex.create(img_format, img_width, img_height, img_depth, true, img_list)
#	tex.create(img_format, img_width, img_height, img_depth, false, img_list)
	return tex