Runtime animation retargeting (#627)
* runtime animation retargeting * improvements, fixes * character controller script: separate animation file from character * editor fix * script improvements * foot positioning fixes
This commit is contained in:
@@ -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
|
||||
|
||||
</br>
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
+8
-181
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
+1
-58
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
+233
-8
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-3
@@ -40,7 +40,7 @@ namespace wi::scene
|
||||
wi::ecs::ComponentManager<EnvironmentProbeComponent>& probes = componentLibrary.Register<EnvironmentProbeComponent>("wi::scene::Scene::probes");
|
||||
wi::ecs::ComponentManager<ForceFieldComponent>& forces = componentLibrary.Register<ForceFieldComponent>("wi::scene::Scene::forces", 1); // version = 1
|
||||
wi::ecs::ComponentManager<DecalComponent>& decals = componentLibrary.Register<DecalComponent>("wi::scene::Scene::decals");
|
||||
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations");
|
||||
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations", 1); // version = 1
|
||||
wi::ecs::ComponentManager<AnimationDataComponent>& animation_datas = componentLibrary.Register<AnimationDataComponent>("wi::scene::Scene::animation_datas");
|
||||
wi::ecs::ComponentManager<EmittedParticleSystem>& emitters = componentLibrary.Register<EmittedParticleSystem>("wi::scene::Scene::emitters");
|
||||
wi::ecs::ComponentManager<HairParticleSystem>& hairs = componentLibrary.Register<HairParticleSystem>("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);
|
||||
|
||||
@@ -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<Scene_BindLua>::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<Scene_BindLua>::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<Scene_BindLua>::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<Scene_BindLua>::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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<AnimationChannel> channels;
|
||||
wi::vector<AnimationSampler> samplers;
|
||||
wi::vector<RetargetSourceData> retargets;
|
||||
|
||||
// Non-serialzied attributes:
|
||||
wi::vector<float> morph_weights_temp;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user