#undef UFBXT_TEST_GROUP
#define UFBXT_TEST_GROUP "material"

#if UFBXT_IMPL

void ufbxt_check_texture_content(ufbx_scene *scene, ufbx_texture *texture, const char *filename)
{
	char buf[512];

	ufbxt_assert(texture->content.size > 0);
	ufbxt_assert(texture->content.data);

	snprintf(buf, sizeof(buf), "%stextures/%s", data_root, filename);
	void *ref = malloc(texture->content.size);
	ufbxt_assert(ref);

	FILE *f = fopen(buf, "rb");
	ufbxt_assert(f);
	size_t num_read = fread(ref, 1, texture->content.size, f);
	fclose(f);

	ufbxt_assert(num_read == texture->content.size);
	ufbxt_assert(!memcmp(ref, texture->content.data, texture->content.size));

	free(ref);
}

void ufbxt_check_material_texture_ex(ufbx_scene *scene, ufbx_texture *texture, const char *directory, const char *filename, bool require_content)
{
	char buf[512];

	snprintf(buf, sizeof(buf), "%s%s", directory, filename);
	ufbxt_assert(!strcmp(texture->relative_filename.data, buf));

	if (require_content && (scene->metadata.version >= 7000 || !scene->metadata.ascii)) {
		ufbxt_assert(texture->content.size);
	}

	if (texture->content.size) {
		ufbxt_check_texture_content(scene, texture, filename);
	}
}

void ufbxt_check_material_texture(ufbx_scene *scene, ufbx_texture *texture, const char *filename, bool require_content)
{
	ufbxt_check_material_texture_ex(scene, texture, "textures\\", filename, require_content);
}

#endif

UFBXT_FILE_TEST(maya_textured_cube)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "phong1");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 6);

	ufbxt_check_material_texture(scene, material->fbx.diffuse_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->fbx.specular_color.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->fbx.reflection_color.texture, "checkerboard_reflection.png", true);
	ufbxt_check_material_texture(scene, material->fbx.transparency_color.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->fbx.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->fbx.ambient_color.texture, "checkerboard_ambient.png", true);
}
#endif

UFBXT_FILE_TEST(synthetic_texture_split)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "phong1");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 6);

	ufbxt_check_material_texture(scene, material->fbx.diffuse_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->fbx.specular_color.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->fbx.reflection_color.texture, "checkerboard_reflection.png", true);
	ufbxt_check_material_texture(scene, material->fbx.transparency_color.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->fbx.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->fbx.ambient_color.texture, "checkerboard_ambient.png", true);
}
#endif

UFBXT_TEST(ignore_embedded)
#if UFBXT_IMPL
{
	char path[512];

	ufbxt_file_iterator iter = { "maya_textured_cube" };
	while (ufbxt_next_file(&iter, path, sizeof(path))) {
		ufbx_load_opts opts = { 0 };
		opts.ignore_embedded = true;

		ufbx_scene *scene = ufbx_load_file(path, &opts, NULL);
		ufbxt_assert(scene);
		ufbxt_check_scene(scene);
		
		for (size_t i = 0; i < scene->videos.count; i++) {
			ufbxt_assert(scene->videos.data[i]->content.data == NULL);
			ufbxt_assert(scene->videos.data[i]->content.size == 0);
		}
		
		for (size_t i = 0; i < scene->textures.count; i++) {
			ufbxt_assert(scene->textures.data[i]->content.data == NULL);
			ufbxt_assert(scene->textures.data[i]->content.size == 0);
		}

		ufbx_free_scene(scene);
	}
}
#endif

UFBXT_FILE_TEST(maya_shared_textures)
#if UFBXT_IMPL
{
	ufbx_material *material;

	material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Shared");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 6);

	ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_LAMBERT);
	ufbxt_check_material_texture(scene, material->fbx.diffuse_color.texture, "checkerboard_ambient.png", true); // sic: test has wrong texture
	ufbxt_check_material_texture(scene, material->fbx.diffuse_factor.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->fbx.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->fbx.ambient_color.texture, "checkerboard_ambient.png", true);
	ufbxt_check_material_texture(scene, material->fbx.transparency_color.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->fbx.bump.texture, "checkerboard_bump.png", true);

	material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Special");
	ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_PHONG);
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 1);

	ufbxt_assert(!strcmp(material->fbx.diffuse_color.texture->relative_filename.data, "textures\\tiny_clouds.png"));
}
#endif

UFBXT_FILE_TEST(maya_arnold_textures)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "aiStandardSurface1");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 5);

	ufbxt_assert(material->shader_type == UFBX_SHADER_ARNOLD_STANDARD_SURFACE);
	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_color.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", true);
	ufbxt_check_material_texture(scene, material->pbr.diffuse_roughness.texture, "checkerboard_roughness.png", true);
}
#endif

