Files
Stefan MaierandClaude Opus 5.5 448a5d5353 Merge upstream 2026-10-07: network version 21, co-op and netcode fixes, renderer speed-ups, hardening
Conflicts: port_config.c (both sides' debug settings), hud_weapon.c (the
VR crosshair filter before upstream's state index check),
first_person_weapons.c (the VR arms kept; upstream's animation node check
added, and its node count limit applied to the VR arms too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:42:13 +02:00

2785 lines
85 KiB
C

/*
MODEL_ANIMATIONS.C
symbols in this file:
0010FE40 0010:
_animation_loop_frame_index (0000)
0010FE50 0010:
_animation_second_key_frame_index (0000)
0010FE60 0010:
_animation_sound_frame_index (0000)
0010FE70 0050:
_code_0010fe70 (0000)
0010FEC0 00a0:
_build_damage_animation_index (0000)
0010FF60 0080:
_animation_get_x_offsets (0000)
0010FFE0 0080:
_animation_set_frame_size (0000)
00110060 0060:
_quaternion_decompress_8byte (0000)
001100C0 00c0:
_quaternion_decompress_6byte (0000)
00110180 0020:
_quaternion_decompress_6byte_renormalized (0000)
001101A0 0060:
_quaternion_compress_8byte (0000)
00110200 0090:
_quaternion_compress_6byte (0000)
00110290 0160:
_animation_graph_node_matrices_from_orientations (0000)
001103F0 0110:
_interpolate_node_orientations (0000)
00110500 0060:
_animation_graph_get_animation_by_name (0000)
00110560 01d0:
_code_00110560 (0000)
00110730 0040:
_animation_frame_get_xy_translation (0000)
00110770 00b0:
_animation_choose_random_permutation_internal (0000)
00110820 0360:
_inverse_kinematics_adjust_matrices (0000)
00110B80 0310:
_code_00110b80 (0000)
00110E90 0300:
_code_00110e90 (0000)
00111190 02f0:
_code_00111190 (0000)
00111480 0130:
_animation_update_internal (0000)
001115B0 0300:
_animation_get_node_orientations (0000)
001118B0 01e0:
_replacement_animation_apply (0000)
00111A90 0210:
_overlay_animation_apply (0000)
00111CA0 0240:
_overlay_animation_apply_scaled (0000)
00111EE0 03c0:
_overlay_animation_apply_continuous (0000)
001122A0 0400:
_overlay_animation_apply_continuous_scaled (0000)
001126A0 0620:
_aiming_screen_apply (0000)
00112CC0 0040:
_animation_get_root_matrix (0000)
00112D00 00b0:
_animation_get_root_velocity (0000)
0027F208 000a:
??_C@_09KCBFNMAJ@animation?$AA@ (0000)
0027F214 0029:
??_C@_0CJ@JGGOLPOM@c?3?2halo?2SOURCE?2models?2model_anim@ (0000)
0027F240 0035:
??_C@_0DF@NDBCCBGA@damage_part?$DO?$DN0?5?$CG?$CG?5damage_part?$DMNU@ (0000)
0027F278 004e:
??_C@_0EO@PDDEEPOK@damage_direction?$DO?$DN0?5?$CG?$CG?5damage_di@ (0000)
0027F2C8 003f:
??_C@_0DP@CAICGJCA@damage_type?$DO?$DN0?5?$CG?$CG?5damage_type?$DMNU@ (0000)
0027F308 0004:
__real@38000100 (0000)
0027F30C 0024:
??_C@_0CE@FLNDNLBA@write_index?$DMMAXIMUM_NODES_PER_MO@ (0000)
0027F330 0018:
??_C@_0BI@NCOKEGAP@frame_index?$DMframe_count?$AA@ (0000)
0027F348 000e:
??_C@_0O@HJPIDGAL@frame_count?$DO0?$AA@ (0000)
0027F358 007a:
??_C@_0HK@JCHJDAIG@target_frame_index?$DO?$DNkeyframe_fra@ (0000)
0027F3D4 0035:
??_C@_0DF@BHOCMEDJ@keyframe_index?$DO?$DN0?5?$CG?$CG?5keyframe_in@ (0000)
0027F40C 001b:
??_C@_0BL@NIECDBGN@?$CL?$CLinfinite_loop_killer?$DM200?$AA@ (0000)
0027F428 0033:
??_C@_0DD@DKIDONPD@keyframe_index?$DO?$DN0?5?$CG?$CG?5keyframe_in@ (0000)
0027F460 0055:
??_C@_0FF@IFOFHFLN@target_frame_index?$DO?$DN0?5?$CG?$CG?5target_@ (0000)
0027F4B8 001c:
??_C@_0BM@EIBPKAOD@keyframe_frame_indices?$FL0?$FN?$DO0?$AA@ (0000)
0027F4D4 0017:
??_C@_0BH@FMNFCNKN@keyframe_frame_indices?$AA@ (0000)
0027F4EC 0011:
??_C@_0BB@INOOFBHG@keyframe_count?$DO1?$AA@ (0000)
0027F500 008e:
??_C@_0IO@GIPANHA@?$CIanimation_update_kind_affects_g@ (0000)
0027F590 0004:
__real@3f7ae148 (0000)
0027F594 0032:
??_C@_0DC@FHNJEFLD@real_frame_index?$DM?5?$CIreal?$CJnext_key@ (0000)
0027F5C8 0032:
??_C@_0DC@FGDAEANG@real_frame_index?$DO?$DN?$CIreal?$CJthis_key@ (0000)
0027F600 0043:
??_C@_0ED@ICKGJKPC@keyframe_frame_indices?$FLkeyframe_@ (0000)
0027F648 0048:
??_C@_0EI@OOKDDBPM@frame_index?$DO?$DN0?5?$CG?$CG?5frame_index?$DM?$DNk@ (0000)
0027F690 0012:
??_C@_0BC@GPLHPFMP@keyframe_count?$DO?$DN0?$AA@ (0000)
0027F6A4 002e:
??_C@_0CO@NEGCNKNH@real_frame_index?$DM?$CIreal?$CJanimation@ (0000)
0027F6D4 0017:
??_C@_0BH@JENGDKGC@real_frame_index?$DO?$DN0?40f?$AA@ (0000)
0027F6F0 006f:
??_C@_0GP@FNDBHMKG@compressed?5?$HM?$HM?5?$CIbyte?5?$CK?$CJdefault_da@ (0000)
0027F760 006b:
??_C@_0GL@JOCEOLB@compressed?5?$HM?$HM?5?$CIbyte?5?$CK?$CJdata?9?$CIbyte@ (0000)
0027F7D0 006d:
??_C@_0GN@PACKELMI@compressed?5?$HM?$HM?5?$CI?$CIbyte?5?$CK?$CJdata?9?$CIbyt@ (0000)
0027F840 0077:
??_C@_0HH@GKOAHDOE@compressed?5?$HM?$HM?5?$CI?$CIbyte?5?$CK?$CJnext_data@ (0000)
0027F8B8 0048:
??_C@_0EI@JELMFEDP@?$CD?$CD?$CD?5ERROR?5animation?5frame?5index?5@ (0000)
0027F900 0048:
??_C@_0EI@GGOLLJMH@?$CD?$CD?$CD?5ERROR?5animation?5frame?5index?5@ (0000)
0027F948 0026:
??_C@_0CG@FPLKLLFK@d0?$DN?$DN?$CFf?5direction?$CI?$CFf?$CJ?5yaw_delta?$CI?$CF@ (0000)
0027F970 0019:
??_C@_0BJ@LEBMJMIB@animation?9?$DOframe_count?$DO1?$AA@ (0000)
0030A388 0001:
_hs_model_animation_compression_enabled (0000)
0045662C 0024:
_hs_model_animation_data_compressed_size (0000)
_hs_model_animation_data_uncompressed_size (0004)
_hs_model_animation_data_compression_savings_in_bytes (0008)
_hs_model_animation_data_compression_savings_in_bytes_at_import (000c)
_hs_model_animation_data_compression_savings_in_percent (0010)
_hs_model_animation_bullshit (0014)
*/
/* ---------- headers */
#include "cseries.h"
#include "cseries/errors.h"
#include "models/model_animation_definitions.h"
#include "models/models.h"
#include "models/model_definitions.h"
#include "objects/objects.h"
/* ---------- constants */
enum
{
COMPRESSED_QUATERNION_COMPONENT_MAXIMUM = 32767,
NUMBER_OF_ANIMATION_DAMAGE_TYPES = 4,
NUMBER_OF_ANIMATION_DAMAGE_DIRECTIONS = 4,
NUMBER_OF_DAMAGE_PARTS = 11,
};
enum
{
animation_update_kind_render_only = 0,
animation_update_kind_affects_game_state,
};
/* No shared header declares the update results yet; first_person_weapons.c keeps a partial copy. */
enum animation_update_result
{
_animation_running = 0,
_animation_key_frame,
_animation_will_restart_on_next_frame,
_animation_restarted,
_animation_looped,
NUMBER_OF_ANIMATION_UPDATE_RESULTS,
};
enum
{
COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS = 12,
};
/* ---------- macros */
/* ---------- structures */
struct compressed_quaternion_8byte
{
short i;
short j;
short k;
short w;
};
struct compressed_quaternion_6byte
{
word words[3];
};
typedef char verify_compressed_quaternion_8byte_size[
sizeof(struct compressed_quaternion_8byte) == 0x08 ? 1 : -1];
typedef char verify_compressed_quaternion_6byte_size[
sizeof(struct compressed_quaternion_6byte) == 0x06 ? 1 : -1];
struct animation_frame_info_dx_dy
{
real dx;
real dy;
};
struct animation_frame_info_dx_dy_dyaw
{
real dx;
real dy;
real dyaw;
};
struct animation_frame_info_dx_dy_dz_dyaw
{
real dx;
real dy;
real dz;
real dyaw;
};
typedef char verify_animation_frame_info_dx_dy_size[
sizeof(struct animation_frame_info_dx_dy) == 0x08 ? 1 : -1];
typedef char verify_animation_frame_info_dx_dy_dyaw_size[
sizeof(struct animation_frame_info_dx_dy_dyaw) == 0x0C ? 1 : -1];
typedef char verify_animation_frame_info_dx_dy_dz_dyaw_size[
sizeof(struct animation_frame_info_dx_dy_dz_dyaw) == 0x10 ? 1 : -1];
struct compressed_animation_header
{
long rotation_keyframe_frame_indices_offset;
long default_rotations_offset;
long rotation_keyframes_offset;
long translation_node_headers_offset;
long translation_keyframe_frame_indices_offset;
long default_translations_offset;
long translation_keyframes_offset;
long scale_node_headers_offset;
long scale_keyframe_frame_indices_offset;
long default_scales_offset;
long scale_keyframes_offset;
unsigned long rotation_node_headers[1];
};
/* Recovered animation-graph block layouts kept TU-private to preserve VC7 header scheduling. */
struct animation_graph_node
{
char name[TAG_STRING_LENGTH+1];
short next_sibling_node_index;
short first_child_node_index;
short parent_node_index;
word pad;
unsigned long flags;
real_vector3d base_vector;
real range;
long pad1;
};
typedef char verify_animation_graph_node_size[
sizeof(struct animation_graph_node) == 0x40 ? 1 : -1];
/* No shared header declares this block element yet; first_person_weapons.c keeps the same copy. */
struct animation_graph_sound_reference
{
struct tag_reference sound;
long unused;
};
typedef char verify_animation_graph_sound_reference_size[
sizeof(struct animation_graph_sound_reference) == 0x14 ? 1 : -1];
typedef char verify_compressed_animation_header_rotation_node_headers_offset[
offsetof(struct compressed_animation_header, rotation_node_headers) == 0x2C ? 1 : -1];
/* ---------- prototypes */
static boolean animation_is_compressed(
struct animation const *animation);
static short animation_keyframe_search(
short const *keyframe_frame_indices,
short keyframe_count,
short target_frame_index);
static void animation_get_keyframe_rotation(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real_quaternion *rotation);
static void animation_get_keyframe_translation(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real_point3d *translation);
static void animation_get_keyframe_scale(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real *scale);
static long animation_frame_data_offset(
struct animation const *animation,
short frame_index);
static boolean animation_data_contains(
struct animation const *animation,
long offset,
long relative_offset,
long first_element_index,
long element_count,
long element_size);
static long animation_node_flag_count(
unsigned long low_flags,
unsigned long high_flags,
short node_count);
static boolean animation_frame_valid(
struct animation const *animation,
short frame_index,
boolean reads_default_data);
static void animation_data_error(
struct animation const *animation,
char const *problem);
static void animation_set_rest_orientations(
struct real_orientation *node_orientations,
short node_count);
/* ---------- globals */
boolean hs_model_animation_compression_enabled = TRUE;
long hs_model_animation_data_compressed_size = 0;
long hs_model_animation_data_uncompressed_size = 0;
long hs_model_animation_data_compression_savings_in_bytes = 0;
long hs_model_animation_data_compression_savings_in_bytes_at_import = 0;
real hs_model_animation_data_compression_savings_in_percent = 0.f;
long hs_model_animation_bullshit[4] = { 0 };
/* ---------- public code */
short animation_loop_frame_index(
struct animation const *animation)
{
return animation->private_loop_frame_index;
}
short animation_second_key_frame_index(
struct animation const *animation)
{
return animation->private_second_key_frame_index;
}
short animation_sound_frame_index(
struct animation const *animation)
{
return animation->private_sound_frame_index;
}
short build_damage_animation_index(
short damage_type,
short damage_direction,
short damage_part)
{
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
55,
damage_type>=0 && damage_type<NUMBER_OF_ANIMATION_DAMAGE_TYPES);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
56,
damage_direction>=0 && damage_direction<NUMBER_OF_ANIMATION_DAMAGE_DIRECTIONS);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
57,
damage_part>=0 && damage_part<NUMBER_OF_DAMAGE_PARTS);
return (damage_type * NUMBER_OF_ANIMATION_DAMAGE_DIRECTIONS + damage_direction) *
NUMBER_OF_DAMAGE_PARTS + damage_part;
}
void animation_get_x_offsets(
struct animation const *animation,
real *key_frame_x_offset,
real *total_x_offset)
{
short frame_index;
real x_offset = 0.f;
real key_x_offset = 0.f;
byte const *frame_info = animation->frame_info.address;
short frame_count = animation->frame_count;
long frame_info_size =
animation->frame_info_type==1 ? (long)sizeof(struct animation_frame_info_dx_dy) :
animation->frame_info_type==2 ? (long)sizeof(struct animation_frame_info_dx_dy_dyaw) :
animation->frame_info_type==3 ? (long)sizeof(struct animation_frame_info_dx_dy_dz_dyaw) :
0;
/* port: no offsets from frame info the animation doesn't have (a map's
frame count and size) */
if (frame_count>0 && frame_info_size*frame_count>animation->frame_info.size)
{
animation_data_error(animation, "frame info");
frame_count = 0;
}
for (frame_index = 0; frame_index < frame_count; frame_index++)
{
switch (animation->frame_info_type)
{
case 1:
x_offset += ((struct animation_frame_info_dx_dy const *)frame_info)->dx;
frame_info += sizeof(struct animation_frame_info_dx_dy);
break;
case 2:
x_offset += ((struct animation_frame_info_dx_dy_dyaw const *)frame_info)->dx;
frame_info += sizeof(struct animation_frame_info_dx_dy_dyaw);
break;
case 3:
x_offset += ((struct animation_frame_info_dx_dy_dz_dyaw const *)frame_info)->dx;
frame_info += sizeof(struct animation_frame_info_dx_dy_dz_dyaw);
break;
}
if (frame_index == animation->private_key_frame_index)
{
key_x_offset = x_offset;
}
}
if (total_x_offset)
{
*total_x_offset = x_offset;
}
if (key_frame_x_offset)
{
*key_frame_x_offset = key_x_offset;
}
return;
}
void animation_set_frame_size(
struct animation *animation)
{
short frame_size = 0;
short node_index;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
123,
animation);
for (node_index = 0; node_index < animation->node_count; node_index++)
{
if (BIT_VECTOR_TEST_FLAG(animation->nodes_with_rotation_flags, node_index))
{
frame_size += sizeof(struct compressed_quaternion_8byte);
}
if (BIT_VECTOR_TEST_FLAG(animation->nodes_with_translation_flags, node_index))
{
frame_size += sizeof(real_point3d);
}
if (BIT_VECTOR_TEST_FLAG(animation->nodes_with_scale_flags, node_index))
{
frame_size += sizeof(real);
}
}
animation->frame_size = frame_size;
return;
}
short animation_update_internal(
long render_or_affects_game_state,
long animation_graph_index,
struct animation_state *state,
long *sound_index)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
struct animation const *animation;
short result = _animation_running;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
147,
state);
animation = TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
state->index,
struct animation);
if (sound_index)
{
/* port: a sound the graph has (a map's index) */
if (animation->sound_index!=NONE &&
!VALID_INDEX(animation->sound_index, animation_graph->sound_references.count))
{
animation_data_error(animation, "sound");
*sound_index = NONE;
}
else if (animation->sound_index!=NONE && animation->private_sound_frame_index==state->frame_index)
{
struct animation_graph_sound_reference const *sound_reference = TAG_BLOCK_GET_ELEMENT(
&animation_graph->sound_references,
animation->sound_index,
struct animation_graph_sound_reference);
*sound_index = sound_reference->sound.index;
}
else
{
*sound_index = NONE;
}
}
state->frame_index++;
if (state->frame_index>=animation->frame_count)
{
if (animation->private_loop_frame_index>0)
{
state->frame_index = MIN(animation->private_loop_frame_index, animation->frame_count-1);
result = _animation_looped;
}
else
{
state->index = animation_choose_random_permutation_internal(
render_or_affects_game_state,
animation_graph_index,
animation->runtime_parent_animation_index);
state->frame_index = 0;
result = _animation_restarted;
}
}
else if (state->frame_index+1==animation->frame_count && animation->private_loop_frame_index==0)
{
result = _animation_will_restart_on_next_frame;
}
else if (state->frame_index==animation->private_key_frame_index ||
state->frame_index==animation->private_second_key_frame_index)
{
result = _animation_key_frame;
}
return result;
}
#ifdef HALO_ANDROID
short animation_graph_node_count(
long animation_graph_index)
{
return (short)animation_graph_definition_get(animation_graph_index)->nodes.count;
}
char const *animation_graph_node_name(
long animation_graph_index,
short node_index)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
return TAG_BLOCK_GET_ELEMENT(&animation_graph->nodes, node_index, struct animation_graph_node)->name;
}
short animation_graph_node_parent(
long animation_graph_index,
short node_index)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
return TAG_BLOCK_GET_ELEMENT(&animation_graph->nodes, node_index, struct animation_graph_node)->parent_node_index;
}
/* the node whose name ends with name ("frame l hand" for "l hand"); NONE */
short animation_graph_find_node(
long animation_graph_index,
char const *name)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
long name_length = csstrlen(name);
short node_index;
for (node_index = 0; node_index < animation_graph->nodes.count; node_index++)
{
char const *node_name = TAG_BLOCK_GET_ELEMENT(
&animation_graph->nodes,
node_index,
struct animation_graph_node)->name;
long node_name_length = csstrlen(node_name);
if (node_name_length >= name_length && !csstrcasecmp(node_name + node_name_length - name_length, name))
return node_index;
}
return NONE;
}
#endif
void animation_graph_node_matrices_from_orientations(
long animation_graph_index,
real_matrix4x3 *node_matrices,
struct real_orientation const *node_orientations,
real_point3d const *origin,
real_vector3d const *forward,
real_vector3d const *up)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(
animation_graph_index);
real_matrix4x3 root_matrix;
short node_indices[MAXIMUM_NODES_PER_MODEL];
short read_index;
short write_index;
/* port: the nodes the queue and the matrices hold (a map's count) */
short node_count = (short)MIN(animation_graph->nodes.count, MAXIMUM_NODES_PER_MODEL);
matrix4x3_from_point_and_vectors(&root_matrix, origin, forward, up);
if (node_count > 0)
{
read_index = 0;
write_index = 1;
node_indices[0] = 0;
do
{
short node_index = node_indices[read_index++];
struct animation_graph_node *node = TAG_BLOCK_GET_ELEMENT(
&animation_graph->nodes,
node_index,
struct animation_graph_node);
real_matrix4x3 const *parent_matrix;
real_matrix4x3 local_matrix;
if (!node_index)
parent_matrix = &root_matrix;
/* port: a parent the graph doesn't have is the root (a map's index) */
else if (!VALID_INDEX(node->parent_node_index, node_count))
{
if (model_data_report_once(animation_graph))
{
error(_error_silent, "### ERROR an animation graph has a bad node parent index");
}
parent_matrix = &node_matrices[0];
}
else
parent_matrix = &node_matrices[node->parent_node_index];
matrix4x3_from_orientation(&local_matrix, &node_orientations[node_index]);
matrix4x3_multiply(parent_matrix, &local_matrix, &node_matrices[node_index]);
/* port: only nodes the graph has, and no more than the queue holds
(a map's links, which could go in a loop) */
if (node->next_sibling_node_index != NONE)
{
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1250,
write_index<MAXIMUM_NODES_PER_MODEL);
if (VALID_INDEX(node->next_sibling_node_index, node_count) && write_index<MAXIMUM_NODES_PER_MODEL)
{
node_indices[write_index++] = node->next_sibling_node_index;
}
else if (model_data_report_once(animation_graph))
{
error(_error_silent, "### ERROR an animation graph has a bad node sibling index");
}
}
if (node->first_child_node_index != NONE)
{
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1256,
write_index<MAXIMUM_NODES_PER_MODEL);
if (VALID_INDEX(node->first_child_node_index, node_count) && write_index<MAXIMUM_NODES_PER_MODEL)
{
node_indices[write_index++] = node->first_child_node_index;
}
else if (model_data_report_once(animation_graph))
{
error(_error_silent, "### ERROR an animation graph has a bad node child index");
}
}
}
while (read_index != write_index);
}
return;
}
short animation_graph_get_animation_by_name(
long animation_graph_index,
char const *animation_name)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
short animation_index;
for (animation_index = 0; animation_index < animation_graph->animations.count; animation_index++)
{
struct animation const *animation = TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
animation_index,
struct animation);
if (!_stricmp(animation_name, animation->name))
{
return animation_index;
}
}
return NONE;
}
void animation_frame_get_xy_translation(
struct animation const *animation,
short frame_index,
real_vector2d *translation)
{
if (animation->frame_info_type == 1)
{
*translation = *(real_vector2d const *)animation_get_frame_info(
animation,
frame_index,
sizeof(struct animation_frame_info_dx_dy));
}
else
{
translation->i = 0.f;
translation->j = 0.f;
}
return;
}
short animation_choose_random_permutation_internal(
long render_or_affects_game_state,
long animation_graph_index,
short animation_index)
{
struct animation_graph const *animation_graph = animation_graph_definition_get(animation_graph_index);
real random;
short first_animation_index = animation_index;
long permutation_count = 0;
if (render_or_affects_game_state == animation_update_kind_affects_game_state)
{
random = real_seed_random(get_global_random_seed_address());
}
else
{
random = real_seed_random(get_global_local_random_seed_address());
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1008,
(animation_update_kind_affects_game_state==render_or_affects_game_state) ||
(animation_update_kind_render_only==render_or_affects_game_state));
}
while (animation_index != NONE)
{
struct animation const *animation;
/* port: only animations the graph has, each once (a map's links, which
could go in a loop): a bad list is its first animation, or none */
if (!VALID_INDEX(animation_index, animation_graph->animations.count) ||
permutation_count++>=animation_graph->animations.count)
{
if (model_data_report_once(animation_graph))
{
error(_error_silent, "### ERROR an animation graph has a bad animation permutation list");
}
animation_index = VALID_INDEX(first_animation_index, animation_graph->animations.count) ?
first_animation_index :
(short)NONE;
break;
}
animation = TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
animation_index,
struct animation);
if (random <= animation->runtime_normalized_weight)
{
break;
}
animation_index = animation->next_animation_index;
}
return animation_index;
}
void interpolate_node_orientations(
short node_count,
struct real_orientation *original_node_orientations,
struct real_orientation *target_node_orientations,
short frame_index,
short frame_count)
{
real fraction = (real)(frame_index + 1) / (real)frame_count;
real inverse_fraction = 1.f - fraction;
short node_index;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1277,
frame_count>0);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1278,
frame_index<frame_count);
/* port: no more nodes than the engine's arrays hold (a map's count) */
if (node_count>MAXIMUM_NODES_PER_ANIMATION)
{
node_count = MAXIMUM_NODES_PER_ANIMATION;
}
for (node_index = 0; node_index < node_count; node_index++)
{
struct real_orientation *target = &target_node_orientations[node_index];
struct real_orientation const *original = &original_node_orientations[node_index];
target->scale = original->scale * inverse_fraction + target->scale * fraction;
quaternions_interpolate_and_normalize(
&original->rotation,
&target->rotation,
fraction,
&target->rotation);
target->translation.x = original->translation.x * inverse_fraction + target->translation.x * fraction;
target->translation.y = original->translation.y * inverse_fraction + target->translation.y * fraction;
target->translation.z = original->translation.z * inverse_fraction + target->translation.z * fraction;
}
return;
}
void animation_get_root_matrix(
struct model const *model,
struct animation const *animation,
short frame_index,
real_matrix4x3 *root_matrix)
{
struct real_orientation node_orientations[MAXIMUM_NODES_PER_ANIMATION];
animation_get_node_orientations(model, animation, frame_index, node_orientations);
matrix4x3_from_point_and_quaternion(
root_matrix,
&node_orientations[0].translation,
&node_orientations[0].rotation);
return;
}
void animation_get_root_velocity(
struct model const *model,
struct animation const *animation,
short frame_index,
real_vector3d *root_velocity)
{
struct real_orientation node_orientations[MAXIMUM_NODES_PER_ANIMATION];
struct real_orientation previous_node_orientations[MAXIMUM_NODES_PER_ANIMATION];
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
221,
animation->frame_count>1);
if (frame_index == 0)
{
frame_index = 1;
}
animation_get_node_orientations(model, animation, frame_index, node_orientations);
animation_get_node_orientations(model, animation, frame_index - 1, previous_node_orientations);
vector_from_points3d(
&previous_node_orientations[0].translation,
&node_orientations[0].translation,
root_velocity);
return;
}
void animation_get_node_orientations(
struct model const *model,
struct animation const *animation,
short frame_index,
struct real_orientation *node_orientations)
{
/* port: and a node count the engine's arrays hold, and frame data inside
the animation's data (a map's counts and offsets; without a model, a
first-person weapon's, nothing else checked the count) */
if (animation->type==_animation_base &&
(!model ||
((!animation->node_list_checksum || animation->node_list_checksum==model->node_list_checksum || !model->node_list_checksum) &&
model->nodes.count==animation->node_count)) &&
animation_frame_valid(animation, frame_index, TRUE))
{
boolean compressed = TEST_FLAG(animation->flags, _animation_compressed_bit) &&
(hs_model_animation_compression_enabled || !animation->compressed_data_offset);
byte *data = animation_get_frame_data(animation, frame_index);
long data_offset = animation_frame_data_offset(animation, frame_index);
byte *default_data = animation_get_default_data(animation);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
if (compressed)
{
animation_get_keyframe_rotation(animation, (real)frame_index, rotation_index++, node_index, &orientation->rotation);
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte *)data, &orientation->rotation);
data += sizeof(struct compressed_quaternion_8byte);
}
}
else if (compressed)
{
struct compressed_animation_header const *header = (struct compressed_animation_header const *)data;
/* port: a default inside the animation's data (a map's offset) */
if (animation_data_contains(
animation,
data_offset,
header->default_rotations_offset,
node_index,
1,
sizeof(struct compressed_quaternion_6byte)))
{
quaternion_decompress_6byte(
(struct compressed_quaternion_6byte *)(data+header->default_rotations_offset)+node_index,
&orientation->rotation);
quaternion_normalize(&orientation->rotation);
}
else
{
animation_data_error(animation, "compressed default rotation");
orientation->rotation = *global_identity_quaternion;
}
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte *)default_data, &orientation->rotation);
default_data += sizeof(struct compressed_quaternion_8byte);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
if (compressed)
{
animation_get_keyframe_translation(animation, (real)frame_index, translation_index++, node_index, &orientation->translation);
}
else
{
orientation->translation = *(real_point3d *)data;
data += sizeof(real_point3d);
}
}
else if (compressed)
{
struct compressed_animation_header const *header = (struct compressed_animation_header const *)data;
/* port: a default inside the animation's data (a map's offset) */
if (animation_data_contains(
animation,
data_offset,
header->default_translations_offset,
node_index,
1,
sizeof(real_point3d)))
{
orientation->translation = *((real_point3d *)(data+header->default_translations_offset)+node_index);
}
else
{
animation_data_error(animation, "compressed default translation");
orientation->translation = *global_origin3d;
}
}
else
{
orientation->translation = *(real_point3d *)default_data;
default_data += sizeof(real_point3d);
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
if (compressed)
{
animation_get_keyframe_scale(animation, (real)frame_index, scale_index++, node_index, &orientation->scale);
}
else
{
orientation->scale = *(real *)data;
data += sizeof(real);
}
}
else if (compressed)
{
orientation->scale = 1.0f;
}
else
{
orientation->scale = *(real *)default_data;
default_data += sizeof(real);
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
321,
compressed || (byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
322,
compressed || (byte *)default_data-(byte *)animation_get_default_data(animation)==animation->default_data.size);
}
else if (model)
{
model_get_node_orientations(model, node_orientations);
}
else
{
/* port: no model to take the default pose from (a first-person
weapon's), so the rest pose */
animation_set_rest_orientations(
node_orientations,
(short)PIN(animation->node_count, 0, MAXIMUM_NODES_PER_ANIMATION));
}
return;
}
void replacement_animation_apply(
struct animation const *animation,
short frame_index,
struct real_orientation *node_orientations)
{
/* port: and the frame inside the animation's data (a map's counts and
offsets) */
if (animation->type==_animation_replacement && frame_index>=0 && frame_index<animation->frame_count &&
animation_frame_valid(animation, frame_index, FALSE))
{
boolean compressed = animation_is_compressed(animation);
byte *data = animation_get_frame_data(animation, frame_index);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
if (compressed)
{
animation_get_keyframe_rotation(animation, (real)frame_index, rotation_index++, node_index, &orientation->rotation);
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte *)data, &orientation->rotation);
data += sizeof(struct compressed_quaternion_8byte);
}
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
if (compressed)
{
animation_get_keyframe_translation(animation, (real)frame_index, translation_index++, node_index, &orientation->translation);
}
else
{
orientation->translation = *(real_point3d *)data;
data += sizeof(real_point3d);
}
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
if (compressed)
{
animation_get_keyframe_scale(animation, (real)frame_index, scale_index++, node_index, &orientation->scale);
}
else
{
orientation->scale = *(real *)data;
data += sizeof(real);
}
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
391,
compressed || ((byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size));
}
return;
}
void overlay_animation_apply(
struct animation const *animation,
short frame_index,
struct real_orientation *node_orientations)
{
/* port: and the frame inside the animation's data (a map's counts and
offsets) */
if (animation->type==_animation_overlay && frame_index>=0 && frame_index<animation->frame_count &&
animation_frame_valid(animation, frame_index, FALSE))
{
boolean compressed = animation_is_compressed(animation);
byte *data = animation_get_frame_data(animation, frame_index);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
real_quaternion rotation;
if (compressed)
{
animation_get_keyframe_rotation(animation, (real)frame_index, rotation_index++, node_index, &rotation);
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte *)data, &rotation);
data += sizeof(struct compressed_quaternion_8byte);
}
quaternions_multiply(&rotation, &orientation->rotation, &orientation->rotation);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
real_point3d translation;
if (compressed)
{
animation_get_keyframe_translation(animation, (real)frame_index, translation_index++, node_index, &translation);
}
else
{
translation = *(real_point3d *)data;
data += sizeof(real_point3d);
}
orientation->translation.x += translation.x;
orientation->translation.y += translation.y;
orientation->translation.z += translation.z;
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
real scale;
if (compressed)
{
animation_get_keyframe_scale(animation, (real)frame_index, scale_index++, node_index, &scale);
}
else
{
scale = *(real *)data;
data += sizeof(real);
}
orientation->scale *= scale;
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
470,
compressed || ((byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size));
}
return;
}
void overlay_animation_apply_scaled(
struct animation const *animation,
short frame_index,
real animation_scale,
struct real_orientation *node_orientations)
{
real inverse_animation_scale = 1.0f-animation_scale;
/* port: and the frame inside the animation's data (a map's counts and
offsets) */
if (animation->type==_animation_overlay && frame_index>=0 && frame_index<animation->frame_count &&
animation_frame_valid(animation, frame_index, FALSE))
{
boolean compressed = animation_is_compressed(animation);
byte *data = animation_get_frame_data(animation, frame_index);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
real_quaternion rotation;
if (compressed)
{
animation_get_keyframe_rotation(animation, (real)frame_index, rotation_index++, node_index, &rotation);
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte *)data, &rotation);
data += sizeof(struct compressed_quaternion_8byte);
}
quaternions_interpolate(global_identity_quaternion, &rotation, animation_scale, &rotation);
quaternions_multiply(&rotation, &orientation->rotation, &orientation->rotation);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
real_point3d translation;
if (compressed)
{
animation_get_keyframe_translation(animation, (real)frame_index, translation_index++, node_index, &translation);
}
else
{
translation = *(real_point3d *)data;
data += sizeof(real_point3d);
}
orientation->translation.x += translation.x*animation_scale;
orientation->translation.y += translation.y*animation_scale;
orientation->translation.z += translation.z*animation_scale;
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
real scale;
if (compressed)
{
animation_get_keyframe_scale(animation, (real)frame_index, scale_index++, node_index, &scale);
}
else
{
scale = *(real *)data;
data += sizeof(real);
}
orientation->scale *= scale*animation_scale+inverse_animation_scale;
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
554,
compressed || ((byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size));
}
return;
}
void overlay_animation_apply_continuous(
struct animation const *animation,
real real_frame_index,
struct real_orientation *node_orientations)
{
real fraction;
short frame_index;
/* port: no frames, no overlay (a map's frame count of 0 made the frame
-1, read before the animation's data, every tick) */
if (animation->frame_count<=0)
{
animation_data_error(animation, "frame count");
return;
}
fraction = (real)fmod((double)real_frame_index, 1.0);
frame_index = (short)fast_ftol((real)floor(fabs(real_frame_index)));
if (real_frame_index < 0.0f || real_frame_index > (real)animation->frame_count)
{
error(
_error_silent,
"### ERROR animation frame index out of bounds A(%f,%x) -- tell Bernie!!",
real_frame_index,
*((long *)&real_frame_index));
}
if (frame_index >= animation->frame_count)
{
frame_index = animation->frame_count - 1;
fraction = 1.0f;
real_frame_index = (real)frame_index;
}
/* port: and both frames inside the animation's data (a map's counts and
offsets) */
if (animation->type == _animation_overlay &&
animation_frame_valid(animation, frame_index, FALSE) &&
animation_frame_valid(animation, frame_index == animation->frame_count - 1 ? 0 : frame_index + 1, FALSE))
{
boolean compressed = animation_is_compressed(animation);
short next_frame_index = frame_index == animation->frame_count - 1 ? 0 : frame_index + 1;
byte *data = animation_get_frame_data(animation, frame_index);
byte *next_data = animation_get_frame_data(animation, next_frame_index);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index < animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index & (LONG_BITS - 1)))
{
short long_index = node_index >> LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
real_quaternion rotation;
if (compressed)
{
animation_get_keyframe_rotation(
animation,
(real)frame_index,
(short)rotation_index++,
node_index,
&rotation);
}
else
{
real_quaternion this_rotation;
real_quaternion next_rotation;
quaternion_decompress_8byte(
(struct compressed_quaternion_8byte const *)data,
&this_rotation);
data += sizeof(struct compressed_quaternion_8byte);
quaternion_decompress_8byte(
(struct compressed_quaternion_8byte const *)next_data,
&next_rotation);
next_data += sizeof(struct compressed_quaternion_8byte);
quaternions_interpolate_and_normalize(
&this_rotation,
&next_rotation,
fraction,
&rotation);
}
quaternions_multiply(&rotation, &orientation->rotation, &orientation->rotation);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
real_point3d translation;
if (compressed)
{
animation_get_keyframe_translation(
animation,
real_frame_index,
(short)translation_index++,
node_index,
&translation);
}
else
{
real_point3d const *this_translation;
real_point3d const *next_translation;
this_translation = (real_point3d const *)data;
data += sizeof(real_point3d);
next_translation = (real_point3d const *)next_data;
next_data += sizeof(real_point3d);
points_interpolate(
this_translation,
next_translation,
fraction,
&translation);
}
orientation->translation.x += translation.x;
orientation->translation.y += translation.y;
orientation->translation.z += translation.z;
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
real scale;
if (compressed)
{
animation_get_keyframe_scale(
animation,
real_frame_index,
(short)scale_index++,
node_index,
&scale);
}
else
{
real this_scale;
real next_scale;
this_scale = *(real const *)data;
data += sizeof(real);
next_scale = *(real const *)next_data;
next_data += sizeof(real);
scalars_interpolate(
this_scale,
next_scale,
fraction,
&scale);
}
orientation->scale *= scale;
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
693,
compressed || ((byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size));
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
694,
compressed || ((byte *)next_data-(byte *)animation_get_frame_data(animation, next_frame_index)==animation->frame_size));
}
return;
}
void inverse_kinematics_adjust_matrices(
struct real_matrix4x3 *desired_hand_matrix,
struct real_matrix4x3 *shoulder_matrix,
struct real_matrix4x3 *elbow_matrix,
struct real_matrix4x3 *hand_matrix)
{
real upper_arm_length = distance3d(&shoulder_matrix->position, &elbow_matrix->position);
real forearm_length = distance3d(&elbow_matrix->position, &hand_matrix->position);
real hand_distance = distance3d(&desired_hand_matrix->position, &shoulder_matrix->position);
real inverse_hand_distance;
real_vector3d shoulder_to_elbow;
real_vector3d hand_direction;
real_vector3d bend_plane_normal;
real_vector3d bend_direction;
real maximum_reach;
real upper_arm_length_squared;
real elbow_projection;
real elbow_remainder;
real elbow_height;
real_point3d new_elbow_position;
shoulder_to_elbow.i = elbow_matrix->position.x-shoulder_matrix->position.x;
shoulder_to_elbow.j = elbow_matrix->position.y-shoulder_matrix->position.y;
shoulder_to_elbow.k = elbow_matrix->position.z-shoulder_matrix->position.z;
inverse_hand_distance = 1.0f/hand_distance;
hand_direction.i = (desired_hand_matrix->position.x-shoulder_matrix->position.x)*inverse_hand_distance;
hand_direction.j = (desired_hand_matrix->position.y-shoulder_matrix->position.y)*inverse_hand_distance;
hand_direction.k = (desired_hand_matrix->position.z-shoulder_matrix->position.z)*inverse_hand_distance;
cross_product3d(&hand_direction, &shoulder_to_elbow, &bend_plane_normal);
normalize3d(&bend_plane_normal);
cross_product3d(&bend_plane_normal, &hand_direction, &bend_direction);
maximum_reach = (upper_arm_length+forearm_length)*0.98f;
if (maximum_reach<hand_distance)
{
desired_hand_matrix->position.x = shoulder_matrix->position.x+hand_direction.i*maximum_reach;
desired_hand_matrix->position.y = shoulder_matrix->position.y+hand_direction.j*maximum_reach;
desired_hand_matrix->position.z = shoulder_matrix->position.z+hand_direction.k*maximum_reach;
hand_distance = maximum_reach;
}
upper_arm_length_squared = upper_arm_length*upper_arm_length;
elbow_projection =
(hand_distance*hand_distance+upper_arm_length_squared-forearm_length*forearm_length)/
(hand_distance+hand_distance);
elbow_remainder = hand_distance-elbow_projection;
elbow_height = square_root(upper_arm_length_squared-elbow_projection*elbow_projection);
{
real_vector3d *shoulder_forward = &shoulder_matrix->forward;
real_vector3d *shoulder_left = &shoulder_matrix->left;
real_vector3d *shoulder_up = &shoulder_matrix->up;
shoulder_forward->i = elbow_projection*hand_direction.i+elbow_height*bend_direction.i;
shoulder_forward->j = elbow_projection*hand_direction.j+elbow_height*bend_direction.j;
shoulder_forward->k = elbow_projection*hand_direction.k+elbow_height*bend_direction.k;
normalize3d(shoulder_forward);
cross_product3d(shoulder_forward, shoulder_left, shoulder_up);
normalize3d(shoulder_up);
cross_product3d(shoulder_up, shoulder_forward, shoulder_left);
new_elbow_position.x = shoulder_matrix->position.x+shoulder_forward->i*upper_arm_length;
new_elbow_position.y = shoulder_matrix->position.y+shoulder_forward->j*upper_arm_length;
new_elbow_position.z = shoulder_matrix->position.z+shoulder_forward->k*upper_arm_length;
}
{
real_vector3d *elbow_forward = &elbow_matrix->forward;
real_vector3d *elbow_left = &elbow_matrix->left;
real_vector3d *elbow_up = &elbow_matrix->up;
elbow_forward->i = elbow_remainder*hand_direction.i-elbow_height*bend_direction.i;
elbow_forward->j = elbow_remainder*hand_direction.j-elbow_height*bend_direction.j;
elbow_forward->k = elbow_remainder*hand_direction.k-elbow_height*bend_direction.k;
normalize3d(elbow_forward);
cross_product3d(elbow_forward, elbow_left, elbow_up);
normalize3d(elbow_up);
cross_product3d(elbow_up, elbow_forward, elbow_left);
}
elbow_matrix->position = new_elbow_position;
*hand_matrix = *desired_hand_matrix;
return;
}
void overlay_animation_apply_continuous_scaled(
struct animation const *animation,
real real_frame_index,
real animation_scale,
struct real_orientation *node_orientations)
{
real inverse_animation_scale = 1.0f-animation_scale;
real fraction = (real)fmod((double)real_frame_index, 1.0);
short frame_index = (short)fast_ftol((real)floor(real_frame_index));
/* port: no frames, no overlay (a map's frame count of 0 made the frame
-1, read before the animation's data, every frame) */
if (animation->frame_count<=0)
{
animation_data_error(animation, "frame count");
return;
}
if (real_frame_index<0.0f || real_frame_index>(real)animation->frame_count)
{
error(
_error_silent,
"### ERROR animation frame index out of bounds B(%f,%x) -- tell Bernie!!",
real_frame_index,
*((long *)&real_frame_index));
}
if (frame_index>=animation->frame_count)
{
frame_index = animation->frame_count-1;
fraction = 1.0f;
real_frame_index = (real)frame_index;
}
/* port: and both frames inside the animation's data (a map's counts and
offsets) */
if (animation->type==_animation_overlay &&
animation_frame_valid(animation, frame_index, FALSE) &&
animation_frame_valid(animation, frame_index==animation->frame_count-1 ? 0 : frame_index+1, FALSE))
{
boolean compressed = animation_is_compressed(animation);
short next_frame_index = frame_index==animation->frame_count-1 ? 0 : frame_index+1;
byte *data = animation_get_frame_data(animation, frame_index);
byte *next_data = animation_get_frame_data(animation, next_frame_index);
long rotation_index = 0;
unsigned long rotation_flags;
long translation_index = 0;
unsigned long translation_flags;
long scale_index = 0;
unsigned long scale_flags;
short node_index;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
scale_flags = animation->nodes_with_scale_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
real_quaternion rotation;
if (compressed)
{
animation_get_keyframe_rotation(
animation,
(real)frame_index,
(short)rotation_index++,
node_index,
&rotation);
}
else
{
real_quaternion this_rotation;
real_quaternion next_rotation;
quaternion_decompress_8byte(
(struct compressed_quaternion_8byte const *)data,
&this_rotation);
data += sizeof(struct compressed_quaternion_8byte);
quaternion_decompress_8byte(
(struct compressed_quaternion_8byte const *)next_data,
&next_rotation);
next_data += sizeof(struct compressed_quaternion_8byte);
quaternions_interpolate_and_normalize(
&this_rotation,
&next_rotation,
fraction,
&rotation);
}
quaternions_interpolate_and_normalize(
global_identity_quaternion,
&rotation,
animation_scale,
&rotation);
quaternions_multiply(&rotation, &orientation->rotation, &orientation->rotation);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
real_point3d translation;
if (compressed)
{
animation_get_keyframe_translation(
animation,
real_frame_index,
(short)translation_index++,
node_index,
&translation);
}
else
{
real_point3d const *this_translation;
real_point3d const *next_translation;
this_translation = (real_point3d const *)data;
data += sizeof(real_point3d);
next_translation = (real_point3d const *)next_data;
next_data += sizeof(real_point3d);
points_interpolate(
this_translation,
next_translation,
fraction,
&translation);
}
orientation->translation.x += translation.x*animation_scale;
orientation->translation.y += translation.y*animation_scale;
orientation->translation.z += translation.z*animation_scale;
}
translation_flags >>= 1;
if (TEST_FLAG(scale_flags, 0))
{
real scale;
if (compressed)
{
animation_get_keyframe_scale(
animation,
real_frame_index,
(short)scale_index++,
node_index,
&scale);
}
else
{
real this_scale;
real next_scale;
this_scale = *(real const *)data;
data += sizeof(real);
next_scale = *(real const *)next_data;
next_data += sizeof(real);
scalars_interpolate(
this_scale,
next_scale,
fraction,
&scale);
}
orientation->scale *= scale*animation_scale+inverse_animation_scale;
}
scale_flags >>= 1;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
820,
compressed || ((byte *)data-(byte *)animation_get_frame_data(animation, frame_index)==animation->frame_size));
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
821,
compressed || ((byte *)next_data-(byte *)animation_get_frame_data(animation, next_frame_index)==animation->frame_size));
}
return;
}
void aiming_screen_apply(
struct animation const *animation,
struct animation_aiming_screen_bounds const *aiming_screen_bounds,
real yaw,
real pitch,
struct real_orientation *node_orientations)
{
short grid_width = aiming_screen_bounds->negative_yaw_frame_count+
aiming_screen_bounds->positive_yaw_frame_count+1;
short grid_height = aiming_screen_bounds->negative_pitch_frame_count+
aiming_screen_bounds->positive_pitch_frame_count+1;
short yaw_frame_index;
boolean compressed;
real yaw_delta;
real yaw_frame;
real yaw_fraction;
real pitch_delta;
real pitch_frame;
short pitch_frame_index;
real pitch_fraction;
short next_yaw_cell;
short next_pitch_cell;
short frame_index00;
short frame_index10;
short frame_index01;
short frame_index11;
byte *data00;
byte *data10;
byte *data01;
byte *data11;
long rotation_index;
long translation_index;
unsigned long rotation_flags;
unsigned long translation_flags;
short node_index;
if (animation->type!=_animation_overlay ||
animation->frame_count<grid_width*grid_height)
{
return;
}
compressed = animation_is_compressed(animation);
yaw_delta = yaw<0.0f ? aiming_screen_bounds->negative_yaw_delta :
aiming_screen_bounds->positive_yaw_delta;
yaw_frame = yaw_delta==0.0f ? 0.0f : yaw/yaw_delta;
yaw_frame_index = (short)yaw_frame;
yaw_fraction = (real)fmod((double)yaw_frame, 1.0);
if (yaw_fraction<0.0f)
{
yaw_fraction += 1.0f;
yaw_frame_index--;
}
if (yaw_frame_index>=aiming_screen_bounds->positive_yaw_frame_count)
{
yaw_frame_index = aiming_screen_bounds->positive_yaw_frame_count-1;
yaw_fraction = 1.0f;
}
if (yaw_frame_index<-aiming_screen_bounds->negative_yaw_frame_count)
{
yaw_frame_index = -aiming_screen_bounds->negative_yaw_frame_count;
yaw_fraction = 0.0f;
}
yaw_frame_index += aiming_screen_bounds->negative_yaw_frame_count;
match_vassert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
869,
yaw_fraction>=0.0f && yaw_fraction<=1.0f,
csprintf(
temporary,
"d0==%f direction(%f) yaw_delta(%f,%f)",
yaw_fraction,
yaw,
aiming_screen_bounds->negative_yaw_delta,
aiming_screen_bounds->positive_yaw_delta));
pitch_delta = pitch<0.0f ? aiming_screen_bounds->negative_pitch_delta :
aiming_screen_bounds->positive_pitch_delta;
pitch_frame = pitch_delta==0.0f ? 0.0f : pitch/pitch_delta;
pitch_frame_index = (short)pitch_frame;
pitch_fraction = (real)fmod((double)pitch_frame, 1.0);
if (pitch_fraction<0.0f)
{
pitch_fraction += 1.0f;
pitch_frame_index--;
}
if (pitch_frame_index>=aiming_screen_bounds->positive_pitch_frame_count)
{
pitch_frame_index = aiming_screen_bounds->positive_pitch_frame_count-1;
pitch_fraction = 1.0f;
}
if (pitch_frame_index<-aiming_screen_bounds->negative_pitch_frame_count)
{
pitch_frame_index = -aiming_screen_bounds->negative_pitch_frame_count;
pitch_fraction = 0.0f;
}
pitch_frame_index += aiming_screen_bounds->negative_pitch_frame_count;
if (pitch_frame_index<0 || pitch_frame_index>=grid_height ||
yaw_frame_index<0 || yaw_frame_index>=grid_width)
{
return;
}
next_yaw_cell = yaw_frame_index+1==grid_width ? yaw_frame_index : yaw_frame_index+1;
next_pitch_cell = pitch_frame_index+1==grid_height ? pitch_frame_index : pitch_frame_index+1;
frame_index00 = yaw_frame_index+pitch_frame_index*grid_width;
frame_index10 = next_yaw_cell+pitch_frame_index*grid_width;
frame_index01 = yaw_frame_index+next_pitch_cell*grid_width;
frame_index11 = next_yaw_cell+next_pitch_cell*grid_width;
/* port: the four frames inside the animation's data (a map's counts and
offsets) */
if (!animation_frame_valid(animation, frame_index00, FALSE) ||
!animation_frame_valid(animation, frame_index10, FALSE) ||
!animation_frame_valid(animation, frame_index01, FALSE) ||
!animation_frame_valid(animation, frame_index11, FALSE))
{
return;
}
data00 = animation_get_frame_data(animation, frame_index00);
data10 = animation_get_frame_data(animation, frame_index10);
data01 = animation_get_frame_data(animation, frame_index01);
data11 = animation_get_frame_data(animation, frame_index11);
rotation_index = 0;
translation_index = 0;
for (node_index = 0; node_index<animation->node_count; node_index++)
{
struct real_orientation *orientation = &node_orientations[node_index];
if (!(node_index&(LONG_BITS-1)))
{
short long_index = node_index>>LONG_BITS_BITS;
translation_flags = animation->nodes_with_translation_flags[long_index];
rotation_flags = animation->nodes_with_rotation_flags[long_index];
}
if (TEST_FLAG(rotation_flags, 0))
{
real_quaternion rotation00;
real_quaternion rotation10;
real_quaternion rotation01;
real_quaternion rotation11;
real_quaternion yaw_rotation0;
real_quaternion yaw_rotation1;
real_quaternion rotation;
if (compressed)
{
animation_get_keyframe_rotation(animation, (real)frame_index00, (short)rotation_index, node_index, &rotation00);
animation_get_keyframe_rotation(animation, (real)frame_index10, (short)rotation_index, node_index, &rotation10);
animation_get_keyframe_rotation(animation, (real)frame_index01, (short)rotation_index, node_index, &rotation01);
animation_get_keyframe_rotation(animation, (real)frame_index11, (short)rotation_index++, node_index, &rotation11);
}
else
{
quaternion_decompress_8byte((struct compressed_quaternion_8byte const *)data00, &rotation00);
data00 += sizeof(struct compressed_quaternion_8byte);
quaternion_decompress_8byte((struct compressed_quaternion_8byte const *)data10, &rotation10);
data10 += sizeof(struct compressed_quaternion_8byte);
quaternion_decompress_8byte((struct compressed_quaternion_8byte const *)data01, &rotation01);
data01 += sizeof(struct compressed_quaternion_8byte);
quaternion_decompress_8byte((struct compressed_quaternion_8byte const *)data11, &rotation11);
data11 += sizeof(struct compressed_quaternion_8byte);
}
quaternions_interpolate_and_normalize(&rotation00, &rotation10, yaw_fraction, &yaw_rotation0);
quaternions_interpolate_and_normalize(&rotation01, &rotation11, yaw_fraction, &yaw_rotation1);
quaternions_interpolate_and_normalize(&yaw_rotation0, &yaw_rotation1, pitch_fraction, &rotation);
quaternions_multiply(&rotation, &orientation->rotation, &orientation->rotation);
}
rotation_flags >>= 1;
if (TEST_FLAG(translation_flags, 0))
{
real_point3d translation00;
real_point3d translation10;
real_point3d translation01;
real_point3d translation11;
real inverse_yaw_fraction = 1.0f-yaw_fraction;
real inverse_pitch_fraction = 1.0f-pitch_fraction;
if (compressed)
{
animation_get_keyframe_translation(animation, (real)frame_index00, (short)translation_index, node_index, &translation00);
animation_get_keyframe_translation(animation, (real)frame_index10, (short)translation_index, node_index, &translation10);
animation_get_keyframe_translation(animation, (real)frame_index01, (short)translation_index, node_index, &translation01);
animation_get_keyframe_translation(animation, (real)frame_index11, (short)translation_index++, node_index, &translation11);
}
else
{
translation00 = *(real_point3d const *)data00;
data00 += sizeof(real_point3d);
translation10 = *(real_point3d const *)data10;
data10 += sizeof(real_point3d);
translation01 = *(real_point3d const *)data01;
data01 += sizeof(real_point3d);
translation11 = *(real_point3d const *)data11;
data11 += sizeof(real_point3d);
}
orientation->translation.x +=
(translation01.x*inverse_yaw_fraction+translation11.x*yaw_fraction)*pitch_fraction+
(translation00.x*inverse_yaw_fraction+translation10.x*yaw_fraction)*inverse_pitch_fraction;
orientation->translation.y +=
(translation01.y*inverse_yaw_fraction+translation11.y*yaw_fraction)*pitch_fraction+
(translation00.y*inverse_yaw_fraction+translation10.y*yaw_fraction)*inverse_pitch_fraction;
orientation->translation.z +=
(translation00.z*inverse_yaw_fraction+translation10.z*yaw_fraction)*inverse_pitch_fraction+
(translation01.z*inverse_yaw_fraction+translation11.z*yaw_fraction)*pitch_fraction;
}
translation_flags >>= 1;
}
return;
}
void quaternion_decompress_8byte(
struct compressed_quaternion_8byte const *compressed,
real_quaternion *quaternion)
{
real const scale = 1.f / COMPRESSED_QUATERNION_COMPONENT_MAXIMUM;
quaternion->v.i = compressed->i * scale;
quaternion->v.j = compressed->j * scale;
quaternion->v.k = compressed->k * scale;
quaternion->w = compressed->w * scale;
return;
}
void quaternion_decompress_6byte(
struct compressed_quaternion_6byte const *compressed,
real_quaternion *quaternion)
{
word word0 = compressed->words[0];
word word1 = compressed->words[1];
word word2 = compressed->words[2];
short i = (short)((word0 >> 12) | (word0 & 0xFFF0));
short j = (short)(((word1 >> 4) & 0x0FF0) | (word0 & 0x000F) | (word0 << 12));
short k = (short)(((((word2 >> 4) & 0x0F00) | (word1 & 0x00F0)) >> 4) | (word1 << 8));
short w = (short)(((word2 >> 8) & 0x000F) | (word2 << 4));
real const scale = 1.f / COMPRESSED_QUATERNION_COMPONENT_MAXIMUM;
quaternion->v.i = i * scale;
quaternion->v.j = j * scale;
quaternion->v.k = k * scale;
quaternion->w = w * scale;
return;
}
void quaternion_decompress_6byte_renormalized(
void const *compressed,
real_quaternion *quaternion)
{
quaternion_decompress_6byte(compressed, quaternion);
quaternion_normalize(quaternion);
return;
}
void quaternion_compress_8byte(
real_quaternion const *quaternion,
struct compressed_quaternion_8byte *compressed)
{
compressed->i = (short)(quaternion->v.i * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
compressed->j = (short)(quaternion->v.j * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
compressed->k = (short)(quaternion->v.k * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
compressed->w = (short)(quaternion->w * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
return;
}
void quaternion_compress_6byte(
real_quaternion const *quaternion,
struct compressed_quaternion_6byte *compressed)
{
long j = (long)(quaternion->v.j * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
long k = (long)(quaternion->v.k * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
long w = (long)(quaternion->w * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
long i = (long)(quaternion->v.i * COMPRESSED_QUATERNION_COMPONENT_MAXIMUM);
compressed->words[0] = (word)((i & 0xFFF0) | ((word)j >> 12));
compressed->words[1] = (word)(((j & 0xFFF0) << 4) | (((word)k >> 8) & 0x00FF));
compressed->words[2] = (word)(((k & 0x00F0) << 8) | ((word)w >> 4));
return;
}
/* ---------- private code */
static boolean animation_is_compressed(
struct animation const *animation)
{
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
38,
animation);
return TEST_FLAG(animation->flags, _animation_compressed_bit) &&
(hs_model_animation_compression_enabled || !animation->compressed_data_offset);
}
// binary search for the keyframe containing target_frame_index
static short animation_keyframe_search(
short const *keyframe_frame_indices,
short keyframe_count,
short target_frame_index)
{
short low = 0;
short high = keyframe_count-1;
short keyframe_index;
short infinite_loop_killer = 0;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1334,
keyframe_count>1);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1335,
keyframe_frame_indices);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1336,
keyframe_frame_indices[0]>0);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1337,
target_frame_index>=0 && target_frame_index<keyframe_frame_indices[keyframe_count-1]);
while (TRUE)
{
keyframe_index = (low+high)>>1;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1343,
keyframe_index>=0 && keyframe_index<keyframe_count);
if (keyframe_index+1<keyframe_count && keyframe_frame_indices[keyframe_index+1]<=target_frame_index)
{
low = keyframe_index;
}
else if (keyframe_frame_indices[keyframe_index]>target_frame_index)
{
high = keyframe_index;
}
else
{
break;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1356,
++infinite_loop_killer<200);
/* port: a map's frame indices out of order would loop forever */
if (infinite_loop_killer>=200)
{
break;
}
}
/* port: a keyframe with one after it, whatever the map's frame indices */
if (keyframe_index>keyframe_count-2)
{
keyframe_index = keyframe_count-2;
}
if (keyframe_index<0)
{
keyframe_index = 0;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1360,
keyframe_index>=0 && keyframe_index<keyframe_count-1);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1361,
target_frame_index>=keyframe_frame_indices[keyframe_index] && target_frame_index<keyframe_frame_indices[keyframe_index+1]);
return keyframe_index;
}
static void animation_get_keyframe_rotation(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real_quaternion *rotation)
{
byte *data = tag_data_get_pointer(&animation->data, animation->compressed_data_offset, 0);
struct compressed_animation_header const *header = (struct compressed_animation_header const *)data;
struct compressed_quaternion_6byte const *default_rotations;
unsigned long node_header;
short first_keyframe_index;
short keyframe_count;
word const *keyframe_frame_indices;
struct compressed_quaternion_6byte const *keyframe_rotations;
short frame_index;
struct compressed_quaternion_6byte const *this_keyframe;
struct compressed_quaternion_6byte const *next_keyframe;
short this_keyframe_frame_index;
short next_keyframe_frame_index;
/* port: the header, this node's header and default, and its keyframes
inside the animation's data (a map's offsets); a bad one is no rotation */
if (!animation_data_contains(animation, animation->compressed_data_offset, 0, 0, 1, offsetof(struct compressed_animation_header, rotation_node_headers)) ||
!animation_data_contains(animation, animation->compressed_data_offset, offsetof(struct compressed_animation_header, rotation_node_headers), adjusted_node_index, 1, sizeof(unsigned long)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->default_rotations_offset, node_index, 1, sizeof(struct compressed_quaternion_6byte)))
{
animation_data_error(animation, "compressed rotation");
*rotation = *global_identity_quaternion;
return;
}
default_rotations = (struct compressed_quaternion_6byte const *)(data+header->default_rotations_offset);
node_header = header->rotation_node_headers[adjusted_node_index];
first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
if (keyframe_count>0 &&
(!animation_data_contains(animation, animation->compressed_data_offset, header->rotation_keyframes_offset, first_keyframe_index, keyframe_count, sizeof(struct compressed_quaternion_6byte)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->rotation_keyframe_frame_indices_offset, first_keyframe_index, keyframe_count, sizeof(word))))
{
animation_data_error(animation, "compressed rotation");
*rotation = *global_identity_quaternion;
return;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1428,
real_frame_index>=0.0f);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1430,
real_frame_index<(real)animation->frame_count);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1432,
keyframe_count>=0);
if (keyframe_count==0)
{
// this node never changes rotation
quaternion_decompress_6byte(&default_rotations[node_index], rotation);
quaternion_normalize(rotation);
return;
}
keyframe_rotations = (struct compressed_quaternion_6byte const *)(data+header->rotation_keyframes_offset)+first_keyframe_index;
keyframe_frame_indices = (word const *)(data+header->rotation_keyframe_frame_indices_offset)+first_keyframe_index;
frame_index = (short)fast_ftol(floor(real_frame_index));
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1451,
frame_index>=0 && frame_index<=keyframe_frame_indices[keyframe_count-1]);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1452,
keyframe_frame_indices[keyframe_count-1]==animation->frame_count-1);
/* port: past the last keyframe (a map's keyframes short of the last
frame) holds the last one */
if (frame_index>keyframe_frame_indices[keyframe_count-1])
{
quaternion_decompress_6byte(&keyframe_rotations[keyframe_count-1], rotation);
quaternion_normalize(rotation);
return;
}
if (frame_index<keyframe_frame_indices[0])
{
this_keyframe_frame_index = 0;
this_keyframe = &default_rotations[node_index];
next_keyframe_frame_index = keyframe_frame_indices[0];
next_keyframe = keyframe_rotations;
}
else if (frame_index==keyframe_frame_indices[keyframe_count-1])
{
this_keyframe_frame_index = keyframe_frame_indices[keyframe_count-1];
this_keyframe = &keyframe_rotations[keyframe_count-1];
next_keyframe_frame_index = this_keyframe_frame_index+1;
next_keyframe = &default_rotations[node_index];
}
else
{
short keyframe_index = animation_keyframe_search(keyframe_frame_indices, keyframe_count, frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1472,
keyframe_index>=0 && keyframe_index<keyframe_count-1);
this_keyframe_frame_index = keyframe_frame_indices[keyframe_index];
this_keyframe = &keyframe_rotations[keyframe_index];
next_keyframe_frame_index = keyframe_frame_indices[keyframe_index+1];
next_keyframe = &keyframe_rotations[keyframe_index+1];
}
if (real_frame_index==(real)this_keyframe_frame_index)
{
quaternion_decompress_6byte(this_keyframe, rotation);
quaternion_normalize(rotation);
}
else
{
real fraction = (real_frame_index-(real)this_keyframe_frame_index)/(next_keyframe_frame_index-this_keyframe_frame_index);
real_quaternion this_rotation;
real_quaternion next_rotation;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1491,
real_frame_index>=(real)this_keyframe_frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1492,
real_frame_index< (real)next_keyframe_frame_index);
quaternion_decompress_6byte(this_keyframe, &this_rotation);
quaternion_decompress_6byte(next_keyframe, &next_rotation);
quaternions_interpolate_and_normalize(&this_rotation, &next_rotation, fraction, rotation);
}
return;
}
static void animation_get_keyframe_translation(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real_point3d *translation)
{
byte *data = tag_data_get_pointer(&animation->data, animation->compressed_data_offset, 0);
struct compressed_animation_header const *header = (struct compressed_animation_header const *)data;
real_point3d const *default_translations;
unsigned long node_header;
short first_keyframe_index;
short keyframe_count;
/* port: the header, this node's header and default, and its keyframes
inside the animation's data (a map's offsets); a bad one is no
translation */
if (!animation_data_contains(animation, animation->compressed_data_offset, 0, 0, 1, offsetof(struct compressed_animation_header, rotation_node_headers)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->translation_node_headers_offset, adjusted_node_index, 1, sizeof(unsigned long)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->default_translations_offset, node_index, 1, sizeof(real_point3d)))
{
animation_data_error(animation, "compressed translation");
*translation = *global_origin3d;
return;
}
default_translations = (real_point3d const *)(data+header->default_translations_offset);
node_header = ((unsigned long const *)(data+header->translation_node_headers_offset))[adjusted_node_index];
first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
if (keyframe_count>0 &&
(!animation_data_contains(animation, animation->compressed_data_offset, header->translation_keyframes_offset, first_keyframe_index, keyframe_count, sizeof(real_point3d)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->translation_keyframe_frame_indices_offset, first_keyframe_index, keyframe_count, sizeof(word))))
{
animation_data_error(animation, "compressed translation");
*translation = *global_origin3d;
return;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1522,
real_frame_index>=0.0f);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1524,
real_frame_index<(real)animation->frame_count);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1526,
keyframe_count>=0);
if (keyframe_count==0)
{
*translation = default_translations[node_index];
}
else
{
real_point3d const *keyframe_translations = (real_point3d const *)(data+header->translation_keyframes_offset)+first_keyframe_index;
word const *keyframe_frame_indices = (word const *)(data+header->translation_keyframe_frame_indices_offset)+first_keyframe_index;
short frame_index = (short)fast_ftol(floor(real_frame_index));
real_point3d const *this_keyframe;
real_point3d const *next_keyframe;
short this_keyframe_frame_index;
short next_keyframe_frame_index;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1545,
frame_index>=0 && frame_index<=keyframe_frame_indices[keyframe_count-1]);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1546,
keyframe_frame_indices[keyframe_count-1]==animation->frame_count-1);
/* port: past the last keyframe (a map's keyframes short of the last
frame) holds the last one */
if (frame_index>keyframe_frame_indices[keyframe_count-1])
{
*translation = keyframe_translations[keyframe_count-1];
return;
}
if (frame_index<keyframe_frame_indices[0])
{
this_keyframe_frame_index = 0;
this_keyframe = &default_translations[node_index];
next_keyframe_frame_index = keyframe_frame_indices[0];
next_keyframe = keyframe_translations;
}
else if (frame_index==keyframe_frame_indices[keyframe_count-1])
{
this_keyframe_frame_index = keyframe_frame_indices[keyframe_count-1];
this_keyframe = &keyframe_translations[keyframe_count-1];
next_keyframe_frame_index = this_keyframe_frame_index+1;
next_keyframe = &default_translations[node_index];
}
else
{
short keyframe_index = animation_keyframe_search(keyframe_frame_indices, keyframe_count, frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1566,
keyframe_index>=0 && keyframe_index<keyframe_count-1);
this_keyframe_frame_index = keyframe_frame_indices[keyframe_index];
this_keyframe = &keyframe_translations[keyframe_index];
next_keyframe_frame_index = keyframe_frame_indices[keyframe_index+1];
next_keyframe = &keyframe_translations[keyframe_index+1];
}
if (real_frame_index==(real)this_keyframe_frame_index)
{
*translation = *this_keyframe;
}
else
{
real fraction = (real_frame_index-(real)this_keyframe_frame_index)/(next_keyframe_frame_index-this_keyframe_frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1583,
real_frame_index>=(real)this_keyframe_frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1584,
real_frame_index< (real)next_keyframe_frame_index);
points_interpolate(this_keyframe, next_keyframe, fraction, translation);
}
}
return;
}
static void animation_get_keyframe_scale(
struct animation const *animation,
real real_frame_index,
short adjusted_node_index,
short node_index,
real *scale)
{
byte *data = tag_data_get_pointer(&animation->data, animation->compressed_data_offset, 0);
struct compressed_animation_header const *header = (struct compressed_animation_header const *)data;
real const *default_scales;
unsigned long node_header;
short first_keyframe_index;
short keyframe_count;
/* port: the header, this node's header and default, and its keyframes
inside the animation's data (a map's offsets); a bad one is no scale */
if (!animation_data_contains(animation, animation->compressed_data_offset, 0, 0, 1, offsetof(struct compressed_animation_header, rotation_node_headers)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->scale_node_headers_offset, adjusted_node_index, 1, sizeof(unsigned long)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->default_scales_offset, adjusted_node_index, 1, sizeof(real)))
{
animation_data_error(animation, "compressed scale");
*scale = 1.0f;
return;
}
default_scales = (real const *)(data+header->default_scales_offset);
node_header = ((unsigned long const *)(data+header->scale_node_headers_offset))[adjusted_node_index];
first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
if (keyframe_count>0 &&
(!animation_data_contains(animation, animation->compressed_data_offset, header->scale_keyframes_offset, first_keyframe_index, keyframe_count, sizeof(real)) ||
!animation_data_contains(animation, animation->compressed_data_offset, header->scale_keyframe_frame_indices_offset, first_keyframe_index, keyframe_count, sizeof(word))))
{
animation_data_error(animation, "compressed scale");
*scale = 1.0f;
return;
}
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1610,
real_frame_index>=0.0f);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1612,
real_frame_index<(real)animation->frame_count);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1614,
keyframe_count>=0);
if (keyframe_count==0)
{
*scale = default_scales[adjusted_node_index];
}
else
{
real const *keyframe_scales = (real const *)(data+header->scale_keyframes_offset)+first_keyframe_index;
word const *keyframe_frame_indices = (word const *)(data+header->scale_keyframe_frame_indices_offset)+first_keyframe_index;
short frame_index = (short)fast_ftol(floor(real_frame_index));
real this_keyframe_scale;
real next_keyframe_scale;
short this_keyframe_frame_index;
short next_keyframe_frame_index;
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1634,
frame_index>=0 && frame_index<=keyframe_frame_indices[keyframe_count-1]);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1635,
keyframe_frame_indices[keyframe_count-1]==animation->frame_count-1);
/* port: past the last keyframe (a map's keyframes short of the last
frame) holds the last one */
if (frame_index>keyframe_frame_indices[keyframe_count-1])
{
*scale = keyframe_scales[keyframe_count-1];
return;
}
if (frame_index<keyframe_frame_indices[0])
{
this_keyframe_frame_index = 0;
this_keyframe_scale = default_scales[adjusted_node_index];
next_keyframe_frame_index = keyframe_frame_indices[0];
next_keyframe_scale = keyframe_scales[0];
}
else if (frame_index==keyframe_frame_indices[keyframe_count-1])
{
this_keyframe_frame_index = keyframe_frame_indices[keyframe_count-1];
this_keyframe_scale = keyframe_scales[keyframe_count-1];
next_keyframe_frame_index = this_keyframe_frame_index+1;
next_keyframe_scale = default_scales[adjusted_node_index];
}
else
{
short keyframe_index = animation_keyframe_search(keyframe_frame_indices, keyframe_count, frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1655,
keyframe_index>=0 && keyframe_index<keyframe_count-1);
this_keyframe_frame_index = keyframe_frame_indices[keyframe_index];
this_keyframe_scale = keyframe_scales[keyframe_index];
next_keyframe_frame_index = keyframe_frame_indices[keyframe_index+1];
next_keyframe_scale = keyframe_scales[keyframe_index+1];
}
if (real_frame_index==(real)this_keyframe_frame_index)
{
*scale = this_keyframe_scale;
}
else
{
real fraction = (real_frame_index-(real)this_keyframe_frame_index)/(next_keyframe_frame_index-this_keyframe_frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1672,
real_frame_index>=(real)this_keyframe_frame_index);
match_assert(
"c:\\halo\\SOURCE\\models\\model_animations.c",
1673,
real_frame_index< (real)next_keyframe_frame_index);
scalars_interpolate(this_keyframe_scale, next_keyframe_scale, fraction, scale);
}
}
return;
}
/* port: where animation_get_frame_data's frame starts in the animation's data */
static long animation_frame_data_offset(
struct animation const *animation,
short frame_index)
{
if (TEST_FLAG(animation->flags, _animation_compressed_bit) && hs_model_animation_compression_enabled)
{
return animation->compressed_data_offset;
}
return (long)frame_index*animation->frame_size;
}
/* port: TRUE if element_count elements of element_size bytes, from
first_element_index on, relative_offset bytes past offset, are inside the
animation's data. Each part is a map's number, checked on its own so that
none of the sums can wrap. */
static boolean animation_data_contains(
struct animation const *animation,
long offset,
long relative_offset,
long first_element_index,
long element_count,
long element_size)
{
long size = animation->data.size;
if (offset<0 || offset>size)
{
return FALSE;
}
size -= offset;
if (relative_offset<0 || relative_offset>size)
{
return FALSE;
}
size -= relative_offset;
if (first_element_index<0 || element_count<0 || element_size<0 ||
first_element_index>SHORT_MAX || element_count>UNSIGNED_SHORT_MAX)
{
return FALSE;
}
return element_size==0 || first_element_index+element_count<=size/element_size;
}
/* port: how many of the first node_count nodes have their flag set */
static long animation_node_flag_count(
unsigned long low_flags,
unsigned long high_flags,
short node_count)
{
unsigned long flags[2];
long count = 0;
short long_index;
flags[0] = low_flags;
flags[1] = high_flags;
for (long_index = 0; long_index<2; long_index++)
{
short bit_count = (short)(node_count-long_index*LONG_BITS);
unsigned long word = flags[long_index];
if (bit_count<=0)
{
break;
}
if (bit_count<LONG_BITS)
{
word &= ((unsigned long)1<<bit_count)-1;
}
word = word-((word>>1)&0x55555555UL);
word = (word&0x33333333UL)+((word>>2)&0x33333333UL);
word = (word+(word>>4))&0x0F0F0F0FUL;
count += (long)(((word*0x01010101UL)&0xFFFFFFFFUL)>>24);
}
return count;
}
/* port: TRUE if the animation's node count fits the engine's arrays and the
frame it reads is inside its data (a map's counts, frame size, offsets and
sizes). The node flags say how many bytes a frame and the default data
hold. A bad animation is reported once and isn't read. */
static boolean animation_frame_valid(
struct animation const *animation,
short frame_index,
boolean reads_default_data)
{
long frame_offset = animation_frame_data_offset(animation, frame_index);
short node_count = animation->node_count;
char const *problem = NULL;
if (node_count<0 || node_count>MAXIMUM_NODES_PER_ANIMATION)
{
problem = "node count";
}
else if (animation_is_compressed(animation))
{
if (!animation_data_contains(animation, frame_offset, 0, 0, 1, offsetof(struct compressed_animation_header, rotation_node_headers)) ||
!animation_data_contains(animation, animation->compressed_data_offset, 0, 0, 1, offsetof(struct compressed_animation_header, rotation_node_headers)))
{
problem = "compressed data offset";
}
}
else
{
long rotation_count = animation_node_flag_count(
animation->nodes_with_rotation_flags[0],
animation->nodes_with_rotation_flags[1],
node_count);
long translation_count = animation_node_flag_count(
animation->nodes_with_translation_flags[0],
animation->nodes_with_translation_flags[1],
node_count);
long scale_count = animation_node_flag_count(
animation->nodes_with_scale_flags[0],
animation->nodes_with_scale_flags[1],
node_count);
long frame_bytes = rotation_count*(long)sizeof(struct compressed_quaternion_8byte)+
translation_count*(long)sizeof(real_point3d)+
scale_count*(long)sizeof(real);
long default_bytes = (node_count-rotation_count)*(long)sizeof(struct compressed_quaternion_8byte)+
(node_count-translation_count)*(long)sizeof(real_point3d)+
(node_count-scale_count)*(long)sizeof(real);
if (!animation_data_contains(animation, frame_offset, 0, 0, 1, frame_bytes))
{
problem = "frame";
}
else if (reads_default_data && default_bytes>animation->default_data.size)
{
problem = "default data";
}
}
if (problem)
{
animation_data_error(animation, problem);
return FALSE;
}
return TRUE;
}
/* port: a map's animation with data past what it has */
static void animation_data_error(
struct animation const *animation,
char const *problem)
{
if (model_data_report_once(animation))
{
error(
_error_silent,
"### ERROR animation '%.31s' has a bad %s; it is skipped",
animation->name,
problem);
}
return;
}
/* port: the rest pose, for nodes with no model to take a default pose from */
static void animation_set_rest_orientations(
struct real_orientation *node_orientations,
short node_count)
{
short node_index;
for (node_index = 0; node_index<node_count; node_index++)
{
node_orientations[node_index].rotation = *global_identity_quaternion;
node_orientations[node_index].translation = *global_origin3d;
node_orientations[node_index].scale = 1.0f;
}
return;
}