diff --git a/includes/acl/compression/impl/rigid_shell_utils.h b/includes/acl/compression/impl/rigid_shell_utils.h index 7d51f04d..926c8616 100644 --- a/includes/acl/compression/impl/rigid_shell_utils.h +++ b/includes/acl/compression/impl/rigid_shell_utils.h @@ -63,14 +63,17 @@ namespace acl if (num_samples == 0) return nullptr; // No samples present, no shell distances + const float sample_rate = raw_clip.get_sample_rate(); + const float duration = raw_clip.get_duration(); const bool has_additive_base = raw_clip.has_additive_base(); const additive_clip_format8 additive_format = raw_clip.get_additive_format(); + const uint32_t base_num_samples = has_additive_base ? additive_base_clip.get_num_samples() : 0; + const float base_duration = has_additive_base ? additive_base_clip.get_duration() : 0.0F; const clip_topology_t* topology = raw_clip.get_topology(); // We retain only one dominant sub-transform per transform but in reality, it could change from keyframe to keyframe - // To keep things simple, we use the first keyframe to compute dominance - const uint32_t sample_index = 0; - const uint32_t base_sample_index = 0; + // To that end, we compute the local shell distance in object space for each transform at every sample and we + // retain the largest value. We then use this maximum value to compute our dominance. rtm::qvvf* object_transforms = allocate_type_array(allocator, num_transforms); @@ -87,54 +90,79 @@ namespace acl transform_shell_metadata.dominant_transform_index = transform_index; } - // Retrieve the object space transforms for this sample - for (const uint32_t transform_index : topology->roots_first_iterator()) + for (uint32_t sample_index = 0; sample_index < num_samples; ++sample_index) { - const uint32_t parent_index = topology->transforms[transform_index].parent_index; - - // Sample our local transform - const rtm::quatf rotation = raw_clip.get_transform_rotation(transform_index, sample_index); - const rtm::vector4f translation = raw_clip.get_transform_translation(transform_index, sample_index); - const rtm::vector4f scale = raw_clip.get_transform_scale(transform_index, sample_index); - rtm::qvvf local_transform = rtm::qvv_set(rotation, translation, scale); - + uint32_t base_sample_index = 0; if (has_additive_base) { - const rtm::quatf base_rotation = additive_base_clip.get_transform_rotation(transform_index, base_sample_index); - const rtm::vector4f base_translation = additive_base_clip.get_transform_translation(transform_index, base_sample_index); - const rtm::vector4f base_scale = additive_base_clip.get_transform_scale(transform_index, base_sample_index); - const rtm::qvvf base_transform = rtm::qvv_set(base_rotation, base_translation, base_scale); + // The sample time is calculated from the full clip duration to be consistent with decompression + const float sample_time = rtm::scalar_min(float(sample_index) / sample_rate, duration); + + const float normalized_sample_time = base_num_samples > 1 ? (sample_time / duration) : 0.0F; + const float additive_sample_time = base_num_samples > 1 ? (normalized_sample_time * base_duration) : 0.0F; - local_transform = rtm::qvv_normalize(acl::apply_additive_to_base(additive_format, base_transform, local_transform)); + // With uniform sample distributions, we do not interpolate. + base_sample_index = get_uniform_sample_key(additive_base_clip, transform_segment_adapter_t(), additive_sample_time); } - // Compute our object space transform - rtm::qvvf object_transform; - if (parent_index != k_invalid_track_index) - object_transform = rtm::qvv_normalize(rtm::qvv_mul(local_transform, object_transforms[parent_index])); - else - object_transform = local_transform; + // Retrieve the object space transforms for this sample + for (const uint32_t transform_index : topology->roots_first_iterator()) + { + const uint32_t parent_index = topology->transforms[transform_index].parent_index; - object_transforms[transform_index] = object_transform; - } + // Sample our local transform + const rtm::quatf rotation = raw_clip.get_transform_rotation(transform_index, sample_index); + const rtm::vector4f translation = raw_clip.get_transform_translation(transform_index, sample_index); + const rtm::vector4f scale = raw_clip.get_transform_scale(transform_index, sample_index); + rtm::qvvf local_transform = rtm::qvv_set(rotation, translation, scale); - // Apply the object space scale the shell distance for each transform - // This will essentially transform the shell distance to object space - for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index) - { - rigid_shell_metadata_t& transform_shell_metadata = shell_metadata[transform_index]; + if (has_additive_base) + { + const rtm::quatf base_rotation = additive_base_clip.get_transform_rotation(transform_index, base_sample_index); + const rtm::vector4f base_translation = additive_base_clip.get_transform_translation(transform_index, base_sample_index); + const rtm::vector4f base_scale = additive_base_clip.get_transform_scale(transform_index, base_sample_index); + const rtm::qvvf base_transform = rtm::qvv_set(base_rotation, base_translation, base_scale); + + local_transform = rtm::qvv_normalize(acl::apply_additive_to_base(additive_format, base_transform, local_transform)); + } + + // Compute our object space transform + rtm::qvvf object_transform; + if (parent_index != k_invalid_track_index) + object_transform = rtm::qvv_normalize(rtm::qvv_mul(local_transform, object_transforms[parent_index])); + else + object_transform = local_transform; + + object_transforms[transform_index] = object_transform; + } + + // Apply the object space scale the shell distance for each transform + // This will essentially compute the shell distance in object space + for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index) + { + const rtm::qvvf& object_transform = object_transforms[transform_index]; - const rtm::qvvf& object_transform = object_transforms[transform_index]; + // Apply the object space scale to our local space shell distance + const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); + const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); + const rtm::scalarf local_shell_distance = rtm::scalar_set(raw_clip.get_transform_shell_distance(transform_index)); + const float object_shell_distance = rtm::scalar_cast(rtm::scalar_mul(largest_scale, local_shell_distance)); - const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); - const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); - const rtm::scalarf object_shell_distance = rtm::scalar_mul(largest_scale, rtm::scalar_set(transform_shell_metadata.local_shell_distance)); + // Compute our transform length in object space + rtm::vector4f object_parent_position = rtm::vector_zero(); + const uint32_t parent_index = topology->transforms[transform_index].parent_index; + if (parent_index != k_invalid_track_index) + object_parent_position = object_transforms[parent_index].translation; - transform_shell_metadata.local_shell_distance = rtm::scalar_cast(object_shell_distance); + const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); + const float shell_distance = distance_to_parent + object_shell_distance; + + rigid_shell_metadata_t& transform_shell_metadata = shell_metadata[transform_index]; + transform_shell_metadata.local_shell_distance = rtm::scalar_max(shell_distance, transform_shell_metadata.local_shell_distance); + } } - // Now that we computed the object space transforms for this sample, - // we identity which transforms are dominant + // Now that we computed the shell distances, we identity which transforms are dominant for (const uint32_t transform_index : topology->leaves_first_iterator()) { const uint32_t parent_index = topology->transforms[transform_index].parent_index; @@ -147,22 +175,14 @@ namespace acl // the parent's shell distance. const rigid_shell_metadata_t& transform_shell = shell_metadata[transform_index]; - - // Compute our transform length in object space - const rtm::qvvf& object_transform = object_transforms[transform_index]; - rtm::vector4f object_parent_position = rtm::vector_zero(); - if (parent_index != k_invalid_track_index) - object_parent_position = object_transforms[parent_index].translation; - - const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); - const float shell_distance = distance_to_parent + transform_shell.local_shell_distance; + const float shell_distance = transform_shell.local_shell_distance; rigid_shell_metadata_t& parent_shell = shell_metadata[parent_index]; if (shell_distance > parent_shell.local_shell_distance) { // We are the new dominant transform, use our shell distance and precision - parent_shell.local_shell_distance = shell_distance; + parent_shell.local_shell_distance += shell_distance; parent_shell.precision = transform_shell.precision; parent_shell.dominant_transform_index = transform_shell.dominant_transform_index; } @@ -189,36 +209,18 @@ namespace acl const clip_context& owner_clip_context = *segment.clip; iallocator& allocator = *owner_clip_context.allocator; + const float sample_rate = owner_clip_context.sample_rate; + const float duration = owner_clip_context.duration; const bool has_additive_base = owner_clip_context.has_additive_base; const additive_clip_format8 additive_format = owner_clip_context.additive_format; + const uint32_t base_num_samples = has_additive_base ? additive_base_clip_context.num_samples : 0; + const float base_duration = has_additive_base ? additive_base_clip_context.duration : 0.0F; const clip_topology_t* topology = owner_clip_context.topology; const bool has_scale = owner_clip_context.has_scale; // We retain only one dominant sub-transform per transform but in reality, it could change from keyframe to keyframe - // To keep things simple, we use the first keyframe to compute dominance - const uint32_t segment_sample_index = 0; - uint32_t base_sample_index = 0; - - if (has_additive_base) - { - const float sample_rate = owner_clip_context.sample_rate; - const float duration = owner_clip_context.duration; - - const uint32_t base_num_samples = additive_base_clip_context.num_samples; - const float base_duration = additive_base_clip_context.duration; - - const segment_context& base_segment = additive_base_clip_context.segments[0]; - const uint32_t clip_sample_index = segment.clip_sample_offset + segment_sample_index; - - // The sample time is calculated from the full clip duration to be consistent with decompression - const float sample_time = rtm::scalar_min(float(clip_sample_index) / sample_rate, duration); - - const float normalized_sample_time = base_num_samples > 1 ? (sample_time / duration) : 0.0F; - const float additive_sample_time = base_num_samples > 1 ? (normalized_sample_time * base_duration) : 0.0F; - - // With uniform sample distributions, we do not interpolate. - base_sample_index = get_uniform_sample_key(base_segment, additive_sample_time); - } + // To that end, we compute the local shell distance in object space for each transform at every sample and we + // retain the largest value. We then use this maximum value to compute our dominance. rtm::qvvf* object_transforms = allocate_type_array(allocator, num_transforms); @@ -233,62 +235,91 @@ namespace acl shell_metadata.dominant_transform_index = transform_index; } - sample_context context; - context.sample_key = segment_sample_index; - - // Retrieve the object space transforms for this sample - for (const uint32_t transform_index : topology->roots_first_iterator()) + for (uint32_t segment_sample_index = 0; segment_sample_index < num_samples; ++segment_sample_index) { - const uint32_t parent_index = topology->transforms[transform_index].parent_index; - - // Sample our local transform - const transform_streams& sampling_bone_stream = segment.bone_streams[transform_index]; - - const rtm::quatf rotation = acl_impl::sample_rotation(context, sampling_bone_stream); - const rtm::vector4f translation = acl_impl::sample_translation(context, sampling_bone_stream); - const rtm::vector4f scale = has_scale ? acl_impl::sample_scale(context, sampling_bone_stream) : sampling_bone_stream.default_value.scale; - rtm::qvvf local_transform = rtm::qvv_set(rotation, translation, scale); + uint32_t base_sample_index = 0; if (has_additive_base) { const segment_context& base_segment = additive_base_clip_context.segments[0]; - const transform_streams& base_bone_stream = base_segment.bone_streams[transform_index]; + const uint32_t clip_sample_index = segment.clip_sample_offset + segment_sample_index; + + // The sample time is calculated from the full clip duration to be consistent with decompression + const float sample_time = rtm::scalar_min(float(clip_sample_index) / sample_rate, duration); - const rtm::quatf base_rotation = base_bone_stream.rotations.get_sample_clamped(base_sample_index); - const rtm::vector4f base_translation = base_bone_stream.translations.get_sample_clamped(base_sample_index); - const rtm::vector4f base_scale = base_bone_stream.scales.get_sample_clamped(base_sample_index); - const rtm::qvvf base_transform = rtm::qvv_set(base_rotation, base_translation, base_scale); + const float normalized_sample_time = base_num_samples > 1 ? (sample_time / duration) : 0.0F; + const float additive_sample_time = base_num_samples > 1 ? (normalized_sample_time * base_duration) : 0.0F; - local_transform = rtm::qvv_normalize(acl::apply_additive_to_base(additive_format, base_transform, local_transform)); + // With uniform sample distributions, we do not interpolate. + base_sample_index = get_uniform_sample_key(base_segment, additive_sample_time); } - // Compute our object space transform - rtm::qvvf object_transform; - if (parent_index != k_invalid_track_index) - object_transform = rtm::qvv_normalize(rtm::qvv_mul(local_transform, object_transforms[parent_index])); - else - object_transform = local_transform; + sample_context context; + context.sample_key = segment_sample_index; - object_transforms[transform_index] = object_transform; - } + // Retrieve the object space transforms for this sample + for (const uint32_t transform_index : topology->roots_first_iterator()) + { + const uint32_t parent_index = topology->transforms[transform_index].parent_index; - // Apply the object space scale the shell distance for each transform - // This will essentially transform the shell distance to object space - for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index) - { - rigid_shell_metadata_t& transform_shell_metadata = out_shell_metadata[transform_index]; + // Sample our local transform + const transform_streams& sampling_bone_stream = segment.bone_streams[transform_index]; + + const rtm::quatf rotation = acl_impl::sample_rotation(context, sampling_bone_stream); + const rtm::vector4f translation = acl_impl::sample_translation(context, sampling_bone_stream); + const rtm::vector4f scale = has_scale ? acl_impl::sample_scale(context, sampling_bone_stream) : sampling_bone_stream.default_value.scale; + rtm::qvvf local_transform = rtm::qvv_set(rotation, translation, scale); - const rtm::qvvf& object_transform = object_transforms[transform_index]; + if (has_additive_base) + { + const segment_context& base_segment = additive_base_clip_context.segments[0]; + const transform_streams& base_bone_stream = base_segment.bone_streams[transform_index]; + + const rtm::quatf base_rotation = base_bone_stream.rotations.get_sample_clamped(base_sample_index); + const rtm::vector4f base_translation = base_bone_stream.translations.get_sample_clamped(base_sample_index); + const rtm::vector4f base_scale = base_bone_stream.scales.get_sample_clamped(base_sample_index); + const rtm::qvvf base_transform = rtm::qvv_set(base_rotation, base_translation, base_scale); + + local_transform = rtm::qvv_normalize(acl::apply_additive_to_base(additive_format, base_transform, local_transform)); + } + + // Compute our object space transform + rtm::qvvf object_transform; + if (parent_index != k_invalid_track_index) + object_transform = rtm::qvv_normalize(rtm::qvv_mul(local_transform, object_transforms[parent_index])); + else + object_transform = local_transform; + + object_transforms[transform_index] = object_transform; + } + + // Apply the object space scale the shell distance for each transform + // This will essentially compute the shell distance in object space + for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index) + { + const rtm::qvvf& object_transform = object_transforms[transform_index]; + + // Apply the object space scale to our local space shell distance + const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); + const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); + const rtm::scalarf local_shell_distance = rtm::scalar_set(owner_clip_context.metadata[transform_index].shell_distance); + const float object_shell_distance = rtm::scalar_cast(rtm::scalar_mul(largest_scale, local_shell_distance)); + + // Compute our transform length in object space + rtm::vector4f object_parent_position = rtm::vector_zero(); + const uint32_t parent_index = topology->transforms[transform_index].parent_index; + if (parent_index != k_invalid_track_index) + object_parent_position = object_transforms[parent_index].translation; - const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); - const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); - const rtm::scalarf object_shell_distance = rtm::scalar_mul(largest_scale, rtm::scalar_set(transform_shell_metadata.local_shell_distance)); + const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); + const float shell_distance = distance_to_parent + object_shell_distance; - transform_shell_metadata.local_shell_distance = rtm::scalar_cast(object_shell_distance); + rigid_shell_metadata_t& transform_shell_metadata = out_shell_metadata[transform_index]; + transform_shell_metadata.local_shell_distance = rtm::scalar_max(shell_distance, transform_shell_metadata.local_shell_distance); + } } - // Now that we computed the object space transforms for this sample, - // we identity which transforms are dominant + // Now that we computed the shell distances, we identity which transforms are dominant for (const uint32_t transform_index : topology->leaves_first_iterator()) { const uint32_t parent_index = topology->transforms[transform_index].parent_index; @@ -301,22 +332,14 @@ namespace acl // the parent's shell distance. const rigid_shell_metadata_t& transform_shell = out_shell_metadata[transform_index]; - - // Compute our transform length in object space - const rtm::qvvf& object_transform = object_transforms[transform_index]; - rtm::vector4f object_parent_position = rtm::vector_zero(); - if (parent_index != k_invalid_track_index) - object_parent_position = object_transforms[parent_index].translation; - - const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); - const float shell_distance = distance_to_parent + transform_shell.local_shell_distance; + const float shell_distance = transform_shell.local_shell_distance; rigid_shell_metadata_t& parent_shell = out_shell_metadata[parent_index]; if (shell_distance > parent_shell.local_shell_distance) { // We are the new dominant transform, use our shell distance and precision - parent_shell.local_shell_distance = shell_distance; + parent_shell.local_shell_distance += shell_distance; parent_shell.precision = transform_shell.precision; parent_shell.dominant_transform_index = transform_shell.dominant_transform_index; } @@ -335,7 +358,8 @@ namespace acl if (num_transforms == 0) return; // No transforms present, no shell distances - if (segment.num_samples == 0) + const uint32_t num_samples = segment.num_samples; + if (num_samples == 0) return; // No samples present, no shell distances const clip_context& owner_clip_context = *segment.clip; @@ -347,21 +371,42 @@ namespace acl const transform_metadata& metadata = owner_clip_context.metadata[transform_index]; rigid_shell_metadata_t& shell_metadata = out_shell_metadata[transform_index]; + shell_metadata.local_shell_distance = metadata.shell_distance; + shell_metadata.precision = metadata.precision; + shell_metadata.dominant_transform_index = transform_index; + } + + for (uint32_t segment_sample_index = 0; segment_sample_index < num_samples; ++segment_sample_index) + { + const rtm::qvvf* object_pose_transforms = object_transforms + (segment_sample_index * num_transforms); + // Apply the object space scale the shell distance for each transform - // This will essentially transform the shell distance to object space - const rtm::qvvf& object_transform = object_transforms[transform_index]; + // This will essentially compute the shell distance in object space + for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index) + { + const rtm::qvvf& object_transform = object_pose_transforms[transform_index]; - const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); - const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); - const rtm::scalarf object_shell_distance = rtm::scalar_mul(largest_scale, rtm::scalar_set(metadata.shell_distance)); + // Apply the object space scale to our local space shell distance + const rtm::vector4f abs_scale = rtm::vector_abs(object_transform.scale); + const rtm::scalarf largest_scale = rtm::scalar_max(rtm::scalar_max(rtm::vector_get_x_as_scalar(abs_scale), rtm::vector_get_y_as_scalar(abs_scale)), rtm::vector_get_z_as_scalar(abs_scale)); + const rtm::scalarf local_shell_distance = rtm::scalar_set(owner_clip_context.metadata[transform_index].shell_distance); + const float object_shell_distance = rtm::scalar_cast(rtm::scalar_mul(largest_scale, local_shell_distance)); - shell_metadata.local_shell_distance = rtm::scalar_cast(object_shell_distance); - shell_metadata.precision = metadata.precision; - shell_metadata.dominant_transform_index = transform_index; + // Compute our transform length in object space + rtm::vector4f object_parent_position = rtm::vector_zero(); + const uint32_t parent_index = topology->transforms[transform_index].parent_index; + if (parent_index != k_invalid_track_index) + object_parent_position = object_pose_transforms[parent_index].translation; + + const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); + const float shell_distance = distance_to_parent + object_shell_distance; + + rigid_shell_metadata_t& transform_shell_metadata = out_shell_metadata[transform_index]; + transform_shell_metadata.local_shell_distance = rtm::scalar_max(shell_distance, transform_shell_metadata.local_shell_distance); + } } - // Now that we computed the object space transforms for this sample, - // we identity which transforms are dominant + // Now that we computed the shell distances, we identity which transforms are dominant for (const uint32_t transform_index : topology->leaves_first_iterator()) { const uint32_t parent_index = topology->transforms[transform_index].parent_index; @@ -374,22 +419,14 @@ namespace acl // the parent's shell distance. const rigid_shell_metadata_t& transform_shell = out_shell_metadata[transform_index]; - - // Compute our transform length in object space - const rtm::qvvf& object_transform = object_transforms[transform_index]; - rtm::vector4f object_parent_position = rtm::vector_zero(); - if (parent_index != k_invalid_track_index) - object_parent_position = object_transforms[parent_index].translation; - - const float distance_to_parent = rtm::vector_distance3(object_transform.translation, object_parent_position); - const float shell_distance = distance_to_parent + transform_shell.local_shell_distance; + const float shell_distance = transform_shell.local_shell_distance; rigid_shell_metadata_t& parent_shell = out_shell_metadata[parent_index]; if (shell_distance > parent_shell.local_shell_distance) { // We are the new dominant transform, use our shell distance and precision - parent_shell.local_shell_distance = shell_distance; + parent_shell.local_shell_distance += shell_distance; parent_shell.precision = transform_shell.precision; parent_shell.dominant_transform_index = transform_shell.dominant_transform_index; }