UFBXT_FILE_TEST(max_physical_material_properties)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "PhysicalMaterial");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PHYSICAL_MATERIAL);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_factor.value_real));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.roughness.value_real));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.metalness.value_real));
	ufbxt_assert(80 == (int)round(100.0f * material->pbr.specular_ior.value_real)); // 3ds Max doesn't allow lower than 0.1 IOR
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.transmission_factor.value_real));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.transmission_color.value_vec4.x));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.transmission_color.value_vec4.y));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.transmission_color.value_vec4.z));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.transmission_color.value_vec4.w));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.transmission_roughness.value_real));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.transmission_depth.value_real)); // ??? Unit conversion probably
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.subsurface_factor.value_real));
	ufbxt_assert(17 == (int)round(100.0f * material->pbr.subsurface_tint_color.value_vec4.x));
	ufbxt_assert(18 == (int)round(100.0f * material->pbr.subsurface_tint_color.value_vec4.y));
	ufbxt_assert(19 == (int)round(100.0f * material->pbr.subsurface_tint_color.value_vec4.z));
	ufbxt_assert(20 == (int)round(100.0f * material->pbr.subsurface_tint_color.value_vec4.w));
	ufbxt_assert(21 == (int)round(100.0f * material->pbr.subsurface_color.value_vec4.x));
	ufbxt_assert(22 == (int)round(100.0f * material->pbr.subsurface_color.value_vec4.y));
	ufbxt_assert(23 == (int)round(100.0f * material->pbr.subsurface_color.value_vec4.z));
	ufbxt_assert(24 == (int)round(100.0f * material->pbr.subsurface_color.value_vec4.w));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.subsurface_radius.value_real)); // ??? Unit conversion probably
	ufbxt_assert(26 == (int)round(100.0f * material->pbr.subsurface_scale.value_real));
	ufbxt_assert(27 == (int)round(100.0f * material->pbr.emission_factor.value_real));
	ufbxt_assert(28 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert(29 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(30 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(31 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(32 == (int)round(100.0f * material->pbr.specular_anisotropy.value_real));
	ufbxt_assert(33 == (int)round(100.0f * material->pbr.specular_rotation.value_real));
	ufbxt_assert(34 == (int)round(100.0f * material->pbr.normal_map.value_real));
	ufbxt_assert(35 == (int)round(100.0f * material->pbr.coat_normal.value_real));
	ufbxt_assert(36 == (int)round(100.0f * material->pbr.displacement_map.value_real));
	ufbxt_assert(37 == (int)round(100.0f * material->pbr.diffuse_roughness.value_real));
	ufbxt_assert(38 == (int)round(100.0f * material->pbr.coat_factor.value_real));
	ufbxt_assert(39 == (int)round(100.0f * material->pbr.coat_color.value_vec4.x));
	ufbxt_assert(40 == (int)round(100.0f * material->pbr.coat_color.value_vec4.y));
	ufbxt_assert(41 == (int)round(100.0f * material->pbr.coat_color.value_vec4.z));
	ufbxt_assert(42 == (int)round(100.0f * material->pbr.coat_color.value_vec4.w));
	ufbxt_assert(43 == (int)round(100.0f * material->pbr.coat_roughness.value_real));
	ufbxt_assert(44 == (int)round(100.0f * material->pbr.coat_ior.value_real));
	ufbxt_assert(45 == (int)round(100.0f * material->pbr.coat_affect_base_color.value_real));
	ufbxt_assert(46 == (int)round(100.0f * material->pbr.coat_affect_base_roughness.value_real));

	ufbxt_assert(material->pbr.base_factor.texture_enabled);
	ufbxt_assert(material->pbr.base_color.texture_enabled);
	ufbxt_assert(material->pbr.specular_factor.texture_enabled);
	ufbxt_assert(material->pbr.specular_color.texture_enabled);
	ufbxt_assert(material->pbr.roughness.texture_enabled);
	ufbxt_assert(material->pbr.metalness.texture_enabled);
	ufbxt_assert(material->pbr.diffuse_roughness.texture_enabled);
	ufbxt_assert(material->pbr.specular_anisotropy.texture_enabled);
	ufbxt_assert(material->pbr.specular_rotation.texture_enabled);
	ufbxt_assert(material->pbr.transmission_factor.texture_enabled);
	ufbxt_assert(material->pbr.transmission_color.texture_enabled);
	ufbxt_assert(material->pbr.transmission_roughness.texture_enabled);
	ufbxt_assert(material->pbr.specular_ior.texture_enabled);
	ufbxt_assert(material->pbr.subsurface_factor.texture_enabled);
	ufbxt_assert(material->pbr.subsurface_tint_color.texture_enabled);
	ufbxt_assert(material->pbr.subsurface_scale.texture_enabled);
	ufbxt_assert(material->pbr.emission_factor.texture_enabled);
	ufbxt_assert(material->pbr.emission_color.texture_enabled);
	ufbxt_assert(material->pbr.coat_factor.texture_enabled);
	ufbxt_assert(material->pbr.coat_color.texture_enabled);
	ufbxt_assert(material->pbr.coat_roughness.texture_enabled);
	ufbxt_assert(material->pbr.normal_map.texture_enabled);
	ufbxt_assert(material->pbr.coat_normal.texture_enabled);
	ufbxt_assert(material->pbr.displacement_map.texture_enabled);
	ufbxt_assert(material->pbr.opacity.texture_enabled);

	ufbxt_assert(material->pbr.base_factor.value_components == 1);
	ufbxt_assert(material->pbr.base_color.value_components == 4);
	ufbxt_assert(material->pbr.specular_factor.value_components == 1);
	ufbxt_assert(material->pbr.specular_color.value_components == 4);
	ufbxt_assert(material->pbr.roughness.value_components == 1);
	ufbxt_assert(material->pbr.metalness.value_components == 1);
	ufbxt_assert(material->pbr.diffuse_roughness.value_components == 1);
	ufbxt_assert(material->pbr.specular_anisotropy.value_components == 1);
	ufbxt_assert(material->pbr.specular_rotation.value_components == 1);
	ufbxt_assert(material->pbr.transmission_factor.value_components == 1);
	ufbxt_assert(material->pbr.transmission_color.value_components == 4);
	ufbxt_assert(material->pbr.transmission_roughness.value_components == 1);
	ufbxt_assert(material->pbr.transmission_extra_roughness.value_components == 0);
	ufbxt_assert(material->pbr.specular_ior.value_components == 1);
	ufbxt_assert(material->pbr.subsurface_factor.value_components == 1);
	ufbxt_assert(material->pbr.subsurface_tint_color.value_components == 4);
	ufbxt_assert(material->pbr.subsurface_color.value_components == 4);
	ufbxt_assert(material->pbr.subsurface_radius.value_components == 1);
	ufbxt_assert(material->pbr.subsurface_scale.value_components == 1);
	ufbxt_assert(material->pbr.emission_factor.value_components == 1);
	ufbxt_assert(material->pbr.emission_color.value_components == 4);
	ufbxt_assert(material->pbr.coat_factor.value_components == 1);
	ufbxt_assert(material->pbr.coat_color.value_components == 4);
	ufbxt_assert(material->pbr.coat_roughness.value_components == 1);
	ufbxt_assert(material->pbr.normal_map.value_components == 1);
	ufbxt_assert(material->pbr.coat_normal.value_components == 1);
	ufbxt_assert(material->pbr.displacement_map.value_components == 1);
	ufbxt_assert(material->pbr.opacity.value_components == 0);
}
#endif

UFBXT_FILE_TEST(max_physical_material_inverted)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "InvertedMaterial");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PHYSICAL_MATERIAL);

	ufbxt_assert_close_real(err, material->pbr.roughness.value_real, 0.9f);
	ufbxt_assert_close_real(err, material->pbr.transmission_roughness.value_real, 0.8f);
	ufbxt_assert_close_real(err, material->pbr.coat_roughness.value_real, 0.7f);
}
#endif

UFBXT_FILE_TEST(max_physical_material_textures)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "PhysicalMaterial");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PHYSICAL_MATERIAL);
	ufbxt_check_material_texture(scene, material->pbr.base_factor.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_factor.texture, "checkerboard_reflection.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_color.texture, "checkerboard_reflection.png", true);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", true);
	ufbxt_check_material_texture(scene, material->pbr.diffuse_roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_anisotropy.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_rotation.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.transmission_factor.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->pbr.transmission_color.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->pbr.transmission_roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_ior.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.subsurface_factor.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.subsurface_tint_color.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.subsurface_scale.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.emission_factor.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_factor.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_color.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_roughness.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_bump.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_normal.texture, "checkerboard_bump.png", true);
	ufbxt_check_material_texture(scene, material->pbr.displacement_map.texture, "checkerboard_displacement.png", true);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", true);

}
#endif

UFBXT_FILE_TEST(max_pbr_metal_rough_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "01 - Default");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PBR_METAL_ROUGH);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.metalness.value_real));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.roughness.value_real));
	// ufbxt_assert( 7 == (int)round(100.0f * material->pbr.normal_map.value_real)); (not in file?!)
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.displacement_map.value_real));

	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", false);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", false);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_roughness.png", false);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_ambient.png", false);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", false);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", false);
	ufbxt_check_material_texture(scene, material->pbr.displacement_map.texture, "checkerboard_displacement.png", false);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", false);

	ufbxt_assert(material->features.metalness.enabled);
	ufbxt_assert(!material->features.specular.enabled);
}
#endif

UFBXT_FILE_TEST(max_pbr_metal_gloss_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "01 - Default");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PBR_METAL_ROUGH);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.metalness.value_real));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.glossiness.value_real));
	// ufbxt_assert( 7 == (int)round(100.0f * material->pbr.normal_map.value_real)); (not in file?!)
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.displacement_map.value_real));

	ufbxt_assert(100 - 6 == (int)round(100.0f * material->pbr.roughness.value_real));

	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", false);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", false);
	ufbxt_check_material_texture(scene, material->pbr.glossiness.texture, "checkerboard_roughness.png", false);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_ambient.png", false);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", false);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", false);
	ufbxt_check_material_texture(scene, material->pbr.displacement_map.texture, "checkerboard_displacement.png", false);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", false);

	ufbxt_assert(material->pbr.roughness.texture == NULL);

	ufbxt_assert(material->features.metalness.enabled);
	ufbxt_assert(!material->features.specular.enabled);
}
#endif

UFBXT_FILE_TEST(max_pbr_spec_rough_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "02 - Default");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PBR_SPEC_GLOSS);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.specular_color.value_vec4.x));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.specular_color.value_vec4.y));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.specular_color.value_vec4.z));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.specular_color.value_vec4.w));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.roughness.value_real));
	// ufbxt_assert(10 == (int)round(100.0f * material->pbr.normal_map.value_real)); (not in file?!)
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(15 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.displacement_map.value_real));

	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", false);
	ufbxt_check_material_texture(scene, material->pbr.specular_color.texture, "checkerboard_specular.png", false);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_weight.png", false);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_ambient.png", false);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", false);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", false);
	ufbxt_check_material_texture(scene, material->pbr.displacement_map.texture, "checkerboard_displacement.png", false);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", false);

	ufbxt_assert(!material->features.metalness.enabled);
	ufbxt_assert(material->features.specular.enabled);
}
#endif

UFBXT_FILE_TEST(max_pbr_spec_gloss_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "02 - Default");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_3DS_MAX_PBR_SPEC_GLOSS);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.specular_color.value_vec4.x));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.specular_color.value_vec4.y));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.specular_color.value_vec4.z));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.specular_color.value_vec4.w));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.glossiness.value_real));
	// ufbxt_assert(10 == (int)round(100.0f * material->pbr.normal_map.value_real)); (not in file?!)
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(15 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.displacement_map.value_real));

	ufbxt_assert(100 - 9 == (int)round(100.0f * material->pbr.roughness.value_real));

	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", false);
	ufbxt_check_material_texture(scene, material->pbr.specular_color.texture, "checkerboard_specular.png", false);
	ufbxt_check_material_texture(scene, material->pbr.glossiness.texture, "checkerboard_weight.png", false);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_ambient.png", false);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", false);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", false);
	ufbxt_check_material_texture(scene, material->pbr.displacement_map.texture, "checkerboard_displacement.png", false);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", false);

	ufbxt_assert(material->pbr.roughness.texture == NULL);

	ufbxt_assert(!material->features.metalness.enabled);
	ufbxt_assert(material->features.specular.enabled);
}
#endif

