diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index aaa0e0af7..41f4a45c4 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -595,6 +595,7 @@ The scene holds components. Entity handles can be used to retrieve associated co - Entity_GetTransformArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetCameraArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetAnimationArray() : Entity[] result -- returns the array of all entities that have this component type +- Entity_GetAnimationDataArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetMaterialArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetEmitterArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetLightArray() : Entity[] result -- returns the array of all entities that have this component type @@ -609,30 +610,36 @@ The scene holds components. Entity handles can be used to retrieve associated co - Entity_GetSoundArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetColliderArray() : Entity[] result -- returns the array of all entities that have this component type -- Component_RemoveName(Entity entity) : NameComponent? result -- remove the name component of the entity (if exists) -- Component_RemoveLayer(Entity entity) : LayerComponent? result -- remove the layer component of the entity (if exists) -- Component_RemoveTransform(Entity entity) : TransformComponent? result -- remove the transform component of the entity (if exists) -- Component_RemoveCamera(Entity entity) : CameraComponent? result -- remove the camera component of the entity (if exists) -- Component_RemoveAnimation(Entity entity) : AnimationComponent? result -- remove the animation component of the entity (if exists) -- Component_RemoveMaterial(Entity entity) : MaterialComponent? result -- remove the material component of the entity (if exists) -- Component_RemoveEmitter(Entity entity) : EmitterComponent? result -- remove the emitter component of the entity (if exists) -- Component_RemoveLight(Entity entity) : LightComponent? result -- remove the light component of the entity (if exists) -- Component_RemoveObject(Entity entity) : ObjectComponent? result -- remove the object component of the entity (if exists) -- Component_RemoveInverseKinematics(Entity entity) : InverseKinematicsComponent? result -- remove the IK component of the entity (if exists) -- Component_RemoveSpring(Entity entity) : SpringComponent? result -- remove the spring component of the entity (if exists) -- Component_RemoveScript(Entity entity) : ScriptComponent? result -- remove the script component of the entity (if exists) -- Component_RemoveRigidBodyPhysics(Entity entity) : RigidBodyPhysicsComponent? result -- remove the RigidBodyPhysicsComponent of the entity (if exists) -- Component_RemoveSoftBodyPhysics(Entity entity) : SoftBodyPhysicsComponent? result -- remove the SoftBodyPhysicsComponent of the entity (if exists) -- Component_RemoveForceField(Entity entity) : ForceFieldComponent? result -- remove the ForceFieldComponent of the entity (if exists) -- Component_RemoveWeather(Entity entity) : WeatherComponent? result -- remove the WeatherComponent of the entity (if exists) -- Component_RemoveSound(Entity entity) : SoundComponent? result -- remove the SoundComponent of the entity (if exists) -- Component_RemoveCollider(Entity entity) : ColliderComponent? result -- remove the ColliderComponent of the entity (if exists) +- Component_RemoveName(Entity entity) -- remove the name component of the entity (if exists) +- Component_RemoveLayer(Entity entity) -- remove the layer component of the entity (if exists) +- Component_RemoveTransform(Entity entity) -- remove the transform component of the entity (if exists) +- Component_RemoveCamera(Entity entity) -- remove the camera component of the entity (if exists) +- Component_RemoveAnimation(Entity entity) -- remove the animation component of the entity (if exists) +- Component_RemoveAnimationData(Entity entity) +- Component_RemoveMaterial(Entity entity) -- remove the material component of the entity (if exists) +- Component_RemoveEmitter(Entity entity) -- remove the emitter component of the entity (if exists) +- Component_RemoveLight(Entity entity) -- remove the light component of the entity (if exists) +- Component_RemoveObject(Entity entity) -- remove the object component of the entity (if exists) +- Component_RemoveInverseKinematics(Entity entity) -- remove the IK component of the entity (if exists) +- Component_RemoveSpring(Entity entity) -- remove the spring component of the entity (if exists) +- Component_RemoveScript(Entity entity) -- remove the script component of the entity (if exists) +- Component_RemoveRigidBodyPhysics(Entity entity) -- remove the RigidBodyPhysicsComponent of the entity (if exists) +- Component_RemoveSoftBodyPhysics(Entity entity) -- remove the SoftBodyPhysicsComponent of the entity (if exists) +- Component_RemoveForceField(Entity entity) -- remove the ForceFieldComponent of the entity (if exists) +- Component_RemoveWeather(Entity entity) -- remove the WeatherComponent of the entity (if exists) +- Component_RemoveSound(Entity entity) -- remove the SoundComponent of the entity (if exists) +- Component_RemoveCollider(Entity entity) -- remove the ColliderComponent of the entity (if exists) - Component_Attach(Entity entity,parent, opt bool child_already_in_local_space = false) -- attaches entity to parent (adds a hierarchy component to entity). From now on, entity will inherit certain properties from parent, such as transform (entity will move with parent) or layer (entity's layer will be a sublayer of parent's layer). If child_already_in_local_space is false, then child will be transformed into parent's local space, if true, it will be used as-is. - Component_Detach(Entity entity) -- detaches entity from parent (if hierarchycomponent exists for it). Restores entity's original layer, and applies current transformation to entity - Component_DetachChildren(Entity parent) -- detaches all children from parent, as if calling Component_Detach for all of its children - GetBounds() : AABB result -- returns an AABB fully containing objects in the scene. Only valid after scene has been updated. +- GetWeather() : WeatherComponent +- SetWeather(WeatherComponent weather) + + +- RetargetAnimation(Entity dst, src, bool bake_data) : Entity entity -- Retargets an animation from a Humanoid to an other Humanoid such that the new animation will play back on the destination humanoid. dst : destination humanoid that the animation will be fit onto src : the animation to copy, it should already target humanoid bones. bake_data : if true, the retargeted data will be baked into a new animation data. If false, it will reuse the source animation data without creating a new one and retargeting will be applied at runtime on every Update. Returns entity ID of the new animation or INVALID_ENTITY if retargeting was not successful #### NameComponent Holds a string that can more easily identify an entity to humans than an entity ID. @@ -952,7 +959,7 @@ Describes a Rigid Body Physics object. - BoxParams_HalfExtents : Vector - SphereParams_Radius : floatd - CapsuleParams_Radius : float -- CapsuleParams_Reight : float +- CapsuleParams_Height : float - TargetMeshLOD : int
@@ -1023,6 +1030,12 @@ Describes a Collider object. - GetCapsule() : Capsule - GetSphere() : Sphere +[outer] ColliderShape = { + Sphere = 0, + Capsule = 1, + Plane = 2, +} + #### ExpressionComponent - FindExpressionID(string name) : int -- Find an expression within the ExpressionComponent by name - SetWeight(int id, float weight) -- Set expression weight by ID. The ID can be a non-preset expression. Use FindExpressionID() to retrieve non-preset expression IDs diff --git a/Content/scripts/character_controller/assets/animations.wiscene b/Content/scripts/character_controller/assets/animations.wiscene new file mode 100644 index 000000000..61718c8e9 Binary files /dev/null and b/Content/scripts/character_controller/assets/animations.wiscene differ diff --git a/Content/scripts/character_controller/assets/character.wiscene b/Content/scripts/character_controller/assets/character.wiscene index 8220bc4db..d5fb895a4 100644 Binary files a/Content/scripts/character_controller/assets/character.wiscene and b/Content/scripts/character_controller/assets/character.wiscene differ diff --git a/Content/scripts/character_controller/character_controller.lua b/Content/scripts/character_controller/character_controller.lua index 45b34c8a5..5012c6f85 100644 --- a/Content/scripts/character_controller/character_controller.lua +++ b/Content/scripts/character_controller/character_controller.lua @@ -39,6 +39,24 @@ local States = { DANCE = "dance" } + +local animations = {} +local function LoadAnimations(model_name) + local anim_scene = Scene() + LoadModel(anim_scene, model_name) + animations = { + IDLE = anim_scene.Entity_FindByName("idle"), + WALK = anim_scene.Entity_FindByName("walk"), + JOG = anim_scene.Entity_FindByName("jog"), + RUN = anim_scene.Entity_FindByName("run"), + JUMP = anim_scene.Entity_FindByName("jump"), + SWIM_IDLE = anim_scene.Entity_FindByName("swim_idle"), + SWIM = anim_scene.Entity_FindByName("swim"), + DANCE = anim_scene.Entity_FindByName("dance") + } + scene.Merge(anim_scene) +end + local character_capsules = {} local function Character(model_name, start_position, face, controllable) @@ -74,7 +92,9 @@ local function Character(model_name, start_position, face, controllable) scale = Vector(1, 1, 1), rotation = Vector(0,math.pi,0), start_position = Vector(0, 1, 0), + position = Vector(), controllable = true, + root_bone_offset = 0, patrol_waypoints = {}, patrol_next = 0, @@ -91,41 +111,71 @@ local function Character(model_name, start_position, face, controllable) else self.layerMask = Layers.NPC end - local character_scene = Scene() - self.model = LoadModel(character_scene, model_name) - local layer = character_scene.Component_GetLayer(self.model) + self.model = LoadModel(model_name) + local layer = scene.Component_GetLayer(self.model) layer.SetLayerMask(self.layerMask) self.state = States.IDLE self.state_prev = self.state + + for i,entity in ipairs(scene.Entity_GetHumanoidArray()) do + if scene.Entity_IsDescendant(entity, self.model) then + self.humanoid = entity + local humanoid = scene.Component_GetHumanoid(self.humanoid) + humanoid.SetLookAtEnabled(false) + self.neck = humanoid.GetBoneEntity(HumanoidBone.Neck) + self.head = humanoid.GetBoneEntity(HumanoidBone.Head) + self.left_hand = humanoid.GetBoneEntity(HumanoidBone.LeftHand) + self.right_hand = humanoid.GetBoneEntity(HumanoidBone.RightHand) + self.left_foot = humanoid.GetBoneEntity(HumanoidBone.LeftFoot) + self.right_foot = humanoid.GetBoneEntity(HumanoidBone.RightFoot) + + -- Create a base capsule collider if it's not yet configured for character: + -- It will be used for movement logic and GPU collision effects + if scene.Component_GetCollider(entity) == nil then + local collider = scene.Component_CreateCollider(entity) + collider.SetCPUEnabled(false) + collider.SetGPUEnabled(true) + collider.Shape = ColliderShape.Capsule + collider.Radius = 0.3 + collider.Offset = Vector(0, collider.Radius, 0) + collider.Tail = Vector(0, 1.4, 0) + local head_transform = scene.Component_GetTransform(self.head) + if head_transform ~= nil then + collider.Tail = head_transform.GetPosition() + end + end + self.collider = entity + + break + end + end + for i,entity in ipairs(scene.Entity_GetExpressionArray()) do + if scene.Entity_IsDescendant(entity, self.model) then + self.expression = entity + self.happy = 0 + break + end + end + + self.root = scene.Entity_FindByName("Root", self.model) - for i,entity in ipairs(character_scene.Entity_GetAnimationArray()) do - table.insert(self.all_anims, entity) + self.idle_anim = scene.RetargetAnimation(self.humanoid, animations.IDLE, false) + self.walk_anim = scene.RetargetAnimation(self.humanoid, animations.WALK, false) + self.jog_anim = scene.RetargetAnimation(self.humanoid, animations.JOG, false) + self.run_anim = scene.RetargetAnimation(self.humanoid, animations.RUN, false) + self.jump_anim = scene.RetargetAnimation(self.humanoid, animations.JUMP, false) + self.swim_idle_anim = scene.RetargetAnimation(self.humanoid, animations.SWIM_IDLE, false) + self.swim_anim = scene.RetargetAnimation(self.humanoid, animations.SWIM, false) + self.dance_anim = scene.RetargetAnimation(self.humanoid, animations.DANCE, false) + + for i,entity in ipairs(scene.Entity_GetAnimationArray()) do + if scene.Entity_IsDescendant(entity, self.model) then + table.insert(self.all_anims, entity) + end end - self.idle_anim = character_scene.Entity_FindByName("idle") - self.walk_anim = character_scene.Entity_FindByName("walk") - self.jog_anim = character_scene.Entity_FindByName("jog") - self.run_anim = character_scene.Entity_FindByName("run") - self.jump_anim = character_scene.Entity_FindByName("jump") - self.swim_idle_anim = character_scene.Entity_FindByName("swim_idle") - self.swim_anim = character_scene.Entity_FindByName("swim") - self.dance_anim = character_scene.Entity_FindByName("dance") - - self.collider = character_scene.Entity_GetHumanoidArray()[1] - self.expression = character_scene.Entity_GetExpressionArray()[1] - self.happy=0 - self.humanoid = character_scene.Entity_GetHumanoidArray()[1] - local humanoid = character_scene.Component_GetHumanoid(self.humanoid) - humanoid.SetLookAtEnabled(false) - self.neck = humanoid.GetBoneEntity(HumanoidBone.Neck) - self.head = humanoid.GetBoneEntity(HumanoidBone.Head) - self.left_hand = humanoid.GetBoneEntity(HumanoidBone.LeftHand) - self.right_hand = humanoid.GetBoneEntity(HumanoidBone.RightHand) - self.left_foot = humanoid.GetBoneEntity(HumanoidBone.LeftFoot) - self.right_foot = humanoid.GetBoneEntity(HumanoidBone.RightFoot) - - local model_transform = character_scene.Component_GetTransform(self.model) + local model_transform = scene.Component_GetTransform(self.model) model_transform.ClearTransform() model_transform.Scale(self.scale) model_transform.Rotate(self.rotation) @@ -133,16 +183,11 @@ local function Character(model_name, start_position, face, controllable) model_transform.UpdateTransform() self.target = CreateEntity() - local target_transform = character_scene.Component_CreateTransform(self.target) - target_transform.Translate(character_scene.Component_GetTransform(self.neck).GetPosition()) + local target_transform = scene.Component_CreateTransform(self.target) + target_transform.Translate(scene.Component_GetTransform(self.neck).GetPosition()) target_transform.Translate(self.start_position) - - character_scene.Component_Attach(self.target, self.model) + scene.Component_Attach(self.target, self.model) - self.root = character_scene.Entity_FindByName("Root") - self.root_bone_offset = 0 - - scene.Merge(character_scene) end, Jump = function(self,f) @@ -515,14 +560,13 @@ local function Character(model_name, start_position, face, controllable) end character_capsules[self.model] = capsule + self.position = model_transform.GetPosition() end, Update_IK = function(self) -- IK foot placement: - local root_bone_transform = scene.Component_GetTransform(self.root) - root_bone_transform.ClearTransform() - self.root_bone_offset = math.lerp(self.root_bone_offset, 0, 0.1) + local base_y = self.position.GetY() local ik_foot = INVALID_ENTITY local ik_pos = Vector() -- Compute root bone offset: @@ -540,10 +584,12 @@ local function Character(model_name, start_position, face, controllable) local diff_left = 0 local diff_right = 0 if collEntity_left ~= INVALID_ENTITY then - diff_left = vector.Subtract(collPos_left, pos_left).GetY() + --DrawAxis(collPos_left, 0.2) + diff_left = collPos_left.GetY() - base_y end if collEntity_right ~= INVALID_ENTITY then - diff_right = vector.Subtract(collPos_right, pos_right).GetY() + --DrawAxis(collPos_right, 0.2) + diff_right = collPos_right.GetY() - base_y end local diff = diff_left if collPos_left.GetY() > collPos_right.GetY() + 0.01 then @@ -558,9 +604,18 @@ local function Character(model_name, start_position, face, controllable) ik_pos = collPos_right end end - self.root_bone_offset = math.lerp(self.root_bone_offset, diff + 0.1, 0.2) + self.root_bone_offset = math.lerp(self.root_bone_offset, diff, 0.1) + else + self.root_bone_offset = math.lerp(self.root_bone_offset, 0, 0.1) end + + -- Offset root transform to lower foot pos: + local root_bone_transform = scene.Component_GetTransform(self.root) + root_bone_transform.ClearTransform() + local root_pos = root_bone_transform.GetPosition() root_bone_transform.Translate(Vector(0, self.root_bone_offset)) + --DrawDebugText(self.root_bone_offset, self.position, Vector(1,1,1,1), 0.1, DEBUG_TEXT_CAMERA_FACING) + --DrawPoint(vector.Add(root_pos, Vector(0, self.root_bone_offset)), 0.1, Vector(1,0,0,1)) -- Remove IK effectors by default: if scene.Component_GetInverseKinematics(self.left_foot) ~= nil then @@ -758,13 +813,14 @@ local function ThirdPersonCamera(character) return self end - ClearWorld() LoadModel(script_dir() .. "assets/level.wiscene") --LoadModel(script_dir() .. "assets/terrain.wiscene") --LoadModel(script_dir() .. "assets/waypoints.wiscene", matrix.Translation(Vector(1,0,2))) --dofile(script_dir() .. "../dungeon_generator/dungeon_generator.lua") +LoadAnimations(script_dir() .. "assets/animations.wiscene") + local player = Character(script_dir() .. "assets/character.wiscene", Vector(0,0.5,0), Vector(0,0,1), true) local npcs = { -- Patrolling NPC IDs: 1,2,3 diff --git a/Editor/AnimationWindow.cpp b/Editor/AnimationWindow.cpp index a114fb2ba..d47c2a60e 100644 --- a/Editor/AnimationWindow.cpp +++ b/Editor/AnimationWindow.cpp @@ -5,65 +5,6 @@ using namespace wi::ecs; using namespace wi::scene; -XMMATRIX ComputeWorldMatrixRecursive(Scene& scene, Entity entity, XMMATRIX localMatrix) -{ - HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); - if (hier != nullptr) - { - Entity parentID = hier->parentID; - while (parentID != INVALID_ENTITY) - { - TransformComponent* transform_parent = scene.transforms.GetComponent(parentID); - if (transform_parent == nullptr) - break; - - localMatrix *= transform_parent->GetLocalMatrix(); - - const HierarchyComponent* hier_recursive = scene.hierarchy.GetComponent(parentID); - if (hier_recursive != nullptr) - { - parentID = hier_recursive->parentID; - } - else - { - parentID = INVALID_ENTITY; - } - } - } - return localMatrix; -} -XMMATRIX ComputeInverseParentMatrixRecursive(Scene& scene, Entity entity) -{ - XMMATRIX inverseParentMatrix = XMMatrixIdentity(); - - HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); - if (hier != nullptr) - { - Entity parentID = hier->parentID; - while (parentID != INVALID_ENTITY) - { - TransformComponent* transform_parent = scene.transforms.GetComponent(parentID); - if (transform_parent == nullptr) - break; - - inverseParentMatrix *= transform_parent->GetLocalMatrix(); - - const HierarchyComponent* hier_recursive = scene.hierarchy.GetComponent(parentID); - if (hier_recursive != nullptr) - { - parentID = hier_recursive->parentID; - } - else - { - parentID = INVALID_ENTITY; - } - } - - inverseParentMatrix = XMMatrixInverse(nullptr, inverseParentMatrix); - } - return inverseParentMatrix; -} - void AnimationWindow::Create(EditorComponent* _editor) { editor = _editor; @@ -970,132 +911,18 @@ void AnimationWindow::Create(EditorComponent* _editor) retargetCombo.OnSelect([=](wi::gui::EventArgs args) { retargetCombo.SetSelectedWithoutCallback(-1); wi::scene::Scene& scene = editor->GetCurrentScene(); - const AnimationComponent* animation_source = scene.animations.GetComponent(entity); - if (animation_source == nullptr) - return; - const HumanoidComponent* humanoid_dest = scene.humanoids.GetComponent((Entity)args.userdata); - if (humanoid_dest == nullptr) - return; - bool retarget_valid = false; - Scene retarget_scene; - Entity retarget_entity = CreateEntity(); - AnimationComponent& animation = retarget_scene.animations.Create(retarget_entity); - animation = *animation_source; - animation.channels.clear(); - animation.samplers.clear(); - - NameComponent name; - const NameComponent* name_source = scene.names.GetComponent(entity); - if (name_source != nullptr) + Entity retarget_entity = scene.RetargetAnimation((Entity)args.userdata, entity, true); + if (retarget_entity != INVALID_ENTITY) { - name.name += name_source->name; - } - scene.names.Create(retarget_entity) = name; - - TransformComponent transform; - const TransformComponent* transform_source = scene.transforms.GetComponent(entity); - if (transform_source != nullptr) - { - transform = *transform_source; - } - scene.transforms.Create(retarget_entity) = transform; - - scene.Component_Attach(retarget_entity, (Entity)args.userdata); - - for (auto& channel : animation_source->channels) - { - bool found = false; - for (size_t i = 0; (i < scene.humanoids.GetCount()) && !found; ++i) + NameComponent name; + const NameComponent* name_source = scene.names.GetComponent(entity); + if (name_source != nullptr) { - const HumanoidComponent& humanoid_source = scene.humanoids[i]; - for (size_t humanoidBoneIndex = 0; humanoidBoneIndex < arraysize(humanoid_source.bones); ++humanoidBoneIndex) - { - Entity bone_source = humanoid_source.bones[humanoidBoneIndex]; - if (bone_source == channel.target) - { - retarget_valid = true; - found = true; - Entity bone_dest = humanoid_dest->bones[humanoidBoneIndex]; - - auto& retarget_channel = animation.channels.emplace_back(); - retarget_channel = channel; - retarget_channel.target = bone_dest; - retarget_channel.samplerIndex = (int)animation.samplers.size(); - - auto& sampler = animation_source->samplers[channel.samplerIndex]; - - auto& retarget_sampler = animation.samplers.emplace_back(); - retarget_sampler = sampler; - retarget_sampler.backwards_compatibility_data = {}; - - Entity retarget_animation_data_entity = CreateEntity(); - auto& retarget_animation_data = retarget_scene.animation_datas.Create(retarget_animation_data_entity); - retarget_sampler.data = retarget_animation_data_entity; - - auto& animation_data = scene.animation_datas.Contains(sampler.data) ? *scene.animation_datas.GetComponent(sampler.data) : sampler.backwards_compatibility_data; - retarget_animation_data = animation_data; - - TransformComponent* transform_source = scene.transforms.GetComponent(bone_source); - TransformComponent* transform_dest = scene.transforms.GetComponent(bone_dest); - if (transform_source != nullptr && transform_dest != nullptr) - { - XMMATRIX bindMatrix = ComputeWorldMatrixRecursive(scene, bone_source, transform_source->GetLocalMatrix()); - XMMATRIX inverseBindMatrix = XMMatrixInverse(nullptr, bindMatrix); - XMMATRIX targetMatrix = ComputeWorldMatrixRecursive(scene, bone_dest, transform_dest->GetLocalMatrix()); - XMMATRIX inverseParentMatrix = ComputeInverseParentMatrixRecursive(scene, bone_dest); - XMVECTOR S, R, T; // matrix decompose destinations - - switch (channel.path) - { - case AnimationComponent::AnimationChannel::Path::SCALE: - for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3) - { - XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset]; - TransformComponent transform = *transform_source; - transform.scale_local = *data; - XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix()); - localMatrix = targetMatrix * localMatrix * inverseParentMatrix; - XMMatrixDecompose(&S, &R, &T, localMatrix); - XMStoreFloat3(data, S); - } - break; - case AnimationComponent::AnimationChannel::Path::ROTATION: - for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 4) - { - XMFLOAT4* data = (XMFLOAT4*)&retarget_animation_data.keyframe_data[offset]; - TransformComponent transform = *transform_source; - transform.rotation_local = *data; - XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix()); - localMatrix = targetMatrix * localMatrix * inverseParentMatrix; - XMMatrixDecompose(&S, &R, &T, localMatrix); - XMStoreFloat4(data, R); - } - break; - case AnimationComponent::AnimationChannel::Path::TRANSLATION: - for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3) - { - XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset]; - TransformComponent transform = *transform_source; - transform.translation_local = *data; - XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix()); - localMatrix = targetMatrix * localMatrix * inverseParentMatrix; - XMMatrixDecompose(&S, &R, &T, localMatrix); - XMStoreFloat3(data, T); - } - break; - default: - break; - } - } - break; - } - } + name.name += name_source->name; } - } - if (retarget_valid) - { - scene.Merge(retarget_scene); + scene.names.Create(retarget_entity) = name; + editor->optionsWnd.RefreshEntityTree(); } }); diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index b3911423a..479d82969 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -373,63 +373,6 @@ void EditorComponent::Load() GetGUI().AddWidget(&openButton); - //closeButton.Create(""); - //closeButton.SetShadowRadius(2); - //closeButton.font.params.shadowColor = wi::Color::Transparent(); - //closeButton.SetTooltip("Close the current scene.\nThis will clear everything from the currently selected scene, delete the scene and kill all script processes.\nThis operation cannot be undone!"); - //closeButton.SetColor(wi::Color(255, 130, 100, 180), wi::gui::WIDGETSTATE::IDLE); - //closeButton.SetColor(wi::Color(255, 200, 150, 255), wi::gui::WIDGETSTATE::FOCUS); - //closeButton.OnClick([&](wi::gui::EventArgs args) { - - // wi::lua::KillProcesses(); - // componentsWnd.terrainWnd.terrain_preset = {}; - - // translator.selected.clear(); - // wi::scene::Scene& scene = GetCurrentScene(); - // wi::renderer::ClearWorld(scene); - // optionsWnd.cameraWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.objectWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.meshWnd.SetEntity(INVALID_ENTITY, -1); - // componentsWnd.lightWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.soundWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.decalWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.envProbeWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.materialWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.emitterWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.hairWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.forceFieldWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.springWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.ikWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.transformWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.layerWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.nameWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.animWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.scriptWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.rigidWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.softWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.colliderWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.hierarchyWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.cameraComponentWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.expressionWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.armatureWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.humanoidWnd.SetEntity(INVALID_ENTITY); - // componentsWnd.terrainWnd.SetEntity(INVALID_ENTITY); - - // optionsWnd.RefreshEntityTree(); - // ResetHistory(); - // GetCurrentEditorScene().path.clear(); - - // wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { - // if (scenes.size() > 1) - // { - // scenes.erase(scenes.begin() + current_scene); - // } - // SetCurrentScene(std::max(0, current_scene - 1)); - // }); - // }); - //GetGUI().AddWidget(&closeButton); - - logButton.Create(""); logButton.SetShadowRadius(2); logButton.font.params.shadowColor = wi::Color::Transparent(); @@ -3236,7 +3179,7 @@ void EditorComponent::RefreshSceneList() componentsWnd.terrainWnd.terrain_preset.props.clear(); translator.selected.clear(); - wi::scene::Scene& scene = GetCurrentScene(); + wi::scene::Scene& scene = scenes[i]->scene; wi::renderer::ClearWorld(scene); optionsWnd.cameraWnd.SetEntity(wi::ecs::INVALID_ENTITY); componentsWnd.objectWnd.SetEntity(wi::ecs::INVALID_ENTITY); diff --git a/Editor/ModelImporter_GLTF.cpp b/Editor/ModelImporter_GLTF.cpp index 527db4e65..1182dc618 100644 --- a/Editor/ModelImporter_GLTF.cpp +++ b/Editor/ModelImporter_GLTF.cpp @@ -1642,7 +1642,7 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) } animationcomponent.samplers[i].data = CreateEntity(); - scene.Component_Attach(animationcomponent.samplers[i].data, state.rootEntity); + scene.Component_Attach(animationcomponent.samplers[i].data, entity); AnimationDataComponent& animationdata = scene.animation_datas.Create(animationcomponent.samplers[i].data); // AnimationSampler input = keyframe times diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index e518aec92..0089f6f41 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -475,6 +475,10 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level) { item.name += ICON_ANIMATION " "; } + if (scene.animation_datas.Contains(entity)) + { + item.name += "[animation_data] "; + } if (scene.armatures.Contains(entity)) { item.name += ICON_ARMATURE " "; @@ -697,6 +701,10 @@ void OptionsWindow::RefreshEntityTree() { PushToEntityTree(scene.animations.GetEntity(i), 0); } + for (size_t i = 0; i < scene.animation_datas.GetCount(); ++i) + { + PushToEntityTree(scene.animation_datas.GetEntity(i), 0); + } } if (has_flag(filter, Filter::EnvironmentProbe)) diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 1bc0103db..6c801d233 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -1424,11 +1424,11 @@ namespace wi::scene union Interpolator { - XMFLOAT4 f4 = {}; + XMFLOAT4 f4; XMFLOAT3 f3; XMFLOAT2 f2; float f; - } interpolator; + } interpolator = {}; TransformComponent* target_transform = nullptr; MeshComponent* target_mesh = nullptr; @@ -1735,8 +1735,16 @@ namespace wi::scene const XMFLOAT4* data = (const XMFLOAT4*)animationdata->keyframe_data.data(); XMVECTOR vLeft = XMLoadFloat4(&data[keyLeft]); XMVECTOR vRight = XMLoadFloat4(&data[keyRight]); - XMVECTOR vAnim = XMQuaternionSlerp(vLeft, vRight, t); - vAnim = XMQuaternionNormalize(vAnim); + XMVECTOR vAnim; + if (channel.path == AnimationComponent::AnimationChannel::Path::ROTATION) + { + vAnim = XMQuaternionSlerp(vLeft, vRight, t); + vAnim = XMQuaternionNormalize(vAnim); + } + else + { + vAnim = XMVectorLerp(vLeft, vRight, t); + } XMStoreFloat4(&interpolator.f4, vAnim); } break; @@ -1819,7 +1827,10 @@ namespace wi::scene XMVECTOR vRightTanIn = dt * XMLoadFloat4(&data[keyRight * 3 + 0]); XMVECTOR vRight = XMLoadFloat4(&data[keyRight * 3 + 1]); XMVECTOR vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn; - vAnim = XMQuaternionNormalize(vAnim); + if (channel.path == AnimationComponent::AnimationChannel::Path::ROTATION) + { + vAnim = XMQuaternionNormalize(vAnim); + } XMStoreFloat4(&interpolator.f4, vAnim); } break; @@ -1855,7 +1866,23 @@ namespace wi::scene case AnimationComponent::AnimationChannel::Path::TRANSLATION: { const XMVECTOR aT = XMLoadFloat3(&target_transform->translation_local); - const XMVECTOR bT = XMLoadFloat3(&interpolator.f3); + XMVECTOR bT = XMLoadFloat3(&interpolator.f3); + if (channel.retargetIndex >= 0 && channel.retargetIndex < (int)animation.retargets.size()) + { + // Retargeting transfer from source to destination: + const AnimationComponent::RetargetSourceData& retarget = animation.retargets[channel.retargetIndex]; + TransformComponent* source_transform = transforms.GetComponent(retarget.source); + if (source_transform != nullptr) + { + XMMATRIX dstRelativeMatrix = XMLoadFloat4x4(&retarget.dstRelativeMatrix); + XMMATRIX srcRelativeParentMatrix = XMLoadFloat4x4(&retarget.srcRelativeParentMatrix); + XMVECTOR S, R; // matrix decompose destinations + TransformComponent transform = *source_transform; + XMStoreFloat3(&transform.translation_local, bT); + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&S, &R, &bT, localMatrix); + } + } const XMVECTOR T = XMVectorLerp(aT, bT, t); XMStoreFloat3(&target_transform->translation_local, T); } @@ -1863,7 +1890,23 @@ namespace wi::scene case AnimationComponent::AnimationChannel::Path::ROTATION: { const XMVECTOR aR = XMLoadFloat4(&target_transform->rotation_local); - const XMVECTOR bR = XMLoadFloat4(&interpolator.f4); + XMVECTOR bR = XMLoadFloat4(&interpolator.f4); + if (channel.retargetIndex >= 0 && channel.retargetIndex < (int)animation.retargets.size()) + { + // Retargeting transfer from source to destination: + const AnimationComponent::RetargetSourceData& retarget = animation.retargets[channel.retargetIndex]; + TransformComponent* source_transform = transforms.GetComponent(retarget.source); + if (source_transform != nullptr) + { + XMMATRIX dstRelativeMatrix = XMLoadFloat4x4(&retarget.dstRelativeMatrix); + XMMATRIX srcRelativeParentMatrix = XMLoadFloat4x4(&retarget.srcRelativeParentMatrix); + XMVECTOR S, T; // matrix decompose destinations + TransformComponent transform = *source_transform; + XMStoreFloat4(&transform.rotation_local, bR); + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&S, &bR, &T, localMatrix); + } + } const XMVECTOR R = XMQuaternionSlerp(aR, bR, t); XMStoreFloat4(&target_transform->rotation_local, R); } @@ -1871,7 +1914,23 @@ namespace wi::scene case AnimationComponent::AnimationChannel::Path::SCALE: { const XMVECTOR aS = XMLoadFloat3(&target_transform->scale_local); - const XMVECTOR bS = XMLoadFloat3(&interpolator.f3); + XMVECTOR bS = XMLoadFloat3(&interpolator.f3); + if (channel.retargetIndex >= 0 && channel.retargetIndex < (int)animation.retargets.size()) + { + // Retargeting transfer from source to destination: + const AnimationComponent::RetargetSourceData& retarget = animation.retargets[channel.retargetIndex]; + TransformComponent* source_transform = transforms.GetComponent(retarget.source); + if (source_transform != nullptr) + { + XMMATRIX dstRelativeMatrix = XMLoadFloat4x4(&retarget.dstRelativeMatrix); + XMMATRIX srcRelativeParentMatrix = XMLoadFloat4x4(&retarget.srcRelativeParentMatrix); + XMVECTOR R, T; // matrix decompose destinations + TransformComponent transform = *source_transform; + XMStoreFloat3(&transform.scale_local, bS); + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&bS, &R, &T, localMatrix); + } + } const XMVECTOR S = XMVectorLerp(aS, bS, t); XMStoreFloat3(&target_transform->scale_local, S); } @@ -5336,4 +5395,170 @@ namespace wi::scene return scene.Intersects(capsule, filterMask, layerMask, lod); } + + XMMATRIX Scene::ComputeParentMatrixRecursive(Entity entity) const + { + XMMATRIX parentMatrix = XMMatrixIdentity(); + + HierarchyComponent* hier = hierarchy.GetComponent(entity); + if (hier != nullptr) + { + Entity parentID = hier->parentID; + while (parentID != INVALID_ENTITY) + { + TransformComponent* transform_parent = transforms.GetComponent(parentID); + if (transform_parent == nullptr) + break; + + parentMatrix *= transform_parent->GetLocalMatrix(); + + const HierarchyComponent* hier_recursive = hierarchy.GetComponent(parentID); + if (hier_recursive != nullptr) + { + parentID = hier_recursive->parentID; + } + else + { + parentID = INVALID_ENTITY; + } + } + } + return parentMatrix; + } + + Entity Scene::RetargetAnimation(Entity dst, Entity src, bool bake_data) + { + const AnimationComponent* animation_source = animations.GetComponent(src); + if (animation_source == nullptr) + return INVALID_ENTITY; + const HumanoidComponent* humanoid_dest = humanoids.GetComponent(dst); + if (humanoid_dest == nullptr) + return INVALID_ENTITY; + + bool retarget_valid = false; + Scene retarget_scene; + Entity retarget_entity = CreateEntity(); + AnimationComponent& animation = retarget_scene.animations.Create(retarget_entity); + animation = *animation_source; + animation.channels.clear(); + animation.samplers.clear(); + animation.retargets.clear(); + + for (auto& channel : animation_source->channels) + { + bool found = false; + for (size_t i = 0; (i < humanoids.GetCount()) && !found; ++i) + { + const HumanoidComponent& humanoid_source = humanoids[i]; + for (size_t humanoidBoneIndex = 0; humanoidBoneIndex < arraysize(humanoid_source.bones); ++humanoidBoneIndex) + { + Entity bone_source = humanoid_source.bones[humanoidBoneIndex]; + if (bone_source == channel.target) + { + retarget_valid = true; + found = true; + Entity bone_dest = humanoid_dest->bones[humanoidBoneIndex]; + + auto& retarget_channel = animation.channels.emplace_back(); + retarget_channel = channel; + retarget_channel.target = bone_dest; + retarget_channel.samplerIndex = (int)animation.samplers.size(); + + auto& sampler = animation_source->samplers[channel.samplerIndex]; + + auto& retarget_sampler = animation.samplers.emplace_back(); + retarget_sampler = sampler; + retarget_sampler.backwards_compatibility_data = {}; + + TransformComponent* transform_source = transforms.GetComponent(bone_source); + TransformComponent* transform_dest = transforms.GetComponent(bone_dest); + if (transform_source != nullptr && transform_dest != nullptr) + { + XMMATRIX srcParentMatrix = ComputeParentMatrixRecursive(bone_source); + XMMATRIX srcMatrix = transform_source->GetLocalMatrix() * srcParentMatrix; + XMMATRIX inverseSrcMatrix = XMMatrixInverse(nullptr, srcMatrix); + + XMMATRIX dstParentMatrix = ComputeParentMatrixRecursive(bone_dest); + XMMATRIX dstMatrix = transform_dest->GetLocalMatrix() * dstParentMatrix; + XMMATRIX inverseDstParentMatrix = XMMatrixInverse(nullptr, dstParentMatrix); + + XMMATRIX dstRelativeMatrix = dstMatrix * inverseSrcMatrix; + XMMATRIX srcRelativeParentMatrix = srcParentMatrix * inverseDstParentMatrix; + + if (bake_data) + { + // Create new animation data and bake the retargeted result into it: + Entity retarget_data_entity = CreateEntity(); + auto& retarget_animation_data = retarget_scene.animation_datas.Create(retarget_data_entity); + retarget_sampler.data = retarget_data_entity; + retarget_scene.Component_Attach(retarget_data_entity, retarget_entity); + + auto& animation_data = animation_datas.Contains(sampler.data) ? *animation_datas.GetComponent(sampler.data) : sampler.backwards_compatibility_data; + retarget_animation_data = animation_data; + + XMVECTOR S, R, T; // matrix decompose destinations + + switch (channel.path) + { + case AnimationComponent::AnimationChannel::Path::SCALE: + for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3) + { + XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset]; + TransformComponent transform = *transform_source; + transform.scale_local = *data; + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&S, &R, &T, localMatrix); + XMStoreFloat3(data, S); + } + break; + case AnimationComponent::AnimationChannel::Path::ROTATION: + for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 4) + { + XMFLOAT4* data = (XMFLOAT4*)&retarget_animation_data.keyframe_data[offset]; + TransformComponent transform = *transform_source; + transform.rotation_local = *data; + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&S, &R, &T, localMatrix); + XMStoreFloat4(data, R); + } + break; + case AnimationComponent::AnimationChannel::Path::TRANSLATION: + for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3) + { + XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset]; + TransformComponent transform = *transform_source; + transform.translation_local = *data; + XMMATRIX localMatrix = dstRelativeMatrix * transform.GetLocalMatrix() * srcRelativeParentMatrix; + XMMatrixDecompose(&S, &R, &T, localMatrix); + XMStoreFloat3(data, T); + } + break; + default: + break; + } + } + else + { + // Don't bake retarget data, but inform the animation channel of original source data: + retarget_channel.retargetIndex = (int)animation.retargets.size(); + AnimationComponent::RetargetSourceData& retarget = animation.retargets.emplace_back(); + retarget.source = bone_source; + XMStoreFloat4x4(&retarget.dstRelativeMatrix, dstRelativeMatrix); + XMStoreFloat4x4(&retarget.srcRelativeParentMatrix, srcRelativeParentMatrix); + } + } + break; + } + } + } + } + if (retarget_valid) + { + retarget_scene.Component_Attach(retarget_entity, dst); + Merge(retarget_scene); + return retarget_entity; + } + return INVALID_ENTITY; + } + } diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 74cf98344..999360cf0 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -40,7 +40,7 @@ namespace wi::scene wi::ecs::ComponentManager& probes = componentLibrary.Register("wi::scene::Scene::probes"); wi::ecs::ComponentManager& forces = componentLibrary.Register("wi::scene::Scene::forces", 1); // version = 1 wi::ecs::ComponentManager& decals = componentLibrary.Register("wi::scene::Scene::decals"); - wi::ecs::ComponentManager& animations = componentLibrary.Register("wi::scene::Scene::animations"); + wi::ecs::ComponentManager& animations = componentLibrary.Register("wi::scene::Scene::animations", 1); // version = 1 wi::ecs::ComponentManager& animation_datas = componentLibrary.Register("wi::scene::Scene::animation_datas"); wi::ecs::ComponentManager& emitters = componentLibrary.Register("wi::scene::Scene::emitters"); wi::ecs::ComponentManager& hairs = componentLibrary.Register("wi::scene::Scene::hairs"); @@ -417,6 +417,17 @@ namespace wi::scene using CapsuleIntersectionResult = SphereIntersectionResult; CapsuleIntersectionResult Intersects(const wi::primitive::Capsule& capsule, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, uint32_t lod = 0) const; + // Goes through the hierarchy backwards and computes parent's world space matrix: + XMMATRIX ComputeParentMatrixRecursive(wi::ecs::Entity entity) const; + + // Retargets an animation from a Humanoid to an other Humanoid such that the new animation will play back on the destination humanoid + // dst : destination humanoid that the animation will be fit onto + // src : the animation to copy, it should already target humanoid bones + // bake_data : if true, the retargeted data will be baked into a new animation data. + // if false, it will reuse the source animation data without creating a new one and retargeting will be applied at runtime on every Update + // + // returns entity ID of the new animation or INVALID_ENTITY if retargeting was not successful + wi::ecs::Entity RetargetAnimation(wi::ecs::Entity dst, wi::ecs::Entity src, bool bake_data); }; // Returns skinned vertex position in armature local space @@ -443,7 +454,7 @@ namespace wi::scene // Helper function to open a wiscene file and add the contents to the global scene // fileName : file path // transformMatrix : everything will be transformed by this matrix (optional) - // attached : everything will be attached to a base entity + // attached : if true, everything will be attached to a base entity // // returns INVALID_ENTITY if attached argument was false, else it returns the base entity handle wi::ecs::Entity LoadModel(const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), bool attached = false); @@ -452,7 +463,7 @@ namespace wi::scene // scene : the scene that will contain the model // fileName : file path // transformMatrix : everything will be transformed by this matrix (optional) - // attached : everything will be attached to a base entity + // attached : if true, everything will be attached to a base entity // // returns INVALID_ENTITY if attached argument was false, else it returns the base entity handle wi::ecs::Entity LoadModel(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), bool attached = false); diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index e5a610599..36611ce91 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -401,6 +401,20 @@ HumanoidBone = { RightLittleIntermediate = 53, RightLittleDistal = 54, } + +ColliderShape = { + Sphere = 0, + Capsule = 1, + Plane = 2, +} + +RigidBodyShape = { + Box = 0, + Sphere = 1, + Capsule = 2, + ConvexHull = 3, + TriangleMesh = 4, +} )"; void Bind() @@ -537,6 +551,7 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Entity_GetTransformArray), lunamethod(Scene_BindLua, Entity_GetCameraArray), lunamethod(Scene_BindLua, Entity_GetAnimationArray), + lunamethod(Scene_BindLua, Entity_GetAnimationDataArray), lunamethod(Scene_BindLua, Entity_GetMaterialArray), lunamethod(Scene_BindLua, Entity_GetMeshArray), lunamethod(Scene_BindLua, Entity_GetEmitterArray), @@ -560,6 +575,7 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_RemoveTransform), lunamethod(Scene_BindLua, Component_RemoveCamera), lunamethod(Scene_BindLua, Component_RemoveAnimation), + lunamethod(Scene_BindLua, Component_RemoveAnimationData), lunamethod(Scene_BindLua, Component_RemoveMaterial), lunamethod(Scene_BindLua, Component_RemoveMesh), lunamethod(Scene_BindLua, Component_RemoveEmitter), @@ -583,6 +599,9 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_DetachChildren), lunamethod(Scene_BindLua, GetBounds), + lunamethod(Scene_BindLua, GetWeather), + lunamethod(Scene_BindLua, SetWeather), + lunamethod(Scene_BindLua, RetargetAnimation), { NULL, NULL } }; Luna::PropertyType Scene_BindLua::properties[] = { @@ -1887,6 +1906,17 @@ int Scene_BindLua::Entity_GetAnimationArray(lua_State* L) } return 1; } +int Scene_BindLua::Entity_GetAnimationDataArray(lua_State* L) +{ + lua_createtable(L, (int)scene->animation_datas.GetCount(), 0); + int newTable = lua_gettop(L); + for (size_t i = 0; i < scene->animation_datas.GetCount(); ++i) + { + wi::lua::SSetLongLong(L, scene->animation_datas.GetEntity(i)); + lua_rawseti(L, newTable, lua_Integer(i + 1)); + } + return 1; +} int Scene_BindLua::Entity_GetMaterialArray(lua_State* L) { lua_createtable(L, (int)scene->materials.GetCount(), 0); @@ -2160,6 +2190,23 @@ int Scene_BindLua::Component_RemoveAnimation(lua_State* L) } return 0; } +int Scene_BindLua::Component_RemoveAnimationData(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + if (scene->animation_datas.Contains(entity)) + { + scene->animation_datas.Remove(entity); + } + } + else + { + wi::lua::SError(L, "Scene::Component_RemoveAnimationData(Entity entity) not enough arguments!"); + } + return 0; +} int Scene_BindLua::Component_RemoveMaterial(lua_State* L) { int argc = wi::lua::SGetArgCount(L); @@ -2535,6 +2582,25 @@ int Scene_BindLua::SetWeather(lua_State* L) return 0; } +int Scene_BindLua::RetargetAnimation(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 2) + { + Entity dst = (Entity)wi::lua::SGetLongLong(L, 1); + Entity src = (Entity)wi::lua::SGetLongLong(L, 2); + bool bake_data = wi::lua::SGetBool(L, 3); + + Entity entity = scene->RetargetAnimation(dst, src, bake_data); + wi::lua::SSetLongLong(L, entity); + return 1; + } + else + { + wi::lua::SError(L, "RetargetAnimation(Entity dst, Entity src, bool bake_data) not enough arguments!"); + } + return 0; +} diff --git a/WickedEngine/wiScene_BindLua.h b/WickedEngine/wiScene_BindLua.h index 8b631d5e1..d80b7714d 100644 --- a/WickedEngine/wiScene_BindLua.h +++ b/WickedEngine/wiScene_BindLua.h @@ -120,6 +120,7 @@ namespace wi::lua::scene int Entity_GetTransformArray(lua_State* L); int Entity_GetCameraArray(lua_State* L); int Entity_GetAnimationArray(lua_State* L); + int Entity_GetAnimationDataArray(lua_State* L); int Entity_GetMaterialArray(lua_State* L); int Entity_GetMeshArray(lua_State* L); int Entity_GetEmitterArray(lua_State* L); @@ -143,6 +144,7 @@ namespace wi::lua::scene int Component_RemoveTransform(lua_State* L); int Component_RemoveCamera(lua_State* L); int Component_RemoveAnimation(lua_State* L); + int Component_RemoveAnimationData(lua_State* L); int Component_RemoveMaterial(lua_State* L); int Component_RemoveMesh(lua_State* L); int Component_RemoveEmitter(lua_State* L); @@ -169,6 +171,8 @@ namespace wi::lua::scene int GetWeather(lua_State* L); int SetWeather(lua_State* L); + + int RetargetAnimation(lua_State* L); }; class NameComponent_BindLua diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 9fa7fa158..33a942ffe 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -1102,10 +1102,11 @@ namespace wi::scene { EMPTY = 0, }; - uint32_t _flags = LOOPED; + uint32_t _flags = EMPTY; wi::ecs::Entity target = wi::ecs::INVALID_ENTITY; int samplerIndex = -1; + int retargetIndex = -1; enum class Path { @@ -1193,8 +1194,16 @@ namespace wi::scene // The data is now not part of the sampler, so it can be shared. This is kept only for backwards compatibility with previous versions. AnimationDataComponent backwards_compatibility_data; }; + struct RetargetSourceData + { + wi::ecs::Entity source = wi::ecs::INVALID_ENTITY; + XMFLOAT4X4 dstRelativeMatrix = wi::math::IDENTITY_MATRIX; + XMFLOAT4X4 srcRelativeParentMatrix = wi::math::IDENTITY_MATRIX; + }; + wi::vector channels; wi::vector samplers; + wi::vector retargets; // Non-serialzied attributes: wi::vector morph_weights_temp; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 953c371ad..d6c0f14f1 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -998,6 +998,10 @@ namespace wi::scene archive >> (uint32_t&)channels[i].path; SerializeEntity(archive, channels[i].target, seri); archive >> channels[i].samplerIndex; + if (seri.GetVersion() >= 1) + { + archive >> channels[i].retargetIndex; + } } size_t samplerCount; @@ -1018,6 +1022,19 @@ namespace wi::scene } } + if (seri.GetVersion() >= 1) + { + size_t retargetCount; + archive >> retargetCount; + retargets.resize(retargetCount); + for (size_t i = 0; i < retargetCount; ++i) + { + SerializeEntity(archive, retargets[i].source, seri); + archive >> retargets[i].dstRelativeMatrix; + archive >> retargets[i].srcRelativeParentMatrix; + } + } + } else { @@ -1042,6 +1059,10 @@ namespace wi::scene archive << (uint32_t&)channels[i].path; SerializeEntity(archive, channels[i].target, seri); archive << channels[i].samplerIndex; + if (seri.GetVersion() >= 1) + { + archive << channels[i].retargetIndex; + } } archive << samplers.size(); @@ -1051,6 +1072,17 @@ namespace wi::scene archive << samplers[i].mode; SerializeEntity(archive, samplers[i].data, seri); } + + if (seri.GetVersion() >= 1) + { + archive << retargets.size(); + for (size_t i = 0; i < retargets.size(); ++i) + { + SerializeEntity(archive, retargets[i].source, seri); + archive << retargets[i].dstRelativeMatrix; + archive << retargets[i].srcRelativeParentMatrix; + } + } } } void AnimationDataComponent::Serialize(wi::Archive& archive, EntitySerializer& seri) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 9c4dee8ca..9bbee3e48 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 141; + const int revision = 142; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);