diff --git a/Content/models/animation_test.wiscene b/Content/models/animation_test.wiscene new file mode 100644 index 000000000..05c033b35 Binary files /dev/null and b/Content/models/animation_test.wiscene differ diff --git a/Content/models/morph_target_animation_test.wiscene b/Content/models/morph_target_animation_test.wiscene new file mode 100644 index 000000000..b2f542d39 Binary files /dev/null and b/Content/models/morph_target_animation_test.wiscene differ diff --git a/Editor/AnimationWindow.cpp b/Editor/AnimationWindow.cpp index 9c8245d66..294043e6c 100644 --- a/Editor/AnimationWindow.cpp +++ b/Editor/AnimationWindow.cpp @@ -81,8 +81,7 @@ void AnimationWindow::Create(EditorComponent* _editor) loopedCheckBox.SetCheckText(ICON_LOOP); AddWidget(&loopedCheckBox); - playButton.Create("Play"); - playButton.SetTooltip("Play/Pause animation."); + playButton.Create(ICON_PLAY); playButton.SetSize(XMFLOAT2(100, hei)); playButton.SetPos(XMFLOAT2(loopedCheckBox.GetPos().x + loopedCheckBox.GetSize().x + 5, y)); playButton.OnClick([&](wi::gui::EventArgs args) { @@ -101,9 +100,9 @@ void AnimationWindow::Create(EditorComponent* _editor) }); AddWidget(&playButton); - stopButton.Create("Stop"); - stopButton.SetTooltip("Stop animation."); - stopButton.SetSize(XMFLOAT2(100, hei)); + stopButton.Create(ICON_STOP); + stopButton.SetTooltip("Stop"); + stopButton.SetSize(XMFLOAT2(70, hei)); stopButton.SetPos(XMFLOAT2(playButton.GetPos().x + playButton.GetSize().x + 5, y)); stopButton.OnClick([&](wi::gui::EventArgs args) { AnimationComponent* animation = editor->GetCurrentScene().animations.GetComponent(entity); @@ -197,7 +196,12 @@ void AnimationWindow::Create(EditorComponent* _editor) recordCombo.AddItem("Rotation " ICON_ROTATE); recordCombo.AddItem("Scale " ICON_SCALE); recordCombo.AddItem("Morph weights " ICON_MESH); - recordCombo.AddItem("Close loop " ICON_LOOP); + recordCombo.AddItem("Light [color] " ICON_POINTLIGHT); + recordCombo.AddItem("Light [intensity] " ICON_POINTLIGHT); + recordCombo.AddItem("Light [range] " ICON_POINTLIGHT); + recordCombo.AddItem("Light [inner cone] " ICON_POINTLIGHT); + recordCombo.AddItem("Light [outer cone] " ICON_POINTLIGHT); + recordCombo.AddItem("Close loop " ICON_LOOP, ~0ull); recordCombo.OnSelect([&](wi::gui::EventArgs args) { if (args.iValue == 0) return; @@ -209,7 +213,7 @@ void AnimationWindow::Create(EditorComponent* _editor) { const float current_time = animation->timer; - if (args.iValue == 6) + if (args.userdata == ~0ull) { // Close loop: for (auto& channel : animation->channels) @@ -237,6 +241,8 @@ void AnimationWindow::Create(EditorComponent* _editor) switch (channel.path) { case wi::scene::AnimationComponent::AnimationChannel::TRANSLATION: + case wi::scene::AnimationComponent::AnimationChannel::SCALE: + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR: { animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 0]); animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 1]); @@ -251,13 +257,6 @@ void AnimationWindow::Create(EditorComponent* _editor) animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 4 + 3]); } break; - case wi::scene::AnimationComponent::AnimationChannel::SCALE: - { - animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 0]); - animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 1]); - animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 2]); - } - break; case wi::scene::AnimationComponent::AnimationChannel::WEIGHTS: { const MeshComponent* mesh = scene.meshes.GetComponent(channel.target); @@ -278,6 +277,14 @@ void AnimationWindow::Create(EditorComponent* _editor) } } break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY: + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE: + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE: + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE: + { + animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst]); + } + break; default: break; } @@ -287,7 +294,7 @@ void AnimationWindow::Create(EditorComponent* _editor) else { // Add keyframe type: - wi::vector paths; + wi::vector paths; // stack allocation would be better here.. switch (args.iValue) { @@ -309,6 +316,21 @@ void AnimationWindow::Create(EditorComponent* _editor) case 5: paths.push_back(AnimationComponent::AnimationChannel::Path::WEIGHTS); break; + case 6: + paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_COLOR); + break; + case 7: + paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY); + break; + case 8: + paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_RANGE); + break; + case 9: + paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE); + break; + case 10: + paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE); + break; } for (auto path : paths) @@ -421,6 +443,78 @@ void AnimationWindow::Create(EditorComponent* _editor) } } break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR: + { + const LightComponent* light = scene.lights.GetComponent(channel.target); + if (light != nullptr) + { + animation_data->keyframe_data.push_back(light->color.x); + animation_data->keyframe_data.push_back(light->color.y); + animation_data->keyframe_data.push_back(light->color.z); + } + else + { + animation_data->keyframe_times.pop_back(); + animation->channels.pop_back(); + } + } + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY: + { + const LightComponent* light = scene.lights.GetComponent(channel.target); + if (light != nullptr) + { + animation_data->keyframe_data.push_back(light->intensity); + } + else + { + animation_data->keyframe_times.pop_back(); + animation->channels.pop_back(); + } + } + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE: + { + const LightComponent* light = scene.lights.GetComponent(channel.target); + if (light != nullptr) + { + animation_data->keyframe_data.push_back(light->range); + } + else + { + animation_data->keyframe_times.pop_back(); + animation->channels.pop_back(); + } + } + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE: + { + const LightComponent* light = scene.lights.GetComponent(channel.target); + if (light != nullptr) + { + animation_data->keyframe_data.push_back(light->innerConeAngle); + } + else + { + animation_data->keyframe_times.pop_back(); + animation->channels.pop_back(); + } + } + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE: + { + const LightComponent* light = scene.lights.GetComponent(channel.target); + if (light != nullptr) + { + animation_data->keyframe_data.push_back(light->outerConeAngle); + } + else + { + animation_data->keyframe_times.pop_back(); + animation->channels.pop_back(); + } + } + break; default: break; } @@ -438,7 +532,7 @@ void AnimationWindow::Create(EditorComponent* _editor) keyframesList.Create("Keyframes"); keyframesList.SetSize(XMFLOAT2(wid, 200)); - keyframesList.SetPos(XMFLOAT2(x, y += step)); + keyframesList.SetPos(XMFLOAT2(4, y += step)); keyframesList.OnSelect([=](wi::gui::EventArgs args) { wi::scene::Scene& scene = editor->GetCurrentScene(); AnimationComponent* animation = scene.animations.GetComponent(entity); @@ -453,9 +547,7 @@ void AnimationWindow::Create(EditorComponent* _editor) const AnimationDataComponent* animation_data = scene.animation_datas.GetComponent(sam.data); if (animation_data != nullptr && animation_data->keyframe_times.size() > timeIndex) { - wi::vector tmp = animation_data->keyframe_times; - std::sort(tmp.begin(), tmp.end()); - float time = tmp[timeIndex]; + float time = animation_data->keyframe_times[timeIndex]; animation->timer = time; } } @@ -479,6 +571,8 @@ void AnimationWindow::Create(EditorComponent* _editor) switch (channel.path) { case AnimationComponent::AnimationChannel::Path::TRANSLATION: + case AnimationComponent::AnimationChannel::Path::SCALE: + case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR: animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex); animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 3, animation_data->keyframe_data.begin() + timeIndex * 3 + 3); break; @@ -486,10 +580,6 @@ void AnimationWindow::Create(EditorComponent* _editor) animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex); animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 4, animation_data->keyframe_data.begin() + timeIndex * 4 + 4); break; - case AnimationComponent::AnimationChannel::Path::SCALE: - animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex); - animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 3, animation_data->keyframe_data.begin() + timeIndex * 3 + 3); - break; case AnimationComponent::AnimationChannel::Path::WEIGHTS: { MeshComponent* mesh = scene.meshes.GetComponent(channel.target); @@ -506,6 +596,12 @@ void AnimationWindow::Create(EditorComponent* _editor) } } break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY: + case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE: + case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE: + case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE: + animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex); + break; default: break; } @@ -561,11 +657,13 @@ void AnimationWindow::Update() if (animation.IsPlaying()) { - playButton.SetText("Pause"); + playButton.SetText(ICON_PAUSE); + playButton.SetTooltip("Pause"); } else { - playButton.SetText("Play"); + playButton.SetText(ICON_PLAY); + playButton.SetTooltip("Play"); } if(!animation.samplers.empty()) @@ -607,7 +705,6 @@ void AnimationWindow::RefreshKeyframesList() wi::gui::TreeList::Item item; switch (channel.path) { - default: case wi::scene::AnimationComponent::AnimationChannel::TRANSLATION: item.name += ICON_TRANSLATE " "; break; @@ -620,6 +717,23 @@ void AnimationWindow::RefreshKeyframesList() case wi::scene::AnimationComponent::AnimationChannel::WEIGHTS: item.name += ICON_MESH " "; break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR: + item.name += ICON_POINTLIGHT " [color] "; + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY: + item.name += ICON_POINTLIGHT " [intensity] "; + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE: + item.name += ICON_POINTLIGHT " [range] "; + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE: + item.name += ICON_POINTLIGHT " [inner cone] "; + break; + case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE: + item.name += ICON_POINTLIGHT " [outer cone] "; + break; + default: + break; } const NameComponent* name = scene.names.GetComponent(channel.target); if (name == nullptr) @@ -660,3 +774,12 @@ void AnimationWindow::RefreshKeyframesList() channelIndex++; } } + + +void AnimationWindow::ResizeLayout() +{ + wi::gui::Window::ResizeLayout(); + const float padding = 4; + const float width = GetWidgetAreaSize().x - padding * 2; + keyframesList.SetSize(XMFLOAT2(width, keyframesList.GetSize().y)); +} diff --git a/Editor/AnimationWindow.h b/Editor/AnimationWindow.h index 8c13052e8..d7a49c004 100644 --- a/Editor/AnimationWindow.h +++ b/Editor/AnimationWindow.h @@ -28,5 +28,7 @@ public: void Update(); void RefreshKeyframesList(); + + void ResizeLayout() override; }; diff --git a/Editor/IKWindow.cpp b/Editor/IKWindow.cpp index c30fe77bc..23c10c531 100644 --- a/Editor/IKWindow.cpp +++ b/Editor/IKWindow.cpp @@ -9,7 +9,7 @@ using namespace wi::scene; void IKWindow::Create(EditorComponent* _editor) { editor = _editor; - wi::gui::Window::Create("Inverse Kinematics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); + wi::gui::Window::Create(ICON_IK " Inverse Kinematics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); SetSize(XMFLOAT2(400, 110)); closeButton.SetTooltip("Delete InverseKinematicsComponent"); diff --git a/Editor/IconDefinitions.h b/Editor/IconDefinitions.h index 71c8bb0be..6287cc14a 100644 --- a/Editor/IconDefinitions.h +++ b/Editor/IconDefinitions.h @@ -24,6 +24,8 @@ #define ICON_MATERIAL ICON_FA_FILL_DRIP #define ICON_WEATHER ICON_FA_CLOUD #define ICON_BONE ICON_FA_BONE +#define ICON_IK ICON_FA_HAND_FIST +#define ICON_NAME ICON_FA_COMMENT_DOTS #define ICON_TERRAIN ICON_FA_MOUNTAIN_SUN @@ -46,3 +48,6 @@ #define ICON_SQUARE ICON_FA_SQUARE_FULL #define ICON_CUBE ICON_FA_CUBE #define ICON_LOOP ICON_FA_REPEAT +#define ICON_PLAY ICON_FA_PLAY +#define ICON_PAUSE ICON_FA_PAUSE +#define ICON_STOP ICON_FA_STOP diff --git a/Editor/NameWindow.cpp b/Editor/NameWindow.cpp index a1e4f136e..51c6991f5 100644 --- a/Editor/NameWindow.cpp +++ b/Editor/NameWindow.cpp @@ -9,7 +9,7 @@ using namespace wi::scene; void NameWindow::Create(EditorComponent* _editor) { editor = _editor; - wi::gui::Window::Create("Name", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); + wi::gui::Window::Create(ICON_NAME " Name", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); SetSize(XMFLOAT2(360, 60)); closeButton.SetTooltip("Delete NameComponent"); diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index a96e77a41..4bfc8b77c 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -272,6 +272,7 @@ void OptionsWindow::Create(EditorComponent* _editor) filterCombo.AddItem("Force " ICON_FORCE, (uint64_t)Filter::Force); filterCombo.AddItem("Emitter " ICON_EMITTER, (uint64_t)Filter::Emitter); filterCombo.AddItem("Hairparticle " ICON_HAIR, (uint64_t)Filter::Hairparticle); + filterCombo.AddItem("Inverse Kinematics " ICON_IK, (uint64_t)Filter::IK); filterCombo.SetTooltip("Apply filtering to the Entities"); filterCombo.OnSelect([&](wi::gui::EventArgs args) { filter = (Filter)args.userdata; @@ -642,6 +643,8 @@ void OptionsWindow::Create(EditorComponent* _editor) editor->saveButton.sprites[i].params.enableCornerRounding(); editor->saveButton.sprites[i].params.corners_rounding[2].radius = 10; } + editor->componentsWnd.weatherWnd.default_sky_horizon = dark_point; + editor->componentsWnd.weatherWnd.default_sky_zenith = theme_color_idle; }); AddWidget(&themeCombo); @@ -866,11 +869,16 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level) { item.name += ICON_WEATHER " "; } + if (scene.inverse_kinematics.Contains(entity)) + { + item.name += ICON_IK " "; + } if (entity == terragen.terrainEntity) { item.name += ICON_TERRAIN " "; } - for (size_t i = 0; i < scene.armatures.GetCount(); ++i) + bool bone_found = false; + for (size_t i = 0; i < scene.armatures.GetCount() && !bone_found; ++i) { const ArmatureComponent& armature = scene.armatures[i]; for (Entity bone : armature.boneCollection) @@ -878,6 +886,7 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level) if (entity == bone) { item.name += ICON_BONE " "; + bone_found = true; break; } } @@ -1080,7 +1089,7 @@ void OptionsWindow::RefreshEntityTree() } } - if (has_flag(filter, Filter::All)) + if (has_flag(filter, Filter::IK)) { for (size_t i = 0; i < scene.inverse_kinematics.GetCount(); ++i) { diff --git a/Editor/OptionsWindow.h b/Editor/OptionsWindow.h index 1f7aab669..71ea19ec8 100644 --- a/Editor/OptionsWindow.h +++ b/Editor/OptionsWindow.h @@ -55,6 +55,7 @@ public: Force = 1 << 10, Emitter = 1 << 11, Hairparticle = 1 << 12, + IK = 1 << 13, All = ~0ull, } filter = Filter::All; diff --git a/Editor/WeatherWindow.cpp b/Editor/WeatherWindow.cpp index aef7300a8..d004a7318 100644 --- a/Editor/WeatherWindow.cpp +++ b/Editor/WeatherWindow.cpp @@ -772,8 +772,8 @@ void WeatherWindow::Update() { scene.weather = {}; scene.weather.SetSimpleSky(true); - scene.weather.zenith = XMFLOAT3(1, 1, 1); - scene.weather.horizon = wi::Color(20, 20, 20); + scene.weather.zenith = default_sky_zenith; + scene.weather.horizon = default_sky_horizon; } } diff --git a/Editor/WeatherWindow.h b/Editor/WeatherWindow.h index 5db68977a..b5e631169 100644 --- a/Editor/WeatherWindow.h +++ b/Editor/WeatherWindow.h @@ -18,6 +18,9 @@ public: wi::scene::WeatherComponent& GetWeather() const; void InvalidateProbes() const; + XMFLOAT3 default_sky_horizon = XMFLOAT3(0, 0, 0); + XMFLOAT3 default_sky_zenith = XMFLOAT3(0, 0, 0); + wi::gui::Button primaryButton; wi::gui::CheckBox heightFogCheckBox; wi::gui::Slider fogStartSlider; diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index c5b3782fe..3cbe89448 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -2791,9 +2791,11 @@ namespace wi::scene const float right = animationdata->keyframe_times[keyRight]; TransformComponent transform; + LightComponent light; TransformComponent* target_transform = nullptr; MeshComponent* target_mesh = nullptr; + LightComponent* target_light = nullptr; if (channel.path == AnimationComponent::AnimationChannel::Path::WEIGHTS) { @@ -2805,6 +2807,19 @@ namespace wi::scene continue; animation.morph_weights_temp.resize(target_mesh->targets.size()); } + else if ( + channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_COLOR || + channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY || + channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_RANGE || + channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE || + channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE + ) + { + target_light = lights.GetComponent(channel.target); + if (target_light == nullptr) + continue; + light = *target_light; + } else { target_transform = transforms.GetComponent(channel.target); @@ -2850,6 +2865,36 @@ namespace wi::scene } } break; + case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3); + light.color = ((const XMFLOAT3*)animationdata->keyframe_data.data())[key]; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + light.intensity = animationdata->keyframe_data[key]; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + light.range = animationdata->keyframe_data[key]; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + light.innerConeAngle = animationdata->keyframe_data[key]; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + light.outerConeAngle = animationdata->keyframe_data[key]; + } + break; } } break; @@ -2912,6 +2957,52 @@ namespace wi::scene } } break; + case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3); + const XMFLOAT3* data = (const XMFLOAT3*)animationdata->keyframe_data.data(); + XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft]); + XMVECTOR vRight = XMLoadFloat3(&data[keyRight]); + XMVECTOR vAnim = XMVectorLerp(vLeft, vRight, t); + XMStoreFloat3(&light.color, vAnim); + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft]; + float vRight = animationdata->keyframe_data[keyRight]; + float vAnim = wi::math::Lerp(vLeft, vRight, t); + light.intensity = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft]; + float vRight = animationdata->keyframe_data[keyRight]; + float vAnim = wi::math::Lerp(vLeft, vRight, t); + light.range = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft]; + float vRight = animationdata->keyframe_data[keyRight]; + float vAnim = wi::math::Lerp(vLeft, vRight, t); + light.innerConeAngle = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft]; + float vRight = animationdata->keyframe_data[keyRight]; + float vAnim = wi::math::Lerp(vLeft, vRight, t); + light.outerConeAngle = vAnim; + } + break; } } break; @@ -2985,17 +3076,73 @@ namespace wi::scene } } break; + case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3 * 3); + const XMFLOAT3* data = (const XMFLOAT3*)animationdata->keyframe_data.data(); + XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft * 3 + 1]); + XMVECTOR vLeftTanOut = dt * XMLoadFloat3(&data[keyLeft * 3 + 2]); + XMVECTOR vRightTanIn = dt * XMLoadFloat3(&data[keyRight * 3 + 0]); + XMVECTOR vRight = XMLoadFloat3(&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; + XMStoreFloat3(&light.color, vAnim); + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1]; + float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2]; + float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0]; + float vRight = animationdata->keyframe_data[keyRight * 3 + 1]; + float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn; + light.intensity = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1]; + float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2]; + float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0]; + float vRight = animationdata->keyframe_data[keyRight * 3 + 1]; + float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn; + light.range = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1]; + float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2]; + float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0]; + float vRight = animationdata->keyframe_data[keyRight * 3 + 1]; + float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn; + light.innerConeAngle = vAnim; + } + break; + case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE: + { + assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size()); + float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1]; + float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2]; + float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0]; + float vRight = animationdata->keyframe_data[keyRight * 3 + 1]; + float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn; + light.outerConeAngle = vAnim; + } + break; } } break; } + const float t = animation.amount; + if (target_transform != nullptr) { target_transform->SetDirty(); - const float t = animation.amount; - const XMVECTOR aS = XMLoadFloat3(&target_transform->scale_local); const XMVECTOR aR = XMLoadFloat4(&target_transform->rotation_local); const XMVECTOR aT = XMLoadFloat3(&target_transform->translation_local); @@ -3015,8 +3162,6 @@ namespace wi::scene if (target_mesh != nullptr) { - const float t = animation.amount; - for (size_t j = 0; j < target_mesh->targets.size(); ++j) { target_mesh->targets[j].weight = wi::math::Lerp(target_mesh->targets[j].weight, animation.morph_weights_temp[j], t); @@ -3025,6 +3170,15 @@ namespace wi::scene target_mesh->dirty_morph = true; } + if (target_light != nullptr) + { + target_light->color = wi::math::Lerp(target_light->color, light.color, t); + target_light->intensity = wi::math::Lerp(target_light->intensity, light.intensity, t); + target_light->range = wi::math::Lerp(target_light->range, light.range, t); + target_light->innerConeAngle = wi::math::Lerp(target_light->innerConeAngle, light.innerConeAngle, t); + target_light->outerConeAngle = wi::math::Lerp(target_light->outerConeAngle, light.outerConeAngle, t); + } + } if (animation.IsPlaying()) @@ -3109,6 +3263,12 @@ namespace wi::scene const XMVECTOR windDir = XMLoadFloat3(&weather.windDirection); const XMVECTOR gravity = XMVectorSet(0, -9.8f, 0, 0); + if (springs.GetCount() > 0) + { + transforms_temp.resize(transforms.GetCount()); + transforms_temp = transforms.GetComponentArray(); // make copy + } + for (size_t i = 0; i < springs.GetCount(); ++i) { SpringComponent& spring = springs[i]; @@ -3117,31 +3277,34 @@ namespace wi::scene continue; } Entity entity = springs.GetEntity(i); - TransformComponent* transform = transforms.GetComponent(entity); - if (transform == nullptr) + size_t transform_index = transforms.GetIndex(entity); + if (transform_index == ~0ull) { assert(0); continue; } + TransformComponent& transform = transforms_temp[transform_index]; if (spring.IsResetting()) { spring.Reset(false); - spring.center_of_mass = transform->GetPosition(); + spring.center_of_mass = transform.GetPosition(); spring.velocity = XMFLOAT3(0, 0, 0); } const HierarchyComponent* hier = hierarchy.GetComponent(entity); - TransformComponent* parent_transform = hier == nullptr ? nullptr : transforms.GetComponent(hier->parentID); - if (parent_transform != nullptr) + size_t parent_index = hier == nullptr ? ~0ull : transforms.GetIndex(hier->parentID); + //TransformComponent* parent_transform = hier == nullptr ? nullptr : transforms.GetComponent(hier->parentID); + if (parent_index != ~0ull) { // Spring hierarchy resolve depends on spring component order! // It works best when parent spring is located before child spring! // It will work the other way, but results will be less convincing - transform->UpdateTransform_Parented(*parent_transform); + const TransformComponent& parent_transform = transforms_temp[parent_index]; + transform.UpdateTransform_Parented(parent_transform); } - const XMVECTOR position_current = transform->GetPositionV(); + const XMVECTOR position_current = transform.GetPositionV(); XMVECTOR position_prev = XMLoadFloat3(&spring.center_of_mass); XMVECTOR force = (position_current - position_prev) * spring.stiffness; @@ -3158,9 +3321,10 @@ namespace wi::scene velocity += force * dt; XMVECTOR position_target = position_prev + velocity * dt; - if (parent_transform != nullptr) + if (parent_index != ~0ull) { - const XMVECTOR position_parent = parent_transform->GetPositionV(); + TransformComponent& parent_transform = transforms_temp[parent_index]; + const XMVECTOR position_parent = parent_transform.GetPositionV(); const XMVECTOR parent_to_child = position_current - position_parent; const XMVECTOR parent_to_target = position_target - position_parent; @@ -3177,25 +3341,34 @@ namespace wi::scene const XMVECTOR axis = XMVector3Normalize(XMVector3Cross(dir_parent_to_child, dir_parent_to_target)); const float angle = XMScalarACos(XMVectorGetX(XMVector3Dot(dir_parent_to_child, dir_parent_to_target))); // don't use std::acos! const XMVECTOR Q = XMQuaternionNormalize(XMQuaternionRotationNormal(axis, angle)); - TransformComponent saved_parent = *parent_transform; + TransformComponent saved_parent = parent_transform; saved_parent.ApplyTransform(); saved_parent.Rotate(Q); saved_parent.UpdateTransform(); - std::swap(saved_parent.world, parent_transform->world); // only store temporary result, not modifying actual local space! + std::swap(saved_parent.world, parent_transform.world); // only store temporary result, not modifying actual local space! } XMStoreFloat3(&spring.center_of_mass, position_target); velocity *= spring.damping; XMStoreFloat3(&spring.velocity, velocity); - *((XMFLOAT3*)&transform->world._41) = spring.center_of_mass; + *((XMFLOAT3*)&transform.world._41) = spring.center_of_mass; + } + + if (springs.GetCount() > 0) + { + for (size_t i = 0; i < transforms.GetCount(); ++i) + { + // IK shouldn't modify local space, so only update the world matrices! + transforms[i].world = transforms_temp[i].world; + } } } void Scene::RunInverseKinematicsUpdateSystem(wi::jobsystem::context& ctx) { if (inverse_kinematics.GetCount() > 0) { - ik_temp.resize(transforms.GetCount()); - ik_temp = transforms.GetComponentArray(); // make copy + transforms_temp.resize(transforms.GetCount()); + transforms_temp = transforms.GetComponentArray(); // make copy } bool recompute_hierarchy = false; @@ -3214,15 +3387,15 @@ namespace wi::scene { continue; } - TransformComponent* transform = &ik_temp[transform_index]; - TransformComponent* target = &ik_temp[target_index]; + TransformComponent& transform = transforms_temp[transform_index]; + TransformComponent& target = transforms_temp[target_index]; - const XMVECTOR target_pos = target->GetPositionV(); + const XMVECTOR target_pos = target.GetPositionV(); for (uint32_t iteration = 0; iteration < ik.iteration_count; ++iteration) { TransformComponent* stack[32] = {}; Entity parent_entity = hier->parentID; - TransformComponent* child_transform = transform; + TransformComponent* child_transform = &transform; for (uint32_t chain = 0; chain < std::min(ik.chain_length, (uint32_t)arraysize(stack)); ++chain) { recompute_hierarchy = true; // any IK will trigger a full transform hierarchy recompute step at the end(**) @@ -3234,19 +3407,19 @@ namespace wi::scene size_t parent_index = transforms.GetIndex(parent_entity); if (parent_index == ~0ull) continue; - TransformComponent* parent_transform = &ik_temp[parent_index]; - const XMVECTOR parent_pos = parent_transform->GetPositionV(); - const XMVECTOR dir_parent_to_ik = XMVector3Normalize(transform->GetPositionV() - parent_pos); + TransformComponent& parent_transform = transforms_temp[parent_index]; + const XMVECTOR parent_pos = parent_transform.GetPositionV(); + const XMVECTOR dir_parent_to_ik = XMVector3Normalize(transform.GetPositionV() - parent_pos); const XMVECTOR dir_parent_to_target = XMVector3Normalize(target_pos - parent_pos); const XMVECTOR axis = XMVector3Normalize(XMVector3Cross(dir_parent_to_ik, dir_parent_to_target)); const float angle = XMScalarACos(XMVectorGetX(XMVector3Dot(dir_parent_to_ik, dir_parent_to_target))); const XMVECTOR Q = XMQuaternionNormalize(XMQuaternionRotationNormal(axis, angle)); // parent to world space: - parent_transform->ApplyTransform(); + parent_transform.ApplyTransform(); // rotate parent: - parent_transform->Rotate(Q); - parent_transform->UpdateTransform(); + parent_transform.Rotate(Q); + parent_transform.UpdateTransform(); // parent back to local space (if parent has parent): const HierarchyComponent* hier_parent = hierarchy.GetComponent(parent_entity); if (hier_parent != nullptr) @@ -3255,15 +3428,15 @@ namespace wi::scene size_t parent_of_parent_index = transforms.GetIndex(parent_of_parent_entity); if (parent_of_parent_index != ~0ull) { - const TransformComponent* transform_parent_of_parent = &ik_temp[parent_of_parent_index]; + const TransformComponent* transform_parent_of_parent = &transforms_temp[parent_of_parent_index]; XMMATRIX parent_of_parent_inverse = XMMatrixInverse(nullptr, XMLoadFloat4x4(&transform_parent_of_parent->world)); - parent_transform->MatrixTransform(parent_of_parent_inverse); + parent_transform.MatrixTransform(parent_of_parent_inverse); // Do not call UpdateTransform() here, to keep parent world matrix in world space! } } // update chain from parent to children: - const TransformComponent* recurse_parent = parent_transform; + const TransformComponent* recurse_parent = &parent_transform; for (int recurse_chain = (int)chain; recurse_chain >= 0; --recurse_chain) { stack[recurse_chain]->UpdateTransform_Parented(*recurse_parent); @@ -3277,7 +3450,7 @@ namespace wi::scene } // move up in the chain by one: - child_transform = parent_transform; + child_transform = &parent_transform; parent_entity = hier_parent->parentID; assert(chain < (uint32_t)arraysize(stack) - 1); // if this is encountered, just extend stack array size @@ -3299,8 +3472,8 @@ namespace wi::scene size_t parent_index = transforms.GetIndex(parentcomponent.parentID); if (transform_index != ~0ull && parent_index != ~0ull) { - TransformComponent* transform_child = &ik_temp[transform_index]; - TransformComponent* transform_parent = &ik_temp[parent_index]; + TransformComponent* transform_child = &transforms_temp[transform_index]; + TransformComponent* transform_parent = &transforms_temp[parent_index]; transform_child->UpdateTransform_Parented(*transform_parent); } } @@ -3311,7 +3484,7 @@ namespace wi::scene for (size_t i = 0; i < transforms.GetCount(); ++i) { // IK shouldn't modify local space, so only update the world matrices! - transforms[i].world = ik_temp[i].world; + transforms[i].world = transforms_temp[i].world; } } } diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index eb7634165..5146a5d49 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -845,7 +845,6 @@ namespace wi::scene LIGHTMAPONLY_STATIC = 1 << 3, }; uint32_t _flags = EMPTY; - XMFLOAT3 color = XMFLOAT3(1, 1, 1); enum LightType { @@ -860,6 +859,8 @@ namespace wi::scene ENUM_FORCE_UINT32 = 0xFFFFFFFF, }; LightType type = POINT; + + XMFLOAT3 color = XMFLOAT3(1, 1, 1); float intensity = 1.0f; // Brightness of light in. The units that this is defined in depend on the type of light. Point and spot lights use luminous intensity in candela (lm/sr) while directional lights use illuminance in lux (lm/m2). https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual float range = 10.0f; float outerConeAngle = XM_PIDIV4; @@ -1112,6 +1113,11 @@ namespace wi::scene ROTATION, SCALE, WEIGHTS, + LIGHT_COLOR, + LIGHT_INTENSITY, + LIGHT_RANGE, + LIGHT_INNERCONE, + LIGHT_OUTERCONE, UNKNOWN, TYPE_FORCE_UINT32 = 0xFFFFFFFF } path = TRANSLATION; @@ -1445,7 +1451,7 @@ namespace wi::scene uint32_t impostorMaterialOffset = ~0u; mutable std::atomic_bool lightmap_refresh_needed{ false }; - wi::vector ik_temp; + wi::vector transforms_temp; // Ocean GPU state: wi::Ocean ocean; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index e5b38b164..a5b1d5d71 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 9; + const int revision = 10; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);