UFBXT_FILE_TEST(max_gltf_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "01 - Default");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_GLTF_MATERIAL);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec4.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec4.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec4.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec4.w));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.metalness.value_real));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.roughness.value_real));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.normal_map.value_real));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.ambient_occlusion.value_real));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.emission_color.value_vec4.x));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.emission_color.value_vec4.y));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.emission_color.value_vec4.z));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.emission_color.value_vec4.w));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.coat_factor.value_real));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.coat_roughness.value_real));
	ufbxt_assert(15 == (int)round(100.0f * material->pbr.coat_normal.value_real));
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.sheen_color.value_vec4.x));
	ufbxt_assert(17 == (int)round(100.0f * material->pbr.sheen_color.value_vec4.y));
	ufbxt_assert(18 == (int)round(100.0f * material->pbr.sheen_color.value_vec4.z));
	ufbxt_assert(19 == (int)round(100.0f * material->pbr.sheen_color.value_vec4.w));
	ufbxt_assert(20 == (int)round(100.0f * material->pbr.sheen_roughness.value_real));
	ufbxt_assert(21 == (int)round(100.0f * material->pbr.specular_factor.value_real));
	ufbxt_assert(22 == (int)round(100.0f * material->pbr.specular_color.value_vec4.x));
	ufbxt_assert(23 == (int)round(100.0f * material->pbr.specular_color.value_vec4.y));
	ufbxt_assert(24 == (int)round(100.0f * material->pbr.specular_color.value_vec4.z));
	ufbxt_assert(25 == (int)round(100.0f * material->pbr.specular_color.value_vec4.w));
	ufbxt_assert(26 == (int)round(100.0f * material->pbr.transmission_factor.value_real));
	// TODO? Volume
	ufbxt_assert(133 == (int)round(100.0f * material->pbr.specular_ior.value_real));

	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->pbr.opacity.texture, "checkerboard_transparency.png", true);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", true);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", true);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_factor.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.coat_normal.texture, "checkerboard_normal.png", true);
	ufbxt_check_material_texture(scene, material->pbr.sheen_color.texture, "checkerboard_ambient.png", true);
	ufbxt_check_material_texture(scene, material->pbr.sheen_roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_factor.texture, "checkerboard_weight.png", true);
	ufbxt_check_material_texture(scene, material->pbr.specular_color.texture, "checkerboard_specular.png", true);
	ufbxt_check_material_texture(scene, material->pbr.transmission_factor.texture, "checkerboard_transparency.png", true);

	ufbxt_assert(material->features.metalness.enabled);
	ufbxt_assert(!material->features.metalness.is_explicit);
	ufbxt_assert(material->features.double_sided.enabled);
	ufbxt_assert(material->features.double_sided.is_explicit);
	ufbxt_assert(material->features.coat.enabled);
	ufbxt_assert(material->features.coat.is_explicit);
	ufbxt_assert(material->features.sheen.enabled);
	ufbxt_assert(material->features.sheen.is_explicit);
	ufbxt_assert(material->features.specular.enabled);
	ufbxt_assert(material->features.specular.is_explicit);
	ufbxt_assert(material->features.transmission.enabled);
	ufbxt_assert(material->features.transmission.is_explicit);
	ufbxt_assert(material->features.ior.enabled);
	ufbxt_assert(material->features.ior.is_explicit);
}
#endif

UFBXT_FILE_TEST(maya_shaderfx_pbs_material)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "StingrayPBS1");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_SHADERFX_GRAPH);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec3.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec3.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec3.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.metalness.value_vec3.x));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.roughness.value_vec3.x));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.emission_color.value_vec3.x));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.emission_color.value_vec3.y));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.emission_color.value_vec3.z));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.emission_factor.value_real));
	ufbxt_assert_close_real(err, material->pbr.base_factor.value_real, 1.0f);

	ufbxt_check_material_texture(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png", true);
	ufbxt_check_material_texture(scene, material->pbr.normal_map.texture, "checkerboard_normal.png", true);
	ufbxt_check_material_texture(scene, material->pbr.metalness.texture, "checkerboard_metallic.png", true);
	ufbxt_check_material_texture(scene, material->pbr.roughness.texture, "checkerboard_roughness.png", true);
	ufbxt_check_material_texture(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png", true);
	ufbxt_check_material_texture(scene, material->pbr.ambient_occlusion.texture, "checkerboard_weight.png", true);

	ufbxt_assert(material->pbr.base_color.texture_enabled == true);
	ufbxt_assert(material->pbr.normal_map.texture_enabled == false);
	ufbxt_assert(material->pbr.metalness.texture_enabled == true);
	ufbxt_assert(material->pbr.roughness.texture_enabled == false);
	ufbxt_assert(material->pbr.emission_color.texture_enabled == true);
	ufbxt_assert(material->pbr.ambient_occlusion.texture_enabled == false);

	ufbx_shader *shader = material->shader;
	ufbxt_assert(shader);

	if (scene->metadata.version >= 7000) {
		ufbx_blob blob = ufbx_find_blob(&shader->props, "ShaderGraph", ufbx_empty_blob);
		ufbxt_assert(blob.size == 32918);
		uint32_t hash_ref = ufbxt_fnv1a(blob.data, blob.size);
		ufbxt_assert(hash_ref == 0x7be121c5u);
	}
}
#endif

UFBXT_FILE_TEST(blender_279_internal_textures)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material.001");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 5);

	ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_PHONG);
	ufbxt_assert(!strcmp(material->fbx.diffuse_color.texture->relative_filename.data, "textures\\checkerboard_diffuse.png"));
	ufbxt_assert(!strcmp(material->fbx.specular_color.texture->relative_filename.data, "textures\\checkerboard_specular.png"));
	ufbxt_assert(!strcmp(material->fbx.specular_factor.texture->relative_filename.data, "textures\\checkerboard_weight.png"));
	ufbxt_assert(!strcmp(material->fbx.ambient_factor.texture->relative_filename.data, "textures\\checkerboard_ambient.png"));
	ufbxt_assert(!strcmp(material->fbx.emission_factor.texture->relative_filename.data, "textures\\checkerboard_emissive.png"));
}
#endif

UFBXT_FILE_TEST(blender_293_textures)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material.001");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 5);

	ufbxt_assert(material->shader_type == UFBX_SHADER_BLENDER_PHONG);
	ufbxt_assert(!strcmp(material->pbr.base_color.texture->relative_filename.data, "textures\\checkerboard_diffuse.png"));
	ufbxt_assert(!strcmp(material->pbr.roughness.texture->relative_filename.data, "textures\\checkerboard_roughness.png"));
	ufbxt_assert(!strcmp(material->pbr.metalness.texture->relative_filename.data, "textures\\checkerboard_metallic.png"));
	ufbxt_assert(!strcmp(material->pbr.emission_color.texture->relative_filename.data, "textures\\checkerboard_emissive.png"));
	ufbxt_assert(!strcmp(material->pbr.opacity.texture->relative_filename.data, "textures\\checkerboard_weight.png"));
}
#endif

UFBXT_FILE_TEST(blender_293_embedded_textures)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material.001");
	ufbxt_assert(material);
	ufbxt_assert(material->textures.count == 5);

	ufbxt_assert(material->shader_type == UFBX_SHADER_BLENDER_PHONG);
	ufbxt_check_texture_content(scene, material->pbr.base_color.texture, "checkerboard_diffuse.png");
	ufbxt_check_texture_content(scene, material->pbr.roughness.texture, "checkerboard_roughness.png");
	ufbxt_check_texture_content(scene, material->pbr.metalness.texture, "checkerboard_metallic.png");
	ufbxt_check_texture_content(scene, material->pbr.emission_color.texture, "checkerboard_emissive.png");
	ufbxt_check_texture_content(scene, material->pbr.opacity.texture, "checkerboard_weight.png");
}
#endif

UFBXT_FILE_TEST(blender_293_material_mapping)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material.001");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_BLENDER_PHONG);
	ufbxt_assert_close_real(err, material->fbx.specular_exponent.value_vec3.x, 76.913f);
	ufbxt_assert_close_real(err, material->fbx.transparency_factor.value_vec3.x, 0.544f);
	ufbxt_assert_close_real(err, material->pbr.opacity.value_vec3.x, 0.456f);
	ufbxt_assert_close_real(err, material->pbr.roughness.value_vec3.x, 0.123f);
}
#endif

