mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-22 01:00:14 +02:00
Integrate coherent B3, P2/K17 prerequisites, completed K1-K16 + S, and the independent profile/effects rulings. Five gains and two explicitly approved B3 debits: net +3 strict functions, +2770 meaningful/+2800 padded bytes (+2774 progress credit). No additional losses; Halo objects 394 to 396 and data unchanged. Clean rebuild, identical 8252-row stable sweep, 68 valid parks, admission/provider checks, 467-unit warning comparison, unchanged fake scan, and 1318 tests passed. Preserve canonical helper recoveries and unrelated work. Full evidence and disclosures in docs/object_matching_logs/astra_five_function_reconciliation_20260928.md.
2265 lines
67 KiB
C
2265 lines
67 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);
|
|
|
|
/* ---------- 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;
|
|
|
|
for (frame_index = 0; frame_index < animation->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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
matrix4x3_from_point_and_vectors(&root_matrix, origin, forward, up);
|
|
|
|
if (animation_graph->nodes.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;
|
|
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]);
|
|
|
|
if (node->next_sibling_node_index != NONE)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\models\\model_animations.c",
|
|
1250,
|
|
write_index<MAXIMUM_NODES_PER_MODEL);
|
|
node_indices[write_index++] = node->next_sibling_node_index;
|
|
}
|
|
|
|
if (node->first_child_node_index != NONE)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\models\\model_animations.c",
|
|
1256,
|
|
write_index<MAXIMUM_NODES_PER_MODEL);
|
|
node_indices[write_index++] = node->first_child_node_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;
|
|
|
|
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 = 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);
|
|
|
|
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)
|
|
{
|
|
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)))
|
|
{
|
|
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);
|
|
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;
|
|
|
|
quaternion_decompress_6byte(
|
|
(struct compressed_quaternion_6byte *)(data+header->default_rotations_offset)+node_index,
|
|
&orientation->rotation);
|
|
quaternion_normalize(&orientation->rotation);
|
|
}
|
|
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;
|
|
|
|
orientation->translation = *((real_point3d *)(data+header->default_translations_offset)+node_index);
|
|
}
|
|
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
|
|
{
|
|
model_get_node_orientations(model, node_orientations);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void replacement_animation_apply(
|
|
struct animation const *animation,
|
|
short frame_index,
|
|
struct real_orientation *node_orientations)
|
|
{
|
|
if (animation->type==_animation_replacement && frame_index>=0 && frame_index<animation->frame_count)
|
|
{
|
|
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)
|
|
{
|
|
if (animation->type==_animation_overlay && frame_index>=0 && frame_index<animation->frame_count)
|
|
{
|
|
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;
|
|
|
|
if (animation->type==_animation_overlay && frame_index>=0 && frame_index<animation->frame_count)
|
|
{
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
if (animation->type == _animation_overlay)
|
|
{
|
|
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));
|
|
|
|
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;
|
|
}
|
|
|
|
if (animation->type==_animation_overlay)
|
|
{
|
|
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;
|
|
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);
|
|
}
|
|
|
|
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 = (struct compressed_quaternion_6byte const *)(data+header->default_rotations_offset);
|
|
unsigned long node_header = header->rotation_node_headers[adjusted_node_index];
|
|
short first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
|
|
short keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
|
|
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;
|
|
|
|
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);
|
|
|
|
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 = (real_point3d const *)(data+header->default_translations_offset);
|
|
unsigned long node_header = ((unsigned long const *)(data+header->translation_node_headers_offset))[adjusted_node_index];
|
|
short first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
|
|
short keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
|
|
|
|
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);
|
|
|
|
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 = (real const *)(data+header->default_scales_offset);
|
|
unsigned long node_header = ((unsigned long const *)(data+header->scale_node_headers_offset))[adjusted_node_index];
|
|
short first_keyframe_index = (short)(node_header>>COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS);
|
|
short keyframe_count = (short)(node_header&(FLAG(COMPRESSED_ANIMATION_NODE_HEADER_KEYFRAME_COUNT_BITS)-1));
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|