mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
Add role type casting support for TimeSamples in USDC reader
When reading USDC files, TimeSamples were stored with base types (e.g., float3) instead of role types (e.g., color3f, point3f). This caused type mismatches when the typeName specified a role type. Changes: - timesamples.hh: Add cast_to_role_type() method declaration - timesamples.cc: Implement cast_to_role_type() with a helper function that maps role types to their underlying base types - usdc-reader.cc: Apply role type casting when processing timeSamples fields based on the attribute's typeName The casting is a zero-copy operation that only updates the type_id metadata without modifying the underlying data. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -991,6 +991,87 @@ bool TimeSamples::init(uint32_t type_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to get underlying type_id from a type_id
|
||||
// For role types (color3f, point3f, etc.), returns the base type's type_id
|
||||
// For non-role types, returns the same type_id
|
||||
uint32_t GetUnderlyingTypeIdFromTypeId(uint32_t tyid) {
|
||||
// Strip array bit if present
|
||||
bool is_array = (tyid & TYPE_ID_1D_ARRAY_BIT) != 0;
|
||||
uint32_t base_tyid = tyid & (~TYPE_ID_1D_ARRAY_BIT);
|
||||
|
||||
// Map role types to their underlying types
|
||||
// This is needed because we don't have TypeTraits access at runtime
|
||||
#define MAP_ROLE_TO_UNDERLYING(role_id, underlying_id) \
|
||||
if (base_tyid == role_id) return is_array ? (underlying_id | TYPE_ID_1D_ARRAY_BIT) : underlying_id;
|
||||
|
||||
// Texcoord types
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD2H, TYPE_ID_HALF2)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD2F, TYPE_ID_FLOAT2)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD2D, TYPE_ID_DOUBLE2)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD3H, TYPE_ID_HALF3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD3F, TYPE_ID_FLOAT3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_TEXCOORD3D, TYPE_ID_DOUBLE3)
|
||||
|
||||
// Normal types
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_NORMAL3H, TYPE_ID_HALF3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_NORMAL3F, TYPE_ID_FLOAT3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_NORMAL3D, TYPE_ID_DOUBLE3)
|
||||
|
||||
// Vector types
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_VECTOR3H, TYPE_ID_HALF3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_VECTOR3F, TYPE_ID_FLOAT3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_VECTOR3D, TYPE_ID_DOUBLE3)
|
||||
|
||||
// Point types
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_POINT3H, TYPE_ID_HALF3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_POINT3F, TYPE_ID_FLOAT3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_POINT3D, TYPE_ID_DOUBLE3)
|
||||
|
||||
// Color types
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR3H, TYPE_ID_HALF3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR3F, TYPE_ID_FLOAT3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR3D, TYPE_ID_DOUBLE3)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR4H, TYPE_ID_HALF4)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR4F, TYPE_ID_FLOAT4)
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_COLOR4D, TYPE_ID_DOUBLE4)
|
||||
|
||||
// Frame type
|
||||
MAP_ROLE_TO_UNDERLYING(TYPE_ID_FRAME4D, TYPE_ID_MATRIX4D)
|
||||
|
||||
#undef MAP_ROLE_TO_UNDERLYING
|
||||
|
||||
// Not a role type, return as-is
|
||||
return tyid;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TimeSamples::cast_to_role_type(uint32_t role_type_id) {
|
||||
if (_type_id == 0) {
|
||||
return false; // Not initialized
|
||||
}
|
||||
|
||||
// If already the target type, nothing to do
|
||||
if (_type_id == role_type_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get underlying type_ids for both current and target types
|
||||
uint32_t current_underlying = GetUnderlyingTypeIdFromTypeId(_type_id);
|
||||
uint32_t target_underlying = GetUnderlyingTypeIdFromTypeId(role_type_id);
|
||||
|
||||
// Check if the underlying types match
|
||||
if (current_underlying != target_underlying) {
|
||||
return false; // Incompatible types
|
||||
}
|
||||
|
||||
// Safe to cast - just update the type_id
|
||||
_type_id = role_type_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace value
|
||||
} // namespace tinyusdz
|
||||
|
||||
|
||||
@@ -180,6 +180,12 @@ struct TimeSamples {
|
||||
/// This determines whether to use POD optimization or regular storage
|
||||
bool init(uint32_t type_id);
|
||||
|
||||
/// Cast the TimeSamples' type to a role type if the underlying types are compatible.
|
||||
/// This allows reinterpreting stored base types (e.g., float3) as role types (e.g., color3f).
|
||||
/// @param role_type_id The target role type's type_id
|
||||
/// @return true if the cast was successful, false if the underlying types don't match
|
||||
bool cast_to_role_type(uint32_t role_type_id);
|
||||
|
||||
/// Check if unified storage has samples (i.e., _times is not empty)
|
||||
/// Returns true if any samples have been added to unified storage path.
|
||||
/// Note: After PODTimeSamples removal, this checks unified storage, not a separate POD type.
|
||||
|
||||
@@ -1032,7 +1032,23 @@ bool USDCReader::Impl::ParseProperty(const SpecType spec_type,
|
||||
// same TimeSamples from the fieldset. Using std::move would leave the
|
||||
// CrateValue empty after the first use, causing subsequent attributes
|
||||
// to get an empty TimeSamples.
|
||||
var.set_timesamples(ts);
|
||||
//
|
||||
// We make a copy and apply role type casting to the copy if needed.
|
||||
value::TimeSamples ts_copy = ts;
|
||||
|
||||
// Apply role type casting if typeName specifies a role type
|
||||
// (e.g., cast float3 to color3f, point3f, etc.)
|
||||
if (typeName) {
|
||||
uint32_t role_type_id = value::GetTypeId(typeName.value().str());
|
||||
if (role_type_id != value::TYPE_ID_INVALID) {
|
||||
if (ts_copy.cast_to_role_type(role_type_id)) {
|
||||
DCOUT(fmt::format("Cast TimeSamples to role type {}", typeName.value().str()));
|
||||
}
|
||||
// It's ok if casting fails - the base type is still valid
|
||||
}
|
||||
}
|
||||
|
||||
var.set_timesamples(ts_copy);
|
||||
} else {
|
||||
PUSH_ERROR_AND_RETURN_TAG(kTag,
|
||||
"`timeSamples` is not TimeSamples data.");
|
||||
@@ -1320,7 +1336,7 @@ bool USDCReader::Impl::ParseProperty(const SpecType spec_type,
|
||||
#endif
|
||||
|
||||
// Do role type cast for default value.
|
||||
// (TODO: do role type cast for timeSamples?)
|
||||
// (NOTE: role type cast for timeSamples is done earlier when processing timeSamples field)
|
||||
if (defaultValue.has_value()) {
|
||||
if (typeName) {
|
||||
if (defaultValue.value().type_id() == value::TypeTraits<value::ValueBlock>::type_id()) {
|
||||
|
||||
Reference in New Issue
Block a user