UFBXT_FILE_TEST(maya_different_shaders)
#if UFBXT_IMPL
{
	ufbx_material *lambert1 = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "lambert1");
	ufbx_material *phong1 = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "phong1");
	ufbx_material *arnold = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "aiStandardSurface1");
	ufbxt_assert(lambert1 && phong1 && arnold);

	ufbx_vec3 r = { 1.0f, 0.0f, 0.0f };
	ufbx_vec3 g = { 0.0f, 1.0f, 0.0f };
	ufbx_vec3 b = { 0.0f, 0.0f, 1.0f };

	ufbxt_assert(lambert1->shader_type == UFBX_SHADER_FBX_LAMBERT);
	ufbxt_assert(!strcmp(lambert1->shading_model_name.data, "lambert"));
	ufbxt_assert_close_vec3(err, lambert1->fbx.diffuse_color.value_vec3, g);
	ufbxt_assert_close_vec3(err, lambert1->pbr.base_color.value_vec3, g);
	ufbxt_assert_close_real(err, lambert1->pbr.specular_factor.value_vec3.x, 0.0f);

	ufbxt_assert(phong1->shader_type == UFBX_SHADER_FBX_PHONG);
	ufbxt_assert(!strcmp(phong1->shading_model_name.data, "phong"));
	ufbxt_assert_close_vec3(err, phong1->fbx.diffuse_color.value_vec3, b);
	ufbxt_assert_close_vec3(err, phong1->pbr.base_color.value_vec3, b);
	ufbxt_assert_close_real(err, phong1->pbr.specular_factor.value_vec3.x, 1.0f);

	ufbxt_assert(arnold->shader_type == UFBX_SHADER_ARNOLD_STANDARD_SURFACE);
	ufbxt_assert(!strcmp(arnold->shading_model_name.data, "unknown"));
	ufbxt_assert_close_vec3(err, arnold->pbr.base_color.value_vec3, r);
}
#endif

UFBXT_TEST(blender_phong_quirks)
#if UFBXT_IMPL
{
	for (int quirks = 0; quirks <= 1; quirks++) {
		ufbx_load_opts opts = { 0 };
		opts.disable_quirks = (quirks == 0);

		char buf[512];
		snprintf(buf, sizeof(buf), "%s%s", data_root, "blender_293_textures_7400_binary.fbx");

		ufbx_scene *scene = ufbx_load_file(buf, &opts, NULL);
		ufbxt_assert(scene);

		// Exporter should be detected even with quirks off
		ufbxt_assert(scene->metadata.exporter == UFBX_EXPORTER_BLENDER_BINARY);
		ufbxt_assert(scene->metadata.exporter_version == ufbx_pack_version(4, 22, 0));

		ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material.001");
		ufbxt_assert(material);
		if (quirks) {
			ufbxt_assert(material->shader_type == UFBX_SHADER_BLENDER_PHONG);
		} else {
			ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_PHONG);
		}

		ufbx_free_scene(scene);
	}
}
#endif

UFBXT_FILE_TEST(maya_phong_properties)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "phong1");
	ufbxt_assert(material);
	ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_PHONG);

	ufbxt_assert( 1 == (int)round(100.0f * material->fbx.diffuse_color.value_vec3.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->fbx.diffuse_color.value_vec3.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->fbx.diffuse_color.value_vec3.z));
	ufbxt_assert( 4 == (int)round(100.0f * material->fbx.transparency_color.value_vec3.x));
	ufbxt_assert( 5 == (int)round(100.0f * material->fbx.transparency_color.value_vec3.y));
	ufbxt_assert( 6 == (int)round(100.0f * material->fbx.transparency_color.value_vec3.z));
	ufbxt_assert( 7 == (int)round(100.0f * material->fbx.ambient_color.value_vec3.x));
	ufbxt_assert( 8 == (int)round(100.0f * material->fbx.ambient_color.value_vec3.y));
	ufbxt_assert( 9 == (int)round(100.0f * material->fbx.ambient_color.value_vec3.z));
	ufbxt_assert(10 == (int)round(100.0f * material->fbx.emission_color.value_vec3.x));
	ufbxt_assert(11 == (int)round(100.0f * material->fbx.emission_color.value_vec3.y));
	ufbxt_assert(12 == (int)round(100.0f * material->fbx.emission_color.value_vec3.z));
	ufbxt_assert(13 == (int)round(100.0f * material->fbx.diffuse_factor.value_vec3.x));
	ufbxt_assert(17 == (int)round(  1.0f * material->fbx.specular_exponent.value_vec3.x));
	ufbxt_assert(18 == (int)round(100.0f * material->fbx.specular_color.value_vec3.x));
	ufbxt_assert(19 == (int)round(100.0f * material->fbx.specular_color.value_vec3.y));
	ufbxt_assert(20 == (int)round(100.0f * material->fbx.specular_color.value_vec3.z));
	ufbxt_assert(21 == (int)round(100.0f * material->fbx.reflection_factor.value_vec3.x));
	ufbxt_assert(22 == (int)round(100.0f * material->fbx.reflection_color.value_vec3.x));
	ufbxt_assert(23 == (int)round(100.0f * material->fbx.reflection_color.value_vec3.y));
	ufbxt_assert(24 == (int)round(100.0f * material->fbx.reflection_color.value_vec3.z));

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_color.value_vec3.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec3.y));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec3.z));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.base_factor.value_vec3.x));
	ufbxt_assert_close_real(err, material->pbr.metalness.value_real, 0.0f);
	ufbxt_assert_close_real(err, material->pbr.roughness.value_real, 0.587689f);
}
#endif

UFBXT_FILE_TEST(maya_arnold_properties)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "aiStandardSurface1");
	ufbxt_assert(material);
	ufbxt_assert(material->shader_type == UFBX_SHADER_ARNOLD_STANDARD_SURFACE);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_factor.value_vec3.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec3.x));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec3.y));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec3.z));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.diffuse_roughness.value_vec3.x));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.metalness.value_vec3.x));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.specular_factor.value_vec3.x));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.specular_color.value_vec3.x));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.specular_color.value_vec3.y));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.specular_color.value_vec3.z));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.roughness.value_vec3.x));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.specular_ior.value_vec3.x));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.specular_anisotropy.value_vec3.x));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.specular_rotation.value_vec3.x));
	ufbxt_assert(15 == (int)round(100.0f * material->pbr.transmission_factor.value_vec3.x));
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.x));
	ufbxt_assert(17 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.y));
	ufbxt_assert(18 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.z));
	ufbxt_assert(19 == (int)round(100.0f * material->pbr.transmission_depth.value_vec3.x));
	ufbxt_assert(20 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.x));
	ufbxt_assert(21 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.y));
	ufbxt_assert(22 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.z));
	ufbxt_assert(23 == (int)round(100.0f * material->pbr.transmission_scatter_anisotropy.value_vec3.x));
	ufbxt_assert(24 == (int)round(100.0f * material->pbr.transmission_dispersion.value_vec3.x));
	ufbxt_assert(25 == (int)round(100.0f * material->pbr.transmission_extra_roughness.value_vec3.x));
	ufbxt_assert(26 == (int)round(100.0f * material->pbr.subsurface_factor.value_vec3.x));
	ufbxt_assert(27 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.x));
	ufbxt_assert(28 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.y));
	ufbxt_assert(29 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.z));
	ufbxt_assert(30 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.x));
	ufbxt_assert(31 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.y));
	ufbxt_assert(32 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.z));
	ufbxt_assert(33 == (int)round(100.0f * material->pbr.subsurface_scale.value_vec3.x));
	ufbxt_assert(34 == (int)round(100.0f * material->pbr.subsurface_anisotropy.value_vec3.x));
	ufbxt_assert(35 == (int)round(100.0f * material->pbr.coat_factor.value_vec3.x));
	ufbxt_assert(36 == (int)round(100.0f * material->pbr.coat_color.value_vec3.x));
	ufbxt_assert(37 == (int)round(100.0f * material->pbr.coat_color.value_vec3.y));
	ufbxt_assert(38 == (int)round(100.0f * material->pbr.coat_color.value_vec3.z));
	ufbxt_assert(39 == (int)round(100.0f * material->pbr.coat_roughness.value_vec3.x));
	ufbxt_assert(40 == (int)round(100.0f * material->pbr.coat_ior.value_vec3.x));
	ufbxt_assert(41 == (int)round(100.0f * material->pbr.coat_anisotropy.value_vec3.x));
	ufbxt_assert(42 == (int)round(100.0f * material->pbr.coat_rotation.value_vec3.x));
	ufbxt_assert(43 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.x));
	ufbxt_assert(44 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.y));
	ufbxt_assert(45 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.z));
	ufbxt_assert(46 == (int)round(100.0f * material->pbr.sheen_factor.value_vec3.x));
	ufbxt_assert(47 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.x));
	ufbxt_assert(48 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.y));
	ufbxt_assert(49 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.z));
	ufbxt_assert(50 == (int)round(100.0f * material->pbr.sheen_roughness.value_vec3.x));
	ufbxt_assert(51 == (int)round(100.0f * material->pbr.emission_factor.value_vec3.x));
	ufbxt_assert(52 == (int)round(100.0f * material->pbr.emission_color.value_vec3.x));
	ufbxt_assert(53 == (int)round(100.0f * material->pbr.emission_color.value_vec3.y));
	ufbxt_assert(54 == (int)round(100.0f * material->pbr.emission_color.value_vec3.z));
	ufbxt_assert(55 == (int)round(100.0f * material->pbr.thin_film_thickness.value_vec3.x));
	ufbxt_assert(56 == (int)round(100.0f * material->pbr.thin_film_ior.value_vec3.x));
	ufbxt_assert(57 == (int)round(100.0f * material->pbr.opacity.value_vec3.x));
	ufbxt_assert(58 == (int)round(100.0f * material->pbr.opacity.value_vec3.y));
	ufbxt_assert(59 == (int)round(100.0f * material->pbr.opacity.value_vec3.z));
	ufbxt_assert(60 == (int)round(100.0f * material->pbr.tangent_map.value_vec3.x));
	ufbxt_assert(61 == (int)round(100.0f * material->pbr.tangent_map.value_vec3.y));
	ufbxt_assert(62 == (int)round(100.0f * material->pbr.tangent_map.value_vec3.z));
	ufbxt_assert(63 == (int)round(100.0f * material->pbr.indirect_diffuse.value_vec3.x));
	ufbxt_assert(64 == (int)round(100.0f * material->pbr.indirect_specular.value_vec3.x));
	ufbxt_assert(65 == (int)round(100.0f * material->pbr.matte_color.value_vec3.x));
	ufbxt_assert(66 == (int)round(100.0f * material->pbr.matte_color.value_vec3.y));
	ufbxt_assert(67 == (int)round(100.0f * material->pbr.matte_color.value_vec3.z));
	ufbxt_assert(68 == (int)round(100.0f * material->pbr.matte_factor.value_vec3.x));
	ufbxt_assert(69 == material->pbr.transmission_priority.value_int);

	// Computed
	ufbxt_assert_close_real(err, material->pbr.transmission_roughness.value_real, 0.36f); // 11+25

	ufbxt_assert(material->pbr.base_factor.value_components == 1);
	ufbxt_assert(material->pbr.base_color.value_components == 3);
	ufbxt_assert(material->pbr.specular_factor.value_components == 1);
	ufbxt_assert(material->pbr.specular_color.value_components == 3);
	ufbxt_assert(material->pbr.roughness.value_components == 1);
	ufbxt_assert(material->pbr.metalness.value_components == 1);
	ufbxt_assert(material->pbr.diffuse_roughness.value_components == 1);
	ufbxt_assert(material->pbr.specular_anisotropy.value_components == 1);
	ufbxt_assert(material->pbr.specular_rotation.value_components == 1);
	ufbxt_assert(material->pbr.transmission_factor.value_components == 1);
	ufbxt_assert(material->pbr.transmission_color.value_components == 3);
	ufbxt_assert(material->pbr.transmission_roughness.value_components == 0);
	ufbxt_assert(material->pbr.specular_ior.value_components == 1);
	ufbxt_assert(material->pbr.subsurface_factor.value_components == 1);
	ufbxt_assert(material->pbr.subsurface_tint_color.value_components == 0);
	ufbxt_assert(material->pbr.subsurface_color.value_components == 3);
	ufbxt_assert(material->pbr.subsurface_radius.value_components == 3);
	ufbxt_assert(material->pbr.subsurface_scale.value_components == 1);
	ufbxt_assert(material->pbr.emission_factor.value_components == 1);
	ufbxt_assert(material->pbr.emission_color.value_components == 3);
	ufbxt_assert(material->pbr.coat_factor.value_components == 1);
	ufbxt_assert(material->pbr.coat_color.value_components == 3);
	ufbxt_assert(material->pbr.coat_roughness.value_components == 1);
	ufbxt_assert(material->pbr.normal_map.value_components == 3);
	ufbxt_assert(material->pbr.coat_normal.value_components == 3);
	ufbxt_assert(material->pbr.displacement_map.value_components == 0);
	ufbxt_assert(material->pbr.opacity.value_components == 3);

	ufbxt_assert(material->pbr.subsurface_type.value_int == 1);
	ufbxt_assert(material->pbr.transmission_enable_in_aov.value_int != 0);
	ufbxt_assert(material->features.thin_walled.enabled);
	ufbxt_assert(material->features.thin_walled.is_explicit);
	ufbxt_assert(material->features.matte.enabled);
	ufbxt_assert(material->features.matte.is_explicit);
	ufbxt_assert(material->features.caustics.enabled);
	ufbxt_assert(material->features.caustics.is_explicit);
	ufbxt_assert(material->features.internal_reflections.enabled);
	ufbxt_assert(material->features.internal_reflections.is_explicit);
	ufbxt_assert(material->features.exit_to_background.enabled);
	ufbxt_assert(material->features.exit_to_background.is_explicit);
}
#endif

