Cleanup: CacheFile, use double precision for time

Both the Alembic and USD libraries use double precision floating
point numbers internally to store time. However the Alembic I/O
code defaulted to floats even though Blender's Scene FPS, which is
generally used for look ups, is stored using a double type. Such
downcasts could lead to imprecise lookups, and would cause
compilation warnings (at least on MSVC).

This modifies the Alembic exporter and importer to make use of
doubles for the current scene time, and only downcasting to float
at the very last steps (e.g. for vertex interpolation). For the
importer, doubles are also used for computing interpolation weights,
as it is based on a time offset.

Although the USD code already used doubles internally, floats were used
at the C API level. Those were replaced as well.

Differential Revision: https://developer.blender.org/D13855
This commit is contained in:
Kévin Dietrich 2022-04-08 17:57:35 +02:00
parent 8b7cd1ed2a
commit f3a475a767
15 changed files with 57 additions and 55 deletions

View File

@ -35,7 +35,7 @@ bool BKE_cachefile_filepath_get(const struct Main *bmain,
const struct CacheFile *cache_file,
char r_filename[1024]);
float BKE_cachefile_time_offset(const struct CacheFile *cache_file, float time, float fps);
double BKE_cachefile_time_offset(const struct CacheFile *cache_file, double time, double fps);
/* Modifiers and constraints open and free readers through these. */
void BKE_cachefile_reader_open(struct CacheFile *cache_file,

View File

@ -395,8 +395,8 @@ bool BKE_cachefile_filepath_get(const Main *bmain,
if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) {
Scene *scene = DEG_get_evaluated_scene(depsgraph);
const float ctime = BKE_scene_ctime_get(scene);
const float fps = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base);
const float frame = BKE_cachefile_time_offset(cache_file, ctime, fps);
const double fps = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base);
const int frame = (int)BKE_cachefile_time_offset(cache_file, (double)ctime, fps);
char ext[32];
BLI_path_frame_strip(r_filepath, ext);
@ -410,10 +410,10 @@ bool BKE_cachefile_filepath_get(const Main *bmain,
return true;
}
float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, const float fps)
double BKE_cachefile_time_offset(const CacheFile *cache_file, const double time, const double fps)
{
const float time_offset = cache_file->frame_offset / fps;
const float frame = (cache_file->override_frame ? cache_file->frame : time);
const double time_offset = (double)cache_file->frame_offset / fps;
const double frame = (cache_file->override_frame ? (double)cache_file->frame : time);
return cache_file->is_sequence ? frame : frame / fps - time_offset;
}

View File

