with gel.Forge, gel.Conversions, physics.Model, openGL.Model.any, opengl.Palette, opengl.Program .lit.colored_textured_skinned, opengl.Geometry.lit_colored_textured_skinned, collada.Document, collada.Library, collada.Library.controllers, collada.Library.animations, ada.Strings.unbounded, ada.Strings.Maps; package body gel.Rig is use linear_Algebra_3D; ----------- --- Utility -- function "+" (From : in ada.strings.unbounded.unbounded_String) return String renames ada.strings.unbounded.to_String; function "+" (From : in String) return ada.strings.unbounded.unbounded_String renames ada.strings.unbounded.to_unbounded_String; function to_gel_joint_Id (Parent, Child : in bone_Id) return gel_joint_Id is use ada.Strings.unbounded; begin return Parent & "_to_" & Child; end to_gel_joint_Id; function to_Math (From : in collada.Matrix_4x4) return math.Matrix_4x4 is begin return (1 => (From (1, 1), From (1, 2), From (1, 3), From (1, 4)), 2 => (From (2, 1), From (2, 2), From (2, 3), From (2, 4)), 3 => (From (3, 1), From (3, 2), From (3, 3), From (3, 4)), 4 => (From (4, 1), From (4, 2), From (4, 3), From (4, 4))); end to_Math; pragma Unreferenced (to_Math); function to_Details (Length : Real := Unspecified; width_Factor, depth_Factor : Real := 0.1; pitch_Limits, yaw_Limits, roll_Limits : gel.Sprite.DoF_Limits := (to_Radians (-15.0), to_Radians ( 15.0))) return bone_Details is begin return (Length, width_Factor, depth_Factor, pitch_Limits, yaw_Limits, roll_Limits); end to_Details; --------- --- Forge -- package body Forge is function new_Rig (in_World : in gel.World.view; Model : in openGL.Model.view; Mass : in Real := 0.0; is_Kinematic : in Boolean := False) return Rig.view is Self : constant Rig.view := new Rig.item; begin Self.define (in_World, Model, Mass, is_Kinematic); return Self; end new_Rig; function new_Rig (bone_Sprites : in bone_id_Map_of_sprite; joint_inv_bind_Matrices : in inverse_bind_matrix_Vector; joint_site_Offets : in joint_Id_Map_of_bone_site_offset; Model : in openGL.Model.view) return Rig.view is the_Box : constant Rig.View := new Rig.item; begin the_Box.bone_Sprites := bone_Sprites; the_Box.joint_inv_bind_Matrices := joint_inv_bind_Matrices; the_Box.phys_joint_site_Offets := joint_site_Offets; the_Box.Model := Model; return the_Box; end new_Rig; end Forge; --------------------------- --- Skin program parameters -- overriding procedure enable (Self : in out skin_program_Parameters) is use joint_id_Maps_of_slot; subtype Program_view is openGL.Program.lit.colored_textured_skinned.view; Cursor : joint_id_Maps_of_slot.Cursor := Self.joint_Map_of_slot.First; Slot : Integer; begin while has_Element (Cursor) loop Slot := Element (Cursor); Program_view (Self.Program).bone_Transform_is (Which => Slot, Now => Self.bone_Transforms.Element (Slot)); next (Cursor); end loop; end enable; ------------- --- Animation -- procedure define_global_Transform_for (Self : in out Item'Class; the_Joint : in collada.Library.visual_scenes.Node_view; Slot : in out Positive) is use collada.Library; which_Joint : constant scene_joint_Id := the_Joint.Id; child_Joints : constant visual_scenes.Nodes := the_Joint.Children; default_scene_Joint : scene_Joint; the_global_Transform : constant Matrix_4x4 := Transpose (the_Joint.global_Transform); -- Transpose to convert to row-major. begin Self.joint_pose_Transforms.insert (which_Joint, the_global_Transform); Self.collada_Joints .insert (which_Joint, the_Joint); default_scene_Joint.Node := the_Joint; Self.scene_Joints.insert (which_Joint, default_scene_Joint); for i in child_Joints'Range loop Slot := Slot + 1; define_global_Transform_for (Self, child_Joints (i), Slot); -- Recurse over children. end loop; end define_global_Transform_for; procedure update_global_Transform_for (Self : in out Item'Class; the_Joint : in collada.Library.visual_scenes.Node_view) is use collada.Library, ada.Strings.unbounded; which_Joint : constant scene_joint_Id := the_Joint.Id; child_Joints : constant visual_scenes.Nodes := the_Joint.Children; the_global_Transform : constant Matrix_4x4 := math.Transpose (the_Joint.global_Transform); -- Transpose to convert to row-major. joint_site_Offet : Vector_3; begin if which_Joint = Self.root_Joint.Name then joint_site_Offet := (0.0, 0.0, 0.0); else joint_site_Offet := Self.anim_joint_site_Offets (which_Joint); end if; Self.joint_pose_Transforms.replace (which_Joint, (the_global_Transform)); Self.scene_Joints (which_Joint).Transform := the_global_Transform; declare use type gel.Sprite.view; the_bone_Id : constant bone_Id := which_Joint; Site : Vector_3; Rotation : Matrix_3x3; begin if Self.bone_Sprites (the_bone_Id) /= null then Site := get_Translation (the_global_Transform); Site := Site - joint_site_Offet * (get_Rotation (the_global_Transform)); Site := Site * Inverse (Self.base_Sprite.Spin); Site := Site + Self.overall_Site; Rotation := Inverse (get_Rotation (the_global_Transform)); Rotation := Self.base_Sprite.Spin * Rotation; Self.bone_Sprites (the_bone_Id).all.Site_is (Site); if which_Joint /= Self.root_Joint.Name then Self.bone_Sprites (the_bone_Id).all.Spin_is (Rotation); end if; end if; end; for i in child_Joints'Range loop Self.update_global_Transform_for (child_Joints (i)); -- Recurse over children. end loop; end update_global_Transform_for; procedure update_all_global_Transforms (Self : in out Item'Class) is begin Self.update_global_Transform_for (Self.root_Joint); -- Re-determine all joint transforms, recursively. end update_all_global_Transforms; procedure set_rotation_Angle (Self : in out Item'Class; for_Joint : in scene_joint_Id; Axis : in axis_Kind; To : in Real) is begin case Axis is when x_Axis => Self.set_x_rotation_Angle (for_Joint, To); when y_Axis => Self.set_y_rotation_Angle (for_Joint, To); when z_Axis => Self.set_z_rotation_Angle (for_Joint, To); end case; end set_rotation_Angle; procedure set_Location (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Vector_3) is begin Self.scene_Joints (for_Joint).Node.set_Location (To); end set_Location; procedure set_Location_x (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_Location_x (To); end set_Location_x; procedure set_Location_y (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_Location_y (To); end set_Location_y; procedure set_Location_z (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_Location_z (To); end set_location_z; procedure set_Transform (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Matrix_4x4) is begin Self.scene_Joints (for_Joint).Node.set_Transform (To); end set_Transform; procedure set_x_rotation_Angle (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_x_rotation_Angle (To); end set_x_rotation_Angle; procedure set_y_rotation_Angle (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_y_rotation_Angle (To); end set_y_rotation_Angle; procedure set_z_rotation_Angle (Self : in out Item'Class; for_Joint : in scene_joint_Id; To : in Real) is begin Self.scene_Joints (for_Joint).Node.set_z_rotation_Angle (To); end set_z_rotation_Angle; ---------- --- Define -- procedure define (Self : in out Item; in_World : in gel .World.view; Model : in openGL.Model.view; Mass : in Real := 0.0; is_Kinematic : in Boolean := False; bone_Details : in bone_id_Map_of_details := bone_id_Maps_of_details.empty_Map) is use collada.Document, collada.Library, collada.Library.visual_Scenes, ada.Strings.unbounded, ada.Strings; type any_Model_view is access all openGL.Model.any.item; the_Model : constant any_Model_view := any_Model_view (Model); the_Document : constant collada.Document.item := to_Document (openGL.to_String (the_Model.model_Name)); function get_root_Joint return visual_Scenes.Node_view is begin if the_Document.Libraries.visual_Scenes.skeletal_Root = "" then return the_Document.Libraries.visual_Scenes.Contents (1).root_Node; else return the_Document.Libraries.visual_Scenes.Contents (1).root_Node.Child (1); end if; end get_root_Joint; the_root_Joint : constant visual_scenes.Node_view := get_root_Joint; prior_bone_Length : Real := 1.0; package joint_id_Maps_of_vector_3 is new ada.Containers.hashed_Maps (Key_type => scene_joint_Id, Element_type => Vector_3, Hash => ada.Strings.unbounded.Hash, equivalent_Keys => ada.Strings.unbounded."=", "=" => "="); subtype joint_id_Map_of_vector_3 is joint_id_Maps_of_vector_3.Map; joint_Sites : joint_id_Map_of_vector_3; procedure set_Site_for (the_Joint : in visual_Scenes.Node_view) is which_Joint : constant scene_joint_Id := the_Joint.Id; child_Joints : constant visual_Scenes.Nodes := the_Joint.Children; begin if which_Joint = Self.root_Joint.Name then joint_Sites.insert (which_Joint, (0.0, 0.0, 0.0)); else joint_Sites.insert (which_Joint, get_Translation (Self.joint_bind_Matrix (which_Joint))); end if; for i in child_Joints'Range loop set_Site_for (child_Joints (i)); -- Recurse over children. end loop; end set_Site_for; procedure create_Bone (the_Bone : in bone_Id; start_Joint : in scene_joint_Id; end_Point : in Vector_3; Scale : in Vector_3; Mass : in Real) is pragma Unreferenced (Mass); use opengl.Palette; new_Sprite : gel.Sprite.view; the_bone_Site : constant Vector_3 := midPoint (joint_Sites (start_Joint), end_Point); begin if the_Bone = Self.root_Joint.Name then declare use standard.physics.Model; Size : constant Vector_3 := (0.1, 0.1, 0.1); physics_Model : constant standard.physics.Model.View := standard.physics.Model.Forge.new_physics_Model (shape_Info => (Kind => Cube, half_Extents => Size / 2.0), Mass => 1.0); begin new_Sprite := gel.Sprite.Forge.new_Sprite ("Skin Sprite", gel.sprite.World_view (in_World), math.Origin_3D, Model, physics_Model, is_Kinematic => is_Kinematic); end; new_Sprite.Site_is ((0.0, 0.0, 0.0)); new_Sprite.Spin_is (Identity_3x3); Self.bone_pose_Transforms.insert (the_Bone, Identity_4x4); Self.skin_Sprite := new_Sprite; else new_Sprite := gel.Forge.new_box_Sprite (in_World => in_World.all'Access, Mass => 1.0, Size => Scale, Colors => (1 => Black, 3 => Green, 4 => Blue, others => Red), is_Kinematic => is_Kinematic); new_Sprite.Site_is (the_bone_Site); new_Sprite.Spin_is (Inverse (get_Rotation (Self.joint_bind_Matrix (start_Joint)))); new_Sprite.is_Visible (False); Self.anim_joint_site_Offets.insert (the_Bone, Inverse (get_Rotation (Self.joint_inv_bind_Matrix (start_Joint))) * (joint_Sites (start_Joint) - the_bone_Site)); Self.phys_joint_site_Offets.insert (the_Bone, joint_Sites (start_Joint) - the_bone_Site); Self.bone_pose_Transforms .insert (the_Bone, to_transform_Matrix (Rotation => get_Rotation (Self.joint_pose_Transforms (start_Joint)), Translation => the_bone_Site)); end if; Self.bone_Sprites.insert (the_Bone, new_Sprite); declare new_Sprite : constant gel.Sprite.view := gel.Forge.new_box_Sprite (in_World => in_World, Mass => 0.0, Size => (0.02, 0.02, 0.02), Colors => (others => Yellow), is_Kinematic => True); begin Self.joint_Sprites.insert (the_Bone, new_Sprite); end; end create_Bone; procedure create_Bone_for (the_Joint : in visual_Scenes.Node_view; Parent : in bone_Id) is use bone_id_Maps_of_details; which_Joint : constant scene_joint_Id := the_Joint.Id; child_Joints : constant visual_Scenes.Nodes := the_Joint.Children; the_bone_Details : Rig.bone_Details; bone_Length : Real; end_Point : Vector_3; new_Joint : gel.Joint.view; function guessed_bone_Length return Real is begin if child_Joints'Length = 0 then return prior_bone_Length; else if which_Joint = Self.root_Joint.Name then return Distance (joint_Sites.Element (which_Joint), joint_Sites.Element (child_Joints (child_Joints'First).Id)); else return Distance (joint_Sites.Element (which_Joint), joint_Sites.Element (child_Joints (child_Joints'Last).Id)); end if; end if; end guessed_bone_Length; begin if bone_Details.contains (which_Joint) then the_bone_Details := bone_Details.Element (which_Joint); if the_bone_Details.Length = Unspecified then bone_Length := guessed_bone_Length; else bone_Length := the_bone_Details.Length; end if; else bone_Length := guessed_bone_Length; end if; end_Point := joint_Sites.Element (which_Joint) + (0.0, bone_Length, 0.0) * get_Rotation (Self.joint_bind_Matrix (which_Joint)); prior_bone_Length := bone_Length; Self.joint_Parent.insert (which_Joint, Parent); create_Bone (which_Joint, which_Joint, end_Point, (the_bone_Details.width_Factor * bone_Length, bone_Length * 0.90, the_bone_Details.depth_Factor * bone_Length), 1.0); if Parent /= (+"") then Self.Sprite (Parent).attach_via_ball_Socket (Self.bone_Sprites (which_Joint), pivot_Axis => x_Rotation_from (0.0), pivot_Anchor => joint_Sites.Element (which_Joint), pitch_Limits => the_bone_Details.pitch_Limits, yaw_Limits => the_bone_Details. yaw_Limits, roll_Limits => the_bone_Details. roll_Limits, new_Joint => new_Joint); Self.Joints.insert (to_gel_joint_Id (Parent, which_Joint), new_Joint); end if; for i in child_Joints'Range loop create_Bone_for (child_Joints (i), -- Recurse over children. parent => which_Joint); end loop; end create_Bone_for; use collada.Library.Controllers; global_transform_Slot : Positive := 1; begin Self.root_Joint := the_root_Joint; -- Remember our root joint. Self.Model := Model.all'unchecked_Access; -- Remember our model. --- Parse Controllers. -- -- Set the bind shape matrix. -- Self.bind_shape_Matrix := Transpose (bind_shape_Matrix_of (the_Document.Libraries.Controllers.Contents (1).Skin)); -- Set the joint slots. -- declare the_Skin : constant Controllers.Skin := the_Document.Libraries.Controllers.Contents (1).Skin; the_joint_Names : constant collada.Text_array := joint_Names_of (the_Skin); begin for i in 1 .. Integer (the_joint_Names'Length) loop Self.program_Parameters.joint_Map_of_slot.insert (the_joint_Names (i), i); end loop; end; -- Set the inverse bind matrices for all joints. -- declare the_Skin : constant Controllers.Skin := the_Document.Libraries.Controllers.Contents (1).Skin; the_bind_Poses : constant collada.Matrix_4x4_array := bind_Poses_of (the_Skin); begin for i in 1 .. Integer (the_bind_Poses'Length) loop Self.joint_inv_bind_Matrices .append (Transpose (the_bind_Poses (i))); -- Transpose corrects for collada column vectors. Self.program_Parameters.bone_Transforms.append (Identity_4x4); end loop; end; --- Parse Visual Scene. -- Self.define_global_Transform_for (the_root_Joint, -- Determine all joint transforms, recursively. Slot => global_transform_Slot); set_Site_for (the_root_Joint); create_Bone_for (the_root_Joint, Parent => +""); -- Create all other bones, recursively. --- Parse the Collada animations file. -- declare use collada.Library.Animations; the_Animations : constant access Animation_array := the_Document.Libraries.Animations.Contents; begin if the_Animations /= null then for Each in the_Animations'Range loop declare the_Animation : constant animations.Animation := the_Animations (Each); the_Inputs : access collada.float_Array := Inputs_of (the_Animation); procedure common_setup (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is default_scene_Joint : rig.scene_Joint; default_Channel : animation_Channel; begin Self.Channels.insert (Channel, default_Channel); Self.Channels (Channel).Target := Self.scene_Joints (scene_Joint).Node.fetch_Transform (Sid); Self.Channels (Channel).target_Joint := scene_Joint; Self.Channels (Channel).Times := Inputs_of (the_Animation); Self.Channels (Channel).Values := Outputs_of (the_Animation); end common_setup; procedure setup_Rotation (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For angle interpolation during 'rotation' animation. -- Self.Channels (Channel).initial_Angle := Self.Channels (Channel).Values (1); Self.Channels (Channel).current_Angle := Self.Channels (Channel).initial_Angle; end setup_Rotation; pragma Unreferenced (setup_Rotation); procedure setup_Location (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For location interpolation during 'translation' animation. -- Self.Channels (Channel).current_Site := (Self.Channels (Channel).Values (1), Self.Channels (Channel).Values (2), Self.Channels (Channel).Values (3)); Self.Channels (Channel).initial_Site := Self.Channels (Channel).current_Site; end setup_Location; pragma Unreferenced (setup_Location); procedure setup_Location_x (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For matrix interpolation during 'full_transform' animation. -- Self.Channels (Channel).Transforms := new Transforms (1 .. Self.Channels (Channel).Values'Length); for i in Self.Channels (Channel).Transforms'Range loop declare the_X_Value : constant Real := Self.Channels (Channel).Values (i); begin Self.Channels (Channel).Transforms (i) := (Rotation => to_Quaternion (Identity_3x3), Translation => (the_X_Value, 0.0, 0.0)); end; end loop; Self.Channels (Channel).initial_Transform := Self.Channels (Channel).Transforms (1); Self.Channels (Channel).current_Transform := Self.Channels (Channel).initial_Transform; Self.Channels (Channel).current_Site := Self.Channels (Channel).initial_Transform.Translation; Self.Channels (Channel).initial_Site := Self.Channels (Channel).current_Site; end setup_Location_x; procedure setup_Location_y (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For matrix interpolation during 'full_transform' animation. -- Self.Channels (Channel).Transforms := new Transforms (1 .. Self.Channels (Channel).Values'Length); for i in Self.Channels (Channel).Transforms'Range loop declare the_Y_Value : constant Real := Self.Channels (Channel).Values (i); begin Self.Channels (Channel).Transforms (i) := (rotation => to_Quaternion (Identity_3x3), translation => (0.0, the_Y_Value, 0.0)); end; end loop; Self.Channels (Channel).initial_Transform := Self.Channels (Channel).Transforms (1); Self.Channels (Channel).current_Transform := Self.Channels (Channel).initial_Transform; Self.Channels (Channel).current_Site := Self.Channels (Channel).initial_Transform.Translation; Self.Channels (Channel).initial_Site := Self.Channels (Channel).current_Site; end setup_Location_y; procedure setup_Location_z (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For matrix interpolation during 'full_transform' animation. -- Self.Channels (Channel).Transforms := new Transforms (1 .. Self.Channels (Channel).Values'Length); for i in Self.Channels (Channel).Transforms'Range loop declare the_Z_Value : constant Real := Self.Channels (Channel).Values (i); begin Self.Channels (Channel).Transforms (i) := (rotation => to_Quaternion (Identity_3x3), translation => (0.0, 0.0, the_Z_Value)); end; end loop; Self.Channels (Channel).initial_Transform := Self.Channels (Channel).Transforms (1); Self.Channels (Channel).current_Transform := Self.Channels (Channel).initial_Transform; Self.Channels (Channel).current_Site := Self.Channels (Channel).initial_Transform.Translation; Self.Channels (Channel).initial_Site := Self.Channels (Channel).current_Site; end setup_Location_z; procedure setup_full_Transform (Channel : in channel_Id; scene_Joint : in scene_Joint_Id; Sid : in String) is begin common_setup (Channel, scene_Joint, Sid); -- For matrix interpolation during 'full_transform' animation. -- Self.Channels (Channel).Transforms := new Transforms (1 .. Collada.matrix_Count (Self.Channels (Channel).Values.all)); for i in Self.Channels (Channel).Transforms'Range loop declare the_Matrix : constant math.Matrix_4x4 := Transpose (Collada.get_Matrix (Self.Channels (Channel).Values.all, which => i)); begin Self.Channels (Channel).Transforms (i) := (Rotation => to_Quaternion (get_Rotation (the_Matrix)), Translation => get_Translation (the_Matrix)); end; end loop; Self.Channels (Channel).initial_Transform := Self.Channels (Channel).Transforms (1); Self.Channels (Channel).current_Transform := Self.Channels (Channel).initial_Transform; Self.Channels (Channel).current_Site := Self.Channels (Channel).initial_Transform.Translation; Self.Channels (Channel).initial_Site := Self.Channels (Channel).current_Site; end setup_full_Transform; function Index (Source : in unbounded_String; Pattern : in String; Going : in Direction := Forward; Mapping : in Maps.character_Mapping := ada.Strings.Maps.Identity) return Natural renames ada.Strings.unbounded.Index; begin if Index (the_Animation.Channel.Target, "hips/transform") /= 0 then setup_full_Transform (+"hips", +"hips", "transform"); elsif Index (the_Animation.Channel.Target, "thigh_L/transform") /= 0 then setup_full_Transform (+"thigh_L", +"thigh_L", "transform"); elsif Index (the_Animation.Channel.Target, "shin_L/transform") /= 0 then setup_full_Transform (+"shin_L", +"shin_L", "transform"); elsif Index (the_Animation.Channel.Target, "foot_L/transform") /= 0 then setup_full_Transform (+"foot_L", +"foot_L", "transform"); elsif Index (the_Animation.Channel.Target, "toe_L/transform") /= 0 then setup_full_Transform (+"toe_L", +"toe_L", "transform"); elsif Index (the_Animation.Channel.Target, "thigh_R/transform") /= 0 then setup_full_Transform (+"thigh_R", +"thigh_R", "transform"); elsif Index (the_Animation.Channel.Target, "shin_R/transform") /= 0 then setup_full_Transform (+"shin_R", +"shin_R", "transform"); elsif Index (the_Animation.Channel.Target, "foot_R/transform") /= 0 then setup_full_Transform (+"foot_R", +"foot_R", "transform"); elsif Index (the_Animation.Channel.Target, "toe_R/transform") /= 0 then setup_full_Transform (+"toe_R", +"toe_R", "transform"); elsif Index (the_Animation.Channel.Target, "spine/transform") /= 0 then setup_full_Transform (+"spine", +"spine", "transform"); elsif Index (the_Animation.Channel.Target, "chest/transform") /= 0 then setup_full_Transform (+"chest", +"chest", "transform"); elsif Index (the_Animation.Channel.Target, "clavicle_R/transform") /= 0 then setup_full_Transform (+"clavicle_R", +"clavicle_R", "transform"); elsif Index (the_Animation.Channel.Target, "upper_arm_R/transform") /= 0 then setup_full_Transform (+"upper_arm_R", +"upper_arm_R", "transform"); elsif Index (the_Animation.Channel.Target, "forearm_R/transform") /= 0 then setup_full_Transform (+"forearm_R", +"forearm_R", "transform"); elsif Index (the_Animation.Channel.Target, "hand_R/transform") /= 0 then setup_full_Transform (+"hand_R", +"hand_R", "transform"); elsif Index (the_Animation.Channel.Target, "thumb_02_R/transform") /= 0 then setup_full_Transform (+"thumb_02_R", +"thumb_02_R", "transform"); elsif Index (the_Animation.Channel.Target, "thumb_03_R/transform") /= 0 then setup_full_Transform (+"thumb_03_R", +"thumb_03_R", "transform"); elsif Index (the_Animation.Channel.Target, "f_ring_01_R/transform") /= 0 then setup_full_Transform (+"f_ring_01_R", +"f_ring_01_R", "transform"); elsif Index (the_Animation.Channel.Target, "f_index_01_R/transform") /= 0 then setup_full_Transform (+"f_index_01_R", +"f_index_01_R", "transform"); elsif Index (the_Animation.Channel.Target, "clavicle_L/transform") /= 0 then setup_full_Transform (+"clavicle_L", +"clavicle_L", "transform"); elsif Index (the_Animation.Channel.Target, "upper_arm_L/transform") /= 0 then setup_full_Transform (+"upper_arm_L", +"upper_arm_L", "transform"); elsif Index (the_Animation.Channel.Target, "forearm_L/transform") /= 0 then setup_full_Transform (+"forearm_L", +"forearm_L", "transform"); elsif Index (the_Animation.Channel.Target, "hand_L/transform") /= 0 then setup_full_Transform (+"hand_L", +"hand_L", "transform"); elsif Index (the_Animation.Channel.Target, "thumb_02_L/transform") /= 0 then setup_full_Transform (+"thumb_02_L", +"thumb_02_L", "transform"); elsif Index (the_Animation.Channel.Target, "thumb_03_L/transform") /= 0 then setup_full_Transform (+"thumb_03_L", +"thumb_03_L", "transform"); elsif Index (the_Animation.Channel.Target, "f_ring_01_L/transform") /= 0 then setup_full_Transform (+"f_ring_01_L", +"f_ring_01_L", "transform"); elsif Index (the_Animation.Channel.Target, "f_index_01_L/transform") /= 0 then setup_full_Transform (+"f_index_01_L", +"f_index_01_L", "transform"); elsif Index (the_Animation.Channel.Target, "neck/transform") /= 0 then setup_full_Transform (+"neck", +"neck", "transform"); elsif Index (the_Animation.Channel.Target, "head/transform") /= 0 then setup_full_Transform (+"head", +"head", "transform"); elsif Index (the_Animation.Channel.Target, "jaw/transform") /= 0 then setup_full_Transform (+"jaw", +"jaw", "transform"); elsif Index (the_Animation.Channel.Target, "eye_R/transform") /= 0 then setup_full_Transform (+"eye_R", +"eye_R", "transform"); elsif Index (the_Animation.Channel.Target, "eye_L/transform") /= 0 then setup_full_Transform (+"eye_L", +"eye_L", "transform"); elsif Index (the_Animation.Channel.Target, "stride_bone/location.X") /= 0 then -- setup_Location_x (+"stride_bone_x", +"stride_bone", "x"); setup_Location_x (+"stride_bone_x", +"human", "x"); elsif Index (the_Animation.Channel.Target, "stride_bone/location.Y") /= 0 then -- setup_Location_y (+"stride_bone_y", +"stride_bone", "y"); setup_Location_y (+"stride_bone_y", +"human", "y"); elsif Index (the_Animation.Channel.Target, "stride_bone/location.Z") /= 0 then -- setup_Location_z (+"stride_bone_z", +"stride_bone", "z"); setup_Location_z (+"stride_bone_z", +"human", "z"); else raise constraint_Error with +the_Animation.Channel.Target & " not handled"; end if; end; end loop; end if; end; end define; procedure enable_Graphics (Self : in out Item) is begin Self .program_Parameters.Program_is (opengl.Program.view (opengl.Geometry.lit_colored_textured_skinned.Program)); Self.skin_Sprite.program_Parameters_are (Self.program_Parameters'unchecked_Access); end enable_Graphics; function Joints (Self : in Item) return gel_joint_id_Map_of_gel_Joint is begin return Self.Joints; end Joints; function joint_inv_bind_Matrices (Self : in Item'Class) return inverse_bind_matrix_Vector is begin return Self.joint_inv_bind_Matrices; end joint_inv_bind_Matrices; procedure joint_inv_bind_Matrices_are (Self : in out Item'Class; Now : in inverse_bind_matrix_Vector) is begin Self.joint_inv_bind_Matrices := Now; end joint_inv_bind_Matrices_are; function joint_site_Offets (Self : in Item'Class) return joint_Id_Map_of_bone_site_offset is begin return Self.phys_joint_site_Offets; end joint_site_Offets; -------------- --- Attributes -- procedure Site_is (Self :in out Item; Now : in Vector_3) is begin Self.base_Sprite.move (to_Site => Now); Self.overall_Site := Now; end Site_is; procedure Spin_is (Self :in out Item; Now : in Matrix_3x3) is begin Self.base_Sprite.rotate (to_Spin => Now); end Spin_is; function Sprite (Self : in Item'Class; Bone : in bone_Id) return gel.Sprite.view is begin return Self.bone_Sprites (Bone); end Sprite; function base_Sprite (Self : in Item'Class) return gel.Sprite.view is begin return Self.bone_Sprites.Element (Self.root_Joint.Name); end base_Sprite; function skin_Sprite (Self : in Item'Class) return gel.Sprite.view is begin return Self.skin_Sprite; end skin_Sprite; function bone_Sprites (Self : in Item) return bone_id_Map_of_sprite is begin return Self.bone_Sprites; end bone_Sprites; procedure set_GL_program_Parameters (Self : in out Item'Class; for_Bone : in controller_joint_Id; To : in Matrix_4x4) is use gel.Conversions; bone_Slot : constant Positive := Self.program_Parameters.joint_Map_of_slot.Element (for_Bone); begin Self.program_Parameters.bone_Transforms.replace_Element (bone_Slot, to_GL (To)); end set_GL_program_Parameters; procedure animation_Transforms_are (Self : in out Item'Class; Now : in bone_id_Map_of_transform) is begin Self.animation_Transforms := Now; end animation_Transforms_are; procedure motion_Mode_is (Self : in out Item; Now : in motion_Mode) is begin Self.Mode := Now; end motion_Mode_is; -------------- --- Operations -- procedure evolve (Self : in out Item'Class; world_Age : in Duration) is function get_root_Transform return Matrix_4x4 is begin case Self.Mode is when Dynamics => return Self.base_Sprite.Transform; when Animation => declare the_Transform : Matrix_4x4; begin set_Rotation (the_Transform, x_Rotation_from (to_Radians (0.0))); set_Translation (the_Transform, -get_Translation (Inverse (Self.joint_pose_Transforms (Self.root_Joint.Name)))); return the_Transform; end; end case; end get_root_Transform; root_Transform : constant Matrix_4x4 := get_root_Transform; inv_root_Transform : constant Matrix_4x4 := Inverse (root_Transform); function joint_Transform_for (the_collada_Joint : in controller_joint_Id) return Matrix_4x4 is begin case Self.Mode is when Dynamics => declare the_bone_Transform : constant Matrix_4x4 := Self.Sprite (the_collada_Joint).Transform; the_joint_site_Offset : Vector_3 := Self.phys_joint_site_Offets (the_collada_Joint); the_joint_Transform : Matrix_4x4; begin the_joint_site_Offset := the_joint_site_Offset * get_Rotation (Self.joint_inv_bind_Matrix (the_collada_Joint)) * get_Rotation (the_bone_Transform); set_Translation (the_joint_Transform, get_Translation (the_bone_Transform) + the_joint_site_Offset); set_Rotation (the_joint_Transform, get_Rotation (the_bone_Transform)); Self.joint_Sprites (the_collada_Joint).all.Site_is (get_Translation (the_joint_Transform)); return the_joint_Transform; end; when Animation => Self.joint_Sprites (the_collada_Joint).all.Site_is ( get_Translation (Self.scene_Joints (the_collada_Joint).Transform)); Self.joint_Sprites (the_collada_Joint).all.Spin_is (Inverse (get_Rotation (Self.scene_Joints (the_collada_Joint).Transform))); return Self.scene_Joints (the_collada_Joint).Transform; end case; end joint_Transform_for; procedure set_Transform_for (the_Bone : in controller_joint_Id) is the_Slot : constant Positive := Self.program_Parameters.joint_Map_of_slot (the_Bone); begin Self.set_GL_program_Parameters (for_Bone => the_Bone, To => Self.bind_shape_Matrix * Self.joint_inv_bind_Matrices.Element (the_Slot) * joint_Transform_for (the_Bone) * inv_root_Transform); end set_Transform_for; procedure set_proxy_Transform_for (the_Bone : in controller_joint_Id; the_Proxy : in controller_joint_Id) is the_Slot : constant Positive := Self.program_Parameters.joint_Map_of_slot (the_Proxy); begin Self.set_GL_program_Parameters (for_bone => the_Bone, to => Self.bind_shape_Matrix * Self.joint_inv_bind_Matrices .Element (the_Slot) * joint_Transform_for (the_Proxy) * inv_root_Transform); end set_proxy_Transform_for; pragma Unreferenced (set_proxy_Transform_for); use joint_Id_Maps_of_bone_site_offset; Cursor : joint_Id_Maps_of_bone_site_offset.Cursor := Self.phys_joint_site_Offets.First; begin if Self.Mode = Animation then Self.animate (world_Age); end if; while has_Element (Cursor) loop if Self.program_Parameters.joint_Map_of_slot.Contains (Key (Cursor)) then set_Transform_for (Key (Cursor)); -- Updates gl skin program params. end if; next (Cursor); end loop; end evolve; procedure assume_Pose (Self : in out Item) is use bone_id_Maps_of_transform; the_Bone : gel.Sprite.view; Cursor : bone_id_Maps_of_transform.Cursor := Self.bone_pose_Transforms.First; begin while has_Element (Cursor) loop the_Bone := Self.bone_Sprites (Key (Cursor)); the_Bone.Transform_is (Element (Cursor)); next (Cursor); end loop; end assume_Pose; function Parent_of (Self : in Item; the_Bone : in bone_Id) return bone_Id is begin if Self.joint_Parent.Contains (the_Bone) then return Self.joint_Parent.Element (the_Bone); else return null_Id; end if; end Parent_of; function joint_site_Offet (Self : in Item; for_Bone : in bone_Id) return math.Vector_3 is begin return Self.phys_joint_site_Offets.Element (for_Bone); end joint_site_Offet; function joint_inv_bind_Matrix (Self : in Item; for_Bone : in bone_Id) return math.Matrix_4x4 is use ada.Strings.unbounded; begin if for_Bone = Self.root_Joint.Name then return math.Identity_4x4; else return Self.joint_inv_bind_Matrices.Element (Self.program_Parameters.joint_Map_of_slot.Element (for_Bone)); end if; end joint_inv_bind_Matrix; function joint_bind_Matrix (Self : in Item; for_Bone : in bone_Id) return Matrix_4x4 is begin return Inverse (Self.joint_inv_bind_Matrix (for_Bone)); end joint_bind_Matrix; ------------- --- Animation -- procedure animate (Self : in out Item; world_Age : in Duration) is Now : Duration; Elapsed : Duration; procedure update_rotation_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id; for_Axis : in axis_Kind) is the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : math.Index renames the_Channel.Cursor; function Reduced (Angle : in Real) return Real -- TODO: Use Degrees type. is begin if Angle > 180.0 then return -360.0 + Angle; elsif Angle < -180.0 then return 360.0 + Angle; else return Angle; end if; end Reduced; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.interp_Delta := Reduced (the_Channel.Values (Cursor) - the_Channel.current_Angle); else the_Channel.interp_Delta := Reduced (the_Channel.Values (Cursor) - the_Channel.current_Angle) / (the_Channel.Times (Cursor)); end if; else the_Channel.interp_Delta := Reduced (the_Channel.Values (Cursor) - the_Channel.current_Angle) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.interp_Delta := the_Channel.interp_Delta / 60.0; -- 60.0 is frames/sec. end if; end if; if Elapsed < Duration (the_Channel.Times (the_Channel.Times'Last)) then the_Channel.current_Angle := Reduced ( the_Channel.current_Angle + the_Channel.interp_Delta); Self.set_rotation_Angle (for_Joint, for_Axis, To => to_Radians (Degrees (the_Channel.current_Angle))); end if; end update_rotation_Animation; pragma Unreferenced (update_rotation_Animation); procedure update_location_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id) is pragma Unreferenced (for_Joint); the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : Index renames the_Channel.Cursor; Elapsed : constant Duration := Now - Self.start_Time; function site_X return Real is begin return the_Channel.Values ((Cursor - 1) * 3 + 1); end site_X; function site_Y return Real is begin return the_Channel.Values ((Cursor - 1) * 3 + 2); end site_Y; function site_Z return Real is begin return the_Channel.Values ((Cursor - 1) * 3 + 3); end site_Z; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.site_interp_Delta (1) := site_X - the_Channel.current_Site (1); the_Channel.site_interp_Delta (2) := site_Y - the_Channel.current_Site (2); the_Channel.site_interp_Delta (3) := site_Z - the_Channel.current_Site (3); else the_Channel.site_interp_Delta (1) := (site_X - the_Channel.current_Site (1)) / (the_Channel.Times (Cursor)); the_Channel.site_interp_Delta (2) := (site_Y - the_Channel.current_Site (2)) / (the_Channel.Times (Cursor)); the_Channel.site_interp_Delta (3) := (site_Z - the_Channel.current_Site (3)) / (the_Channel.Times (Cursor)); end if; else the_Channel.site_interp_Delta (1) := (site_X - the_Channel.current_Site (1)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); the_Channel.site_interp_Delta (2) := (site_Y - the_Channel.current_Site (2)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); the_Channel.site_interp_Delta (3) := (site_Z - the_Channel.current_Site (3)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.site_interp_Delta (1) := the_Channel.site_interp_Delta (1) / 60.0; -- 60.0 is frames/sec. the_Channel.site_interp_Delta (2) := the_Channel.site_interp_Delta (2) / 60.0; -- the_Channel.site_interp_Delta (3) := the_Channel.site_interp_Delta (3) / 60.0; -- end if; Self.set_Location (the_Channel.target_Joint, to => the_Channel.current_Site); the_Channel.current_Site (1) := the_Channel.current_Site (1) + the_Channel.site_interp_Delta (1); the_Channel.current_Site (2) := the_Channel.current_Site (2) + the_Channel.site_interp_Delta (2); the_Channel.current_Site (3) := the_Channel.current_Site (3) + the_Channel.site_interp_Delta (3); end if; end update_location_Animation; pragma Unreferenced (update_location_Animation); procedure update_location_X_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id) is pragma Unreferenced (for_Joint); the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : Index renames the_Channel.Cursor; Elapsed : constant Duration := Now - Self.start_Time; function site_X return Real is begin return the_Channel.Values (Cursor); end site_X; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.site_interp_Delta (1) := site_X - the_Channel.current_Site (1); else the_Channel.site_interp_Delta (1) := (site_X - the_Channel.current_Site (1)) / (the_Channel.Times (Cursor)); end if; else the_Channel.site_interp_Delta (1) := (site_X - the_Channel.current_Site (1)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.site_interp_Delta (1) := the_Channel.site_interp_Delta (1) / 60.0; -- 60.0 is frames/sec. end if; Self.set_Location_x (the_Channel.target_Joint, To => the_Channel.current_Site (1)); the_Channel.current_Site (1) := the_Channel.current_Site (1) + the_Channel.site_interp_Delta (1); end if; end update_location_X_Animation; procedure update_location_Y_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id) is pragma Unreferenced (for_Joint); the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : Index renames the_Channel.Cursor; Elapsed : constant Duration := Now - Self.start_Time; function site_Y return math.Real is begin return the_Channel.Values (Cursor); end site_Y; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.site_interp_Delta (2) := site_Y - the_Channel.current_Site (2); else the_Channel.site_interp_Delta (2) := (site_Y - the_Channel.current_Site (2)) / (the_Channel.Times (Cursor)); end if; else the_Channel.site_interp_Delta (2) := (site_Y - the_Channel.current_Site (2)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.site_interp_Delta (2) := the_Channel.site_interp_Delta (2) / 60.0; -- 60.0 is frames/sec end if; Self.set_Location_y (the_Channel.target_Joint, To => the_Channel.current_Site (2)); the_Channel.current_Site (2) := the_Channel.current_Site (2) + the_Channel.site_interp_Delta (2); end if; end update_location_Y_Animation; procedure update_location_Z_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id) is pragma Unreferenced (for_Joint); the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : math.Index renames the_Channel.Cursor; Elapsed : constant Duration := Now - Self.start_Time; function site_Z return math.Real is begin return the_Channel.Values (Cursor); end site_Z; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.site_interp_Delta (3) := site_Z - the_Channel.current_Site (3); else the_Channel.site_interp_Delta (3) := (site_Z - the_Channel.current_Site (3)) / (the_Channel.Times (Cursor)); end if; else the_Channel.site_interp_Delta (3) := (site_Z - the_Channel.current_Site (3)) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.site_interp_Delta (3) := the_Channel.site_interp_Delta (3) / 60.0; -- 60.0 is frames/sec end if; Self.set_Location_z (the_Channel.target_Joint, To => the_Channel.current_Site (3)); the_Channel.current_Site (3) := the_Channel.current_Site (3) + the_Channel.site_interp_Delta (3); end if; end update_location_Z_Animation; procedure update_full_transform_Animation (for_Channel : in channel_Id; for_Joint : in scene_joint_Id) is pragma Unreferenced (for_Joint); the_Channel : animation_Channel renames Self.Channels (for_Channel); Cursor : Index renames the_Channel.Cursor; Cursor_updated : Boolean := False; new_Transform : Matrix_4x4 := Identity_4x4; begin if Cursor = the_Channel.Times'Last then Cursor := 0; Self.start_Time := Now; end if; -- Rotation -- declare Initial : Transform; begin if Cursor < the_Channel.Times'Last then if Cursor = 0 or else Elapsed > Duration (the_Channel.Times (Cursor)) then Cursor := Cursor + 1; Cursor_updated := True; if Cursor = 1 then Initial := the_Channel.current_Transform; if the_Channel.Times (Cursor) = 0.0 then the_Channel.Transform_interp_Delta := 1.0 / 60.0; else the_Channel.Transform_interp_Delta := the_Channel.Times (Cursor); end if; else Initial := the_Channel.Transforms (Cursor - 1); the_Channel.Transform_interp_Delta := the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1); end if; the_Channel.current_Transform := the_Channel.Transforms (Cursor); the_Channel.Transform_interp_Delta := 1.0 / (the_Channel.Transform_interp_Delta * 60.0); -- 60.0 is frames/sec. the_Channel.slerp_Time := 0.0; else if Cursor > 1 then Initial := the_Channel.Transforms (Cursor - 1); else Initial := the_Channel.Transforms (Cursor); end if; end if; else Initial := the_Channel.Transforms (1); end if; if Elapsed < Duration (the_Channel.Times (the_Channel.Times'Last)) then set_Rotation (new_Transform, to_Matrix (Interpolated (Initial.Rotation, the_Channel.current_Transform.Rotation, Percent => to_Percentage (the_Channel.slerp_Time)))); the_Channel.slerp_Time := the_Channel.slerp_Time + the_Channel.Transform_interp_Delta; end if; end; -- Location -- declare desired_Site : constant Vector_3 := the_Channel.Transforms (Cursor).Translation; begin if Cursor < the_Channel.Times'Last then if Cursor_updated then if Cursor = 1 then if the_Channel.Times (Cursor) = 0.0 then the_Channel.site_interp_Delta := desired_Site - the_Channel.current_Site; else the_Channel.site_interp_Delta := (desired_Site - the_Channel.current_Site) / (the_Channel.Times (Cursor)); end if; else the_Channel.site_interp_Delta := (desired_Site - the_Channel.current_Site) / (the_Channel.Times (Cursor) - the_Channel.Times (Cursor - 1)); end if; the_Channel.site_interp_Delta := the_Channel.site_interp_Delta / 60.0; -- 60.0 is frames/sec. end if; the_Channel.current_Site := the_Channel.current_Site + the_Channel.site_interp_Delta; set_Translation (new_Transform, To => the_Channel.current_Site); end if; end; -- Scale -- -- (TODO) -- Store the new transform. -- Self.set_Transform (the_Channel.target_Joint, To => Transpose (new_Transform)); -- Transpose to convert to collada column vectors. end update_full_transform_Animation; begin Now := world_Age; if Self.start_Time = 0.0 then Self.start_Time := Now; end if; Elapsed := Now - Self.start_Time; declare use channel_id_Maps_of_animation_Channel, ada.Strings.Unbounded; Cursor : channel_id_Maps_of_animation_Channel.Cursor := Self.Channels.First; begin while has_Element (Cursor) loop if Key (Cursor) = (+"stride_bone_x") then update_location_X_Animation (Key (Cursor), Key (Cursor)); elsif Key (Cursor) = (+"stride_bone_y") then update_location_Y_Animation (Key (Cursor), Key (Cursor)); elsif Key (Cursor) = (+"stride_bone_z") then update_location_Z_Animation (Key (Cursor), Key (Cursor)); else update_full_transform_Animation (Key (Cursor), Key (Cursor)); end if; next (Cursor); end loop; end; Self.update_all_global_Transforms; end animate; procedure reset_Animation (Self : in out Item) is use channel_id_Maps_of_animation_Channel; Cursor : channel_id_Maps_of_animation_Channel.Cursor := Self.Channels.First; the_Channel : animation_Channel; begin Self.start_Time := 0.0; while has_Element (Cursor) loop the_Channel := Element (Cursor); the_Channel.Cursor := 0; the_Channel.current_Angle := the_Channel.initial_Angle; the_Channel.current_Site := the_Channel.initial_Site; the_Channel.interp_Delta := 0.0; Self.Channels.replace_Element (Cursor, the_Channel); next (Cursor); end loop; end reset_Animation; end gel.Rig;