UFBXT_FILE_TEST(maya_osl_properties)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "standardSurface2");
	ufbxt_assert(material);
	ufbxt_assert(material->shader_type == UFBX_SHADER_OSL_STANDARD_SURFACE);

	ufbxt_assert( 1 == (int)round(100.0f * material->pbr.base_factor.value_vec3.x));
	ufbxt_assert( 2 == (int)round(100.0f * material->pbr.base_color.value_vec3.x));
	ufbxt_assert( 3 == (int)round(100.0f * material->pbr.base_color.value_vec3.y));
	ufbxt_assert( 4 == (int)round(100.0f * material->pbr.base_color.value_vec3.z));
	ufbxt_assert( 5 == (int)round(100.0f * material->pbr.diffuse_roughness.value_vec3.x));
	ufbxt_assert( 6 == (int)round(100.0f * material->pbr.metalness.value_vec3.x));
	ufbxt_assert( 7 == (int)round(100.0f * material->pbr.specular_factor.value_vec3.x));
	ufbxt_assert( 8 == (int)round(100.0f * material->pbr.specular_color.value_vec3.x));
	ufbxt_assert( 9 == (int)round(100.0f * material->pbr.specular_color.value_vec3.y));
	ufbxt_assert(10 == (int)round(100.0f * material->pbr.specular_color.value_vec3.z));
	ufbxt_assert(11 == (int)round(100.0f * material->pbr.roughness.value_vec3.x));
	ufbxt_assert(12 == (int)round(100.0f * material->pbr.specular_ior.value_vec3.x));
	ufbxt_assert(13 == (int)round(100.0f * material->pbr.specular_anisotropy.value_vec3.x));
	ufbxt_assert(14 == (int)round(100.0f * material->pbr.specular_rotation.value_vec3.x));
	ufbxt_assert(15 == (int)round(100.0f * material->pbr.transmission_factor.value_vec3.x));
	ufbxt_assert(16 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.x));
	ufbxt_assert(17 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.y));
	ufbxt_assert(18 == (int)round(100.0f * material->pbr.transmission_color.value_vec3.z));
	ufbxt_assert(19 == (int)round(100.0f * material->pbr.transmission_depth.value_vec3.x));
	ufbxt_assert(20 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.x));
	ufbxt_assert(21 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.y));
	ufbxt_assert(22 == (int)round(100.0f * material->pbr.transmission_scatter.value_vec3.z));
	ufbxt_assert(23 == (int)round(100.0f * material->pbr.transmission_scatter_anisotropy.value_vec3.x));
	ufbxt_assert(24 == (int)round(100.0f * material->pbr.transmission_dispersion.value_vec3.x));
	ufbxt_assert(25 == (int)round(100.0f * material->pbr.transmission_extra_roughness.value_vec3.x));
	ufbxt_assert(26 == (int)round(100.0f * material->pbr.subsurface_factor.value_vec3.x));
	ufbxt_assert(27 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.x));
	ufbxt_assert(28 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.y));
	ufbxt_assert(29 == (int)round(100.0f * material->pbr.subsurface_color.value_vec3.z));
	ufbxt_assert(30 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.x));
	ufbxt_assert(31 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.y));
	ufbxt_assert(32 == (int)round(100.0f * material->pbr.subsurface_radius.value_vec3.z));
	ufbxt_assert(33 == (int)round(100.0f * material->pbr.subsurface_scale.value_vec3.x));
	ufbxt_assert(34 == (int)round(100.0f * material->pbr.subsurface_anisotropy.value_vec3.x));
	ufbxt_assert(35 == (int)round(100.0f * material->pbr.coat_factor.value_vec3.x));
	ufbxt_assert(36 == (int)round(100.0f * material->pbr.coat_color.value_vec3.x));
	ufbxt_assert(37 == (int)round(100.0f * material->pbr.coat_color.value_vec3.y));
	ufbxt_assert(38 == (int)round(100.0f * material->pbr.coat_color.value_vec3.z));
	ufbxt_assert(39 == (int)round(100.0f * material->pbr.coat_roughness.value_vec3.x));
	ufbxt_assert(40 == (int)round(100.0f * material->pbr.coat_ior.value_vec3.x));
	ufbxt_assert(41 == (int)round(100.0f * material->pbr.coat_anisotropy.value_vec3.x));
	ufbxt_assert(42 == (int)round(100.0f * material->pbr.coat_rotation.value_vec3.x));
	// Not used: ufbxt_assert(43 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.x));
	// Not used: ufbxt_assert(44 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.y));
	// Not used: ufbxt_assert(45 == (int)round(100.0f * material->pbr.coat_normal.value_vec3.z));
	ufbxt_assert(46 == (int)round(100.0f * material->pbr.sheen_factor.value_vec3.x));
	ufbxt_assert(47 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.x));
	ufbxt_assert(48 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.y));
	ufbxt_assert(49 == (int)round(100.0f * material->pbr.sheen_color.value_vec3.z));
	ufbxt_assert(50 == (int)round(100.0f * material->pbr.sheen_roughness.value_vec3.x));
	ufbxt_assert(51 == (int)round(100.0f * material->pbr.emission_factor.value_vec3.x));
	ufbxt_assert(52 == (int)round(100.0f * material->pbr.emission_color.value_vec3.x));
	ufbxt_assert(53 == (int)round(100.0f * material->pbr.emission_color.value_vec3.y));
	ufbxt_assert(54 == (int)round(100.0f * material->pbr.emission_color.value_vec3.z));
	ufbxt_assert(55 == (int)round(100.0f * material->pbr.thin_film_thickness.value_vec3.x));
	ufbxt_assert(56 == (int)round(100.0f * material->pbr.thin_film_ior.value_vec3.x));
	ufbxt_assert(57 == (int)round(100.0f * material->pbr.opacity.value_vec3.x));
	ufbxt_assert(58 == (int)round(100.0f * material->pbr.opacity.value_vec3.y));
	ufbxt_assert(59 == (int)round(100.0f * material->pbr.opacity.value_vec3.z));

	// Computed
	ufbxt_assert_close_real(err, material->pbr.transmission_roughness.value_real, 0.36f); // 11+25

	ufbxt_assert(material->features.thin_walled.enabled);
	ufbxt_assert(material->features.thin_walled.is_explicit);
}
#endif

UFBXT_FILE_TEST(maya_texture_layers)
#if UFBXT_IMPL
{
	// TODO: Recover layered textures from <7000.....
	if (scene->metadata.version < 7000) return;

	ufbx_node *node = ufbx_find_node(scene, "pCube1");
	ufbxt_assert(node && node->mesh && node->mesh->materials.count == 1);
	ufbx_material *material = node->mesh->materials.data[0].material;

	ufbx_texture *layered = material->fbx.diffuse_color.texture;
	ufbxt_assert(layered);
	ufbxt_assert(layered->type == UFBX_TEXTURE_LAYERED);
	ufbxt_assert(layered->layers.count == 3);
	ufbxt_assert(layered->layers.data[0].blend_mode == UFBX_BLEND_MULTIPLY);
	ufbxt_assert_close_real(err, layered->layers.data[0].alpha, 0.75f);
	ufbxt_assert(!strcmp(layered->layers.data[0].texture->relative_filename.data, "textures\\checkerboard_weight.png"));
	ufbxt_assert(layered->layers.data[1].blend_mode == UFBX_BLEND_OVER);
	ufbxt_assert_close_real(err, layered->layers.data[1].alpha, 0.5f);
	ufbxt_assert(!strcmp(layered->layers.data[1].texture->relative_filename.data, "textures\\checkerboard_diffuse.png"));
	ufbxt_assert(layered->layers.data[2].blend_mode == UFBX_BLEND_ADDITIVE);
	ufbxt_assert_close_real(err, layered->layers.data[2].alpha, 1.0f);
	ufbxt_assert(!strcmp(layered->layers.data[2].texture->relative_filename.data, "textures\\checkerboard_ambient.png"));

	ufbxt_assert(layered->file_textures.count == 3);
	for (size_t i = 0; i < 3; i++) {
		ufbxt_assert(layered->file_textures.data[i] == layered->layers.data[i].texture);
	}

	{
		ufbx_texture *texture = material->fbx.emission_color.texture;
		ufbxt_assert(texture);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_emissive.png"));
		ufbxt_assert_close_real(err, texture->uv_transform.translation.x, 1.0f);
		ufbxt_assert_close_real(err, texture->uv_transform.translation.y, 2.0f);
		ufbxt_assert_close_real(err, texture->uv_transform.translation.z, 0.0f);

		ufbx_vec3 uv = { 0.5f, 0.5f, 0.0f };
		uv = ufbx_transform_position(&texture->uv_to_texture, uv);
		ufbxt_assert_close_real(err, uv.x, -0.5f);
		ufbxt_assert_close_real(err, uv.y, -1.5f);
		ufbxt_assert_close_real(err, uv.z, 0.0f);
	}

	{
		ufbx_texture *texture = material->fbx.transparency_color.texture;
		ufbxt_assert(texture);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_transparency.png"));
		ufbxt_assert_close_real(err, texture->uv_transform.translation.x, 0.5f);
		ufbxt_assert_close_real(err, texture->uv_transform.translation.y, -0.20710678f);
		ufbxt_assert_close_real(err, texture->uv_transform.translation.z, 0.0f);
		ufbx_vec3 euler = ufbx_quat_to_euler(texture->uv_transform.rotation, UFBX_ROTATION_ORDER_XYZ);
		ufbxt_assert_close_real(err, euler.x, 0.0f);
		ufbxt_assert_close_real(err, euler.y, 0.0f);
		ufbxt_assert_close_real(err, euler.z, 45.0f);

		{
			ufbx_vec3 uv = { 0.5f, 0.5f, 0.0f };
			uv = ufbx_transform_position(&texture->uv_to_texture, uv);
			ufbxt_assert_close_real(err, uv.x, 0.5f);
			ufbxt_assert_close_real(err, uv.y, 0.5f);
			ufbxt_assert_close_real(err, uv.z, 0.0f);
		}

		{
			ufbx_vec3 uv = { 1.0f, 0.5f, 0.0f };
			uv = ufbx_transform_position(&texture->uv_to_texture, uv);
			ufbxt_assert_close_real(err, uv.x, 0.853553f);
			ufbxt_assert_close_real(err, uv.y, 0.146447f);
			ufbxt_assert_close_real(err, uv.z, 0.0f);
		}
	}
}
#endif

