mirror of
https://github.com/openharmony/third_party_meshoptimizer.git
synced 2026-07-20 23:46:19 -04:00
gltfpack: Implement support for floating-point texture coordinate quantization
Similarly to position quantization, we can use quantized floating point values to represent texture coordinates. This can be useful as it trades off a little bit of memory and compression efficiency for convenience and interoperability - without the use of texture transforms it becomes easy to swap textures, reuse materials, and avoid edge cases in quantization when UV bounds are very large.
This commit is contained in:
+15
-4
@@ -508,7 +508,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
|
||||
|
||||
comma(json_materials);
|
||||
append(json_materials, "{");
|
||||
writeMaterial(json_materials, data, material, settings.quantize && !settings.pos_float ? &qp : NULL, settings.quantize ? &qt_materials[i] : NULL);
|
||||
writeMaterial(json_materials, data, material, settings.quantize && !settings.pos_float ? &qp : NULL, settings.quantize && !settings.tex_float ? &qt_materials[i] : NULL);
|
||||
if (settings.keep_extras)
|
||||
writeExtras(json_materials, material.extras);
|
||||
append(json_materials, "}");
|
||||
@@ -804,7 +804,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
|
||||
const ExtensionInfo extensions[] = {
|
||||
{"KHR_mesh_quantization", settings.quantize, true},
|
||||
{"EXT_meshopt_compression", settings.compress, !settings.fallback},
|
||||
{"KHR_texture_transform", (settings.quantize && !json_textures.empty()) || ext_texture_transform, false},
|
||||
{"KHR_texture_transform", (settings.quantize && !settings.tex_float && !json_textures.empty()) || ext_texture_transform, false},
|
||||
{"KHR_materials_pbrSpecularGlossiness", ext_pbr_specular_glossiness, false},
|
||||
{"KHR_materials_clearcoat", ext_clearcoat, false},
|
||||
{"KHR_materials_transmission", ext_transmission, false},
|
||||
@@ -1213,6 +1213,14 @@ int main(int argc, char** argv)
|
||||
{
|
||||
settings.pos_float = true;
|
||||
}
|
||||
else if (strcmp(arg, "-vtn") == 0)
|
||||
{
|
||||
settings.tex_float = false;
|
||||
}
|
||||
else if (strcmp(arg, "-vtf") == 0)
|
||||
{
|
||||
settings.tex_float = true;
|
||||
}
|
||||
else if (strcmp(arg, "-at") == 0 && i + 1 < argc && isdigit(argv[i + 1][0]))
|
||||
{
|
||||
settings.trn_bits = clamp(atoi(argv[++i]), 1, 24);
|
||||
@@ -1455,6 +1463,9 @@ int main(int argc, char** argv)
|
||||
fprintf(stderr, "\t-vpi: use integer attributes for positions (default)\n");
|
||||
fprintf(stderr, "\t-vpn: use normalized attributes for positions\n");
|
||||
fprintf(stderr, "\t-vpf: use floating point attributes for positions\n");
|
||||
fprintf(stderr, "\nTexture coordinates:\n");
|
||||
fprintf(stderr, "\t-vtn: use normalized attributes for texture coordinates (default)\n");
|
||||
fprintf(stderr, "\t-vtf: use floating point attributes for texture coordinates\n");
|
||||
fprintf(stderr, "\nAnimations:\n");
|
||||
fprintf(stderr, "\t-at N: use N-bit quantization for translations (default: 16; N should be between 1 and 24)\n");
|
||||
fprintf(stderr, "\t-ar N: use N-bit quantization for rotations (default: 12; N should be between 4 and 16)\n");
|
||||
@@ -1524,9 +1535,9 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (settings.fallback && settings.pos_float)
|
||||
if (settings.fallback && (settings.pos_float || settings.tex_float))
|
||||
{
|
||||
fprintf(stderr, "Option -cf can not be used together with -vpf\n");
|
||||
fprintf(stderr, "Option -cf can not be used together with -vpf or -tpf\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ struct Settings
|
||||
|
||||
bool pos_normalized;
|
||||
bool pos_float;
|
||||
bool tex_float;
|
||||
|
||||
int trn_bits;
|
||||
int rot_bits;
|
||||
|
||||
+94
-16
@@ -321,7 +321,7 @@ static uint32_t encodeExpOne(float v, int bits)
|
||||
return (m & mmask) | (unsigned(exp) << 24);
|
||||
}
|
||||
|
||||
static void encodeExpParallel(std::string& bin, const Attr* data, size_t count, int bits)
|
||||
static void encodeExpParallel3(std::string& bin, const Attr* data, size_t count, int bits)
|
||||
{
|
||||
int expx = -128, expy = -128, expz = -128;
|
||||
|
||||
@@ -367,6 +367,47 @@ static void encodeExpParallel(std::string& bin, const Attr* data, size_t count,
|
||||
}
|
||||
}
|
||||
|
||||
static void encodeExpParallel2(std::string& bin, const Attr* data, size_t count, int bits)
|
||||
{
|
||||
int expx = -128, expy = -128;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const Attr& a = data[i];
|
||||
|
||||
// get exponents from all components
|
||||
int ex, ey;
|
||||
frexp(a.f[0], &ex);
|
||||
frexp(a.f[1], &ey);
|
||||
|
||||
// use maximum exponent to encode values; this guarantees that mantissa is [-1, 1]
|
||||
expx = std::max(expx, ex);
|
||||
expy = std::max(expy, ey);
|
||||
}
|
||||
|
||||
// scale the mantissa to make it a K-bit signed integer (K-1 bits for magnitude)
|
||||
expx -= (bits - 1);
|
||||
expy -= (bits - 1);
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const Attr& a = data[i];
|
||||
|
||||
// compute renormalized rounded mantissas
|
||||
int mx = int(ldexp(a.f[0], -expx) + (a.f[0] >= 0 ? 0.5f : -0.5f));
|
||||
int my = int(ldexp(a.f[1], -expy) + (a.f[1] >= 0 ? 0.5f : -0.5f));
|
||||
|
||||
int mmask = (1 << 24) - 1;
|
||||
|
||||
// encode exponent & mantissa
|
||||
uint32_t v[2];
|
||||
v[0] = (mx & mmask) | (unsigned(expx) << 24);
|
||||
v[1] = (my & mmask) | (unsigned(expy) << 24);
|
||||
|
||||
bin.append(reinterpret_cast<const char*>(v), sizeof(v));
|
||||
}
|
||||
}
|
||||
|
||||
static StreamFormat writeVertexStreamRaw(std::string& bin, const Stream& stream, cgltf_type type, size_t components)
|
||||
{
|
||||
assert(components >= 1 && components <= 4);
|
||||
@@ -405,7 +446,7 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua
|
||||
|
||||
if (settings.compressmore)
|
||||
{
|
||||
encodeExpParallel(bin, &stream.data[0], stream.data.size(), qp.bits + 1);
|
||||
encodeExpParallel3(bin, &stream.data[0], stream.data.size(), qp.bits + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -511,24 +552,61 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua
|
||||
if (!settings.quantize)
|
||||
return writeVertexStreamRaw(bin, stream, cgltf_type_vec2, 2);
|
||||
|
||||
float uv_rscale[2] = {
|
||||
qt.scale[0] == 0.f ? 0.f : 1.f / qt.scale[0],
|
||||
qt.scale[1] == 0.f ? 0.f : 1.f / qt.scale[1],
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < stream.data.size(); ++i)
|
||||
if (settings.tex_float)
|
||||
{
|
||||
const Attr& a = stream.data[i];
|
||||
StreamFormat::Filter filter = settings.compress ? StreamFormat::Filter_Exp : StreamFormat::Filter_None;
|
||||
|
||||
uint16_t v[2] = {
|
||||
uint16_t(meshopt_quantizeUnorm((a.f[0] - qt.offset[0]) * uv_rscale[0], qt.bits)),
|
||||
uint16_t(meshopt_quantizeUnorm((a.f[1] - qt.offset[1]) * uv_rscale[1], qt.bits)),
|
||||
};
|
||||
bin.append(reinterpret_cast<const char*>(v), sizeof(v));
|
||||
if (settings.compressmore)
|
||||
{
|
||||
encodeExpParallel2(bin, &stream.data[0], stream.data.size(), qt.bits + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < stream.data.size(); ++i)
|
||||
{
|
||||
const Attr& a = stream.data[i];
|
||||
|
||||
if (filter == StreamFormat::Filter_Exp)
|
||||
{
|
||||
uint32_t v[2];
|
||||
v[0] = encodeExpOne(a.f[0], qt.bits + 1);
|
||||
v[1] = encodeExpOne(a.f[1], qt.bits + 1);
|
||||
bin.append(reinterpret_cast<const char*>(v), sizeof(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
float v[2] = {
|
||||
meshopt_quantizeFloat(a.f[0], qt.bits),
|
||||
meshopt_quantizeFloat(a.f[1], qt.bits)};
|
||||
bin.append(reinterpret_cast<const char*>(v), sizeof(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamFormat format = {cgltf_type_vec2, cgltf_component_type_r_32f, false, 8, filter};
|
||||
return format;
|
||||
}
|
||||
else
|
||||
{
|
||||
float uv_rscale[2] = {
|
||||
qt.scale[0] == 0.f ? 0.f : 1.f / qt.scale[0],
|
||||
qt.scale[1] == 0.f ? 0.f : 1.f / qt.scale[1],
|
||||
};
|
||||
|
||||
StreamFormat format = {cgltf_type_vec2, cgltf_component_type_r_16u, qt.normalized, 4};
|
||||
return format;
|
||||
for (size_t i = 0; i < stream.data.size(); ++i)
|
||||
{
|
||||
const Attr& a = stream.data[i];
|
||||
|
||||
uint16_t v[2] = {
|
||||
uint16_t(meshopt_quantizeUnorm((a.f[0] - qt.offset[0]) * uv_rscale[0], qt.bits)),
|
||||
uint16_t(meshopt_quantizeUnorm((a.f[1] - qt.offset[1]) * uv_rscale[1], qt.bits)),
|
||||
};
|
||||
bin.append(reinterpret_cast<const char*>(v), sizeof(v));
|
||||
}
|
||||
|
||||
StreamFormat format = {cgltf_type_vec2, cgltf_component_type_r_16u, qt.normalized, 4};
|
||||
return format;
|
||||
}
|
||||
}
|
||||
else if (stream.type == cgltf_attribute_type_normal)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user