#define _CRT_SECURE_NO_WARNINGS #include "../ufbx.h" #include "hash_scene.h" #include #include #include #include typedef struct { size_t pos; size_t size; union { size_t size_and_align[2]; bool reverse; }; char data[]; } ufbxt_linear_alloactor; static void *linear_alloc_fn(void *user, size_t size) { ufbxt_linear_alloactor *ator = (ufbxt_linear_alloactor*)user; size = (size + 7u) & ~(size_t)7; size_t pos = ator->pos; if (ator->reverse) { assert(pos >= size); ator->pos = pos - size; return ator->data + (pos - size); } else { assert(ator->size - pos >= size); ator->pos = pos + size; return ator->data + pos; } } static void linear_free_allocator_fn(void *user) { ufbxt_linear_alloactor *ator = (ufbxt_linear_alloactor*)user; free(ator); } static void linear_allocator_init(ufbx_allocator *allocator, size_t size, bool reverse) { ufbxt_linear_alloactor *ator = (ufbxt_linear_alloactor*)malloc(sizeof(ufbxt_linear_alloactor) + size); assert(ator); ator->size = size; ator->reverse = reverse; if (reverse) { ator->pos = ator->size; } else { ator->pos = 0; } allocator->alloc_fn = &linear_alloc_fn; allocator->free_allocator_fn = &linear_free_allocator_fn; allocator->user = ator; } typedef struct { bool dedicated_allocs; bool no_read_buffer; const char *allocator; size_t linear_allocator_size; } ufbxt_hasher_opts; static void setup_allocator(ufbx_allocator_opts *ator_opts, ufbxt_hasher_opts *opts) { if (opts->dedicated_allocs) { ator_opts->huge_threshold = 1; } size_t linear_size = opts->linear_allocator_size ? opts->linear_allocator_size : 128u*1024u*1024u; if (opts->allocator) { if (!strcmp(opts->allocator, "linear")) { linear_allocator_init(&ator_opts->allocator, linear_size, false); } else if (!strcmp(opts->allocator, "linear-reverse")) { linear_allocator_init(&ator_opts->allocator, linear_size, true); } else { fprintf(stderr, "Unknown allocator: %s\n", opts->allocator); exit(1); } } } static ufbx_scene *load_scene(const char *filename, int frame, ufbxt_hasher_opts *hasher_opts) { ufbx_load_opts opts = { 0 }; opts.load_external_files = true; opts.evaluate_caches = true; opts.evaluate_skinning = true; opts.target_axes = ufbx_axes_right_handed_y_up; opts.target_unit_meters = 1.0f; if (hasher_opts->no_read_buffer) { opts.read_buffer_size = 1; } setup_allocator(&opts.temp_allocator, hasher_opts); setup_allocator(&opts.result_allocator, hasher_opts); ufbx_error error; ufbx_scene *scene = ufbx_load_file(filename, &opts, &error); if (!scene) { fprintf(stderr, "Failed to load scene: %s\n", error.description.data); exit(2); } if (frame > 0) { ufbx_evaluate_opts eval_opts = { 0 }; eval_opts.evaluate_caches = true; eval_opts.evaluate_skinning = true; eval_opts.load_external_files = true; double time = scene->anim.time_begin + frame / scene->settings.frames_per_second; ufbx_scene *state = ufbx_evaluate_scene(scene, NULL, time, NULL, &error); if (!state) { fprintf(stderr, "Failed to evaluate scene: %s\n", error.description.data); exit(2); } ufbx_free_scene(scene); scene = state; } return scene; } int main(int argc, char **argv) { const char *filename = NULL; const char *dump_filename = NULL; int max_dump_errors = -1; bool do_check = false; bool dump_all = false; bool verbose = false; ufbxt_hasher_opts hasher_opts = { 0 }; int frame = -1; for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "--frame")) { if (++i < argc) frame = atoi(argv[i]); } else if (!strcmp(argv[i], "--verbose")) { verbose = true; } else if (!strcmp(argv[i], "--check")) { do_check = true; } else if (!strcmp(argv[i], "--dump")) { if (++i < argc) dump_filename = argv[i]; } else if (!strcmp(argv[i], "--dump-all")) { dump_all = true; } else if (!strcmp(argv[i], "--dedicated-allocs")) { hasher_opts.dedicated_allocs = true; } else if (!strcmp(argv[i], "--no-read-buffer")) { hasher_opts.no_read_buffer = true; } else if (!strcmp(argv[i], "--allocator")) { if (++i < argc) hasher_opts.allocator = argv[i]; } else if (!strcmp(argv[i], "--max-dump-errors")) { if (++i < argc) max_dump_errors = atoi(argv[i]); } else { if (filename) { if (argv[i][0] == '-') { fprintf(stderr, "Unknown flag: %s\n", argv[i]); exit(1); } else { fprintf(stderr, "Error: Multiple input files\n"); exit(1); } } filename = argv[i]; } } if (!filename) { fprintf(stderr, "Usage: hash_scene \n"); fprintf(stderr, " hash_scene --check \n"); return 1; } int num_fail = 0; int num_total = 0; FILE *dump_file = NULL; if (do_check) { FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Failed to open hash file\n"); return 1; } uint64_t fbx_hash = 0; char fbx_file[1024]; while (fscanf(f, "%" SCNx64 " %d %s", &fbx_hash, &frame, fbx_file) == 3) { ufbx_scene *scene = load_scene(fbx_file, frame, &hasher_opts); uint64_t hash = ufbxt_hash_scene(scene, NULL); if (hash != fbx_hash || dump_all) { if (num_fail < max_dump_errors || dump_all) { if (!dump_file) { dump_file = fopen(dump_filename, "wb"); assert(dump_file); } fprintf(dump_file, "\n-- %d %s\n\n", frame, fbx_file); ufbxt_hash_scene(scene, dump_file); } if (!dump_all) { printf("%s: FAIL %" PRIx64 " (local) vs %" PRIx64 " (reference)\n", fbx_file, hash, fbx_hash); num_fail++; } } else { if (verbose) { printf("%s: OK\n", fbx_file); } } num_total++; } fclose(f); if (!dump_all) { if (verbose || num_fail > 0) { printf("\n"); } printf("%d/%d hashes match\n", num_total - num_fail, num_total); } } else { ufbx_scene *scene = load_scene(filename, frame, &hasher_opts); if (dump_filename) { dump_file = fopen(dump_filename, "wb"); assert(dump_file); } uint64_t hash = ufbxt_hash_scene(scene, dump_file); printf("%016" PRIx64 "\n", hash); } if (dump_file) { fclose(dump_file); } return num_fail > 0 ? 3 : 0; } #define UFBX_NO_MATH_H #define UFBX_MATH_PREFIX fdlibm_ #include "../ufbx.c"