UFBXT_FILE_TEST(maya_texture_blend_modes)
#if UFBXT_IMPL
{
	// TODO: Recover layered textures from <7000.....
	if (scene->metadata.version < 7000) return;

	ufbx_node *node = ufbx_find_node(scene, "pCube1");
	ufbxt_assert(node && node->mesh && node->mesh->materials.count == 1);
	ufbx_material *material = node->mesh->materials.data[0].material;

	ufbx_texture *layered = material->fbx.diffuse_color.texture;
	ufbxt_assert(layered);
	ufbxt_assert(layered->type == UFBX_TEXTURE_LAYERED);
	ufbxt_assert(layered->layers.count == 14);

	for (size_t i = 0; i < layered->layers.count; i++) {
		ufbx_real alpha = i < 10 ? (ufbx_real)(i + 1) * 0.1f : 1.0f;
		size_t ix = layered->layers.count - i - 1;
		ufbxt_assert_close_real(err, layered->layers.data[ix].alpha, alpha);
	}

	// All point to the same texture and should be deduplicated
	ufbxt_assert(layered->file_textures.count == 1);
	for (size_t i = 0; i < 14; i++) {
		ufbxt_assert(layered->layers.data[i].texture == layered->file_textures.data[0]);
	}

	ufbxt_assert(layered->layers.data[ 0].blend_mode == UFBX_BLEND_REPLACE);    // "CPV Modulate" (unsupported)
	ufbxt_assert(layered->layers.data[ 1].blend_mode == UFBX_BLEND_LUMINOSITY); // "Illuminate"
	ufbxt_assert(layered->layers.data[ 2].blend_mode == UFBX_BLEND_REPLACE);    // "Desaturate" (unsupported)
	ufbxt_assert(layered->layers.data[ 3].blend_mode == UFBX_BLEND_SATURATION); // "Saturate"
	ufbxt_assert(layered->layers.data[ 4].blend_mode == UFBX_BLEND_DARKEN);     // "Darken"
	ufbxt_assert(layered->layers.data[ 5].blend_mode == UFBX_BLEND_LIGHTEN);    // "Lighten"
	ufbxt_assert(layered->layers.data[ 6].blend_mode == UFBX_BLEND_DIFFERENCE); // "Difference"
	ufbxt_assert(layered->layers.data[ 7].blend_mode == UFBX_BLEND_MULTIPLY);   // "Multiply"
	ufbxt_assert(layered->layers.data[ 8].blend_mode == UFBX_BLEND_SUBTRACT);   // "Subtract"
	ufbxt_assert(layered->layers.data[ 9].blend_mode == UFBX_BLEND_ADDITIVE);   // "Add"
	ufbxt_assert(layered->layers.data[10].blend_mode == UFBX_BLEND_REPLACE);    // "Out" (unsupported)
	ufbxt_assert(layered->layers.data[11].blend_mode == UFBX_BLEND_REPLACE);    // "In" (unsupported)
	ufbxt_assert(layered->layers.data[12].blend_mode == UFBX_BLEND_OVER);       // "Over"
	ufbxt_assert(layered->layers.data[13].blend_mode == UFBX_BLEND_REPLACE);    // "None"
}
#endif

UFBXT_FILE_TEST(max_texture_mapping)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "01 - Default");
	ufbxt_assert(material);

	{
		ufbx_texture *texture = material->pbr.base_factor.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_weight.png"));
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(shader->shader_name.data, "OSLBitmap2"));

		uint32_t source_hash = ufbxt_fnv1a(shader->shader_source.data, shader->shader_source.length);
		ufbxt_assert(source_hash == 0x599994a9u);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == texture);
	}

	{
		ufbx_texture *texture = material->pbr.base_color.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(shader->shader_name.data, "RandomTilingBitmap"));
		uint32_t source_hash = ufbxt_fnv1a(shader->shader_source.data, shader->shader_source.length);
		ufbxt_assert(source_hash == 0x60186ba7u);

		{
			ufbx_shader_texture_input *input = ufbx_find_shader_texture_input(shader, "Filename");
			ufbxt_assert(input);
			ufbxt_assert(!strcmp(input->value_str.data, "D:\\Dev\\clean\\ufbx\\data\\textures\\checkerboard_diffuse.png"));
		}

		{
			ufbx_shader_texture_input *input = ufbx_find_shader_texture_input(shader, "Scale");
			ufbxt_assert(input);
			ufbxt_assert_close_real(err, input->value_real, 0.25f);
		}

		ufbxt_assert(texture->file_textures.count == 0);
	}

	{
		ufbx_texture *texture = material->pbr.roughness.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_roughness.png"));
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(shader->shader_name.data, "UberBitmap2"));

		uint32_t source_hash = ufbxt_fnv1a(shader->shader_source.data, shader->shader_source.length);
		ufbxt_assert(source_hash == 0x19833d47);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == texture);
	}

	{
		ufbx_texture *texture = material->pbr.metalness.texture;
		ufbxt_assert(texture && !texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_metallic.png"));

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == texture);
	}

	{
		ufbx_texture *texture = material->pbr.transmission_color.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_transparency.png"));
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(shader->shader_name.data, "ai_image"));
		ufbxt_assert(shader->shader_source.length == 0);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == texture);
	}

	{
		ufbx_texture *texture = material->pbr.transmission_color.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_transparency.png"));
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(shader->shader_source.length == 0);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == texture);
	}

	{
		ufbx_texture *texture = material->pbr.emission_color.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(shader->shader_name.data, "ColorTweak"));

		uint32_t source_hash = ufbxt_fnv1a(shader->shader_source.data, shader->shader_source.length);
		ufbxt_assert(source_hash == 0xd2f4f86f);

		ufbx_shader_texture_input *input = ufbx_find_shader_texture_input(shader, "Input");
		ufbxt_assert(input);
		ufbx_texture *input_texture = input->texture;
		ufbxt_assert(input_texture);
		ufbxt_assert(input_texture->type == UFBX_TEXTURE_FILE);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == input_texture);
	}

	{
		ufbx_texture *texture = material->pbr.normal_map.texture;
		ufbxt_assert(texture && texture->shader);
		ufbx_shader_texture *shader = texture->shader;
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(shader->shader_name.data, "ai_bump2d"));
		ufbxt_assert(shader->shader_source.length == 0);

		ufbx_shader_texture_input *bump_map = ufbx_find_shader_texture_input(shader, "bump_map");
		ufbxt_assert(bump_map);
		ufbx_texture *bump_texture = bump_map->texture;
		ufbxt_assert(bump_texture);
		ufbxt_assert(bump_texture->type == UFBX_TEXTURE_FILE);

		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(texture->file_textures.data[0] == bump_texture);
	}

}
#endif

UFBXT_FILE_TEST(synthetic_missing_material_factor)
#if UFBXT_IMPL
{
	ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "phong1");
	ufbxt_assert(material);

	ufbxt_assert(material->shader_type == UFBX_SHADER_FBX_PHONG);

	ufbxt_assert_close_real(err, material->fbx.diffuse_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->fbx.specular_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->fbx.reflection_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->fbx.transparency_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->fbx.emission_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->fbx.ambient_factor.value_vec3.x, 1.0f);

	// TODO: Figure out how to map transmission/opacity
	ufbxt_assert_close_real(err, material->pbr.base_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.specular_factor.value_vec3.x, 1.0f);
	ufbxt_assert_close_real(err, material->pbr.emission_factor.value_vec3.x, 1.0f);
}
#endif

UFBXT_FILE_TEST(max_instanced_material)
#if UFBXT_IMPL
{
	ufbx_node *red_node = ufbx_find_node(scene, "Red");
	ufbx_node *green_node = ufbx_find_node(scene, "Green");
	ufbx_node *blue_node = ufbx_find_node(scene, "Blue");
	ufbxt_assert(red_node && red_node->mesh);
	ufbxt_assert(green_node && green_node->mesh);
	ufbxt_assert(blue_node && blue_node->mesh);
	ufbx_mesh *red_mesh = red_node->mesh;
	ufbx_mesh *green_mesh = green_node->mesh;
	ufbx_mesh *blue_mesh = blue_node->mesh;

	ufbx_material *red_mat = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "RedMat");
	ufbx_material *green_mat = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "GreenMat");
	ufbx_material *blue_mat = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "BlueMat");

	if (scene->metadata.version < 7000) {
		ufbxt_assert(red_mesh != green_mesh);
		ufbxt_assert(red_mesh != blue_mesh);
		ufbxt_assert(green_mesh != blue_mesh);

		// 6100 is a bit broken with materials?

		ufbxt_assert(green_node->materials.count == 3);
		ufbxt_assert(red_node->materials.count == 2);
		ufbxt_assert(blue_node->materials.count == 1);

		ufbxt_assert(green_mesh->materials.count == 3);
		ufbxt_assert(red_mesh->materials.count == 2);
		ufbxt_assert(blue_mesh->materials.count == 1);

	} else {
		ufbxt_assert(red_mesh == green_mesh);
		ufbxt_assert(red_mesh == blue_mesh);
		ufbxt_assert(green_mesh == blue_mesh);

		ufbxt_assert(green_node->materials.count == 1);
		ufbxt_assert(red_node->materials.count == 1);
		ufbxt_assert(blue_node->materials.count == 1);

		ufbxt_assert(green_node->materials.data[0] == green_mat);
		ufbxt_assert(red_node->materials.data[0] == red_mat);
		ufbxt_assert(blue_node->materials.data[0] == blue_mat);

		ufbxt_assert(red_mesh->materials.data[0].material == green_mat);
	}
}
#endif

