diff --git a/extern/cgltf.h b/extern/cgltf.h index 316a11dfd..94424ecec 100644 --- a/extern/cgltf.h +++ b/extern/cgltf.h @@ -184,6 +184,7 @@ typedef enum cgltf_component_type cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ cgltf_component_type_r_32u, /* UNSIGNED_INT */ cgltf_component_type_r_32f, /* FLOAT */ + cgltf_component_type_r_16f, /* HALF_FLOAT */ cgltf_component_type_max_enum } cgltf_component_type; @@ -4382,7 +4383,7 @@ static int cgltf_parse_json_diffuse_transmission(cgltf_options* options, jsmntok // Defaults cgltf_fill_float_array(out_diff_transmission->diffuse_transmission_color_factor, 3, 1.0f); out_diff_transmission->diffuse_transmission_factor = 0.f; - + for (int j = 0; j < size; ++j) { CGLTF_CHECK_KEY(tokens[i]); diff --git a/gltf/README.md b/gltf/README.md index d75863924..7e609829f 100644 --- a/gltf/README.md +++ b/gltf/README.md @@ -83,6 +83,7 @@ gltfpack supports most Khronos extensions and some multi-vendor extensions in th - KHR_materials_variants - KHR_materials_volume - KHR_mesh_quantization +- KHR_accessor_float16 - KHR_meshopt_compression - KHR_texture_basisu - KHR_texture_transform @@ -93,6 +94,7 @@ gltfpack supports most Khronos extensions and some multi-vendor extensions in th Even if the source file does not use extensions, gltfpack may use some extensions in the output file either by default or when certain options are used: - KHR_mesh_quantization (used by default unless disabled via `-noq`) +- KHR_accessor_float16 (used when requested via `-vph` or `-vth`) - KHR_meshopt_compression (used when requested via `-ce khr` or `-cz`) - KHR_texture_transform (used by default when textures are present, unless disabled via `-noq` or `-vtf`) - KHR_texture_basisu (used when requested via `-tc` or `-tu`) diff --git a/gltf/gltfpack.cpp b/gltf/gltfpack.cpp index 01dde94b3..2507be6c2 100644 --- a/gltf/gltfpack.cpp +++ b/gltf/gltfpack.cpp @@ -849,6 +849,7 @@ static size_t process(cgltf_data* data, const char* input_path, const char* outp const ExtensionInfo extensions[] = { {"KHR_mesh_quantization", settings.quantize, true}, + {"KHR_accessor_float16", settings.quantize && (settings.pos_half || settings.tex_half), true}, {meshopt_ext, settings.compress, !settings.fallback}, {"KHR_texture_transform", (settings.quantize && !settings.tex_float && !json_textures.empty()) || ext_texture_transform, false}, {"KHR_materials_pbrSpecularGlossiness", ext_pbr_specular_glossiness, false}, @@ -1338,10 +1339,20 @@ int main(int argc, char** argv) { settings.pos_float = true; } + else if (strcmp(arg, "-vph") == 0) + { + settings.pos_float = true; + settings.pos_half = true; + } else if (strcmp(arg, "-vtf") == 0) { settings.tex_float = true; } + else if (strcmp(arg, "-vth") == 0) + { + settings.tex_float = true; + settings.tex_half = true; + } else if (strcmp(arg, "-vnf") == 0) { settings.nrm_float = true; @@ -1667,8 +1678,10 @@ 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, "\t-vph: use half precision attributes for positions\n"); fprintf(stderr, "\nVertex attributes:\n"); fprintf(stderr, "\t-vtf: use floating point attributes for texture coordinates\n"); + fprintf(stderr, "\t-vth: use half precision attributes for texture coordinates\n"); fprintf(stderr, "\t-vnf: use floating point attributes for normals\n"); fprintf(stderr, "\t-vi: use interleaved vertex attributes (reduces compression efficiency)\n"); fprintf(stderr, "\t-kv: keep source vertex attributes even if they aren't used\n"); diff --git a/gltf/gltfpack.h b/gltf/gltfpack.h index e1cf03b3c..0adf6c14a 100644 --- a/gltf/gltfpack.h +++ b/gltf/gltfpack.h @@ -126,7 +126,9 @@ struct Settings bool pos_normalized; bool pos_float; + bool pos_half; bool tex_float; + bool tex_half; bool nrm_float; int trn_bits; diff --git a/gltf/stream.cpp b/gltf/stream.cpp index be338314b..779d647cc 100644 --- a/gltf/stream.cpp +++ b/gltf/stream.cpp @@ -164,6 +164,19 @@ QuantizationPosition prepareQuantizationPosition(const std::vector& meshes fprintf(stderr, "Warning: position data has significant error (%.0f%%); consider using floating-point quantization (-vpf) or more bits (-vp N)\n", max_rel_error * 100); } + if (b.isValid() && settings.quantize && settings.pos_half) + { + float amax = 0; + for (int k = 0; k < 3; ++k) + { + amax = std::max(amax, fabsf(b.min.f[0])); + amax = std::max(amax, fabsf(b.max.f[0])); + } + + if (amax > 65500) + fprintf(stderr, "Warning: position data can't be represented by half-precision floating point; consider using floating-point quantization (-vpf)\n"); + } + result.node_scale = result.scale / float((1 << result.bits) - 1) * (result.normalized ? 65535.f : 1.f); return result; @@ -299,7 +312,15 @@ void getPositionBounds(float min[3], float max[3], const Stream& stream, const Q if (settings.quantize) { - if (settings.pos_float) + if (settings.pos_half) + { + for (int k = 0; k < 3; ++k) + { + min[k] = meshopt_dequantizeHalf(meshopt_quantizeHalf(min[k])); + max[k] = meshopt_dequantizeHalf(meshopt_quantizeHalf(max[k])); + } + } + else if (settings.pos_float) { for (int k = 0; k < 3; ++k) { @@ -466,6 +487,26 @@ static StreamFormat writeVertexStreamFloat(std::string& bin, const Stream& strea return format; } +static StreamFormat writeVertexStreamHalf(std::string& bin, const Stream& stream, cgltf_type type, int components, size_t stride) +{ + assert(components >= 1 && components <= 4); + assert(stride >= sizeof(uint16_t) * components); + + for (size_t i = 0; i < stream.data.size(); ++i) + { + const Attr& a = stream.data[i]; + + uint16_t v[4] = {}; + for (int k = 0; k < components; ++k) + v[k] = meshopt_quantizeHalf(a.f[k]); + + bin.append(reinterpret_cast(v), stride); + } + + StreamFormat format = {type, cgltf_component_type_r_16f, false, stride}; + return format; +} + StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings, bool filters) { if (stream.type == cgltf_attribute_type_position) @@ -473,6 +514,9 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua if (!settings.quantize) return writeVertexStreamRaw(bin, stream, cgltf_type_vec3, 3); + if (settings.pos_half) + return writeVertexStreamHalf(bin, stream, cgltf_type_vec3, 3, sizeof(uint16_t) * 4); + if (settings.pos_float) return writeVertexStreamFloat(bin, stream, cgltf_type_vec3, 3, settings.compress && filters, qp.bits, settings.compressmore ? meshopt_EncodeExpSharedComponent : meshopt_EncodeExpSeparate); @@ -552,6 +596,9 @@ StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const Qua if (!settings.quantize) return writeVertexStreamRaw(bin, stream, cgltf_type_vec2, 2); + if (settings.tex_half) + return writeVertexStreamHalf(bin, stream, cgltf_type_vec2, 2, sizeof(uint16_t) * 2); + // expand the encoded range to ensure it covers [0..1) interval // this can slightly reduce precision but we should not need more precision inside 0..1, and this significantly improves compressed size when using encodeExpOne if (settings.tex_float) diff --git a/gltf/write.cpp b/gltf/write.cpp index b61718f56..a2d25abea 100644 --- a/gltf/write.cpp +++ b/gltf/write.cpp @@ -22,6 +22,8 @@ static const char* componentType(cgltf_component_type type) return "5125"; case cgltf_component_type_r_32f: return "5126"; + case cgltf_component_type_r_16f: + return "5131"; default: return "0"; }