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:
Turánszki János
2023-01-22 12:29:13 -05:00
committed by GitHub
parent ab53daad94
commit 30c47c83cc
15 changed files with 509 additions and 315 deletions
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+66
View File
@@ -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;
}
+4
View File
@@ -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
+10 -1
View File
@@ -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;
+32
View File
@@ -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)
+1 -1
View File
@@ -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);