#if UFBXT_IMPL

static bool ufbxt_has_texture(ufbx_texture_list list, const char *name)
{
	for (size_t i = 0; i < list.count; i++) {
		if (!strcmp(list.data[i]->relative_filename.data, name)) return true;
	}
	return false;
}

static void ufbxt_check_shader_input_map(ufbx_texture *texture, const char *name, const char *map, int64_t output_index)
{
	ufbxt_assert(texture->shader);
	ufbx_shader_texture_input *input = ufbx_find_shader_texture_input(texture->shader, name);
	ufbxt_assert(input);
	ufbxt_assert(input->texture);
	ufbxt_assert(!strcmp(input->texture->name.data, map));
	ufbxt_assert(input->texture_output_index == output_index);
}

#endif

UFBXT_FILE_TEST(max_shadergraph)
#if UFBXT_IMPL
{
	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #1");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_checkerboard"));
		ufbxt_assert(texture->file_textures.count == 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #2");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_add"));
		ufbxt_assert(texture->file_textures.count == 0);
		ufbxt_check_shader_input_map(texture, "input1", "Map #1", 0);
		ufbxt_check_shader_input_map(texture, "input2", "Map #3", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #3");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "Checker"));
		ufbxt_assert(texture->file_textures.count == 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #6");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ColorMul"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
		ufbxt_check_shader_input_map(texture, "A", "Map #2", 0);
		ufbxt_check_shader_input_map(texture, "B", "Map #7", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #7");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_diffuse.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "OSLBitmap2"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #11");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_metallic.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_image"));
		ufbxt_assert(texture->file_textures.count == 2);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_metallic.png"));
		ufbxt_check_shader_input_map(texture, "offset", "Map #7", 2);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #13");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ColorMax"));
		ufbxt_assert(texture->file_textures.count == 2);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_weight.png"));
		ufbxt_check_shader_input_map(texture, "A", "Map #15", 0);
		ufbxt_check_shader_input_map(texture, "B", "Map #6", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #14");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_weight.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "UberBitmap2"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_weight.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #15");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "TriTone"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_weight.png"));
		ufbxt_check_shader_input_map(texture, "Input_map", "Map #14", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #16");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_mix_rgba"));
		ufbxt_assert(texture->file_textures.count == 3);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_metallic.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_weight.png"));
		ufbxt_check_shader_input_map(texture, "input1", "Map #13", 0);
		ufbxt_check_shader_input_map(texture, "input2", "Map #11", 0);
		ufbxt_check_shader_input_map(texture, "mix", "Map #7", 1);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #17");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_dot"));
		ufbxt_assert(texture->file_textures.count == 3);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_diffuse.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_metallic.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_weight.png"));
		ufbxt_check_shader_input_map(texture, "input1", "Map #16", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #19");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ColorAdd"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
		ufbxt_check_shader_input_map(texture, "A", "Map #20", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #20");
		ufbxt_assert(texture && !texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_emissive.png"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #21");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_add"));
		ufbxt_assert(texture->file_textures.count == 2);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_specular.png"));
		ufbxt_check_shader_input_map(texture, "input1", "Map #22", 0);
		ufbxt_check_shader_input_map(texture, "input2", "Map #19", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #22");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_specular.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ai_image"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_specular.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #26");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "VectorAdd"));
		ufbxt_assert(texture->file_textures.count == 3);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_specular.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_transparency.png"));
		ufbxt_check_shader_input_map(texture, "A", "Map #28", 0);
		ufbxt_check_shader_input_map(texture, "B", "Map #21", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #28");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_transparency.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "OSLBitmap2"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_transparency.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #29");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "ColorAdd"));
		ufbxt_assert(texture->file_textures.count == 4);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_specular.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_transparency.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_ambient.png"));
		ufbxt_check_shader_input_map(texture, "A", "Map #30", 0);
		ufbxt_check_shader_input_map(texture, "B", "Map #26", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #30");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_ambient.png"));
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_OSL);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, "UberBitmap2"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_ambient.png"));
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #31");
		ufbxt_assert(texture && texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_SHADER);
		ufbxt_assert(texture->shader->type == UFBX_SHADER_TEXTURE_UNKNOWN);
		ufbxt_assert(!strcmp(texture->shader->shader_name.data, ""));
		ufbxt_assert(texture->shader->shader_type_id == UINT64_C(0x0000023000000000));
		ufbxt_assert(texture->file_textures.count == 5);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_emissive.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_specular.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_transparency.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_ambient.png"));
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_displacement.png"));
		ufbxt_check_shader_input_map(texture, "map1", "Map #29", 0);
		ufbxt_check_shader_input_map(texture, "map2", "Map #32", 0);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "Map #32");
		ufbxt_assert(texture && !texture->shader);
		ufbxt_assert(texture->type == UFBX_TEXTURE_FILE);
		ufbxt_assert(!strcmp(texture->relative_filename.data, "textures\\checkerboard_displacement.png"));
		ufbxt_assert(texture->file_textures.count == 1);
		ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_displacement.png"));
	}

	{
		ufbx_material *material = (ufbx_material*)ufbx_find_element(scene, UFBX_ELEMENT_MATERIAL, "Material #25");
		ufbxt_assert(material);
		ufbxt_assert(material->shader_type == UFBX_SHADER_OSL_STANDARD_SURFACE);

		ufbxt_assert(material->pbr.base_factor.texture);
		ufbxt_assert(!strcmp(material->pbr.base_factor.texture->name.data, "Map #17"));

		ufbxt_assert(material->pbr.base_color.texture);
		ufbxt_assert(!strcmp(material->pbr.base_color.texture->name.data, "Map #13"));

		ufbxt_assert(material->pbr.emission_factor.texture);
		ufbxt_assert(!strcmp(material->pbr.emission_factor.texture->name.data, "Map #31"));

		{
			ufbx_texture *texture = material->pbr.roughness.texture;
			ufbxt_assert(texture);
			ufbxt_assert(texture->shader);
			ufbxt_assert(texture->file_textures.count == 1);
			ufbxt_assert(ufbxt_has_texture(texture->file_textures, "textures\\checkerboard_ambient.png"));
			ufbxt_assert(texture->shader->main_texture);
			ufbxt_assert(texture->shader->main_texture_output_index == 6);
			ufbxt_assert(!strcmp(texture->shader->main_texture->name.data, "Map #30"));
			ufbxt_check_shader_input_map(texture, "sourceMap", "Map #30", 6);
		}
	}
}
#endif

UFBXT_FILE_TEST(maya_duplicated_texture)
#if UFBXT_IMPL
{
	ufbxt_assert(scene->texture_files.count == 1);
	ufbxt_assert(scene->textures.count == 4);

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "file1");
		ufbxt_assert(texture->has_file);
		ufbxt_assert(texture->file_index == 0);
		ufbxt_assert(!strcmp(texture->uv_set.data, "map1"));
		ufbxt_assert(!texture->has_uv_transform);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "file2");
		ufbxt_assert(texture->has_file);
		ufbxt_assert(texture->file_index == 0);
		ufbxt_assert(!strcmp(texture->uv_set.data, "map1"));
		ufbxt_assert(!texture->has_uv_transform);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "file3");
		ufbxt_assert(texture->has_file);
		ufbxt_assert(texture->file_index == 0);
		ufbxt_assert(!strcmp(texture->uv_set.data, "map1"));
		ufbxt_assert(texture->has_uv_transform);
		ufbxt_assert_close_real(err, texture->uv_transform.scale.x, 2.0f);
		ufbxt_assert_close_real(err, texture->uv_transform.scale.y, 2.0f);
	}

	{
		ufbx_texture *texture = (ufbx_texture*)ufbx_find_element(scene, UFBX_ELEMENT_TEXTURE, "file4");
		ufbxt_assert(texture->has_file);
		ufbxt_assert(texture->file_index == 0);
		ufbxt_assert(!strcmp(texture->uv_set.data, "second"));
		ufbxt_assert(!texture->has_uv_transform);
	}
}
#endif
