diff --git a/Editor/ArmatureWindow.cpp b/Editor/ArmatureWindow.cpp
new file mode 100644
index 000000000..79a06884a
--- /dev/null
+++ b/Editor/ArmatureWindow.cpp
@@ -0,0 +1,201 @@
+#include "stdafx.h"
+#include "ArmatureWindow.h"
+#include "Editor.h"
+
+using namespace wi::ecs;
+using namespace wi::scene;
+
+void ArmatureWindow::Create(EditorComponent* _editor)
+{
+ editor = _editor;
+
+ wi::gui::Window::Create(ICON_ARMATURE " Armature", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
+ SetSize(XMFLOAT2(670, 300));
+
+ closeButton.SetTooltip("Delete ArmatureComponent");
+ OnClose([=](wi::gui::EventArgs args) {
+
+ wi::Archive& archive = editor->AdvanceHistory();
+ archive << EditorComponent::HISTORYOP_COMPONENT_DATA;
+ editor->RecordEntity(archive, entity);
+
+ editor->GetCurrentScene().armatures.Remove(entity);
+
+ editor->RecordEntity(archive, entity);
+
+ editor->optionsWnd.RefreshEntityTree();
+ });
+
+ float x = 60;
+ float y = 4;
+ float hei = 20;
+ float step = hei + 2;
+ float wid = 220;
+
+ resetPoseButton.Create("Reset Pose");
+ resetPoseButton.SetSize(XMFLOAT2(wid, hei));
+ resetPoseButton.OnClick([=](wi::gui::EventArgs args) {
+ wi::scene::Scene& scene = editor->GetCurrentScene();
+ const ArmatureComponent* armature = scene.armatures.GetComponent(entity);
+ if (armature == nullptr)
+ return;
+
+ XMMATRIX R = XMMatrixIdentity();
+ const TransformComponent* armature_transform = scene.transforms.GetComponent(entity);
+ if (armature_transform != nullptr)
+ {
+ R = XMMatrixInverse(nullptr, XMLoadFloat4x4(&armature_transform->world));
+ }
+
+ for (size_t i = 0; i < armature->boneCollection.size(); ++i)
+ {
+ Entity bone = armature->boneCollection[i];
+ TransformComponent* transform = scene.transforms.GetComponent(bone);
+ if (transform != nullptr)
+ {
+ transform->ClearTransform();
+ transform->MatrixTransform(XMMatrixInverse(nullptr, XMLoadFloat4x4(&armature->inverseBindMatrices[i])) * R);
+ transform->UpdateTransform();
+ const HierarchyComponent* hier = scene.hierarchy.GetComponent(bone);
+ if (hier != nullptr && hier->parentID != INVALID_ENTITY)
+ {
+ scene.Component_Attach(bone, hier->parentID, false);
+ }
+ }
+ }
+ });
+ AddWidget(&resetPoseButton);
+
+ boneList.Create("Bones: ");
+ boneList.SetSize(XMFLOAT2(wid, 200));
+ boneList.SetPos(XMFLOAT2(4, y += step));
+ boneList.OnSelect([=](wi::gui::EventArgs args) {
+
+ if (args.iValue < 0)
+ return;
+
+ wi::Archive& archive = editor->AdvanceHistory();
+ archive << EditorComponent::HISTORYOP_SELECTION;
+ // record PREVIOUS selection state...
+ editor->RecordSelection(archive);
+
+ editor->translator.selected.clear();
+
+ for (int i = 0; i < boneList.GetItemCount(); ++i)
+ {
+ const wi::gui::TreeList::Item& item = boneList.GetItem(i);
+ if (item.selected)
+ {
+ wi::scene::PickResult pick;
+ pick.entity = (Entity)item.userdata;
+ editor->AddSelected(pick);
+ }
+ }
+
+ // record NEW selection state...
+ editor->RecordSelection(archive);
+
+ editor->optionsWnd.RefreshEntityTree();
+
+ });
+ AddWidget(&boneList);
+
+
+ SetMinimized(true);
+ SetVisible(false);
+
+ SetEntity(INVALID_ENTITY);
+}
+
+void ArmatureWindow::SetEntity(Entity entity)
+{
+ if (this->entity == entity)
+ return;
+
+ Scene& scene = editor->GetCurrentScene();
+
+ const ArmatureComponent* armature = scene.armatures.GetComponent(entity);
+
+ if (armature != nullptr || IsCollapsed())
+ {
+ this->entity = entity;
+ RefreshBoneList();
+ }
+}
+void ArmatureWindow::RefreshBoneList()
+{
+ Scene& scene = editor->GetCurrentScene();
+
+ const ArmatureComponent* armature = scene.armatures.GetComponent(entity);
+
+ if (armature != nullptr)
+ {
+ boneList.ClearItems();
+ for (Entity bone : armature->boneCollection)
+ {
+ wi::gui::TreeList::Item item;
+ item.userdata = bone;
+ item.name += ICON_BONE " ";
+
+ const NameComponent* name = scene.names.GetComponent(bone);
+ if (name == nullptr)
+ {
+ item.name += "[no_name] " + std::to_string(entity);
+ }
+ else if (name->name.empty())
+ {
+ item.name += "[name_empty] " + std::to_string(entity);
+ }
+ else
+ {
+ item.name += name->name;
+ }
+ boneList.AddItem(item);
+ }
+ }
+}
+
+void ArmatureWindow::ResizeLayout()
+{
+ wi::gui::Window::ResizeLayout();
+ const float padding = 4;
+ const float width = GetWidgetAreaSize().x;
+ float y = padding;
+ float jump = 20;
+
+ const float margin_left = 110;
+ const float margin_right = 45;
+
+ auto add = [&](wi::gui::Widget& widget) {
+ if (!widget.IsVisible())
+ return;
+ widget.SetPos(XMFLOAT2(margin_left, y));
+ widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y));
+ y += widget.GetSize().y;
+ y += padding;
+ };
+ auto add_right = [&](wi::gui::Widget& widget) {
+ if (!widget.IsVisible())
+ return;
+ widget.SetPos(XMFLOAT2(width - margin_right - widget.GetSize().x, y));
+ y += widget.GetSize().y;
+ y += padding;
+ };
+ auto add_fullwidth = [&](wi::gui::Widget& widget) {
+ if (!widget.IsVisible())
+ return;
+ const float margin_left = padding;
+ const float margin_right = padding;
+ widget.SetPos(XMFLOAT2(margin_left, y));
+ widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y));
+ y += widget.GetSize().y;
+ y += padding;
+ };
+
+ add_fullwidth(resetPoseButton);
+
+ y += jump;
+
+ add_fullwidth(boneList);
+
+}
diff --git a/Editor/ArmatureWindow.h b/Editor/ArmatureWindow.h
new file mode 100644
index 000000000..d43c651d6
--- /dev/null
+++ b/Editor/ArmatureWindow.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "WickedEngine.h"
+
+class EditorComponent;
+
+class ArmatureWindow : public wi::gui::Window
+{
+public:
+ void Create(EditorComponent* editor);
+
+ EditorComponent* editor = nullptr;
+ wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY;
+ void SetEntity(wi::ecs::Entity entity);
+ void RefreshBoneList();
+
+ wi::gui::Button resetPoseButton;
+ wi::gui::TreeList boneList;
+
+ void ResizeLayout() override;
+};
+
diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt
index ee3d51f96..f669c7da2 100644
--- a/Editor/CMakeLists.txt
+++ b/Editor/CMakeLists.txt
@@ -35,6 +35,7 @@ set (SOURCE_FILES
ColliderWindow.cpp
HierarchyWindow.cpp
ExpressionWindow.cpp
+ ArmatureWindow.cpp
OptionsWindow.cpp
ComponentsWindow.cpp
TerrainGenerator.cpp
diff --git a/Editor/ComponentsWindow.cpp b/Editor/ComponentsWindow.cpp
index a91ca318b..03ad3c30d 100644
--- a/Editor/ComponentsWindow.cpp
+++ b/Editor/ComponentsWindow.cpp
@@ -39,6 +39,7 @@ void ComponentsWindow::Create(EditorComponent* _editor)
hierarchyWnd.Create(editor);
cameraComponentWnd.Create(editor);
expressionWnd.Create(editor);
+ armatureWnd.Create(editor);
newComponentCombo.Create("Add: ");
@@ -299,6 +300,7 @@ void ComponentsWindow::Create(EditorComponent* _editor)
AddWidget(&hierarchyWnd);
AddWidget(&cameraComponentWnd);
AddWidget(&expressionWnd);
+ AddWidget(&armatureWnd);
materialWnd.SetVisible(false);
weatherWnd.SetVisible(false);
@@ -324,7 +326,7 @@ void ComponentsWindow::Create(EditorComponent* _editor)
hierarchyWnd.SetVisible(false);
cameraComponentWnd.SetVisible(false);
expressionWnd.SetVisible(false);
-
+ armatureWnd.SetVisible(false);
SetSize(editor->optionsWnd.GetSize());
}
@@ -679,4 +681,17 @@ void ComponentsWindow::ResizeLayout()
{
expressionWnd.SetVisible(false);
}
+
+ if (scene.armatures.Contains(armatureWnd.entity))
+ {
+ armatureWnd.SetVisible(true);
+ armatureWnd.SetPos(pos);
+ armatureWnd.SetSize(XMFLOAT2(width, armatureWnd.GetScale().y));
+ pos.y += armatureWnd.GetSize().y;
+ pos.y += padding;
+ }
+ else
+ {
+ armatureWnd.SetVisible(false);
+ }
}
diff --git a/Editor/ComponentsWindow.h b/Editor/ComponentsWindow.h
index 3d399d694..fb689bb79 100644
--- a/Editor/ComponentsWindow.h
+++ b/Editor/ComponentsWindow.h
@@ -24,6 +24,7 @@
#include "HierarchyWindow.h"
#include "CameraComponentWindow.h"
#include "ExpressionWindow.h"
+#include "ArmatureWindow.h"
class EditorComponent;
@@ -61,4 +62,5 @@ public:
HierarchyWindow hierarchyWnd;
CameraComponentWindow cameraComponentWnd;
ExpressionWindow expressionWnd;
+ ArmatureWindow armatureWnd;
};
diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp
index 7c9db43dd..27a510ceb 100644
--- a/Editor/Editor.cpp
+++ b/Editor/Editor.cpp
@@ -348,6 +348,7 @@ void EditorComponent::Load()
componentsWnd.hierarchyWnd.SetEntity(INVALID_ENTITY);
componentsWnd.cameraComponentWnd.SetEntity(INVALID_ENTITY);
componentsWnd.expressionWnd.SetEntity(INVALID_ENTITY);
+ componentsWnd.armatureWnd.SetEntity(INVALID_ENTITY);
optionsWnd.RefreshEntityTree();
ResetHistory();
@@ -954,7 +955,7 @@ void EditorComponent::Update(float dt)
if (translator.selected.empty() && object->GetRenderTypes() & wi::enums::RENDERTYPE_WATER)
{
// if water, then put a water ripple onto it:
- scene.PutWaterRipple("images/ripple.png", hovered.position);
+ scene.PutWaterRipple("../Content/models/ripple.png", hovered.position);
}
else if (componentsWnd.decalWnd.IsEnabled() && componentsWnd.decalWnd.placementCheckBox.GetCheck() && wi::input::Press(wi::input::MOUSE_BUTTON_LEFT))
{
@@ -1303,6 +1304,7 @@ void EditorComponent::Update(float dt)
componentsWnd.hierarchyWnd.SetEntity(INVALID_ENTITY);
componentsWnd.cameraComponentWnd.SetEntity(INVALID_ENTITY);
componentsWnd.expressionWnd.SetEntity(INVALID_ENTITY);
+ componentsWnd.armatureWnd.SetEntity(INVALID_ENTITY);
}
else
{
@@ -1341,6 +1343,7 @@ void EditorComponent::Update(float dt)
componentsWnd.hierarchyWnd.SetEntity(picked.entity);
componentsWnd.cameraComponentWnd.SetEntity(picked.entity);
componentsWnd.expressionWnd.SetEntity(picked.entity);
+ componentsWnd.armatureWnd.SetEntity(picked.entity);
if (picked.subsetIndex >= 0)
{
diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems
index 304630c49..4ce698489 100644
--- a/Editor/Editor_SOURCE.vcxitems
+++ b/Editor/Editor_SOURCE.vcxitems
@@ -15,6 +15,7 @@
+
@@ -132,6 +133,7 @@
+
diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters
index cde3855c3..dc0c25a2b 100644
--- a/Editor/Editor_SOURCE.vcxitems.filters
+++ b/Editor/Editor_SOURCE.vcxitems.filters
@@ -83,6 +83,7 @@
+
@@ -130,6 +131,7 @@
+
diff --git a/Editor/ObjectWindow.cpp b/Editor/ObjectWindow.cpp
index 0bfa5669c..489bb2b1f 100644
--- a/Editor/ObjectWindow.cpp
+++ b/Editor/ObjectWindow.cpp
@@ -284,6 +284,24 @@ void ObjectWindow::Create(EditorComponent* _editor)
float step = hei + 2;
float wid = 130;
+ meshCombo.Create("Mesh: ");
+ meshCombo.SetSize(XMFLOAT2(wid, hei));
+ meshCombo.OnSelect([=](wi::gui::EventArgs args) {
+ wi::scene::Scene& scene = editor->GetCurrentScene();
+ ObjectComponent* object = scene.objects.GetComponent(entity);
+ if (object == nullptr)
+ return;
+
+ wi::Archive& archive = editor->AdvanceHistory();
+ archive << EditorComponent::HISTORYOP_COMPONENT_DATA;
+ editor->RecordEntity(archive, entity);
+
+ object->meshID = (Entity)args.userdata;
+
+ editor->RecordEntity(archive, entity);
+ });
+ AddWidget(&meshCombo);
+
renderableCheckBox.Create("Renderable: ");
renderableCheckBox.SetTooltip("Set object to be participating in rendering.");
renderableCheckBox.SetSize(XMFLOAT2(hei, hei));
@@ -556,6 +574,23 @@ void ObjectWindow::SetEntity(Entity entity)
{
SetEnabled(true);
+ meshCombo.ClearItems();
+ meshCombo.AddItem("INVALID_ENTITY", (uint64_t)INVALID_ENTITY);
+ for (size_t i = 0; i < scene.meshes.GetCount(); ++i)
+ {
+ Entity meshID = scene.meshes.GetEntity(i);
+ const NameComponent* name = scene.names.GetComponent(meshID);
+ if (name == nullptr)
+ {
+ meshCombo.AddItem(std::to_string(meshID), (uint64_t)meshID);
+ }
+ else
+ {
+ meshCombo.AddItem(name->name, (uint64_t)meshID);
+ }
+ }
+ meshCombo.SetSelectedByUserdataWithoutCallback(object->meshID);
+
renderableCheckBox.SetCheck(object->IsRenderable());
shadowCheckBox.SetCheck(object->IsCastingShadow());
cascadeMaskSlider.SetValue((float)object->cascadeMask);
@@ -591,11 +626,12 @@ void ObjectWindow::ResizeLayout()
float y = padding;
float jump = 20;
+ float margin_left = 140;
+ float margin_right = 40;
+
auto add = [&](wi::gui::Widget& widget) {
if (!widget.IsVisible())
return;
- const float margin_left = 140;
- const float margin_right = 40;
widget.SetPos(XMFLOAT2(margin_left, y));
widget.SetSize(XMFLOAT2(width - margin_left - margin_right, widget.GetScale().y));
y += widget.GetSize().y;
@@ -604,7 +640,6 @@ void ObjectWindow::ResizeLayout()
auto add_right = [&](wi::gui::Widget& widget) {
if (!widget.IsVisible())
return;
- const float margin_right = 40;
widget.SetPos(XMFLOAT2(width - margin_right - widget.GetSize().x, y));
y += widget.GetSize().y;
y += padding;
@@ -620,6 +655,12 @@ void ObjectWindow::ResizeLayout()
y += padding;
};
+ margin_left = 80;
+
+ add(meshCombo);
+
+ margin_left = 140;
+
add_right(renderableCheckBox);
add_right(shadowCheckBox);
add(ditherSlider);
@@ -628,6 +669,9 @@ void ObjectWindow::ResizeLayout()
add(colorComboBox);
add_fullwidth(colorPicker);
add(lightmapResolutionSlider);
+
+ margin_left = 80;
+
add(lightmapSourceUVSetComboBox);
add(generateLightmapButton);
add(stopLightmapGenButton);
diff --git a/Editor/ObjectWindow.h b/Editor/ObjectWindow.h
index f146e437b..712f08a50 100644
--- a/Editor/ObjectWindow.h
+++ b/Editor/ObjectWindow.h
@@ -12,6 +12,7 @@ public:
wi::ecs::Entity entity;
void SetEntity(wi::ecs::Entity entity);
+ wi::gui::ComboBox meshCombo;
wi::gui::CheckBox renderableCheckBox;
wi::gui::CheckBox shadowCheckBox;
wi::gui::Slider ditherSlider;
diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h
index dad647c49..764cfe59c 100644
--- a/WickedEngine/wiScene.h
+++ b/WickedEngine/wiScene.h
@@ -1485,7 +1485,7 @@ namespace wi::scene
Blend,
};
- float blink_frequency = 0; // number of blinks per second
+ float blink_frequency = 0.3f; // number of blinks per second
float blink_length = 0.1f; // blink's completion time in seconds
int blink_count = 2;
float look_frequency = 0; // number of lookAt changes per second