@ -5417,7 +5417,7 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa
}
const float frame = DEG_get_ctime(cob->depsgraph);
const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
const double time = BKE_cachefile_time_offset(cache_file, (double)frame, FPS);
if (!data->reader || !STREQ(data->reader_object_path, data->object_path)) {
STRNCPY(data->reader_object_path, data->object_path);

View File

@ -96,14 +96,14 @@ void ABC_free_handle(struct CacheArchiveHandle *handle);
void ABC_get_transform(struct CacheReader *reader,
float r_mat_world[4][4],
float time,
double time,
float scale);
/* Either modifies existing_mesh in-place or constructs a new mesh. */
struct Mesh *ABC_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
float time,
double time,
const char **err_str,
int read_flags,
const char *velocity_name,
@ -112,7 +112,7 @@ struct Mesh *ABC_read_mesh(struct CacheReader *reader,
bool ABC_mesh_topology_changed(struct CacheReader *reader,
struct Object *ob,
const struct Mesh *existing_mesh,
float time,
double time,
const char **err_str);
void ABC_CacheReader_incref(struct CacheReader *reader);

View File

@ -134,7 +134,7 @@ static void export_startjob(void *customdata,
/* Update the scene for the next frame to render. */
scene->r.cfra = static_cast<int>(frame);
scene->r.subframe = frame - scene->r.cfra;
scene->r.subframe = static_cast<float>(frame - scene->r.cfra);
BKE_scene_graph_update_for_newframe(data->depsgraph);
CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);

View File

@ -48,8 +48,8 @@ struct CDStreamConfig {
Mesh *mesh;
void *(*add_customdata_cb)(Mesh *mesh, const char *name, int data_type);
float weight;
float time;
double weight;
Alembic::Abc::chrono_t time;
int timesample_index;
bool use_vertex_interpolation;
Alembic::AbcGeom::index_t index;
@ -79,8 +79,8 @@ struct CDStreamConfig {
pack_uvs(false),
mesh(NULL),
add_customdata_cb(NULL),
weight(0.0f),
time(0.0f),
weight(0.0),
time(0.0),
index(0),
ceil_index(0),
modifier_error_message(NULL)

View File

@ -121,7 +121,7 @@ struct AbcMeshData {
static void read_mverts_interp(MVert *mverts,
const P3fArraySamplePtr &positions,
const P3fArraySamplePtr &ceil_positions,
const float weight)
const double weight)
{
float tmp[3];
for (int i = 0; i < positions->size(); i++) {
@ -129,7 +129,7 @@ static void read_mverts_interp(MVert *mverts,
const Imath::V3f &floor_pos = (*positions)[i];
const Imath::V3f &ceil_pos = (*ceil_positions)[i];
interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), static_cast<float>(weight));
copy_zup_from_yup(mvert.co, tmp);
mvert.bweight = 0;

View File

@ -97,7 +97,9 @@ void AbcObjectReader::object(Object *ob)
m_object = ob;
}
static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1, const float weight)
static Imath::M44d blend_matrices(const Imath::M44d &m0,
const Imath::M44d &m1,
const double weight)
{
float mat0[4][4], mat1[4][4], ret[4][4];
@ -108,16 +110,16 @@ static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1,
convert_matrix_datatype(m0, mat0);
convert_matrix_datatype(m1, mat1);
interp_m4_m4m4(ret, mat0, mat1, weight);
interp_m4_m4m4(ret, mat0, mat1, static_cast<float>(weight));
return convert_matrix_datatype(ret);
}
Imath::M44d get_matrix(const IXformSchema &schema, const float time)
Imath::M44d get_matrix(const IXformSchema &schema, const chrono_t time)
{
Alembic::AbcGeom::index_t i0, i1;
Alembic::AbcGeom::XformSample s0, s1;
const float weight = get_weight_and_index(
const double weight = get_weight_and_index(
time, schema.getTimeSampling(), schema.getNumSamples(), i0, i1);
schema.get(s0, Alembic::AbcGeom::ISampleSelector(i0));
@ -148,7 +150,7 @@ bool AbcObjectReader::topology_changed(const Mesh * /*existing_mesh*/,
return false;
}
void AbcObjectReader::setupObjectTransform(const float time)
void AbcObjectReader::setupObjectTransform(const chrono_t time)
{
bool is_constant = false;
float transform_from_alembic[4][4];
@ -214,7 +216,7 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
}
void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
const float time,
const chrono_t time,
const float scale,
bool &is_constant)
{

View File

@ -143,7 +143,7 @@ class AbcObjectReader {
const Alembic::Abc::ISampleSelector &sample_sel);
/** Reads the object matrix and sets up an object transform if animated. */
void setupObjectTransform(float time);
void setupObjectTransform(chrono_t time);
void addCacheModifier();
@ -154,13 +154,13 @@ class AbcObjectReader {
void incref();
void decref();
void read_matrix(float r_mat[4][4], float time, float scale, bool &is_constant);
void read_matrix(float r_mat[4][4], chrono_t time, float scale, bool &is_constant);
protected:
/** Determine whether we can inherit our parent's XForm. */
void determine_inherits_xform();
};
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, float time);
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, chrono_t time);
} // namespace blender::io::alembic

View File

@ -73,7 +73,7 @@ Imath::M44d convert_matrix_datatype(float mat[4][4])
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
m[i][j] = mat[i][j];
m[i][j] = static_cast<double>(mat[i][j]);
}
}
@ -112,35 +112,35 @@ bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string
return prop.getPropertyHeader(name) != nullptr;
}
using index_time_pair_t = std::pair<Alembic::AbcCoreAbstract::index_t, float>;
using index_time_pair_t = std::pair<Alembic::AbcCoreAbstract::index_t, Alembic::AbcGeom::chrono_t>;
float get_weight_and_index(float time,
const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling,
int samples_number,
Alembic::AbcGeom::index_t &i0,
Alembic::AbcGeom::index_t &i1)
double get_weight_and_index(Alembic::AbcGeom::chrono_t time,
const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling,
int samples_number,
Alembic::AbcGeom::index_t &i0,
Alembic::AbcGeom::index_t &i1)
{
samples_number = std::max(samples_number, 1);
index_time_pair_t t0 = time_sampling->getFloorIndex(time, samples_number);
i0 = i1 = t0.first;
if (samples_number == 1 || (fabs(time - t0.second) < 0.0001f)) {
return 0.0f;
if (samples_number == 1 || (fabs(time - t0.second) < 0.0001)) {
return 0.0;
}
index_time_pair_t t1 = time_sampling->getCeilIndex(time, samples_number);
i1 = t1.first;
if (i0 == i1) {
return 0.0f;
return 0.0;
}
const float bias = (time - t0.second) / (t1.second - t0.second);
const double bias = (time - t0.second) / (t1.second - t0.second);
if (fabs(1.0f - bias) < 0.0001f) {
if (fabs(1.0 - bias) < 0.0001) {
i0 = i1;
return 0.0f;
return 0.0;
}
return bias;

View File

@ -79,11 +79,11 @@ void get_min_max_time(const Alembic::AbcGeom::IObject &object,
bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name);
float get_weight_and_index(float time,
const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling,
int samples_number,
Alembic::AbcGeom::index_t &i0,
Alembic::AbcGeom::index_t &i1);
double get_weight_and_index(Alembic::AbcCoreAbstract::chrono_t time,
const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling,
int samples_number,
Alembic::AbcGeom::index_t &i0,
Alembic::AbcGeom::index_t &i1);
AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings);

View File

@ -497,7 +497,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
chrono_t min_time = std::numeric_limits<chrono_t>::max();
chrono_t max_time = std::numeric_limits<chrono_t>::min();
ISampleSelector sample_sel(0.0f);
ISampleSelector sample_sel(0.0);
std::vector<AbcObjectReader *>::iterator iter;
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
AbcObjectReader *reader = *iter;
@ -555,7 +555,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
i = 0;
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
AbcObjectReader *reader = *iter;
reader->setupObjectTransform(0.0f);
reader->setupObjectTransform(0.0);
*data->progress = 0.7f + 0.3f * (++i / size);
*data->do_update = true;
@ -724,7 +724,7 @@ bool ABC_import(bContext *C,
/* ************************************************************************** */
void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], float time, float scale)
void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], double time, float scale)
{
if (!reader) {
return;
@ -775,7 +775,7 @@ static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const ch
return abc_reader;
}
static ISampleSelector sample_selector_for_time(float time)
static ISampleSelector sample_selector_for_time(chrono_t time)
{
/* kFloorIndex is used to be compatible with non-interpolating
* properties; they use the floor. */
@ -785,7 +785,7 @@ static ISampleSelector sample_selector_for_time(float time)
Mesh *ABC_read_mesh(CacheReader *reader,
Object *ob,
Mesh *existing_mesh,
const float time,
const double time,
const char **err_str,
const int read_flag,
const char *velocity_name,
@ -804,7 +804,7 @@ Mesh *ABC_read_mesh(CacheReader *reader,
bool ABC_mesh_topology_changed(CacheReader *reader,
Object *ob,
const Mesh *existing_mesh,
const float time,
const double time,
const char **err_str)
{
AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str);

View File

@ -423,7 +423,7 @@ static USDPrimReader *get_usd_reader(CacheReader *reader, Object * /* ob */, con
struct Mesh *USD_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
const float time,
const double time,
const char **err_str,
const int read_flag)
{
@ -437,7 +437,7 @@ struct Mesh *USD_read_mesh(struct CacheReader *reader,
}
bool USD_mesh_topology_changed(
CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str)
CacheReader *reader, Object *ob, Mesh *existing_mesh, const double time, const char **err_str)
{
USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str));

View File

@ -94,14 +94,14 @@ void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time
struct Mesh *USD_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
float time,
double time,
const char **err_str,
int read_flag);
bool USD_mesh_topology_changed(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
float time,
double time,
const char **err_str);
struct CacheReader *CacheReader_open_usd_object(struct CacheArchiveHandle *handle,

View File

@ -154,7 +154,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
CacheFile *cache_file = mcmd->cache_file;
const float frame = DEG_get_ctime(ctx->depsgraph);
const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
const double time = BKE_cachefile_time_offset(cache_file, (double)frame, FPS);
const char *err_str = nullptr;
if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {