Files
third_party_meshoptimizer/gltf/material.cpp
T
Arseny Kapoulkine 524f0fc489 gltfpack: Remove normal streams from meshes with unlit materials
Unlit materials do not require normals or tangents; we already
automatically filter out tangents because of the lack of normal texture
or other material extensions, but normals were not removed until now.

This can reduce the amount of geometry we need to output; this also
mostly subsumes the redundant normal stream filtering for point clouds
as point clouds often have an unlit material, but for now we'll keep
that redundancy.

When material variants are used, we only do this when all material
variants for a given mesh are unlit. The use of the default material
also blocks this optimization as default material implies basic
lighting.
2023-11-16 10:04:03 -08:00

585 lines
16 KiB
C++

// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"
#include <string.h>
static bool areTexturesEqual(const cgltf_texture& lhs, const cgltf_texture& rhs)
{
if (lhs.image != rhs.image)
return false;
if (lhs.sampler != rhs.sampler)
return false;
return true;
}
static bool areTextureViewsEqual(const cgltf_texture_view& lhs, const cgltf_texture_view& rhs)
{
if (lhs.has_transform != rhs.has_transform)
return false;
if (lhs.has_transform)
{
const cgltf_texture_transform& lt = lhs.transform;
const cgltf_texture_transform& rt = rhs.transform;
if (memcmp(lt.offset, rt.offset, sizeof(cgltf_float) * 2) != 0)
return false;
if (lt.rotation != rt.rotation)
return false;
if (memcmp(lt.scale, rt.scale, sizeof(cgltf_float) * 2) != 0)
return false;
if (lt.texcoord != rt.texcoord)
return false;
}
if (lhs.texture != rhs.texture && (!lhs.texture || !rhs.texture || !areTexturesEqual(*lhs.texture, *rhs.texture)))
return false;
if (lhs.texcoord != rhs.texcoord)
return false;
if (lhs.scale != rhs.scale)
return false;
return true;
}
static bool areExtrasEqual(const cgltf_extras& lhs, const cgltf_extras& rhs)
{
if (lhs.data && rhs.data)
return strcmp(lhs.data, rhs.data) == 0;
else
return lhs.data == rhs.data;
}
static bool areMaterialComponentsEqual(const cgltf_pbr_metallic_roughness& lhs, const cgltf_pbr_metallic_roughness& rhs)
{
if (!areTextureViewsEqual(lhs.base_color_texture, rhs.base_color_texture))
return false;
if (!areTextureViewsEqual(lhs.metallic_roughness_texture, rhs.metallic_roughness_texture))
return false;
if (memcmp(lhs.base_color_factor, rhs.base_color_factor, sizeof(cgltf_float) * 4) != 0)
return false;
if (lhs.metallic_factor != rhs.metallic_factor)
return false;
if (lhs.roughness_factor != rhs.roughness_factor)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_pbr_specular_glossiness& lhs, const cgltf_pbr_specular_glossiness& rhs)
{
if (!areTextureViewsEqual(lhs.diffuse_texture, rhs.diffuse_texture))
return false;
if (!areTextureViewsEqual(lhs.specular_glossiness_texture, rhs.specular_glossiness_texture))
return false;
if (memcmp(lhs.diffuse_factor, rhs.diffuse_factor, sizeof(cgltf_float) * 4) != 0)
return false;
if (memcmp(lhs.specular_factor, rhs.specular_factor, sizeof(cgltf_float) * 3) != 0)
return false;
if (lhs.glossiness_factor != rhs.glossiness_factor)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_clearcoat& lhs, const cgltf_clearcoat& rhs)
{
if (!areTextureViewsEqual(lhs.clearcoat_texture, rhs.clearcoat_texture))
return false;
if (!areTextureViewsEqual(lhs.clearcoat_roughness_texture, rhs.clearcoat_roughness_texture))
return false;
if (!areTextureViewsEqual(lhs.clearcoat_normal_texture, rhs.clearcoat_normal_texture))
return false;
if (lhs.clearcoat_factor != rhs.clearcoat_factor)
return false;
if (lhs.clearcoat_roughness_factor != rhs.clearcoat_roughness_factor)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_transmission& lhs, const cgltf_transmission& rhs)
{
if (!areTextureViewsEqual(lhs.transmission_texture, rhs.transmission_texture))
return false;
if (lhs.transmission_factor != rhs.transmission_factor)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_ior& lhs, const cgltf_ior& rhs)
{
if (lhs.ior != rhs.ior)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_specular& lhs, const cgltf_specular& rhs)
{
if (!areTextureViewsEqual(lhs.specular_texture, rhs.specular_texture))
return false;
if (!areTextureViewsEqual(lhs.specular_color_texture, rhs.specular_color_texture))
return false;
if (lhs.specular_factor != rhs.specular_factor)
return false;
if (memcmp(lhs.specular_color_factor, rhs.specular_color_factor, sizeof(cgltf_float) * 3) != 0)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_sheen& lhs, const cgltf_sheen& rhs)
{
if (!areTextureViewsEqual(lhs.sheen_color_texture, rhs.sheen_color_texture))
return false;
if (memcmp(lhs.sheen_color_factor, rhs.sheen_color_factor, sizeof(cgltf_float) * 3) != 0)
return false;
if (!areTextureViewsEqual(lhs.sheen_roughness_texture, rhs.sheen_roughness_texture))
return false;
if (lhs.sheen_roughness_factor != rhs.sheen_roughness_factor)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_volume& lhs, const cgltf_volume& rhs)
{
if (!areTextureViewsEqual(lhs.thickness_texture, rhs.thickness_texture))
return false;
if (lhs.thickness_factor != rhs.thickness_factor)
return false;
if (memcmp(lhs.attenuation_color, rhs.attenuation_color, sizeof(cgltf_float) * 3) != 0)
return false;
if (lhs.attenuation_distance != rhs.attenuation_distance)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_emissive_strength& lhs, const cgltf_emissive_strength& rhs)
{
if (lhs.emissive_strength != rhs.emissive_strength)
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_iridescence& lhs, const cgltf_iridescence& rhs)
{
if (lhs.iridescence_factor != rhs.iridescence_factor)
return false;
if (!areTextureViewsEqual(lhs.iridescence_texture, rhs.iridescence_texture))
return false;
if (lhs.iridescence_ior != rhs.iridescence_ior)
return false;
if (lhs.iridescence_thickness_min != rhs.iridescence_thickness_min)
return false;
if (lhs.iridescence_thickness_max != rhs.iridescence_thickness_max)
return false;
if (!areTextureViewsEqual(lhs.iridescence_thickness_texture, rhs.iridescence_thickness_texture))
return false;
return true;
}
static bool areMaterialComponentsEqual(const cgltf_anisotropy& lhs, const cgltf_anisotropy& rhs)
{
if (lhs.anisotropy_strength != rhs.anisotropy_strength)
return false;
if (lhs.anisotropy_rotation != rhs.anisotropy_rotation)
return false;
if (!areTextureViewsEqual(lhs.anisotropy_texture, rhs.anisotropy_texture))
return false;
return true;
}
static bool areMaterialsEqual(const cgltf_material& lhs, const cgltf_material& rhs, const Settings& settings)
{
if (lhs.has_pbr_metallic_roughness != rhs.has_pbr_metallic_roughness)
return false;
if (lhs.has_pbr_metallic_roughness && !areMaterialComponentsEqual(lhs.pbr_metallic_roughness, rhs.pbr_metallic_roughness))
return false;
if (lhs.has_pbr_specular_glossiness != rhs.has_pbr_specular_glossiness)
return false;
if (lhs.has_pbr_specular_glossiness && !areMaterialComponentsEqual(lhs.pbr_specular_glossiness, rhs.pbr_specular_glossiness))
return false;
if (lhs.has_clearcoat != rhs.has_clearcoat)
return false;
if (lhs.has_clearcoat && !areMaterialComponentsEqual(lhs.clearcoat, rhs.clearcoat))
return false;
if (lhs.has_transmission != rhs.has_transmission)
return false;
if (lhs.has_transmission && !areMaterialComponentsEqual(lhs.transmission, rhs.transmission))
return false;
if (lhs.has_ior != rhs.has_ior)
return false;
if (lhs.has_ior && !areMaterialComponentsEqual(lhs.ior, rhs.ior))
return false;
if (lhs.has_specular != rhs.has_specular)
return false;
if (lhs.has_specular && !areMaterialComponentsEqual(lhs.specular, rhs.specular))
return false;
if (lhs.has_sheen != rhs.has_sheen)
return false;
if (lhs.has_sheen && !areMaterialComponentsEqual(lhs.sheen, rhs.sheen))
return false;
if (lhs.has_volume != rhs.has_volume)
return false;
if (lhs.has_volume && !areMaterialComponentsEqual(lhs.volume, rhs.volume))
return false;
if (lhs.has_emissive_strength != rhs.has_emissive_strength)
return false;
if (lhs.has_emissive_strength && !areMaterialComponentsEqual(lhs.emissive_strength, rhs.emissive_strength))
return false;
if (lhs.has_iridescence != rhs.has_iridescence)
return false;
if (lhs.has_iridescence && !areMaterialComponentsEqual(lhs.iridescence, rhs.iridescence))
return false;
if (lhs.has_anisotropy != rhs.has_anisotropy)
return false;
if (lhs.has_anisotropy && !areMaterialComponentsEqual(lhs.anisotropy, rhs.anisotropy))
return false;
if (!areTextureViewsEqual(lhs.normal_texture, rhs.normal_texture))
return false;
if (!areTextureViewsEqual(lhs.occlusion_texture, rhs.occlusion_texture))
return false;
if (!areTextureViewsEqual(lhs.emissive_texture, rhs.emissive_texture))
return false;
if (memcmp(lhs.emissive_factor, rhs.emissive_factor, sizeof(cgltf_float) * 3) != 0)
return false;
if (lhs.alpha_mode != rhs.alpha_mode)
return false;
if (lhs.alpha_cutoff != rhs.alpha_cutoff)
return false;
if (lhs.double_sided != rhs.double_sided)
return false;
if (lhs.unlit != rhs.unlit)
return false;
if (settings.keep_extras && !areExtrasEqual(lhs.extras, rhs.extras))
return false;
return true;
}
void mergeMeshMaterials(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settings)
{
std::vector<cgltf_material*> material_remap(data->materials_count);
for (size_t i = 0; i < data->materials_count; ++i)
{
material_remap[i] = &data->materials[i];
if (settings.keep_materials && data->materials[i].name && *data->materials[i].name)
continue;
for (size_t j = 0; j < i; ++j)
{
if (settings.keep_materials && data->materials[j].name && *data->materials[j].name)
continue;
if (areMaterialsEqual(data->materials[i], data->materials[j], settings))
{
material_remap[i] = &data->materials[j];
break;
}
}
}
for (size_t i = 0; i < meshes.size(); ++i)
{
Mesh& mesh = meshes[i];
if (mesh.material)
mesh.material = material_remap[mesh.material - data->materials];
for (size_t j = 0; j < mesh.variants.size(); ++j)
mesh.variants[j].material = material_remap[mesh.variants[j].material - data->materials];
}
}
void markNeededMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, const std::vector<Mesh>& meshes, const Settings& settings)
{
// mark all used materials as kept
for (size_t i = 0; i < meshes.size(); ++i)
{
const Mesh& mesh = meshes[i];
if (mesh.material)
{
MaterialInfo& mi = materials[mesh.material - data->materials];
mi.keep = true;
}
for (size_t j = 0; j < mesh.variants.size(); ++j)
{
MaterialInfo& mi = materials[mesh.variants[j].material - data->materials];
mi.keep = true;
}
}
// mark all named materials as kept if requested
if (settings.keep_materials)
{
for (size_t i = 0; i < data->materials_count; ++i)
{
cgltf_material& material = data->materials[i];
if (material.name && *material.name)
{
materials[i].keep = true;
}
}
}
}
bool hasValidTransform(const cgltf_texture_view& view)
{
if (view.has_transform)
{
if (view.transform.offset[0] != 0.0f || view.transform.offset[1] != 0.0f ||
view.transform.scale[0] != 1.0f || view.transform.scale[1] != 1.0f ||
view.transform.rotation != 0.0f)
return true;
if (view.transform.has_texcoord && view.transform.texcoord != view.texcoord)
return true;
}
return false;
}
static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind kind, MaterialInfo& mi, cgltf_data* data, std::vector<TextureInfo>& textures, std::vector<ImageInfo>& images)
{
mi.usesTextureTransform |= hasValidTransform(view);
if (view.texture)
textures[view.texture - data->textures].keep = true;
if (view.texture && view.texture->image)
{
ImageInfo& info = images[view.texture->image - data->images];
mi.textureSetMask |= 1u << view.texcoord;
mi.needsTangents |= (kind == TextureKind_Normal);
if (info.kind == TextureKind_Generic)
info.kind = kind;
else if (info.kind > kind) // this is useful to keep color textures that have attrib data in alpha tagged as color
info.kind = kind;
info.normal_map |= (kind == TextureKind_Normal);
info.srgb |= (kind == TextureKind_Color);
}
}
static void analyzeMaterial(const cgltf_material& material, MaterialInfo& mi, cgltf_data* data, std::vector<TextureInfo>& textures, std::vector<ImageInfo>& images)
{
if (material.has_pbr_metallic_roughness)
{
analyzeMaterialTexture(material.pbr_metallic_roughness.base_color_texture, TextureKind_Color, mi, data, textures, images);
analyzeMaterialTexture(material.pbr_metallic_roughness.metallic_roughness_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_pbr_specular_glossiness)
{
analyzeMaterialTexture(material.pbr_specular_glossiness.diffuse_texture, TextureKind_Color, mi, data, textures, images);
analyzeMaterialTexture(material.pbr_specular_glossiness.specular_glossiness_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_clearcoat)
{
analyzeMaterialTexture(material.clearcoat.clearcoat_texture, TextureKind_Attrib, mi, data, textures, images);
analyzeMaterialTexture(material.clearcoat.clearcoat_roughness_texture, TextureKind_Attrib, mi, data, textures, images);
analyzeMaterialTexture(material.clearcoat.clearcoat_normal_texture, TextureKind_Normal, mi, data, textures, images);
}
if (material.has_transmission)
{
analyzeMaterialTexture(material.transmission.transmission_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_specular)
{
analyzeMaterialTexture(material.specular.specular_texture, TextureKind_Attrib, mi, data, textures, images);
analyzeMaterialTexture(material.specular.specular_color_texture, TextureKind_Color, mi, data, textures, images);
}
if (material.has_sheen)
{
analyzeMaterialTexture(material.sheen.sheen_color_texture, TextureKind_Color, mi, data, textures, images);
analyzeMaterialTexture(material.sheen.sheen_roughness_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_volume)
{
analyzeMaterialTexture(material.volume.thickness_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_iridescence)
{
analyzeMaterialTexture(material.iridescence.iridescence_texture, TextureKind_Attrib, mi, data, textures, images);
analyzeMaterialTexture(material.iridescence.iridescence_thickness_texture, TextureKind_Attrib, mi, data, textures, images);
}
if (material.has_anisotropy)
{
analyzeMaterialTexture(material.anisotropy.anisotropy_texture, TextureKind_Normal, mi, data, textures, images);
}
analyzeMaterialTexture(material.normal_texture, TextureKind_Normal, mi, data, textures, images);
analyzeMaterialTexture(material.occlusion_texture, TextureKind_Attrib, mi, data, textures, images);
analyzeMaterialTexture(material.emissive_texture, TextureKind_Color, mi, data, textures, images);
if (material.unlit)
mi.unlit = true;
}
void analyzeMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, std::vector<TextureInfo>& textures, std::vector<ImageInfo>& images)
{
for (size_t i = 0; i < data->materials_count; ++i)
{
analyzeMaterial(data->materials[i], materials[i], data, textures, images);
}
}
static int getChannels(const cgltf_image& image, ImageInfo& info, const char* input_path)
{
if (info.channels)
return info.channels;
std::string img_data;
std::string mime_type;
if (readImage(image, input_path, img_data, mime_type))
info.channels = hasAlpha(img_data, mime_type.c_str()) ? 4 : 3;
else
info.channels = -1;
return info.channels;
}
static bool shouldKeepAlpha(const cgltf_texture_view& color, float alpha, cgltf_data* data, const char* input_path, std::vector<ImageInfo>& images)
{
if (alpha != 1.f)
return true;
return color.texture && color.texture->image && getChannels(*color.texture->image, images[color.texture->image - data->images], input_path) == 4;
}
void optimizeMaterials(cgltf_data* data, const char* input_path, std::vector<ImageInfo>& images)
{
for (size_t i = 0; i < data->materials_count; ++i)
{
// remove BLEND/MASK from materials that don't have alpha information
cgltf_material& material = data->materials[i];
if (material.alpha_mode != cgltf_alpha_mode_opaque)
{
if (material.has_pbr_metallic_roughness && shouldKeepAlpha(material.pbr_metallic_roughness.base_color_texture, material.pbr_metallic_roughness.base_color_factor[3], data, input_path, images))
continue;
if (material.has_pbr_specular_glossiness && shouldKeepAlpha(material.pbr_specular_glossiness.diffuse_texture, material.pbr_specular_glossiness.diffuse_factor[3], data, input_path, images))
continue;
material.alpha_mode = cgltf_alpha_mode_opaque;
}
}
}
void mergeTextures(cgltf_data* data, std::vector<TextureInfo>& textures)
{
size_t offset = 0;
for (size_t i = 0; i < textures.size(); ++i)
{
TextureInfo& info = textures[i];
if (!info.keep)
continue;
for (size_t j = 0; j < i; ++j)
if (textures[j].keep && areTexturesEqual(data->textures[i], data->textures[j]))
{
info.keep = false;
info.remap = textures[j].remap;
break;
}
if (info.keep)
{
info.remap = int(offset);
offset++;
}
}
}