From 75457f17ee0d0b44ed23611ca94258a44260504a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Sat, 14 Oct 2023 16:21:05 +0200 Subject: [PATCH] Sprites and fonts can be added to the scene (#762) --- .../ScriptingAPI-Documentation.md | 53 +- Editor/CMakeLists.txt | 2 + Editor/ComponentsWindow.cpp | 50 ++ Editor/ComponentsWindow.h | 4 + Editor/Editor.cpp | 6 + Editor/Editor_SOURCE.vcxitems | 4 + Editor/Editor_SOURCE.vcxitems.filters | 4 + Editor/FontWindow.cpp | 490 ++++++++++++++ Editor/FontWindow.h | 42 ++ Editor/GeneralWindow.cpp | 1 + Editor/IconDefinitions.h | 2 + Editor/OptionsWindow.cpp | 54 ++ Editor/OptionsWindow.h | 2 + Editor/SpriteWindow.cpp | 599 ++++++++++++++++++ Editor/SpriteWindow.h | 51 ++ WickedEngine/shaders/ShaderInterop_Font.h | 4 +- WickedEngine/shaders/ShaderInterop_Image.h | 14 +- WickedEngine/shaders/fontPS.hlsl | 4 +- WickedEngine/shaders/imageHF.hlsli | 13 +- WickedEngine/shaders/imageVS.hlsl | 2 +- WickedEngine/wiFont.cpp | 131 +++- WickedEngine/wiFont.h | 15 +- WickedEngine/wiGUI.cpp | 2 +- WickedEngine/wiHelper.cpp | 5 +- WickedEngine/wiImage.cpp | 219 ++++--- WickedEngine/wiImage.h | 5 + WickedEngine/wiRenderPath3D.cpp | 7 +- WickedEngine/wiRenderPath3D_PathTracing.cpp | 18 + WickedEngine/wiRenderer.cpp | 143 +++++ WickedEngine/wiRenderer.h | 7 + WickedEngine/wiResourceManager.cpp | 27 + WickedEngine/wiResourceManager.h | 2 + WickedEngine/wiScene.cpp | 42 ++ WickedEngine/wiScene.h | 5 + WickedEngine/wiScene_BindLua.cpp | 169 +++++ WickedEngine/wiScene_BindLua.h | 10 + WickedEngine/wiSprite.cpp | 152 ++++- WickedEngine/wiSprite.h | 24 +- WickedEngine/wiSpriteFont.cpp | 119 +++- WickedEngine/wiSpriteFont.h | 25 +- WickedEngine/wiVersion.cpp | 3 +- 41 files changed, 2335 insertions(+), 196 deletions(-) create mode 100644 Editor/FontWindow.cpp create mode 100644 Editor/FontWindow.h create mode 100644 Editor/SpriteWindow.cpp create mode 100644 Editor/SpriteWindow.h diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index d94e5c494..28b8547de 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -583,20 +583,25 @@ The scene holds components. Entity handles can be used to retrieve associated co - Entity_Duplicate(Entity entity) : int entity -- duplicates all of an entity's components and creates a new entity with them. Returns the clone entity handle - Entity_IsDescendant(Entity entity, Entity ancestor) : bool result -- Check whether entity is a descendant of ancestor. Returns `true` if entity is in the hierarchy tree of ancestor, `false` otherwise -- Component_CreateName(Entity entity) : NameComponent result -- attach a name component to an entity. The returned NameComponent is associated with the entity and can be manipulated -- Component_CreateLayer(Entity entity) : LayerComponent result -- attach a layer component to an entity. The returned LayerComponent is associated with the entity and can be manipulated -- Component_CreateTransform(Entity entity) : TransformComponent result -- attach a transform component to an entity. The returned TransformComponent is associated with the entity and can be manipulated -- Component_CreateLight(Entity entity) : LightComponent result -- attach a light component to an entity. The returned LightComponent is associated with the entity and can be manipulated -- Component_CreateObject(Entity entity) : ObjectComponent result -- attach an object component to an entity. The returned ObjectComponent is associated with the entity and can be manipulated -- Component_CreateInverseKinematics(Entity entity) : InverseKinematicsComponent result -- attach an IK component to an entity. The returned InverseKinematicsComponent is associated with the entity and can be manipulated -- Component_CreateSpring(Entity entity) : SpringComponent result -- attach a spring component to an entity. The returned SpringComponent is associated with the entity and can be manipulated -- Component_CreateScript(Entity entity) : ScriptComponent result -- attach a script component to an entity. The returned ScriptComponent is associated with the entity and can be manipulated -- Component_CreateRigidBodyPhysics(Entity entity) : RigidBodyPhysicsComponent result -- attach a RigidBodyPhysicsComponent to an entity. The returned RigidBodyPhysicsComponent is associated with the entity and can be manipulated -- Component_CreateSoftBodyPhysics(Entity entity) : SoftBodyPhysicsComponent result -- attach a SoftBodyPhysicsComponent to an entity. The returned SoftBodyPhysicsComponent is associated with the entity and can be manipulated -- Component_CreateForceField(Entity entity) : ForceFieldComponent result -- attach a ForceFieldComponent to an entity. The returned ForceFieldComponent is associated with the entity and can be manipulated -- Component_CreateWeather(Entity entity) : WeatherComponent result -- attach a WeatherComponent to an entity. The returned WeatherComponent is associated with the entity and can be manipulated -- Component_CreateSound(Entity entity) : SoundComponent result -- attach a SoundComponent to an entity. The returned SoundComponent is associated with the entity and can be manipulated -- Component_CreateCollider(Entity entity) : ColliderComponent result -- attach a script to an entity. The returned ColliderComponent is associated with the entity and can be manipulated +- Component_CreateName(Entity entity) : NameComponent result -- attach a name component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateLayer(Entity entity) : LayerComponent result -- attach a layer component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateTransform(Entity entity) : TransformComponent result -- attach a transform component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateLight(Entity entity) : LightComponent result -- attach a light component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateObject(Entity entity) : ObjectComponent result -- attach an object component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateInverseKinematics(Entity entity) : InverseKinematicsComponent result -- attach an IK component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateSpring(Entity entity) : SpringComponent result -- attach a spring component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateScript(Entity entity) : ScriptComponent result -- attach a script component to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateRigidBodyPhysics(Entity entity) : RigidBodyPhysicsComponent result -- attach a RigidBodyPhysicsComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateSoftBodyPhysics(Entity entity) : SoftBodyPhysicsComponent result -- attach a SoftBodyPhysicsComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateForceField(Entity entity) : ForceFieldComponent result -- attach a ForceFieldComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateWeather(Entity entity) : WeatherComponent result -- attach a WeatherComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateSound(Entity entity) : SoundComponent result -- attach a SoundComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateCollider(Entity entity) : ColliderComponent result -- attach a ColliderComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateExpression(Entity entity) : ExpressionComponent result -- attach a ExpressionComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateHumanoid(Entity entity) : HumanoidComponent result -- attach a HumanoidComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateDecal(Entity entity) : DecalComponent result -- attach a DecalComponent to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateSprite(Entity entity) : Sprite result -- attach a Sprite to an entity. The returned component is associated with the entity and can be manipulated +- Component_CreateFont(Entity entity) : SpriteFont result -- attach a SpriteFont to an entity. The returned component is associated with the entity and can be manipulated - Component_GetName(Entity entity) : NameComponent? result -- query the name component of the entity (if exists) - Component_GetLayer(Entity entity) : LayerComponent? result -- query the layer component of the entity (if exists) @@ -616,6 +621,11 @@ The scene holds components. Entity handles can be used to retrieve associated co - Component_GetWeather(Entity entity) : WeatherComponent? result -- query the WeatherComponent of the entity (if exists) - Component_GetSound(Entity entity) : SoundComponent? result -- query the SoundComponent of the entity (if exists) - Component_GetCollider(Entity entity) : ColliderComponent? result -- query the ColliderComponent of the entity (if exists) +- Component_GetExpression(Entity entity) : ExpressionComponent? result -- query the ExpressionComponent of the entity (if exists) +- Component_GetHumanoid(Entity entity) : HumanoidComponent? result -- query the HumanoidComponent of the entity (if exists) +- Component_GetDecal(Entity entity) : DecalComponent? result -- query the DecalComponent of the entity (if exists) +- Component_GetSprite(Entity entity) : Sprite? result -- query the Sprite of the entity (if exists) +- Component_GetFont(Entity entity) : SpriteFont? result -- query the SpriteFont of the entity (if exists) - Component_GetNameArray() : NameComponent[] result -- returns the array of all components of this type - Component_GetLayerArray() : LayerComponent[] result -- returns the array of all components of this type @@ -635,6 +645,11 @@ The scene holds components. Entity handles can be used to retrieve associated co - Component_GetWeatherArray() : WeatherComponent[] result -- returns the array of all components of this type - Component_GetSoundArray() : SoundComponent[] result -- returns the array of all components of this type - Component_GetColliderArray() : ColliderComponent[] result -- returns the array of all components of this type +- Component_GetExpressionArray() : ExpressionComponent[] result -- returns the array of all components of this type +- Component_GetHumanoidArray() : HumanoidComponent[] result -- returns the array of all components of this type +- Component_GetDecalArray() : DecalComponent[] result -- returns the array of all components of this type +- Component_GetSpriteArray() : Sprite[] result -- returns the array of all components of this type +- Component_GetFontArray() : SpriteFont[] result -- returns the array of all components of this type - Entity_GetNameArray() : Entity[] result -- returns the array of all entities that have this component type - Entity_GetLayerArray() : Entity[] result -- returns the array of all entities that have this component type @@ -655,6 +670,11 @@ The scene holds components. Entity handles can be used to retrieve associated co - Entity_GetWeatherArray() : Entity[] result -- returns the array of all entities that have this component type - 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 +- Entity_GetExpressionArray() : Entity[] result -- returns the array of all entities that have this component type +- Entity_GetHumanoidArray() : Entity[] result -- returns the array of all entities that have this component type +- Entity_GetDecalArray() : Entity[] result -- returns the array of all entities that have this component type +- Entity_GetSpriteArray() : Entity[] result -- returns the array of all entities that have this component type +- Entity_GetFontArray() : Entity[] result -- returns the array of all entities that have this component type - 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) @@ -675,6 +695,11 @@ The scene holds components. Entity handles can be used to retrieve associated co - 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_RemoveExpression(Entity entity) -- remove the ExpressionComponent of the entity (if exists) +- Component_RemoveHumanoid(Entity entity) -- remove the HumanoidComponent of the entity (if exists) +- Component_RemoveDecal(Entity entity) -- remove the DecalComponent of the entity (if exists) +- Component_RemoveSprite(Entity entity) -- remove the Sprite of the entity (if exists) +- Component_RemoveFont(Entity entity) -- remove the SpriteFont 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 diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 05e2f9f62..8867b6ff6 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -43,6 +43,8 @@ set (SOURCE_FILES HumanoidWindow.cpp GeneralWindow.cpp ProfilerWindow.cpp + SpriteWindow.cpp + FontWindow.cpp xatlas.cpp EmbeddedResources.cpp ) diff --git a/Editor/ComponentsWindow.cpp b/Editor/ComponentsWindow.cpp index bc7a40b52..71f73fba9 100644 --- a/Editor/ComponentsWindow.cpp +++ b/Editor/ComponentsWindow.cpp @@ -42,6 +42,8 @@ void ComponentsWindow::Create(EditorComponent* _editor) armatureWnd.Create(editor); humanoidWnd.Create(editor); terrainWnd.Create(editor); + spriteWnd.Create(editor); + fontWnd.Create(editor); enum ADD_THING { @@ -68,6 +70,8 @@ void ComponentsWindow::Create(EditorComponent* _editor) ADD_CAMERA, ADD_OBJECT, ADD_VIDEO, + ADD_SPRITE, + ADD_FONT, }; newComponentCombo.Create("Add: "); @@ -99,6 +103,8 @@ void ComponentsWindow::Create(EditorComponent* _editor) newComponentCombo.AddItem("Camera " ICON_CAMERA, ADD_CAMERA); newComponentCombo.AddItem("Object " ICON_OBJECT, ADD_OBJECT); newComponentCombo.AddItem("Video " ICON_VIDEO, ADD_VIDEO); + newComponentCombo.AddItem("Sprite " ICON_SPRITE, ADD_SPRITE); + newComponentCombo.AddItem("Font " ICON_FONT, ADD_FONT); newComponentCombo.OnSelect([=](wi::gui::EventArgs args) { newComponentCombo.SetSelectedWithoutCallback(-1); if (editor->translator.selected.empty()) @@ -215,6 +221,14 @@ void ComponentsWindow::Create(EditorComponent* _editor) if (scene.videos.Contains(entity)) return; break; + case ADD_SPRITE: + if (scene.sprites.Contains(entity)) + return; + break; + case ADD_FONT: + if (scene.fonts.Contains(entity)) + return; + break; default: return; } @@ -314,6 +328,12 @@ void ComponentsWindow::Create(EditorComponent* _editor) case ADD_VIDEO: scene.videos.Create(entity); break; + case ADD_SPRITE: + scene.sprites.Create(entity); + break; + case ADD_FONT: + scene.fonts.Create(entity); + break; default: break; } @@ -354,6 +374,8 @@ void ComponentsWindow::Create(EditorComponent* _editor) AddWidget(&armatureWnd); AddWidget(&humanoidWnd); AddWidget(&terrainWnd); + AddWidget(&spriteWnd); + AddWidget(&fontWnd); materialWnd.SetVisible(false); weatherWnd.SetVisible(false); @@ -383,6 +405,8 @@ void ComponentsWindow::Create(EditorComponent* _editor) armatureWnd.SetVisible(false); humanoidWnd.SetVisible(false); terrainWnd.SetVisible(false); + spriteWnd.SetVisible(false); + fontWnd.SetVisible(false); XMFLOAT2 size = XMFLOAT2(338, 500); if (editor->main->config.GetSection("layout").Has("components.width")) @@ -800,4 +824,30 @@ void ComponentsWindow::ResizeLayout() { terrainWnd.SetVisible(false); } + + if (scene.sprites.Contains(spriteWnd.entity)) + { + spriteWnd.SetVisible(true); + spriteWnd.SetPos(pos); + spriteWnd.SetSize(XMFLOAT2(width, spriteWnd.GetScale().y)); + pos.y += spriteWnd.GetSize().y; + pos.y += padding; + } + else + { + spriteWnd.SetVisible(false); + } + + if (scene.fonts.Contains(fontWnd.entity)) + { + fontWnd.SetVisible(true); + fontWnd.SetPos(pos); + fontWnd.SetSize(XMFLOAT2(width, fontWnd.GetScale().y)); + pos.y += fontWnd.GetSize().y; + pos.y += padding; + } + else + { + fontWnd.SetVisible(false); + } } diff --git a/Editor/ComponentsWindow.h b/Editor/ComponentsWindow.h index 53bc58296..5b48b5f1c 100644 --- a/Editor/ComponentsWindow.h +++ b/Editor/ComponentsWindow.h @@ -27,6 +27,8 @@ #include "ArmatureWindow.h" #include "HumanoidWindow.h" #include "TerrainWindow.h" +#include "SpriteWindow.h" +#include "FontWindow.h" class EditorComponent; @@ -68,4 +70,6 @@ public: ArmatureWindow armatureWnd; HumanoidWindow humanoidWnd; TerrainWindow terrainWnd; + SpriteWindow spriteWnd; + FontWindow fontWnd; }; diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index b066571fb..b6f050538 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1586,6 +1586,8 @@ void EditorComponent::Update(float dt) componentsWnd.armatureWnd.SetEntity(INVALID_ENTITY); componentsWnd.humanoidWnd.SetEntity(INVALID_ENTITY); componentsWnd.terrainWnd.SetEntity(INVALID_ENTITY); + componentsWnd.spriteWnd.SetEntity(INVALID_ENTITY); + componentsWnd.fontWnd.SetEntity(INVALID_ENTITY); } else { @@ -1617,6 +1619,8 @@ void EditorComponent::Update(float dt) componentsWnd.armatureWnd.SetEntity(picked.entity); componentsWnd.humanoidWnd.SetEntity(picked.entity); componentsWnd.terrainWnd.SetEntity(picked.entity); + componentsWnd.spriteWnd.SetEntity(picked.entity); + componentsWnd.fontWnd.SetEntity(picked.entity); if (picked.subsetIndex >= 0) { @@ -3877,6 +3881,8 @@ void EditorComponent::RefreshSceneList() componentsWnd.armatureWnd.SetEntity(wi::ecs::INVALID_ENTITY); componentsWnd.humanoidWnd.SetEntity(wi::ecs::INVALID_ENTITY); componentsWnd.terrainWnd.SetEntity(wi::ecs::INVALID_ENTITY); + componentsWnd.spriteWnd.SetEntity(wi::ecs::INVALID_ENTITY); + componentsWnd.fontWnd.SetEntity(wi::ecs::INVALID_ENTITY); optionsWnd.RefreshEntityTree(); ResetHistory(); diff --git a/Editor/Editor_SOURCE.vcxitems b/Editor/Editor_SOURCE.vcxitems index 6ba3031f7..de1bff56c 100644 --- a/Editor/Editor_SOURCE.vcxitems +++ b/Editor/Editor_SOURCE.vcxitems @@ -110,6 +110,7 @@ + Create Create @@ -121,6 +122,7 @@ Create + @@ -176,8 +178,10 @@ + + diff --git a/Editor/Editor_SOURCE.vcxitems.filters b/Editor/Editor_SOURCE.vcxitems.filters index 17435bd51..15b297388 100644 --- a/Editor/Editor_SOURCE.vcxitems.filters +++ b/Editor/Editor_SOURCE.vcxitems.filters @@ -89,6 +89,8 @@ + + @@ -142,6 +144,8 @@ + + diff --git a/Editor/FontWindow.cpp b/Editor/FontWindow.cpp new file mode 100644 index 000000000..424599cff --- /dev/null +++ b/Editor/FontWindow.cpp @@ -0,0 +1,490 @@ +#include "stdafx.h" +#include "FontWindow.h" + +using namespace wi::ecs; +using namespace wi::scene; + +void FontWindow::Create(EditorComponent* _editor) +{ + editor = _editor; + + wi::gui::Window::Create(ICON_FONT " Font", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); + SetSize(XMFLOAT2(670, 1000)); + + closeButton.SetTooltip("Delete Font"); + OnClose([=](wi::gui::EventArgs args) { + + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + + editor->GetCurrentScene().fonts.Remove(entity); + + editor->RecordEntity(archive, entity); + + editor->optionsWnd.RefreshEntityTree(); + }); + + float x = 60; + float y = 0; + float step = 25; + float siz = 250; + float hei = 20; + + textInput.Create(""); + textInput.SetPos(XMFLOAT2(x, y)); + textInput.OnInput([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + + font->SetText(args.sValue); + }); + AddWidget(&textInput); + + fontStyleButton.Create(""); + fontStyleButton.SetDescription("Style: "); + fontStyleButton.SetTooltip("Load a font style from file (.TTF) to apply to this font."); + fontStyleButton.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + + if (font->fontStyleResource.IsValid()) + { + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + + font->fontStyleResource = {}; + font->fontStyleName = ""; + fontStyleButton.SetText("default"); + fontStyleButton.font.fontStyleResource = {}; + fontStyleButton.font.params.style = 0; + + editor->RecordEntity(archive, entity); + } + else + { + wi::helper::FileDialogParams params; + params.type = wi::helper::FileDialogParams::OPEN; + params.description = "Font (*.TTF)"; + params.extensions = wi::resourcemanager::GetSupportedFontStyleExtensions(); + wi::helper::FileDialog(params, [=](std::string fileName) { + font->fontStyleResource = wi::resourcemanager::Load(fileName); + font->fontStyleName = fileName; + fontStyleButton.SetText(wi::helper::GetFileNameFromPath(font->fontStyleName)); + fontStyleButton.font.fontStyleResource = font->fontStyleResource; + }); + } + }); + AddWidget(&fontStyleButton); + + fontSizeCombo.Create("Size: "); + fontSizeCombo.SetTooltip("Font caching size. Bigger size will take up more memory, but will be better quality."); + fontSizeCombo.AddItem("10", 10); + fontSizeCombo.AddItem("12", 12); + fontSizeCombo.AddItem("14", 14); + fontSizeCombo.AddItem("16", 16); + fontSizeCombo.AddItem("18", 18); + fontSizeCombo.AddItem("20", 20); + fontSizeCombo.AddItem("22", 22); + fontSizeCombo.AddItem("24", 24); + fontSizeCombo.AddItem("26", 26); + fontSizeCombo.AddItem("28", 28); + fontSizeCombo.AddItem("36", 36); + fontSizeCombo.AddItem("48", 48); + fontSizeCombo.AddItem("72", 72); + fontSizeCombo.AddItem("84", 84); + fontSizeCombo.AddItem("96", 96); + fontSizeCombo.AddItem("108", 108); + fontSizeCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.size = int(args.userdata); + }); + AddWidget(&fontSizeCombo); + + hAlignCombo.Create("H Align: "); + hAlignCombo.SetTooltip("Horizontal alignment."); + hAlignCombo.AddItem("Left", wi::font::WIFALIGN_LEFT); + hAlignCombo.AddItem("Center", wi::font::WIFALIGN_CENTER); + hAlignCombo.AddItem("Right", wi::font::WIFALIGN_RIGHT); + hAlignCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.h_align = wi::font::Alignment(args.userdata); + }); + AddWidget(&hAlignCombo); + + vAlignCombo.Create("V Align: "); + vAlignCombo.SetTooltip("Vertical alignment."); + vAlignCombo.AddItem("Top", wi::font::WIFALIGN_TOP); + vAlignCombo.AddItem("Center", wi::font::WIFALIGN_CENTER); + vAlignCombo.AddItem("Bottom", wi::font::WIFALIGN_BOTTOM); + vAlignCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.v_align = wi::font::Alignment(args.userdata); + }); + AddWidget(&vAlignCombo); + + rotationSlider.Create(0, 360, 0, 10000, "Rotation: "); + rotationSlider.SetTooltip("Z Rotation around alignment center point. The editor input is in degrees."); + rotationSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.rotation = wi::math::DegreesToRadians(args.fValue); + }); + AddWidget(&rotationSlider); + + spacingSlider.Create(0, 10, 0, 10000, "Spacing: "); + spacingSlider.SetTooltip("Horizontal spacing between characters."); + spacingSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.spacingX = args.fValue; + }); + AddWidget(&spacingSlider); + + softnessSlider.Create(0, 1, 0, 10000, "Softness: "); + softnessSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.softness = args.fValue; + }); + AddWidget(&softnessSlider); + + boldenSlider.Create(0, 1, 0, 10000, "Bolden: "); + boldenSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.bolden = args.fValue; + }); + AddWidget(&boldenSlider); + + shadowSoftnessSlider.Create(0, 1, 0, 10000, "Shadow Softness: "); + shadowSoftnessSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.shadow_softness = args.fValue; + }); + AddWidget(&shadowSoftnessSlider); + + shadowBoldenSlider.Create(0, 1, 0, 10000, "Shadow Bolden: "); + shadowBoldenSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.shadow_bolden = args.fValue; + }); + AddWidget(&shadowBoldenSlider); + + shadowOffsetXSlider.Create(-2, 2, 0, 10000, "Shadow Offset X: "); + shadowOffsetXSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.shadow_offset_x = args.fValue; + }); + AddWidget(&shadowOffsetXSlider); + + shadowOffsetYSlider.Create(-2, 2, 0, 10000, "Shadow Offset Y: "); + shadowOffsetYSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.shadow_offset_y = args.fValue; + }); + AddWidget(&shadowOffsetYSlider); + + intensitySlider.Create(0, 100, 1, 10000, "Intensity: "); + intensitySlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.intensity = args.fValue; + }); + AddWidget(&intensitySlider); + + shadowIntensitySlider.Create(0, 100, 1, 10000, "Shadow Intensity: "); + shadowIntensitySlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->params.shadow_intensity = args.fValue; + }); + AddWidget(&shadowIntensitySlider); + + hiddenCheckBox.Create("Hidden: "); + hiddenCheckBox.SetTooltip("Hide / unhide the font"); + hiddenCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->SetHidden(args.bValue); + }); + AddWidget(&hiddenCheckBox); + + cameraFacingCheckBox.Create("Camera Facing: "); + cameraFacingCheckBox.SetTooltip("Camera facing fonts will always rotate towards the camera."); + cameraFacingCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->SetCameraFacing(args.bValue); + }); + AddWidget(&cameraFacingCheckBox); + + cameraScalingCheckBox.Create("Camera Scaling: "); + cameraScalingCheckBox.SetTooltip("Camera scaling fonts will always keep the same size on screen, irrespective of the distance to the camera."); + cameraScalingCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->SetCameraScaling(args.bValue); + }); + AddWidget(&cameraScalingCheckBox); + + depthTestCheckBox.Create("Depth Test: "); + depthTestCheckBox.SetTooltip("Depth tested fonts will be clipped against geometry."); + depthTestCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + if (args.bValue) + { + font->params.enableDepthTest(); + } + else + { + font->params.disableDepthTest(); + } + }); + AddWidget(&depthTestCheckBox); + + sdfCheckBox.Create("SDF: "); + sdfCheckBox.SetTooltip("Signed Distance Field rendering is used for improved font upscaling, softness, boldness and soft shadow effects."); + sdfCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + if (args.bValue) + { + font->params.enableSDFRendering(); + } + else + { + font->params.disableSDFRendering(); + } + }); + AddWidget(&sdfCheckBox); + + colorModeCombo.Create("Color mode: "); + colorModeCombo.SetSize(XMFLOAT2(120, hei)); + colorModeCombo.SetPos(XMFLOAT2(x + 150, y += step)); + colorModeCombo.AddItem("Font color"); + colorModeCombo.AddItem("Shadow color"); + colorModeCombo.SetTooltip("Choose the destination data of the color picker."); + colorModeCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font != nullptr) + { + if (args.iValue == 0) + { + colorPicker.SetPickColor(font->params.color); + } + else + { + colorPicker.SetPickColor(font->params.shadowColor); + } + } + }); + AddWidget(&colorModeCombo); + + colorPicker.Create("Color", wi::gui::Window::WindowControls::NONE); + colorPicker.OnColorChanged([&](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font != nullptr) + { + switch (colorModeCombo.GetSelected()) + { + default: + case 0: + font->params.color = args.color; + break; + case 1: + font->params.shadowColor = args.color; + break; + } + } + }); + AddWidget(&colorPicker); + + typewriterInfoLabel.Create("Tip: if you add Sound Component to text, then the typewriter animation will use that sound as typewriter sound effect."); + typewriterInfoLabel.SetSize(XMFLOAT2(80, 80)); + AddWidget(&typewriterInfoLabel); + + typewriterTimeSlider.Create(0, 10, 0, 10000, "Typewriter time: "); + typewriterTimeSlider.SetTooltip("Time to complete typewriter animation (0 = disable)."); + typewriterTimeSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->anim.typewriter.time = args.fValue; + }); + AddWidget(&typewriterTimeSlider); + + typewriterLoopedCheckBox.Create("Typewriter Looped: "); + typewriterLoopedCheckBox.SetTooltip("Whether typewriter animation is looped or not."); + typewriterLoopedCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->anim.typewriter.looped = args.bValue; + }); + AddWidget(&typewriterLoopedCheckBox); + + typewriterStartInput.Create(""); + typewriterStartInput.SetDescription("Typewriter start: "); + typewriterStartInput.SetTooltip("Set the starting character for typewriter animation (0 = first)."); + typewriterStartInput.SetPos(XMFLOAT2(x, y)); + typewriterStartInput.SetSize(XMFLOAT2(siz, hei)); + typewriterStartInput.OnInputAccepted([=](wi::gui::EventArgs args) { + wi::SpriteFont* font = editor->GetCurrentScene().fonts.GetComponent(entity); + if (font == nullptr) + return; + font->anim.typewriter.character_start = (size_t)args.iValue; + }); + AddWidget(&typewriterStartInput); + + SetMinimized(true); + SetVisible(false); + + SetEntity(INVALID_ENTITY); +} + +void FontWindow::SetEntity(wi::ecs::Entity entity) +{ + this->entity = entity; + + Scene& scene = editor->GetCurrentScene(); + const wi::SpriteFont* font = scene.fonts.GetComponent(entity); + if (font == nullptr) + return; + + textInput.SetText(font->GetTextA()); + if (font->fontStyleResource.IsValid()) + { + fontStyleButton.SetText(wi::helper::GetFileNameFromPath(font->fontStyleName)); + fontStyleButton.font.fontStyleResource = font->fontStyleResource; + } + else + { + fontStyleButton.SetText("default"); + fontStyleButton.font.fontStyleResource = {}; + fontStyleButton.font.params.style = 0; + } + fontSizeCombo.SetSelectedByUserdataWithoutCallback(font->params.size); + hAlignCombo.SetSelectedByUserdataWithoutCallback(font->params.h_align); + vAlignCombo.SetSelectedByUserdataWithoutCallback(font->params.v_align); + rotationSlider.SetValue(wi::math::RadiansToDegrees(font->params.rotation)); + spacingSlider.SetValue(font->params.spacingX); + softnessSlider.SetValue(font->params.softness); + boldenSlider.SetValue(font->params.bolden); + shadowSoftnessSlider.SetValue(font->params.shadow_softness); + shadowBoldenSlider.SetValue(font->params.shadow_bolden); + shadowOffsetXSlider.SetValue(font->params.shadow_offset_x); + shadowOffsetYSlider.SetValue(font->params.shadow_offset_y); + intensitySlider.SetValue(font->params.intensity); + shadowIntensitySlider.SetValue(font->params.shadow_intensity); + hiddenCheckBox.SetCheck(font->IsHidden()); + cameraFacingCheckBox.SetCheck(font->IsCameraFacing()); + cameraScalingCheckBox.SetCheck(font->IsCameraScaling()); + depthTestCheckBox.SetCheck(font->params.isDepthTestEnabled()); + sdfCheckBox.SetCheck(font->params.isSDFRenderingEnabled()); + if (colorModeCombo.GetSelected() == 0) + { + colorPicker.SetPickColor(font->params.color); + } + else + { + colorPicker.SetPickColor(font->params.shadowColor); + } + typewriterTimeSlider.SetValue(font->anim.typewriter.time); + typewriterLoopedCheckBox.SetCheck(font->anim.typewriter.looped); + typewriterStartInput.SetValue((int)font->anim.typewriter.character_start); +} + +void FontWindow::ResizeLayout() +{ + wi::gui::Window::ResizeLayout(); + const float padding = 4; + const float width = GetWidgetAreaSize().x; + float y = padding; + float jump = 20; + + const float margin_left = 120; + const float margin_right = 40; + + 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(textInput); + add(fontStyleButton); + add(fontSizeCombo); + add(hAlignCombo); + add(vAlignCombo); + add(rotationSlider); + add(spacingSlider); + add(softnessSlider); + add(boldenSlider); + add(intensitySlider); + add(shadowSoftnessSlider); + add(shadowBoldenSlider); + add(shadowIntensitySlider); + add(shadowOffsetXSlider); + add(shadowOffsetYSlider); + add_right(hiddenCheckBox); + add_right(cameraFacingCheckBox); + add_right(cameraScalingCheckBox); + add_right(depthTestCheckBox); + add_right(sdfCheckBox); + add(colorModeCombo); + add_fullwidth(colorPicker); + add_fullwidth(typewriterInfoLabel); + add(typewriterTimeSlider); + add_right(typewriterLoopedCheckBox); + add(typewriterStartInput); +} diff --git a/Editor/FontWindow.h b/Editor/FontWindow.h new file mode 100644 index 000000000..57799c0e2 --- /dev/null +++ b/Editor/FontWindow.h @@ -0,0 +1,42 @@ +#pragma once +class EditorComponent; + +class FontWindow : 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); + + wi::gui::TextInputField textInput; + wi::gui::Button fontStyleButton; + wi::gui::ComboBox fontSizeCombo; + wi::gui::ComboBox hAlignCombo; + wi::gui::ComboBox vAlignCombo; + wi::gui::Slider rotationSlider; + wi::gui::Slider spacingSlider; + wi::gui::Slider softnessSlider; + wi::gui::Slider boldenSlider; + wi::gui::Slider shadowSoftnessSlider; + wi::gui::Slider shadowBoldenSlider; + wi::gui::Slider shadowOffsetXSlider; + wi::gui::Slider shadowOffsetYSlider; + wi::gui::Slider intensitySlider; + wi::gui::Slider shadowIntensitySlider; + wi::gui::CheckBox hiddenCheckBox; + wi::gui::CheckBox cameraFacingCheckBox; + wi::gui::CheckBox cameraScalingCheckBox; + wi::gui::CheckBox depthTestCheckBox; + wi::gui::CheckBox sdfCheckBox; + wi::gui::ComboBox colorModeCombo; + wi::gui::ColorPicker colorPicker; + + wi::gui::Label typewriterInfoLabel; + wi::gui::Slider typewriterTimeSlider; + wi::gui::CheckBox typewriterLoopedCheckBox; + wi::gui::TextInputField typewriterStartInput; + + void ResizeLayout() override; +}; diff --git a/Editor/GeneralWindow.cpp b/Editor/GeneralWindow.cpp index dc1798e2b..622ba7f8b 100644 --- a/Editor/GeneralWindow.cpp +++ b/Editor/GeneralWindow.cpp @@ -439,6 +439,7 @@ void GeneralWindow::Create(EditorComponent* _editor) // customize individual elements: editor->componentsWnd.materialWnd.textureSlotButton.SetColor(wi::Color::White(), wi::gui::IDLE); + editor->componentsWnd.spriteWnd.textureButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->optionsWnd.paintToolWnd.brushTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->optionsWnd.paintToolWnd.revealTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->aboutLabel.sprites[wi::gui::FOCUS] = editor->aboutLabel.sprites[wi::gui::IDLE]; diff --git a/Editor/IconDefinitions.h b/Editor/IconDefinitions.h index efee94c0e..f31f0234e 100644 --- a/Editor/IconDefinitions.h +++ b/Editor/IconDefinitions.h @@ -34,6 +34,8 @@ #define ICON_EXPRESSION ICON_FA_MASKS_THEATER #define ICON_HUMANOID ICON_FA_PERSON_RAYS #define ICON_TERRAIN ICON_FA_MOUNTAIN_SUN +#define ICON_SPRITE ICON_FA_IMAGE +#define ICON_FONT ICON_FA_FONT #define ICON_SAVE ICON_FA_FLOPPY_DISK #define ICON_OPEN ICON_FA_FOLDER_OPEN diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index 75c71e71c..53cb2bc0c 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -35,6 +35,8 @@ void OptionsWindow::Create(EditorComponent* _editor) NEW_SCRIPT, NEW_COLLIDER, NEW_TERRAIN, + NEW_SPRITE, + NEW_FONT, }; newCombo.Create("New: "); @@ -63,6 +65,8 @@ void OptionsWindow::Create(EditorComponent* _editor) newCombo.AddItem("Script " ICON_SCRIPT, NEW_SCRIPT); newCombo.AddItem("Collider " ICON_COLLIDER, NEW_COLLIDER); newCombo.AddItem("Terrain " ICON_TERRAIN, NEW_TERRAIN); + newCombo.AddItem("Sprite " ICON_SPRITE, NEW_SPRITE); + newCombo.AddItem("Font " ICON_FONT, NEW_FONT); newCombo.OnSelect([&](wi::gui::EventArgs args) { newCombo.SetSelectedWithoutCallback(-1); const EditorComponent::EditorScene& editorscene = editor->GetCurrentEditorScene(); @@ -215,6 +219,30 @@ void OptionsWindow::Create(EditorComponent* _editor) scene.terrains.Create(pick.entity) = editor->componentsWnd.terrainWnd.terrain_preset; scene.names.Create(pick.entity) = "terrain"; break; + case NEW_SPRITE: + { + pick.entity = CreateEntity(); + wi::Sprite& sprite = scene.sprites.Create(pick.entity); + sprite.params.pivot = XMFLOAT2(0.5f, 0.5f); + sprite.anim.repeatable = true; + scene.transforms.Create(pick.entity).Translate(XMFLOAT3(0, 2, 0)); + scene.names.Create(pick.entity) = "sprite"; + } + break; + case NEW_FONT: + { + pick.entity = CreateEntity(); + wi::SpriteFont& font = scene.fonts.Create(pick.entity); + font.SetText("Text"); + font.params.h_align = wi::font::Alignment::WIFALIGN_CENTER; + font.params.v_align = wi::font::Alignment::WIFALIGN_CENTER; + font.params.scaling = 0.1f; + font.params.size = 26; + font.anim.typewriter.looped = true; + scene.transforms.Create(pick.entity).Translate(XMFLOAT3(0, 2, 0)); + scene.names.Create(pick.entity) = "font"; + } + break; default: break; } @@ -265,6 +293,8 @@ void OptionsWindow::Create(EditorComponent* _editor) filterCombo.AddItem(ICON_EXPRESSION, (uint64_t)Filter::Expression); filterCombo.AddItem(ICON_HUMANOID, (uint64_t)Filter::Humanoid); filterCombo.AddItem(ICON_TERRAIN, (uint64_t)Filter::Terrain); + filterCombo.AddItem(ICON_SPRITE, (uint64_t)Filter::Sprite); + filterCombo.AddItem(ICON_FONT, (uint64_t)Filter::Font); filterCombo.SetTooltip("Apply filtering to the Entities by components"); filterCombo.OnSelect([&](wi::gui::EventArgs args) { filter = (Filter)args.userdata; @@ -554,6 +584,14 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level) { item.name += ICON_HUMANOID " "; } + if (scene.sprites.Contains(entity)) + { + item.name += ICON_SPRITE " "; + } + if (scene.fonts.Contains(entity)) + { + item.name += ICON_FONT " "; + } if (scene.lights.Contains(entity)) { const LightComponent* light = scene.lights.GetComponent(entity); @@ -878,6 +916,22 @@ void OptionsWindow::RefreshEntityTree() } } + if (has_flag(filter, Filter::Sprite)) + { + for (size_t i = 0; i < scene.sprites.GetCount(); ++i) + { + PushToEntityTree(scene.sprites.GetEntity(i), 0); + } + } + + if (has_flag(filter, Filter::Font)) + { + for (size_t i = 0; i < scene.fonts.GetCount(); ++i) + { + PushToEntityTree(scene.fonts.GetEntity(i), 0); + } + } + entitytree_added_items.clear(); entitytree_opened_items.clear(); } diff --git a/Editor/OptionsWindow.h b/Editor/OptionsWindow.h index c8ed576bc..79c0364d1 100644 --- a/Editor/OptionsWindow.h +++ b/Editor/OptionsWindow.h @@ -47,6 +47,8 @@ public: Spring = 1 << 20, Humanoid = 1 << 21, Video = 1 << 22, + Sprite = 1 << 23, + Font = 1 << 24, All = ~0ull, } filter = Filter::All; diff --git a/Editor/SpriteWindow.cpp b/Editor/SpriteWindow.cpp new file mode 100644 index 000000000..3e4b4de54 --- /dev/null +++ b/Editor/SpriteWindow.cpp @@ -0,0 +1,599 @@ +#include "stdafx.h" +#include "SpriteWindow.h" + +using namespace wi::ecs; +using namespace wi::scene; +using namespace wi::graphics; + +void SpriteWindow::Create(EditorComponent* _editor) +{ + editor = _editor; + + wi::gui::Window::Create(ICON_SPRITE " Sprite", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); + SetSize(XMFLOAT2(670, 1500)); + + closeButton.SetTooltip("Delete Sprite"); + OnClose([=](wi::gui::EventArgs args) { + + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + + editor->GetCurrentScene().sprites.Remove(entity); + + editor->RecordEntity(archive, entity); + + editor->optionsWnd.RefreshEntityTree(); + }); + + textureButton.Create("Base Texture"); + textureButton.SetSize(XMFLOAT2(200, 200)); + textureButton.sprites[wi::gui::IDLE].params.color = wi::Color::White(); + textureButton.sprites[wi::gui::FOCUS].params.color = wi::Color::Gray(); + textureButton.sprites[wi::gui::ACTIVE].params.color = wi::Color::White(); + textureButton.sprites[wi::gui::DEACTIVATING].params.color = wi::Color::Gray(); + textureButton.OnClick([this](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + + if (sprite->textureResource.IsValid()) + { + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + + sprite->textureResource = {}; + sprite->textureName = ""; + sprite->params.siz = XMFLOAT2(1, 1); + sprite->params.image_subresource = -1; + + editor->RecordEntity(archive, entity); + } + else + { + wi::helper::FileDialogParams params; + params.type = wi::helper::FileDialogParams::OPEN; + params.description = "Texture"; + params.extensions = wi::resourcemanager::GetSupportedImageExtensions(); + wi::helper::FileDialog(params, [=](std::string fileName) { + sprite->textureResource = wi::resourcemanager::Load(fileName); + sprite->textureName = fileName; + textureButton.SetImage(sprite->textureResource); + if (sprite->textureResource.IsValid()) + { + const TextureDesc& desc = sprite->textureResource.GetTexture().GetDesc(); + sprite->params.siz = XMFLOAT2(1, float(desc.height) / float(desc.width)); + } + else + { + sprite->params.siz = XMFLOAT2(1, 1); + } + }); + } + }); + AddWidget(&textureButton); + + maskButton.Create("Mask Texture"); + maskButton.SetSize(XMFLOAT2(200, 200)); + maskButton.sprites[wi::gui::IDLE].params.color = wi::Color::White(); + maskButton.sprites[wi::gui::FOCUS].params.color = wi::Color::Gray(); + maskButton.sprites[wi::gui::ACTIVE].params.color = wi::Color::White(); + maskButton.sprites[wi::gui::DEACTIVATING].params.color = wi::Color::Gray(); + maskButton.OnClick([this](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + + if (sprite->maskResource.IsValid()) + { + wi::Archive& archive = editor->AdvanceHistory(); + archive << EditorComponent::HISTORYOP_COMPONENT_DATA; + editor->RecordEntity(archive, entity); + + sprite->maskResource = {}; + sprite->maskName = ""; + + editor->RecordEntity(archive, entity); + } + else + { + wi::helper::FileDialogParams params; + params.type = wi::helper::FileDialogParams::OPEN; + params.description = "Texture"; + params.extensions = wi::resourcemanager::GetSupportedImageExtensions(); + wi::helper::FileDialog(params, [=](std::string fileName) { + sprite->maskResource = wi::resourcemanager::Load(fileName); + sprite->maskName = fileName; + maskButton.SetImage(sprite->maskResource); + }); + } + }); + AddWidget(&maskButton); + + pivotXSlider.Create(0, 1, 0, 10000, "Pivot X: "); + pivotXSlider.SetTooltip("Horizontal pivot: 0: left, 0.5: center, 1: right"); + pivotXSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.pivot.x = args.fValue; + }); + AddWidget(&pivotXSlider); + + pivotYSlider.Create(0, 1, 0, 10000, "Pivot Y: "); + pivotYSlider.SetTooltip("Vertical pivot: 0: top, 0.5: center, 1: bottom"); + pivotYSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.pivot.y = args.fValue; + }); + AddWidget(&pivotYSlider); + + intensitySlider.Create(0, 100, 1, 10000, "Intensity: "); + intensitySlider.SetTooltip("Color multiplier"); + intensitySlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.intensity = args.fValue; + }); + AddWidget(&intensitySlider); + + rotationSlider.Create(0, 360, 0, 10000, "Rotation: "); + rotationSlider.SetTooltip("Z Rotation around pivot point. The editor input is in degrees."); + rotationSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.rotation = wi::math::DegreesToRadians(args.fValue); + }); + AddWidget(&rotationSlider); + + alphaStartSlider.Create(0, 1, 0, 10000, "Mask Alpha Start: "); + alphaStartSlider.SetTooltip("Constrain mask alpha to not go below this level."); + alphaStartSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.mask_alpha_range_start = args.fValue; + }); + AddWidget(&alphaStartSlider); + + alphaEndSlider.Create(0, 1, 1, 10000, "Mask Alpha End: "); + alphaEndSlider.SetTooltip("Constrain mask alpha to not go above this level."); + alphaEndSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.mask_alpha_range_end = args.fValue; + }); + AddWidget(&alphaEndSlider); + + borderSoftenSlider.Create(0, 1, 0, 10000, "Border Soften: "); + borderSoftenSlider.SetTooltip("Soften the borders of the sprite."); + borderSoftenSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.border_soften = args.fValue; + }); + AddWidget(&borderSoftenSlider); + + cornerRounding0Slider.Create(0, 0.5f, 1, 10000, "Rounding 0: "); + cornerRounding0Slider.SetTooltip("Enable corner rounding for the lop left corner."); + cornerRounding0Slider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.corners_rounding[0].radius = args.fValue; + }); + AddWidget(&cornerRounding0Slider); + + cornerRounding1Slider.Create(0, 0.5f, 0, 10000, "Rounding 1: "); + cornerRounding1Slider.SetTooltip("Enable corner rounding for the lop right corner."); + cornerRounding1Slider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.corners_rounding[1].radius = args.fValue; + }); + AddWidget(&cornerRounding1Slider); + + cornerRounding2Slider.Create(0, 0.5f, 0, 10000, "Rounding 2: "); + cornerRounding2Slider.SetTooltip("Enable corner rounding for the bottom left corner."); + cornerRounding2Slider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.corners_rounding[2].radius = args.fValue; + }); + AddWidget(&cornerRounding2Slider); + + cornerRounding3Slider.Create(0, 0.5f, 0, 10000, "Rounding 3: "); + cornerRounding3Slider.SetTooltip("Enable corner rounding for the bottom right corner."); + cornerRounding3Slider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.corners_rounding[3].radius = args.fValue; + }); + AddWidget(&cornerRounding3Slider); + + qualityCombo.Create("Filtering: "); + qualityCombo.AddItem("Nearest Neighbor", wi::image::QUALITY_NEAREST); + qualityCombo.AddItem("Linear", wi::image::QUALITY_LINEAR); + qualityCombo.AddItem("Anisotropic", wi::image::QUALITY_ANISOTROPIC); + qualityCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.quality = (wi::image::QUALITY)args.userdata; + }); + AddWidget(&qualityCombo); + + samplemodeCombo.Create("Sampling: "); + samplemodeCombo.AddItem("Clamp", wi::image::SAMPLEMODE_CLAMP); + samplemodeCombo.AddItem("Mirror", wi::image::SAMPLEMODE_MIRROR); + samplemodeCombo.AddItem("Wrap", wi::image::SAMPLEMODE_WRAP); + samplemodeCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.sampleFlag = (wi::image::SAMPLEMODE)args.userdata; + }); + AddWidget(&samplemodeCombo); + + blendModeCombo.Create("Blending: "); + blendModeCombo.AddItem("Opaque", wi::enums::BLENDMODE_OPAQUE); + blendModeCombo.AddItem("Alpha", wi::enums::BLENDMODE_ALPHA); + blendModeCombo.AddItem("Premultiplied", wi::enums::BLENDMODE_PREMULTIPLIED); + blendModeCombo.AddItem("Additive", wi::enums::BLENDMODE_ADDITIVE); + blendModeCombo.AddItem("Multiply", wi::enums::BLENDMODE_MULTIPLY); + blendModeCombo.OnSelect([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.blendFlag = (wi::enums::BLENDMODE)args.userdata; + }); + AddWidget(&blendModeCombo); + + hiddenCheckBox.Create("Hidden: "); + hiddenCheckBox.SetTooltip("Hide / unhide the sprite"); + hiddenCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->SetHidden(args.bValue); + }); + AddWidget(&hiddenCheckBox); + + cameraFacingCheckBox.Create("Camera Facing: "); + cameraFacingCheckBox.SetTooltip("Camera facing sprites will always rotate towards the camera."); + cameraFacingCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->SetCameraFacing(args.bValue); + }); + AddWidget(&cameraFacingCheckBox); + + cameraScalingCheckBox.Create("Camera Scaling: "); + cameraScalingCheckBox.SetTooltip("Camera scaling sprites will always keep the same size on screen, irrespective of the distance to the camera."); + cameraScalingCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->SetCameraScaling(args.bValue); + }); + AddWidget(&cameraScalingCheckBox); + + depthTestCheckBox.Create("Depth Test: "); + depthTestCheckBox.SetTooltip("Depth tested sprites will be clipped against geometry."); + depthTestCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + if (args.bValue) + { + sprite->params.enableDepthTest(); + } + else + { + sprite->params.disableDepthTest(); + } + }); + AddWidget(&depthTestCheckBox); + + distortionCheckBox.Create("Distortion: "); + distortionCheckBox.SetTooltip("The distortion effect will use the sprite as a normal map to distort the rendered image.\nUse the color alpha to control distortion amount."); + distortionCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + if (args.bValue) + { + sprite->params.enableExtractNormalMap(); + } + else + { + sprite->params.disableExtractNormalMap(); + } + }); + AddWidget(&distortionCheckBox); + + colorPicker.Create("Color", wi::gui::Window::WindowControls::NONE); + colorPicker.OnColorChanged([&](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->params.color = args.color; + }); + AddWidget(&colorPicker); + + movingTexXSlider.Create(-1000, 1000, 0, 10000, "Scrolling X: "); + movingTexXSlider.SetTooltip("Scrolling animation's speed in X direction."); + movingTexXSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.movingTexAnim.speedX = args.fValue; + }); + AddWidget(&movingTexXSlider); + + movingTexYSlider.Create(-1000, 1000, 0, 10000, "Scrolling Y: "); + movingTexYSlider.SetTooltip("Scrolling animation's speed in Y direction."); + movingTexYSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.movingTexAnim.speedY = args.fValue; + }); + AddWidget(&movingTexYSlider); + + drawrectFrameRateSlider.Create(0, 60, 0, 60, "Spritesheet FPS: "); + drawrectFrameRateSlider.SetTooltip("Sprite Sheet animation's frame rate per second."); + drawrectFrameRateSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.drawRectAnim.frameRate = args.fValue; + UpdateSpriteDrawRectParams(sprite); + }); + AddWidget(&drawrectFrameRateSlider); + + drawrectFrameCountInput.Create(""); + drawrectFrameCountInput.SetDescription("frames: "); + drawrectFrameCountInput.SetTooltip("Set the total frame count of the sprite sheet animation (1 = only 1 frame, no animation)."); + drawrectFrameCountInput.OnInputAccepted([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.drawRectAnim.frameCount = args.iValue; + UpdateSpriteDrawRectParams(sprite); + }); + AddWidget(&drawrectFrameCountInput); + + drawrectHorizontalFrameCountInput.Create(""); + drawrectHorizontalFrameCountInput.SetDescription("Horiz. frames: "); + drawrectHorizontalFrameCountInput.SetTooltip("Set the horizontal frame count of the sprite sheet animation.\n(optional, use if sprite sheet contains multiple rows, default = 0)."); + drawrectHorizontalFrameCountInput.OnInputAccepted([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.drawRectAnim.horizontalFrameCount = args.iValue; + UpdateSpriteDrawRectParams(sprite); + }); + AddWidget(&drawrectHorizontalFrameCountInput); + + wobbleXSlider.Create(0, 1, 0, 10000, "Wobble X: "); + wobbleXSlider.SetTooltip("Wobble animation's amount in X direction."); + wobbleXSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.wobbleAnim.amount.x = args.fValue; + }); + AddWidget(&wobbleXSlider); + + wobbleYSlider.Create(0, 1, 0, 10000, "Wobble Y: "); + wobbleYSlider.SetTooltip("Wobble animation's amount in X direction."); + wobbleYSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.wobbleAnim.amount.y = args.fValue; + }); + AddWidget(&wobbleYSlider); + + wobbleSpeedSlider.Create(0, 4, 0, 10000, "Wobble Speed: "); + wobbleSpeedSlider.SetTooltip("Wobble animation's speed."); + wobbleSpeedSlider.OnSlide([=](wi::gui::EventArgs args) { + wi::Sprite* sprite = editor->GetCurrentScene().sprites.GetComponent(entity); + if (sprite == nullptr) + return; + sprite->anim.wobbleAnim.speed = args.fValue; + }); + AddWidget(&wobbleSpeedSlider); + + SetMinimized(true); + SetVisible(false); + + SetEntity(INVALID_ENTITY); +} + +void SpriteWindow::SetEntity(wi::ecs::Entity entity) +{ + this->entity = entity; + + Scene& scene = editor->GetCurrentScene(); + wi::Sprite* sprite = scene.sprites.GetComponent(entity); + if (sprite == nullptr) + return; + + std::string tooltiptext = "Base Texture will give the base color of the sprite."; + if (sprite->textureResource.IsValid()) + { + const Texture& texture = sprite->textureResource.GetTexture(); + tooltiptext += "\nResolution: " + std::to_string(texture.desc.width) + " * " + std::to_string(texture.desc.height); + tooltiptext += "\nMip levels: " + std::to_string(texture.desc.mip_levels); + tooltiptext += "\nFormat: "; + tooltiptext += GetFormatString(texture.desc.format); + tooltiptext += "\nSwizzle: "; + tooltiptext += GetSwizzleString(texture.desc.swizzle); + tooltiptext += "\nMemory: " + wi::helper::GetMemorySizeText(ComputeTextureMemorySizeInBytes(texture.desc)); + } + textureButton.SetTooltip(tooltiptext); + + tooltiptext = "Mask Texture will be used as a multiplier for base color."; + if (sprite->maskResource.IsValid()) + { + const Texture& texture = sprite->maskResource.GetTexture(); + tooltiptext += "\nResolution: " + std::to_string(texture.desc.width) + " * " + std::to_string(texture.desc.height); + tooltiptext += "\nMip levels: " + std::to_string(texture.desc.mip_levels); + tooltiptext += "\nFormat: "; + tooltiptext += GetFormatString(texture.desc.format); + tooltiptext += "\nSwizzle: "; + tooltiptext += GetSwizzleString(texture.desc.swizzle); + tooltiptext += "\nMemory: " + wi::helper::GetMemorySizeText(ComputeTextureMemorySizeInBytes(texture.desc)); + } + maskButton.SetTooltip(tooltiptext); + + if ( + sprite->params.corners_rounding[0].radius > 0 || + sprite->params.corners_rounding[1].radius > 0 || + sprite->params.corners_rounding[2].radius > 0 || + sprite->params.corners_rounding[3].radius > 0 + ) + { + sprite->params.enableCornerRounding(); + sprite->params.corners_rounding[0].segments = 36; + sprite->params.corners_rounding[1].segments = 36; + sprite->params.corners_rounding[2].segments = 36; + sprite->params.corners_rounding[3].segments = 36; + } + else + { + sprite->params.disableCornerRounding(); + } + + textureButton.SetImage(sprite->textureResource); + maskButton.SetImage(sprite->maskResource); + pivotXSlider.SetValue(sprite->params.pivot.x); + pivotYSlider.SetValue(sprite->params.pivot.y); + intensitySlider.SetValue(sprite->params.intensity); + rotationSlider.SetValue(wi::math::RadiansToDegrees(sprite->params.rotation)); + alphaStartSlider.SetValue(sprite->params.mask_alpha_range_start); + alphaEndSlider.SetValue(sprite->params.mask_alpha_range_end); + borderSoftenSlider.SetValue(sprite->params.border_soften); + cornerRounding0Slider.SetValue(sprite->params.corners_rounding[0].radius); + cornerRounding1Slider.SetValue(sprite->params.corners_rounding[1].radius); + cornerRounding2Slider.SetValue(sprite->params.corners_rounding[2].radius); + cornerRounding3Slider.SetValue(sprite->params.corners_rounding[3].radius); + qualityCombo.SetSelectedByUserdataWithoutCallback(sprite->params.quality); + samplemodeCombo.SetSelectedByUserdataWithoutCallback(sprite->params.sampleFlag); + blendModeCombo.SetSelectedByUserdataWithoutCallback(sprite->params.blendFlag); + hiddenCheckBox.SetCheck(sprite->IsHidden()); + cameraFacingCheckBox.SetCheck(sprite->IsCameraFacing()); + cameraScalingCheckBox.SetCheck(sprite->IsCameraScaling()); + depthTestCheckBox.SetCheck(sprite->params.isDepthTestEnabled()); + distortionCheckBox.SetCheck(sprite->params.isExtractNormalMapEnabled()); + colorPicker.SetPickColor(wi::Color::fromFloat4(sprite->params.color)); + movingTexXSlider.SetValue(sprite->anim.movingTexAnim.speedX); + movingTexYSlider.SetValue(sprite->anim.movingTexAnim.speedY); + drawrectFrameRateSlider.SetValue(sprite->anim.drawRectAnim.frameRate); + drawrectFrameCountInput.SetValue(sprite->anim.drawRectAnim.frameCount); + drawrectHorizontalFrameCountInput.SetValue(sprite->anim.drawRectAnim.horizontalFrameCount); + wobbleXSlider.SetValue(sprite->anim.wobbleAnim.amount.x); + wobbleYSlider.SetValue(sprite->anim.wobbleAnim.amount.y); + wobbleSpeedSlider.SetValue(sprite->anim.wobbleAnim.speed); +} + +void SpriteWindow::ResizeLayout() +{ + wi::gui::Window::ResizeLayout(); + const float padding = 4; + const float width = GetWidgetAreaSize().x; + float y = padding; + float jump = 20; + + const float margin_left = 118; + 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(textureButton); + add_fullwidth(maskButton); + add(pivotXSlider); + add(pivotYSlider); + add(intensitySlider); + add(rotationSlider); + add(alphaStartSlider); + add(alphaEndSlider); + add(borderSoftenSlider); + add(cornerRounding0Slider); + add(cornerRounding1Slider); + add(cornerRounding2Slider); + add(cornerRounding3Slider); + add(qualityCombo); + add(samplemodeCombo); + add(blendModeCombo); + add_right(hiddenCheckBox); + add_right(cameraFacingCheckBox); + add_right(cameraScalingCheckBox); + add_right(depthTestCheckBox); + add_right(distortionCheckBox); + add_fullwidth(colorPicker); + add(movingTexXSlider); + add(movingTexYSlider); + add(drawrectFrameRateSlider); + add(drawrectFrameCountInput); + add(drawrectHorizontalFrameCountInput); + add(wobbleXSlider); + add(wobbleYSlider); + add(wobbleSpeedSlider); +} + +void SpriteWindow::UpdateSpriteDrawRectParams(wi::Sprite* sprite) +{ + if (sprite->anim.drawRectAnim.frameCount > 1 && sprite->textureResource.IsValid()) + { + const TextureDesc& desc = sprite->textureResource.GetTexture().GetDesc(); + XMFLOAT4 rect = XMFLOAT4(0, 0, 0, 0); + int horizontal_frame_count = std::max(1, sprite->anim.drawRectAnim.horizontalFrameCount); + int vertical_frame_count = sprite->anim.drawRectAnim.frameCount / horizontal_frame_count; + rect.z = float(desc.width) / float(horizontal_frame_count); + rect.w = float(desc.height) / float(vertical_frame_count); + sprite->params.enableDrawRect(rect); + } + else + { + sprite->params.disableDrawRect(); + } + sprite->anim.drawRectAnim.restart(); +} diff --git a/Editor/SpriteWindow.h b/Editor/SpriteWindow.h new file mode 100644 index 000000000..95ab44b44 --- /dev/null +++ b/Editor/SpriteWindow.h @@ -0,0 +1,51 @@ +#pragma once +class EditorComponent; + +class SpriteWindow : 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); + + wi::gui::Button textureButton; + wi::gui::Button maskButton; + wi::gui::Slider pivotXSlider; + wi::gui::Slider pivotYSlider; + wi::gui::Slider intensitySlider; + wi::gui::Slider rotationSlider; + wi::gui::Slider alphaStartSlider; + wi::gui::Slider alphaEndSlider; + wi::gui::Slider borderSoftenSlider; + wi::gui::Slider cornerRounding0Slider; + wi::gui::Slider cornerRounding1Slider; + wi::gui::Slider cornerRounding2Slider; + wi::gui::Slider cornerRounding3Slider; + wi::gui::ComboBox qualityCombo; + wi::gui::ComboBox samplemodeCombo; + wi::gui::ComboBox blendModeCombo; + wi::gui::CheckBox mirrorCheckBox; + wi::gui::CheckBox hiddenCheckBox; + wi::gui::CheckBox cameraFacingCheckBox; + wi::gui::CheckBox cameraScalingCheckBox; + wi::gui::CheckBox depthTestCheckBox; + wi::gui::CheckBox distortionCheckBox; + wi::gui::ColorPicker colorPicker; + + wi::gui::Slider movingTexXSlider; + wi::gui::Slider movingTexYSlider; + + wi::gui::Slider drawrectFrameRateSlider; + wi::gui::TextInputField drawrectFrameCountInput; + wi::gui::TextInputField drawrectHorizontalFrameCountInput; + + wi::gui::Slider wobbleXSlider; + wi::gui::Slider wobbleYSlider; + wi::gui::Slider wobbleSpeedSlider; + + void ResizeLayout() override; + + void UpdateSpriteDrawRectParams(wi::Sprite* sprite); +}; diff --git a/WickedEngine/shaders/ShaderInterop_Font.h b/WickedEngine/shaders/ShaderInterop_Font.h index 804dd07a8..6ad1e9bd4 100644 --- a/WickedEngine/shaders/ShaderInterop_Font.h +++ b/WickedEngine/shaders/ShaderInterop_Font.h @@ -24,7 +24,9 @@ struct FontConstants int buffer_index; uint buffer_offset; int texture_index; - uint color; + int padding0; + + float4 color; float softness; float bolden; diff --git a/WickedEngine/shaders/ShaderInterop_Image.h b/WickedEngine/shaders/ShaderInterop_Image.h index bf0402681..4ed5276e1 100644 --- a/WickedEngine/shaders/ShaderInterop_Image.h +++ b/WickedEngine/shaders/ShaderInterop_Image.h @@ -15,8 +15,8 @@ struct ImageConstants float hdr_scaling; uint2 packed_color; // packed half4 - uint2 texMulAdd; // packed half4 - uint2 texMulAdd2; // packed half4 + float4 texMulAdd; + float4 texMulAdd2; int buffer_index; uint buffer_offset; @@ -26,13 +26,13 @@ struct ImageConstants int texture_mask_index; int texture_background_index; float border_soften; - uint mask_alpha_range; + uint mask_alpha_range; // packed half2 // parameters for inverse bilinear interpolation: - uint b0; // packed half2 - uint b1; // packed half2 - uint b2; // packed half2 - uint b3; // packed half2 + float2 b0; + float2 b1; + float2 b2; + float2 b3; }; CONSTANTBUFFER(image, ImageConstants, CBSLOT_IMAGE); diff --git a/WickedEngine/shaders/fontPS.hlsl b/WickedEngine/shaders/fontPS.hlsl index 3d40c35c3..4b6dc2ab1 100644 --- a/WickedEngine/shaders/fontPS.hlsl +++ b/WickedEngine/shaders/fontPS.hlsl @@ -12,14 +12,14 @@ float4 main(VertextoPixel input) : SV_TARGET { Texture2D tex = bindless_textures[font.texture_index]; float value = tex.SampleLevel(sampler_linear_clamp, input.uv, 0).r; - float4 color = unpack_rgba(font.color); + float4 color = font.color; [branch] if (font.flags & FONT_FLAG_SDF_RENDERING) { float2 bary_fw = fwidth(input.bary); float w = max(bary_fw.x, bary_fw.y); // screen coverage dependency - w = max(0.005, w); // min softness to avoid pixelated hard edge in magnification + w = max(w, 1.0 / 255.0); // min softness to avoid pixelated hard edge in magnification w += font.softness; w = saturate(w); float mid = lerp(SDF::onedge_value_unorm, 0, font.bolden); diff --git a/WickedEngine/shaders/imageHF.hlsli b/WickedEngine/shaders/imageHF.hlsli index 6c6918bfb..24b19da26 100644 --- a/WickedEngine/shaders/imageHF.hlsli +++ b/WickedEngine/shaders/imageHF.hlsli @@ -33,9 +33,9 @@ struct VertextoPixel else { // Quad interpolation: http://reedbeta.com/blog/quadrilateral-interpolation-part-2/ - float2 b1 = unpack_half2(image.b1); - float2 b2 = unpack_half2(image.b2); - float2 b3 = unpack_half2(image.b3); + float2 b1 = image.b1; + float2 b2 = image.b2; + float2 b3 = image.b3; // Set up quadratic formula float A = Wedge2D(b2, b3); @@ -63,11 +63,8 @@ struct VertextoPixel else uv.x = (q.y - b2.y * uv.y) / denom.y; - float4 texMulAdd = unpack_half4(image.texMulAdd); - float4 texMulAdd2 = unpack_half4(image.texMulAdd2); - - uv0 = mad(uv, texMulAdd.xy, texMulAdd.zw); - uv1 = mad(uv, texMulAdd2.xy, texMulAdd2.zw); + uv0 = mad(uv, image.texMulAdd.xy, image.texMulAdd.zw); + uv1 = mad(uv, image.texMulAdd2.xy, image.texMulAdd2.zw); } if (image.flags & IMAGE_FLAG_MIRROR) diff --git a/WickedEngine/shaders/imageVS.hlsl b/WickedEngine/shaders/imageVS.hlsl index 3770cb1a8..9b6dc0280 100644 --- a/WickedEngine/shaders/imageVS.hlsl +++ b/WickedEngine/shaders/imageVS.hlsl @@ -23,7 +23,7 @@ VertextoPixel main(uint vI : SV_VertexID) Out.pos = bindless_buffers[image.buffer_index].Load(image.buffer_offset + vI * sizeof(float4)); // Set up inverse bilinear interpolation - Out.q = Out.pos.xy - unpack_half2(image.b0); + Out.q = Out.pos.xy - image.b0; if (image.flags & IMAGE_FLAG_CORNER_ROUNDING) { diff --git a/WickedEngine/wiFont.cpp b/WickedEngine/wiFont.cpp index 836ec2037..11d8d4a7f 100644 --- a/WickedEngine/wiFont.cpp +++ b/WickedEngine/wiFont.cpp @@ -20,21 +20,26 @@ #include #include +using namespace wi::enums; using namespace wi::graphics; namespace wi::font { namespace font_internal { + enum DEPTH_TEST_MODE + { + DEPTH_TEST_OFF, + DEPTH_TEST_ON, + DEPTH_TEST_MODE_COUNT + }; static BlendState blendState; static RasterizerState rasterizerState; - static DepthStencilState depthStencilState; - static DepthStencilState depthStencilState_depth_test; + static DepthStencilState depthStencilStates[DEPTH_TEST_MODE_COUNT]; static Shader vertexShader; static Shader pixelShader; - static PipelineState PSO; - static PipelineState PSO_depth_test; + static PipelineState PSO[DEPTH_TEST_MODE_COUNT]; static thread_local wi::Canvas canvas; @@ -108,7 +113,7 @@ namespace wi::font }; static_assert(sizeof(GlyphHash) == sizeof(uint32_t)); static wi::unordered_set pendingGlyphs; - static std::mutex glyphLock; + static std::mutex locker; struct ParseStatus { @@ -161,7 +166,7 @@ namespace wi::font if (glyph_lookup.count(hash.raw) == 0) { // glyph not packed yet, so add to pending list: - std::scoped_lock locker(glyphLock); + std::scoped_lock lck(locker); pendingGlyphs.insert(hash.raw); continue; } @@ -262,17 +267,17 @@ namespace wi::font wi::renderer::LoadShader(ShaderStage::VS, vertexShader, "fontVS.cso"); wi::renderer::LoadShader(ShaderStage::PS, pixelShader, "fontPS.cso"); - PipelineStateDesc desc; - desc.vs = &vertexShader; - desc.ps = &pixelShader; - desc.bs = &blendState; - desc.dss = &depthStencilState; - desc.rs = &rasterizerState; - desc.pt = PrimitiveTopology::TRIANGLESTRIP; - wi::graphics::GetDevice()->CreatePipelineState(&desc, &PSO); - - desc.dss = &depthStencilState_depth_test; - wi::graphics::GetDevice()->CreatePipelineState(&desc, &PSO_depth_test); + for (int d = 0; d < DEPTH_TEST_MODE_COUNT; ++d) + { + PipelineStateDesc desc; + desc.vs = &vertexShader; + desc.ps = &pixelShader; + desc.bs = &blendState; + desc.dss = &depthStencilStates[d]; + desc.rs = &rasterizerState; + desc.pt = PrimitiveTopology::TRIANGLESTRIP; + wi::graphics::GetDevice()->CreatePipelineState(&desc, &PSO[d]); + } } void Initialize() { @@ -311,12 +316,12 @@ namespace wi::font DepthStencilState dsd; dsd.depth_enable = false; dsd.stencil_enable = false; - depthStencilState = dsd; + depthStencilStates[DEPTH_TEST_OFF] = dsd; dsd.depth_enable = true; dsd.depth_write_mask = DepthWriteMask::ZERO; - dsd.depth_func = ComparisonFunc::GREATER; - depthStencilState_depth_test = dsd; + dsd.depth_func = ComparisonFunc::GREATER_EQUAL; + depthStencilStates[DEPTH_TEST_ON] = dsd; static wi::eventhandler::Handle handle1 = wi::eventhandler::Subscribe(wi::eventhandler::EVENT_RELOAD_SHADERS, [](uint64_t userdata) { LoadShaders(); }); LoadShaders(); @@ -333,7 +338,7 @@ namespace wi::font } void UpdateAtlas(float upscaling) { - std::scoped_lock locker(glyphLock); + std::scoped_lock lck(locker); upscaling = std::max(1.5f, upscaling); // add some minimum upscaling, especially for SDF static float upscaling_prev = 1; @@ -501,6 +506,7 @@ namespace wi::font } int AddFontStyle(const std::string& fontName) { + std::scoped_lock lck(locker); for (size_t i = 0; i < fontStyles.size(); i++) { const FontStyle& fontStyle = *fontStyles[i]; @@ -514,8 +520,9 @@ namespace wi::font InvalidateAtlas(); // invalidate atlas, in case there were missing glyphs, upon adding new font style they could become valid return int(fontStyles.size() - 1); } - int AddFontStyle(const std::string& fontName, const uint8_t* data, size_t size) + int AddFontStyle(const std::string& fontName, const uint8_t* data, size_t size, bool copyData) { + std::scoped_lock lck(locker); for (size_t i = 0; i < fontStyles.size(); i++) { const FontStyle& fontStyle = *fontStyles[i]; @@ -525,6 +532,12 @@ namespace wi::font } } fontStyles.push_back(std::make_unique()); + if (copyData) + { + fontStyles.back()->fontBuffer.resize(size); + std::memcpy(fontStyles.back()->fontBuffer.data(), data, size); + data = fontStyles.back()->fontBuffer.data(); + } fontStyles.back()->Create(fontName, data, size); InvalidateAtlas(); // invalidate atlas, in case there were missing glyphs, upon adding new font style they could become valid return int(fontStyles.size() - 1); @@ -560,14 +573,7 @@ namespace wi::font device->EventBegin("Font", cmd); - if (params.isDepthTestEnabled()) - { - device->BindPipelineState(&PSO_depth_test, cmd); - } - else - { - device->BindPipelineState(&PSO, cmd); - } + device->BindPipelineState(&PSO[params.isDepthTestEnabled()], cmd); font.flags = 0; if (params.isSDFRenderingEnabled()) @@ -625,9 +631,12 @@ namespace wi::font { // font shadow render: XMStoreFloat4x4(&font.transform, XMMatrixTranslation(params.shadow_offset_x, params.shadow_offset_y, 0) * M); - font.color = params.shadowColor.rgba; + font.color = params.shadowColor; + font.color.x *= params.shadow_intensity; + font.color.y *= params.shadow_intensity; + font.color.z *= params.shadow_intensity; font.bolden = params.shadow_bolden; - font.softness = params.shadow_softness; + font.softness = params.shadow_softness * 0.5f; device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd); device->DrawInstanced(4, status.quadCount, 0, 0, cmd); @@ -635,9 +644,12 @@ namespace wi::font // font base render: XMStoreFloat4x4(&font.transform, M); - font.color = params.color.rgba; + font.color = params.color; + font.color.x *= params.intensity; + font.color.y *= params.intensity; + font.color.z *= params.intensity; font.bolden = params.bolden; - font.softness = params.softness; + font.softness = params.softness * 0.5f; device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd); device->DrawInstanced(4, status.quadCount, 0, 0, cmd); @@ -729,6 +741,57 @@ namespace wi::font return ParseText(text.c_str(), text.length(), params).cursor.size; } + Cursor TextCursor(const char* text, size_t text_length, const Params& params) + { + if (text_length == 0) + { + return {}; + } + return ParseText(text, text_length, params).cursor; + } + Cursor TextCursor(const wchar_t* text, size_t text_length, const Params& params) + { + if (text_length == 0) + { + return {}; + } + return ParseText(text, text_length, params).cursor; + } + Cursor TextCursor(const char* text, const Params& params) + { + size_t text_length = strlen(text); + if (text_length == 0) + { + return {}; + } + return ParseText(text, text_length, params).cursor; + } + Cursor TextCursor(const wchar_t* text, const Params& params) + { + size_t text_length = wcslen(text); + if (text_length == 0) + { + return {}; + } + return ParseText(text, text_length, params).cursor; + } + Cursor TextCursor(const std::string& text, const Params& params) + { + if (text.empty()) + { + return {}; + } + return ParseText(text.c_str(), text.length(), params).cursor; + } + Cursor TextCursor(const std::wstring& text, const Params& params) + { + if (text.empty()) + { + return {}; + } + return ParseText(text.c_str(), text.length(), params).cursor; + } + float TextWidth(const char* text, size_t text_length, const Params& params) { return TextSize(text, text_length, params).x; diff --git a/WickedEngine/wiFont.h b/WickedEngine/wiFont.h index 66f3d4fe1..7b552136e 100644 --- a/WickedEngine/wiFont.h +++ b/WickedEngine/wiFont.h @@ -56,6 +56,8 @@ namespace wi::font float shadow_offset_y = 0; // offset for shadow under the text in logical canvas coordinates Cursor cursor; // cursor can be used to continue text drawing by taking the Draw's return value (optional) float hdr_scaling = 1.0f; // a scaling value for use by linear output mapping + float intensity = 1.0f; // color multiplier + float shadow_intensity = 1.0f; // shadow color multiplier const XMMATRIX* customProjection = nullptr; const XMMATRIX* customRotation = nullptr; @@ -140,10 +142,11 @@ namespace wi::font // fontName : name of the font (it doesn't need to be a path) // data : binary data of the .ttf font // size : size of the font binary data + // copyData : whether data is copied away for storage. If false (default) developer must ensure that it is not deleted // Returns fontStyleID that is reusable. If font already exists, just return its ID // NOTE: When loading font with this method, the developer must ensure that font data is - // not deleted while the font is in use - int AddFontStyle(const std::string& fontName, const uint8_t* data, size_t size); + // not deleted while the font is in use (unless copyData is specified as true) + int AddFontStyle(const std::string& fontName, const uint8_t* data, size_t size, bool copyData = false); // Set canvas for the CommandList to handle DPI-aware font rendering on the current thread void SetCanvas(const wi::Canvas& current_canvas); @@ -169,6 +172,14 @@ namespace wi::font XMFLOAT2 TextSize(const std::string& text, const Params& params); XMFLOAT2 TextSize(const std::wstring& text, const Params& params); + // Computes the text's cursor coordinate for a given string + Cursor TextCursor(const char* text, size_t text_length, const Params& params); + Cursor TextCursor(const wchar_t* text, size_t text_length, const Params& params); + Cursor TextCursor(const char* text, const Params& params); + Cursor TextCursor(const wchar_t* text, const Params& params); + Cursor TextCursor(const std::string& text, const Params& params); + Cursor TextCursor(const std::wstring& text, const Params& params); + // Computes the text's width in logical canvas coordinates // Avoid calling TextWidth() and TextHeight() both, instead use TextSize() if you need both measurements! float TextWidth(const char* text, size_t text_length, const Params& params); diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index d67154225..e0d17ce9c 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -1754,7 +1754,7 @@ namespace wi::gui SetName(name); SetText(name); OnSlide([](EventArgs args) {}); - SetSize(XMFLOAT2(200, 40)); + SetSize(XMFLOAT2(200, 20)); valueInputField.Create(name + "_endInputField"); valueInputField.SetLocalizationEnabled(LocalizationEnabled::None); diff --git a/WickedEngine/wiHelper.cpp b/WickedEngine/wiHelper.cpp index 278f4ee7d..bee154849 100644 --- a/WickedEngine/wiHelper.cpp +++ b/WickedEngine/wiHelper.cpp @@ -799,10 +799,7 @@ namespace wi::helper if (is_png) { - // lodepng encoder is better compressing than stb_image_write: - unsigned error = lodepng::encode(filedata, texturedata, desc.width, desc.height); - return error == 0; - //write_result = stbi_write_png_to_func(func, &filedata, (int)mip.width, (int)mip.height, dst_channel_count, mip.address, 0); + write_result = stbi_write_png_to_func(func, &filedata, (int)mip.width, (int)mip.height, dst_channel_count, mip.address, 0); } else if (!extension.compare("JPG") || !extension.compare("JPEG")) { diff --git a/WickedEngine/wiImage.cpp b/WickedEngine/wiImage.cpp index b729df0d9..6d2f20534 100644 --- a/WickedEngine/wiImage.cpp +++ b/WickedEngine/wiImage.cpp @@ -7,8 +7,8 @@ #include "wiEventHandler.h" #include "wiTimer.h" -using namespace wi::graphics; using namespace wi::enums; +using namespace wi::graphics; namespace wi::image { @@ -17,14 +17,20 @@ namespace wi::image static Shader pixelShader; static BlendState blendStates[BLENDMODE_COUNT]; static RasterizerState rasterizerState; - static DepthStencilState depthStencilStates[STENCILMODE_COUNT][STENCILREFMODE_COUNT]; + enum DEPTH_TEST_MODE + { + DEPTH_TEST_OFF, + DEPTH_TEST_ON, + DEPTH_TEST_MODE_COUNT + }; + static DepthStencilState depthStencilStates[STENCILMODE_COUNT][STENCILREFMODE_COUNT][DEPTH_TEST_MODE_COUNT]; enum STRIP_MODE { STRIP_OFF, STRIP_ON, STRIP_MODE_COUNT, }; - static PipelineState imagePSO[BLENDMODE_COUNT][STENCILMODE_COUNT][STENCILREFMODE_COUNT][STRIP_MODE_COUNT]; + static PipelineState imagePSO[BLENDMODE_COUNT][STENCILMODE_COUNT][STENCILREFMODE_COUNT][DEPTH_TEST_MODE_COUNT][STRIP_MODE_COUNT]; static thread_local Texture backgroundTexture; static thread_local wi::Canvas canvas; @@ -94,6 +100,9 @@ namespace wi::image color.x *= darken; color.y *= darken; color.z *= darken; + color.x *= params.intensity; + color.y *= params.intensity; + color.z *= params.intensity; color.w *= params.opacity; XMHALF4 packed_color; @@ -173,10 +182,10 @@ namespace wi::image XMStoreFloat4(corners + i, XMVector2Transform(V[i], M)); // division by w will happen on GPU } - image.b0 = XMHALF2(corners[0].x, corners[0].y).v; - image.b1 = XMHALF2(corners[1].x - corners[0].x, corners[1].y - corners[0].y).v; - image.b2 = XMHALF2(corners[2].x - corners[0].x, corners[2].y - corners[0].y).v; - image.b3 = XMHALF2(corners[0].x - corners[1].x - corners[2].x + corners[3].x, corners[0].y - corners[1].y - corners[2].y + corners[3].y).v; + image.b0 = float2(corners[0].x, corners[0].y); + image.b1 = float2(corners[1].x - corners[0].x, corners[1].y - corners[0].y); + image.b2 = float2(corners[2].x - corners[0].x, corners[2].y - corners[0].y); + image.b3 = float2(corners[0].x - corners[1].x - corners[2].x + corners[3].x, corners[0].y - corners[1].y - corners[2].y + corners[3].y); if (params.isCornerRoundingEnabled()) { @@ -282,49 +291,33 @@ namespace wi::image const float inv_width = 1.0f / float(desc.width); const float inv_height = 1.0f / float(desc.height); - XMFLOAT4 texMulAdd; if (params.isDrawRectEnabled()) { - texMulAdd.x = params.drawRect.z * inv_width; // drawRec.width: mul - texMulAdd.y = params.drawRect.w * inv_height; // drawRec.heigh: mul - texMulAdd.z = params.drawRect.x * inv_width; // drawRec.x: add - texMulAdd.w = params.drawRect.y * inv_height; // drawRec.y: add + image.texMulAdd.x = params.drawRect.z * inv_width; // drawRec.width: mul + image.texMulAdd.y = params.drawRect.w * inv_height; // drawRec.heigh: mul + image.texMulAdd.z = params.drawRect.x * inv_width; // drawRec.x: add + image.texMulAdd.w = params.drawRect.y * inv_height; // drawRec.y: add } else { - texMulAdd = XMFLOAT4(1, 1, 0, 0); // disabled draw rect + image.texMulAdd = XMFLOAT4(1, 1, 0, 0); // disabled draw rect } - texMulAdd.z += params.texOffset.x * inv_width; // texOffset.x: add - texMulAdd.w += params.texOffset.y * inv_height; // texOffset.y: add - XMHALF4 half_texMulAdd; - half_texMulAdd.x = XMConvertFloatToHalf(texMulAdd.x); - half_texMulAdd.y = XMConvertFloatToHalf(texMulAdd.y); - half_texMulAdd.z = XMConvertFloatToHalf(texMulAdd.z); - half_texMulAdd.w = XMConvertFloatToHalf(texMulAdd.w); - image.texMulAdd.x = uint(half_texMulAdd.v); - image.texMulAdd.y = uint(half_texMulAdd.v >> 32ull); + image.texMulAdd.z += params.texOffset.x * inv_width; // texOffset.x: add + image.texMulAdd.w += params.texOffset.y * inv_height; // texOffset.y: add - XMFLOAT4 texMulAdd2; if (params.isDrawRect2Enabled()) { - texMulAdd2.x = params.drawRect2.z * inv_width; // drawRec.width: mul - texMulAdd2.y = params.drawRect2.w * inv_height; // drawRec.heigh: mul - texMulAdd2.z = params.drawRect2.x * inv_width; // drawRec.x: add - texMulAdd2.w = params.drawRect2.y * inv_height; // drawRec.y: add + image.texMulAdd2.x = params.drawRect2.z * inv_width; // drawRec.width: mul + image.texMulAdd2.y = params.drawRect2.w * inv_height; // drawRec.heigh: mul + image.texMulAdd2.z = params.drawRect2.x * inv_width; // drawRec.x: add + image.texMulAdd2.w = params.drawRect2.y * inv_height; // drawRec.y: add } else { - texMulAdd2 = XMFLOAT4(1, 1, 0, 0); // disabled draw rect + image.texMulAdd2 = XMFLOAT4(1, 1, 0, 0); // disabled draw rect } - texMulAdd2.z += params.texOffset2.x * inv_width; // texOffset.x: add - texMulAdd2.w += params.texOffset2.y * inv_height; // texOffset.y: add - XMHALF4 half_texMulAdd2; - half_texMulAdd2.x = XMConvertFloatToHalf(texMulAdd2.x); - half_texMulAdd2.y = XMConvertFloatToHalf(texMulAdd2.y); - half_texMulAdd2.z = XMConvertFloatToHalf(texMulAdd2.z); - half_texMulAdd2.w = XMConvertFloatToHalf(texMulAdd2.w); - image.texMulAdd2.x = uint(half_texMulAdd2.v); - image.texMulAdd2.y = uint(half_texMulAdd2.v >> 32ull); + image.texMulAdd2.z += params.texOffset2.x * inv_width; // texOffset.x: add + image.texMulAdd2.w += params.texOffset2.y * inv_height; // texOffset.y: add device->EventBegin("Image", cmd); @@ -335,7 +328,7 @@ namespace wi::image } device->BindStencilRef(stencilRef, cmd); - device->BindPipelineState(&imagePSO[params.blendFlag][params.stencilComp][params.stencilRefMode][strip_mode], cmd); + device->BindPipelineState(&imagePSO[params.blendFlag][params.stencilComp][params.stencilRefMode][params.isDepthTestEnabled()][strip_mode], cmd); device->BindDynamicConstantBuffer(image, CBSLOT_IMAGE, cmd); @@ -380,23 +373,25 @@ namespace wi::image { for (int m = 0; m < STENCILREFMODE_COUNT; ++m) { - desc.dss = &depthStencilStates[k][m]; - - for (int n = 0; n < STRIP_MODE_COUNT; ++n) + for (int d = 0; d < DEPTH_TEST_MODE_COUNT; ++d) { - switch (n) - { - default: - case STRIP_ON: - desc.pt = PrimitiveTopology::TRIANGLESTRIP; - break; - case STRIP_OFF: - desc.pt = PrimitiveTopology::TRIANGLELIST; - break; - } - device->CreatePipelineState(&desc, &imagePSO[j][k][m][n]); - } + desc.dss = &depthStencilStates[k][m][d]; + for (int n = 0; n < STRIP_MODE_COUNT; ++n) + { + switch (n) + { + default: + case STRIP_ON: + desc.pt = PrimitiveTopology::TRIANGLESTRIP; + break; + case STRIP_OFF: + desc.pt = PrimitiveTopology::TRIANGLELIST; + break; + } + device->CreatePipelineState(&desc, &imagePSO[j][k][m][d][n]); + } + } } } } @@ -425,59 +420,75 @@ namespace wi::image for (int i = 0; i < STENCILREFMODE_COUNT; ++i) { - DepthStencilState dsd; - dsd.depth_enable = false; - dsd.stencil_enable = false; - depthStencilStates[STENCILMODE_DISABLED][i] = dsd; - - dsd.stencil_enable = true; - switch (i) + for (int d = 0; d < DEPTH_TEST_MODE_COUNT; ++d) { - case STENCILREFMODE_ENGINE: - dsd.stencil_read_mask = STENCILREF_MASK_ENGINE; - break; - case STENCILREFMODE_USER: - dsd.stencil_read_mask = STENCILREF_MASK_USER; - break; - default: - dsd.stencil_read_mask = STENCILREF_MASK_ALL; - break; + DepthStencilState dsd; + dsd.depth_write_mask = DepthWriteMask::ZERO; + + switch (d) + { + default: + case DEPTH_TEST_OFF: + dsd.depth_enable = false; + break; + case DEPTH_TEST_ON: + dsd.depth_enable = true; + dsd.depth_func = ComparisonFunc::GREATER_EQUAL; + break; + } + + dsd.stencil_enable = false; + depthStencilStates[STENCILMODE_DISABLED][i][d] = dsd; + + dsd.stencil_enable = true; + switch (i) + { + case STENCILREFMODE_ENGINE: + dsd.stencil_read_mask = STENCILREF_MASK_ENGINE; + break; + case STENCILREFMODE_USER: + dsd.stencil_read_mask = STENCILREF_MASK_USER; + break; + default: + dsd.stencil_read_mask = STENCILREF_MASK_ALL; + break; + } + dsd.stencil_write_mask = 0; + dsd.front_face.stencil_pass_op = StencilOp::KEEP; + dsd.front_face.stencil_fail_op = StencilOp::KEEP; + dsd.front_face.stencil_depth_fail_op = StencilOp::KEEP; + dsd.back_face.stencil_pass_op = StencilOp::KEEP; + dsd.back_face.stencil_fail_op = StencilOp::KEEP; + dsd.back_face.stencil_depth_fail_op = StencilOp::KEEP; + + dsd.front_face.stencil_func = ComparisonFunc::EQUAL; + dsd.back_face.stencil_func = ComparisonFunc::EQUAL; + depthStencilStates[STENCILMODE_EQUAL][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::LESS; + dsd.back_face.stencil_func = ComparisonFunc::LESS; + depthStencilStates[STENCILMODE_LESS][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::LESS_EQUAL; + dsd.back_face.stencil_func = ComparisonFunc::LESS_EQUAL; + depthStencilStates[STENCILMODE_LESSEQUAL][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::GREATER; + dsd.back_face.stencil_func = ComparisonFunc::GREATER; + depthStencilStates[STENCILMODE_GREATER][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::GREATER_EQUAL; + dsd.back_face.stencil_func = ComparisonFunc::GREATER_EQUAL; + depthStencilStates[STENCILMODE_GREATEREQUAL][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::NOT_EQUAL; + dsd.back_face.stencil_func = ComparisonFunc::NOT_EQUAL; + depthStencilStates[STENCILMODE_NOT][i][d] = dsd; + + dsd.front_face.stencil_func = ComparisonFunc::ALWAYS; + dsd.back_face.stencil_func = ComparisonFunc::ALWAYS; + depthStencilStates[STENCILMODE_ALWAYS][i][d] = dsd; } - dsd.stencil_write_mask = 0; - dsd.front_face.stencil_pass_op = StencilOp::KEEP; - dsd.front_face.stencil_fail_op = StencilOp::KEEP; - dsd.front_face.stencil_depth_fail_op = StencilOp::KEEP; - dsd.back_face.stencil_pass_op = StencilOp::KEEP; - dsd.back_face.stencil_fail_op = StencilOp::KEEP; - dsd.back_face.stencil_depth_fail_op = StencilOp::KEEP; - - dsd.front_face.stencil_func = ComparisonFunc::EQUAL; - dsd.back_face.stencil_func = ComparisonFunc::EQUAL; - depthStencilStates[STENCILMODE_EQUAL][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::LESS; - dsd.back_face.stencil_func = ComparisonFunc::LESS; - depthStencilStates[STENCILMODE_LESS][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::LESS_EQUAL; - dsd.back_face.stencil_func = ComparisonFunc::LESS_EQUAL; - depthStencilStates[STENCILMODE_LESSEQUAL][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::GREATER; - dsd.back_face.stencil_func = ComparisonFunc::GREATER; - depthStencilStates[STENCILMODE_GREATER][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::GREATER_EQUAL; - dsd.back_face.stencil_func = ComparisonFunc::GREATER_EQUAL; - depthStencilStates[STENCILMODE_GREATEREQUAL][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::NOT_EQUAL; - dsd.back_face.stencil_func = ComparisonFunc::NOT_EQUAL; - depthStencilStates[STENCILMODE_NOT][i] = dsd; - - dsd.front_face.stencil_func = ComparisonFunc::ALWAYS; - dsd.back_face.stencil_func = ComparisonFunc::ALWAYS; - depthStencilStates[STENCILMODE_ALWAYS][i] = dsd; } diff --git a/WickedEngine/wiImage.h b/WickedEngine/wiImage.h index 300db2205..342a390a8 100644 --- a/WickedEngine/wiImage.h +++ b/WickedEngine/wiImage.h @@ -56,6 +56,7 @@ namespace wi::image OUTPUT_COLOR_SPACE_HDR10_ST2084 = 1 << 6, OUTPUT_COLOR_SPACE_LINEAR = 1 << 7, CORNER_ROUNDING = 1 << 8, + DEPTH_TEST = 1 << 9, }; uint32_t _flags = EMPTY; @@ -71,6 +72,7 @@ namespace wi::image float rotation = 0; float fade = 0; float opacity = 1; + float intensity = 1; float hdr_scaling = 1.0f; // a scaling value for use by linear output mapping float mask_alpha_range_start = 0; // constrain mask alpha to not go below this level float mask_alpha_range_end = 1; // constrain mask alpha to not go above this level @@ -114,6 +116,7 @@ namespace wi::image constexpr bool isHDR10OutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr bool isLinearOutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_LINEAR; } constexpr bool isCornerRoundingEnabled() const { return _flags & CORNER_ROUNDING; } + constexpr bool isDepthTestEnabled() const { return _flags & DEPTH_TEST; } // enables draw rectangle for base texture (cutout texture outside draw rectangle) constexpr void enableDrawRect(const XMFLOAT4& rect) { _flags |= DRAWRECT; drawRect = rect; } @@ -133,6 +136,7 @@ namespace wi::image // enable linear output mapping, which means removing gamma curve and outputting in linear space (useful for blending in HDR space) constexpr void enableLinearOutputMapping(float scaling = 1.0f) { _flags |= OUTPUT_COLOR_SPACE_LINEAR; hdr_scaling = scaling; } constexpr void enableCornerRounding() { _flags |= CORNER_ROUNDING; } + constexpr void enableDepthTest() { _flags |= DEPTH_TEST; } // disable draw rectangle for base texture (whole texture will be drawn, no cutout) constexpr void disableDrawRect() { _flags &= ~DRAWRECT; } @@ -145,6 +149,7 @@ namespace wi::image constexpr void disableHDR10OutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr void disableLinearOutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_LINEAR; } constexpr void disableCornerRounding() { _flags &= ~CORNER_ROUNDING; } + constexpr void disableDepthTest() { _flags &= ~DEPTH_TEST; } Params() = default; diff --git a/WickedEngine/wiRenderPath3D.cpp b/WickedEngine/wiRenderPath3D.cpp index d8ab5fedc..cca5997ad 100644 --- a/WickedEngine/wiRenderPath3D.cpp +++ b/WickedEngine/wiRenderPath3D.cpp @@ -1214,6 +1214,7 @@ namespace wi } wi::renderer::DrawSoftParticles(visibility_reflection, false, cmd); + wi::renderer::DrawSpritesAndFonts(*scene, camera_reflection, false, cmd); device->RenderPassEnd(cmd); @@ -1875,9 +1876,12 @@ namespace wi wi::profiler::EndRange(range); // Transparent Scene } + wi::renderer::DrawDebugWorld(*scene, *camera, *this, cmd); + wi::renderer::DrawLightVisualizers(visibility_main, cmd); wi::renderer::DrawSoftParticles(visibility_main, false, cmd); + wi::renderer::DrawSpritesAndFonts(*scene, *camera, false, cmd); if (getVolumeLightsEnabled() && visibility_main.IsRequestedVolumetricLights()) { @@ -1913,8 +1917,6 @@ namespace wi ); } - wi::renderer::DrawDebugWorld(*scene, *camera, *this, cmd); - device->RenderPassEnd(cmd); // Distortion particles: @@ -1957,6 +1959,7 @@ namespace wi device->BindViewports(1, &vp, cmd); wi::renderer::DrawSoftParticles(visibility_main, true, cmd); + wi::renderer::DrawSpritesAndFonts(*scene, *camera, true, cmd); device->RenderPassEnd(cmd); } diff --git a/WickedEngine/wiRenderPath3D_PathTracing.cpp b/WickedEngine/wiRenderPath3D_PathTracing.cpp index 4d51aa0af..95ffc5289 100644 --- a/WickedEngine/wiRenderPath3D_PathTracing.cpp +++ b/WickedEngine/wiRenderPath3D_PathTracing.cpp @@ -373,6 +373,24 @@ namespace wi wi::profiler::EndRange(range); // Traced Scene } + if (depthBuffer_Main.IsValid()) + { + RenderPassImage rp[] = { + RenderPassImage::DepthStencil(&depthBuffer_Main, RenderPassImage::LoadOp::LOAD), + RenderPassImage::RenderTarget(&traceResult, RenderPassImage::LoadOp::LOAD) + }; + device->RenderPassBegin(rp, arraysize(rp), cmd); + } + else + { + RenderPassImage rp[] = { + RenderPassImage::RenderTarget(&traceResult, RenderPassImage::LoadOp::LOAD) + }; + device->RenderPassBegin(rp, arraysize(rp), cmd); + } + wi::renderer::DrawSpritesAndFonts(*scene, *camera, false, cmd); + device->RenderPassEnd(cmd); + }); if (scene->terrains.GetCount() > 0) diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 745580a37..c072f149d 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -4918,6 +4918,149 @@ void DrawSoftParticles( wi::profiler::EndRange(range); } +void DrawSpritesAndFonts( + const wi::scene::Scene& scene, + const wi::scene::CameraComponent& camera, + bool distortion, + wi::graphics::CommandList cmd +) +{ + if (scene.sprites.GetCount() == 0 && scene.fonts.GetCount() == 0) + return; + if (distortion) + { + device->EventBegin("Sprites and Fonts (distortion)", cmd); + } + else + { + device->EventBegin("Sprites and Fonts", cmd); + } + const XMMATRIX VP = camera.GetViewProjection(); + const XMMATRIX R = XMLoadFloat3x3(&camera.rotationMatrix); + enum TYPE // ordering of the enums is important, it is designed to prioritize font on top of sprite rendering if distance is the same + { + FONT, + SPRITE, + }; + union DistanceSorter { + struct + { + uint64_t id : 32; + uint64_t type : 16; + uint64_t distance : 16; + } bits; + uint64_t raw; + static_assert(sizeof(bits) == sizeof(raw)); + }; + static thread_local wi::vector distance_sorter; + distance_sorter.clear(); + for (size_t i = 0; i < scene.sprites.GetCount(); ++i) + { + const wi::Sprite& sprite = scene.sprites[i]; + if (sprite.IsHidden()) + continue; + if (sprite.params.isExtractNormalMapEnabled() != distortion) + continue; + DistanceSorter sorter = {}; + sorter.bits.id = uint32_t(i); + sorter.bits.type = SPRITE; + Entity entity = scene.sprites.GetEntity(i); + const TransformComponent* transform = scene.transforms.GetComponent(entity); + if (transform != nullptr) + { + sorter.bits.distance = XMConvertFloatToHalf(wi::math::DistanceEstimated(transform->GetPosition(), camera.Eye)); + } + distance_sorter.push_back(sorter.raw); + } + if (!distortion) + { + for (size_t i = 0; i < scene.fonts.GetCount(); ++i) + { + const wi::SpriteFont& font = scene.fonts[i]; + if (font.IsHidden()) + continue; + DistanceSorter sorter = {}; + sorter.bits.id = uint32_t(i); + sorter.bits.type = FONT; + Entity entity = scene.fonts.GetEntity(i); + const TransformComponent* transform = scene.transforms.GetComponent(entity); + if (transform != nullptr) + { + sorter.bits.distance = XMConvertFloatToHalf(wi::math::DistanceEstimated(transform->GetPosition(), camera.Eye)); + } + distance_sorter.push_back(sorter.raw); + } + } + std::sort(distance_sorter.begin(), distance_sorter.end(), std::greater()); + for (auto& x : distance_sorter) + { + DistanceSorter sorter = {}; + sorter.raw = x; + XMMATRIX M = VP; + Entity entity = INVALID_ENTITY; + const TransformComponent* transform = nullptr; + + switch (sorter.bits.type) + { + default: + case SPRITE: + { + const wi::Sprite& sprite = scene.sprites[sorter.bits.id]; + wi::image::Params params = sprite.params; + entity = scene.sprites.GetEntity(sorter.bits.id); + transform = scene.transforms.GetComponent(entity); + if (transform != nullptr) + { + M = XMLoadFloat4x4(&transform->world) * M; + if (sprite.IsCameraScaling()) + { + float scale = 0.05f * wi::math::Distance(transform->GetPosition(), camera.Eye); + M = XMMatrixScaling(scale, scale, scale) * M; + } + } + if (sprite.IsCameraFacing()) + { + M = R * M; + } + params.customProjection = &M; + if (sprite.maskResource.IsValid()) + { + params.setMaskMap(&sprite.maskResource.GetTexture()); + } + else + { + params.setMaskMap(nullptr); + } + wi::image::Draw(sprite.GetTexture(), params, cmd); + } + break; + case FONT: + { + const wi::SpriteFont& font = scene.fonts[sorter.bits.id]; + wi::font::Params params = font.params; + entity = scene.fonts.GetEntity(sorter.bits.id); + transform = scene.transforms.GetComponent(entity); + if (transform != nullptr) + { + M = XMLoadFloat4x4(&transform->world) * M; + if (font.IsCameraScaling()) + { + float scale = 0.05f * wi::math::Distance(transform->GetPosition(), camera.Eye); + M = XMMatrixScaling(scale, scale, scale) * M; + } + } + if (font.IsCameraFacing()) + { + M = R * M; + } + params.customProjection = &M; + wi::font::Draw(font.GetText().c_str(), font.GetCurrentTextLength(), params, cmd); + } + break; + } + } + device->EventEnd(cmd); +} void DrawLightVisualizers( const Visibility& vis, CommandList cmd diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 9185dbeac..1378166d2 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -261,6 +261,13 @@ namespace wi::renderer bool distortion, wi::graphics::CommandList cmd ); + // Draw the sprites and fonts from the scene + void DrawSpritesAndFonts( + const wi::scene::Scene& scene, + const wi::scene::CameraComponent& camera, + bool distortion, + wi::graphics::CommandList cmd + ); // Draw simple light visualizer geometries void DrawLightVisualizers( const Visibility& vis, diff --git a/WickedEngine/wiResourceManager.cpp b/WickedEngine/wiResourceManager.cpp index 0f3170138..05ee71971 100644 --- a/WickedEngine/wiResourceManager.cpp +++ b/WickedEngine/wiResourceManager.cpp @@ -26,6 +26,7 @@ namespace wi std::string script; wi::video::Video video; wi::vector filedata; + int font_style = -1; }; const wi::vector& Resource::GetFileData() const @@ -58,6 +59,11 @@ namespace wi const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get(); return resourceinternal->srgb_subresource; } + int Resource::GetFontStyle() const + { + const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get(); + return resourceinternal->font_style; + } void Resource::SetFileData(const wi::vector& data) { @@ -146,6 +152,7 @@ namespace wi SOUND, SCRIPT, VIDEO, + FONTSTYLE, }; static const wi::unordered_map types = { {"BASIS", DataType::IMAGE}, @@ -162,6 +169,7 @@ namespace wi {"OGG", DataType::SOUND}, {"LUA", DataType::SCRIPT}, {"MP4", DataType::VIDEO}, + {"TTF", DataType::FONTSTYLE}, }; wi::vector GetSupportedImageExtensions() { @@ -211,6 +219,18 @@ namespace wi } return ret; } + wi::vector GetSupportedFontStyleExtensions() + { + wi::vector ret; + for (auto& x : types) + { + if (x.second == DataType::FONTSTYLE) + { + ret.push_back(x.first); + } + } + return ret; + } Resource Load(const std::string& name, Flags flags, const uint8_t* filedata, size_t filesize) { @@ -1072,6 +1092,13 @@ namespace wi } break; + case DataType::FONTSTYLE: + { + resource->font_style = wi::font::AddFontStyle(name, filedata, filesize, true); + success = resource->font_style >= 0; + } + break; + }; } diff --git a/WickedEngine/wiResourceManager.h b/WickedEngine/wiResourceManager.h index 56954b196..db770b5da 100644 --- a/WickedEngine/wiResourceManager.h +++ b/WickedEngine/wiResourceManager.h @@ -24,6 +24,7 @@ namespace wi const std::string& GetScript() const; const wi::video::Video& GetVideo() const; int GetTextureSRGBSubresource() const; + int GetFontStyle() const; void SetFileData(const wi::vector& data); void SetFileData(wi::vector&& data); @@ -53,6 +54,7 @@ namespace wi wi::vector GetSupportedSoundExtensions(); wi::vector GetSupportedVideoExtensions(); wi::vector GetSupportedScriptExtensions(); + wi::vector GetSupportedFontStyleExtensions(); // Order of these must not change as the flags can be serialized! enum class Flags diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 378243138..f029167c4 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -339,6 +339,10 @@ namespace wi::scene RunImpostorUpdateSystem(ctx); + RunSpriteUpdateSystem(ctx); + + RunFontUpdateSystem(ctx); + wi::jobsystem::Wait(ctx); // dependencies // Merge parallel bounds computation (depends on object update system): @@ -4533,6 +4537,44 @@ namespace wi::scene } wi::profiler::EndRange(range); } + void Scene::RunSpriteUpdateSystem(wi::jobsystem::context& ctx) + { + wi::jobsystem::Dispatch(ctx, (uint32_t)sprites.GetCount(), small_subtask_groupsize, [&](wi::jobsystem::JobArgs args) { + Sprite& sprite = sprites[args.jobIndex]; + if (sprite.params.isExtractNormalMapEnabled()) + { + sprite.params.image_subresource = -1; + } + else if (sprite.textureResource.IsValid()) + { + sprite.params.image_subresource = sprite.textureResource.GetTextureSRGBSubresource(); + } + if (sprite.maskResource.IsValid()) + { + sprite.params.mask_subresource = sprite.maskResource.GetTextureSRGBSubresource(); + } + sprite.Update(dt); + }); + } + void Scene::RunFontUpdateSystem(wi::jobsystem::context& ctx) + { + wi::jobsystem::Dispatch(ctx, (uint32_t)fonts.GetCount(), small_subtask_groupsize, [&](wi::jobsystem::JobArgs args) { + SpriteFont& font = fonts[args.jobIndex]; + Entity entity = fonts.GetEntity(args.jobIndex); + const SoundComponent* sound = sounds.GetComponent(entity); + if (sound != nullptr && sound->soundResource.IsValid()) + { + font.anim.typewriter.sound = sound->soundResource.GetSound(); + font.anim.typewriter.soundinstance = sound->soundinstance; + } + else + { + font.anim.typewriter.sound = {}; + font.anim.typewriter.soundinstance = {}; + } + font.Update(dt); + }); + } Scene::RayIntersectionResult Scene::Intersects(const Ray& ray, uint32_t filterMask, uint32_t layerMask, uint32_t lod) const { diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index fa0556d9f..b13bceb49 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -4,6 +4,7 @@ #include "wiSpinLock.h" #include "wiGPUBVH.h" #include "wiSprite.h" +#include "wiSpriteFont.h" #include "wiMath.h" #include "wiECS.h" #include "wiScene_Components.h" @@ -55,6 +56,8 @@ namespace wi::scene wi::ecs::ComponentManager& expressions = componentLibrary.Register("wi::scene::Scene::expressions"); wi::ecs::ComponentManager& humanoids = componentLibrary.Register("wi::scene::Scene::humanoids"); wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 3); // version = 3 + wi::ecs::ComponentManager& sprites = componentLibrary.Register("wi::scene::Scene::sprites"); + wi::ecs::ComponentManager& fonts = componentLibrary.Register("wi::scene::Scene::fonts"); // Non-serialized attributes: float dt = 0; @@ -411,6 +414,8 @@ namespace wi::scene void RunSoundUpdateSystem(wi::jobsystem::context& ctx); void RunVideoUpdateSystem(wi::jobsystem::context& ctx); void RunScriptUpdateSystem(wi::jobsystem::context& ctx); + void RunSpriteUpdateSystem(wi::jobsystem::context& ctx); + void RunFontUpdateSystem(wi::jobsystem::context& ctx); struct RayIntersectionResult diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index 041f3a6a0..e4d936b1b 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -4,6 +4,9 @@ #include "wiEmittedParticle.h" #include "wiTexture_BindLua.h" #include "wiPrimitive_BindLua.h" +#include "wiSprite_BindLua.h" +#include "wiSpriteFont_BindLua.h" + #include #include #include @@ -533,6 +536,8 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_CreateExpression), lunamethod(Scene_BindLua, Component_CreateHumanoid), lunamethod(Scene_BindLua, Component_CreateDecal), + lunamethod(Scene_BindLua, Component_CreateSprite), + lunamethod(Scene_BindLua, Component_CreateFont), lunamethod(Scene_BindLua, Component_GetName), lunamethod(Scene_BindLua, Component_GetLayer), @@ -557,6 +562,8 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_GetExpression), lunamethod(Scene_BindLua, Component_GetHumanoid), lunamethod(Scene_BindLua, Component_GetDecal), + lunamethod(Scene_BindLua, Component_GetSprite), + lunamethod(Scene_BindLua, Component_GetFont), lunamethod(Scene_BindLua, Component_GetNameArray), lunamethod(Scene_BindLua, Component_GetLayerArray), @@ -581,6 +588,8 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_GetExpressionArray), lunamethod(Scene_BindLua, Component_GetHumanoidArray), lunamethod(Scene_BindLua, Component_GetDecalArray), + lunamethod(Scene_BindLua, Component_GetSpriteArray), + lunamethod(Scene_BindLua, Component_GetFontArray), lunamethod(Scene_BindLua, Entity_GetNameArray), lunamethod(Scene_BindLua, Entity_GetLayerArray), @@ -606,6 +615,8 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Entity_GetExpressionArray), lunamethod(Scene_BindLua, Entity_GetHumanoidArray), lunamethod(Scene_BindLua, Entity_GetDecalArray), + lunamethod(Scene_BindLua, Entity_GetSpriteArray), + lunamethod(Scene_BindLua, Entity_GetFontArray), lunamethod(Scene_BindLua, Component_RemoveName), lunamethod(Scene_BindLua, Component_RemoveLayer), @@ -631,6 +642,8 @@ Luna::FunctionType Scene_BindLua::methods[] = { lunamethod(Scene_BindLua, Component_RemoveExpression), lunamethod(Scene_BindLua, Component_RemoveHumanoid), lunamethod(Scene_BindLua, Component_RemoveDecal), + lunamethod(Scene_BindLua, Component_RemoveSprite), + lunamethod(Scene_BindLua, Component_RemoveFont), lunamethod(Scene_BindLua, Component_Attach), lunamethod(Scene_BindLua, Component_Detach), @@ -1194,6 +1207,40 @@ int Scene_BindLua::Component_CreateDecal(lua_State* L) } return 0; } +int Scene_BindLua::Component_CreateSprite(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + + wi::Sprite& component = scene->sprites.Create(entity); + Luna::push(L, component); + return 1; + } + else + { + wi::lua::SError(L, "Scene::Component_CreateSprite(Entity entity) not enough arguments!"); + } + return 0; +} +int Scene_BindLua::Component_CreateFont(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + + wi::SpriteFont& component = scene->fonts.Create(entity); + Luna::push(L, component); + return 1; + } + else + { + wi::lua::SError(L, "Scene::Component_CreateFont(Entity entity) not enough arguments!"); + } + return 0; +} int Scene_BindLua::Component_GetName(lua_State* L) { @@ -1701,6 +1748,50 @@ int Scene_BindLua::Component_GetDecal(lua_State* L) } return 0; } +int Scene_BindLua::Component_GetSprite(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + + wi::Sprite* component = scene->sprites.GetComponent(entity); + if (component == nullptr) + { + return 0; + } + + Luna::push(L, *component); + return 1; + } + else + { + wi::lua::SError(L, "Scene::Component_GetSprite(Entity entity) not enough arguments!"); + } + return 0; +} +int Scene_BindLua::Component_GetFont(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + + wi::SpriteFont* component = scene->fonts.GetComponent(entity); + if (component == nullptr) + { + return 0; + } + + Luna::push(L, *component); + return 1; + } + else + { + wi::lua::SError(L, "Scene::Component_GetFont(Entity entity) not enough arguments!"); + } + return 0; +} int Scene_BindLua::Component_GetNameArray(lua_State* L) { @@ -1955,6 +2046,28 @@ int Scene_BindLua::Component_GetDecalArray(lua_State* L) } return 1; } +int Scene_BindLua::Component_GetSpriteArray(lua_State* L) +{ + lua_createtable(L, (int)scene->sprites.GetCount(), 0); + int newTable = lua_gettop(L); + for (size_t i = 0; i < scene->sprites.GetCount(); ++i) + { + Luna::push(L, scene->sprites[i]); + lua_rawseti(L, newTable, lua_Integer(i + 1)); + } + return 1; +} +int Scene_BindLua::Component_GetFontArray(lua_State* L) +{ + lua_createtable(L, (int)scene->fonts.GetCount(), 0); + int newTable = lua_gettop(L); + for (size_t i = 0; i < scene->fonts.GetCount(); ++i) + { + Luna::push(L, scene->fonts[i]); + lua_rawseti(L, newTable, lua_Integer(i + 1)); + } + return 1; +} int Scene_BindLua::Entity_GetNameArray(lua_State* L) { @@ -2220,6 +2333,28 @@ int Scene_BindLua::Entity_GetDecalArray(lua_State* L) } return 1; } +int Scene_BindLua::Entity_GetSpriteArray(lua_State* L) +{ + lua_createtable(L, (int)scene->sprites.GetCount(), 0); + int newTable = lua_gettop(L); + for (size_t i = 0; i < scene->sprites.GetCount(); ++i) + { + wi::lua::SSetLongLong(L, scene->sprites.GetEntity(i)); + lua_rawseti(L, newTable, lua_Integer(i + 1)); + } + return 1; +} +int Scene_BindLua::Entity_GetFontArray(lua_State* L) +{ + lua_createtable(L, (int)scene->fonts.GetCount(), 0); + int newTable = lua_gettop(L); + for (size_t i = 0; i < scene->fonts.GetCount(); ++i) + { + wi::lua::SSetLongLong(L, scene->fonts.GetEntity(i)); + lua_rawseti(L, newTable, lua_Integer(i + 1)); + } + return 1; +} int Scene_BindLua::Component_RemoveName(lua_State* L) { @@ -2629,6 +2764,40 @@ int Scene_BindLua::Component_RemoveDecal(lua_State* L) } return 0; } +int Scene_BindLua::Component_RemoveSprite(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + if (scene->sprites.Contains(entity)) + { + scene->sprites.Remove(entity); + } + } + else + { + wi::lua::SError(L, "Scene::Component_RemoveSprite(Entity entity) not enough arguments!"); + } + return 0; +} +int Scene_BindLua::Component_RemoveFont(lua_State* L) +{ + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + Entity entity = (Entity)wi::lua::SGetLongLong(L, 1); + if (scene->fonts.Contains(entity)) + { + scene->fonts.Remove(entity); + } + } + else + { + wi::lua::SError(L, "Scene::Component_RemoveFont(Entity entity) not enough arguments!"); + } + return 0; +} int Scene_BindLua::Component_Attach(lua_State* L) { diff --git a/WickedEngine/wiScene_BindLua.h b/WickedEngine/wiScene_BindLua.h index 7a75803cb..435b1ca04 100644 --- a/WickedEngine/wiScene_BindLua.h +++ b/WickedEngine/wiScene_BindLua.h @@ -65,6 +65,8 @@ namespace wi::lua::scene int Component_CreateExpression(lua_State* L); int Component_CreateHumanoid(lua_State* L); int Component_CreateDecal(lua_State* L); + int Component_CreateSprite(lua_State* L); + int Component_CreateFont(lua_State* L); int Component_GetName(lua_State* L); int Component_GetLayer(lua_State* L); @@ -89,6 +91,8 @@ namespace wi::lua::scene int Component_GetExpression(lua_State* L); int Component_GetHumanoid(lua_State* L); int Component_GetDecal(lua_State* L); + int Component_GetSprite(lua_State* L); + int Component_GetFont(lua_State* L); int Component_GetNameArray(lua_State* L); int Component_GetLayerArray(lua_State* L); @@ -113,6 +117,8 @@ namespace wi::lua::scene int Component_GetExpressionArray(lua_State* L); int Component_GetHumanoidArray(lua_State* L); int Component_GetDecalArray(lua_State* L); + int Component_GetSpriteArray(lua_State* L); + int Component_GetFontArray(lua_State* L); int Entity_GetNameArray(lua_State* L); int Entity_GetLayerArray(lua_State* L); @@ -138,6 +144,8 @@ namespace wi::lua::scene int Entity_GetExpressionArray(lua_State* L); int Entity_GetHumanoidArray(lua_State* L); int Entity_GetDecalArray(lua_State* L); + int Entity_GetSpriteArray(lua_State* L); + int Entity_GetFontArray(lua_State* L); int Component_RemoveName(lua_State* L); int Component_RemoveLayer(lua_State* L); @@ -163,6 +171,8 @@ namespace wi::lua::scene int Component_RemoveExpression(lua_State* L); int Component_RemoveHumanoid(lua_State* L); int Component_RemoveDecal(lua_State* L); + int Component_RemoveSprite(lua_State* L); + int Component_RemoveFont(lua_State* L); int Component_Attach(lua_State* L); int Component_Detach(lua_State* L); diff --git a/WickedEngine/wiSprite.cpp b/WickedEngine/wiSprite.cpp index 9eda8a276..d55f84862 100644 --- a/WickedEngine/wiSprite.cpp +++ b/WickedEngine/wiSprite.cpp @@ -2,6 +2,7 @@ #include "wiImage.h" #include "wiRandom.h" #include "wiTextureHelper.h" +#include "wiHelper.h" using namespace wi::graphics; @@ -27,7 +28,7 @@ namespace wi { if (IsHidden()) return; - wi::image::Draw(textureResource.IsValid() ? &textureResource.GetTexture() : wi::texturehelper::getWhite(), params, cmd); + wi::image::Draw(GetTexture(), params, cmd); } void Sprite::FixedUpdate() @@ -69,8 +70,12 @@ namespace wi params.texOffset.y += anim.movingTexAnim.speedY * dt; // Draw rect anim: - if (anim.drawRectAnim.frameCount > 0) + if (anim.drawRectAnim.frameCount > 1) { + if (anim.drawRectAnim._currentFrame == 0) + { + anim.drawRectAnim.originalDrawRect = params.drawRect; + } anim.drawRectAnim._elapsedTime += dt * anim.drawRectAnim.frameRate; if (anim.drawRectAnim._elapsedTime >= 1.0f) { @@ -133,4 +138,147 @@ namespace wi } + const wi::graphics::Texture* Sprite::GetTexture() const + { + return textureResource.IsValid() ? &textureResource.GetTexture() : wi::texturehelper::getWhite(); + } + + void Sprite::Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri) + { + const std::string& dir = archive.GetSourceDirectory(); + + if (archive.IsReadMode()) + { + archive >> _flags; + archive >> textureName; + archive >> maskName; + + archive >> params._flags; + archive >> params.pos; + archive >> params.siz; + archive >> params.scale; + archive >> params.color; + archive >> params.drawRect; + archive >> params.drawRect2; + archive >> params.texOffset; + archive >> params.texOffset2; + archive >> params.pivot; + archive >> params.rotation; + archive >> params.fade; + archive >> params.opacity; + archive >> params.intensity; + archive >> params.hdr_scaling; + archive >> params.mask_alpha_range_start; + archive >> params.mask_alpha_range_end; + archive >> params.border_soften; + archive >> params.stencilRef; + archive >> *(uint32_t*)¶ms.stencilComp; + archive >> *(uint32_t*)¶ms.stencilRefMode; + archive >> *(uint32_t*)¶ms.blendFlag; + archive >> *(uint32_t*)¶ms.sampleFlag; + archive >> *(uint32_t*)¶ms.quality; + for (int i = 0; i < arraysize(params.corners); ++i) + { + archive >> params.corners[i]; + archive >> params.corners_rounding[i].radius; + archive >> params.corners_rounding[i].segments; + } + + archive >> anim.repeatable; + archive >> anim.vel; + archive >> anim.rot; + archive >> anim.scaleX; + archive >> anim.scaleY; + archive >> anim.opa; + archive >> anim.fad; + archive >> anim.movingTexAnim.speedX; + archive >> anim.movingTexAnim.speedY; + archive >> anim.drawRectAnim.frameRate; + archive >> anim.drawRectAnim.frameCount; + archive >> anim.drawRectAnim.horizontalFrameCount; + archive >> anim.wobbleAnim.amount; + archive >> anim.wobbleAnim.speed; + + if (!textureName.empty() || !maskName.empty()) + { + wi::jobsystem::Execute(seri.ctx, [&](wi::jobsystem::JobArgs args) { + if (!textureName.empty()) + { + textureName = dir + textureName; + textureResource = wi::resourcemanager::Load(textureName, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA); + } + if (!maskName.empty()) + { + maskName = dir + maskName; + maskResource = wi::resourcemanager::Load(maskName, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA); + } + }); + } + } + else + { + // If detecting an absolute path in textures, remove it and convert to relative: + if (!dir.empty()) + { + wi::helper::MakePathRelative(dir, textureName); + wi::helper::MakePathRelative(dir, maskName); + } + + archive << _flags; + archive << textureName; + archive << maskName; + + archive << params._flags; + archive << params.pos; + archive << params.siz; + archive << params.scale; + archive << params.color; + if (anim.drawRectAnim.frameCount > 1) + { + params.drawRect = anim.drawRectAnim.originalDrawRect; + anim.drawRectAnim.restart(); + } + archive << params.drawRect; + archive << params.drawRect2; + archive << params.texOffset; + archive << params.texOffset2; + archive << params.pivot; + archive << params.rotation; + archive << params.fade; + archive << params.opacity; + archive << params.intensity; + archive << params.hdr_scaling; + archive << params.mask_alpha_range_start; + archive << params.mask_alpha_range_end; + archive << params.border_soften; + archive << params.stencilRef; + archive << (uint32_t)params.stencilComp; + archive << (uint32_t)params.stencilRefMode; + archive << (uint32_t)params.blendFlag; + archive << (uint32_t)params.sampleFlag; + archive << (uint32_t)params.quality; + for (int i = 0; i < arraysize(params.corners); ++i) + { + archive << params.corners[i]; + archive << params.corners_rounding[i].radius; + archive << params.corners_rounding[i].segments; + } + + archive << anim.repeatable; + archive << anim.vel; + archive << anim.rot; + archive << anim.scaleX; + archive << anim.scaleY; + archive << anim.opa; + archive << anim.fad; + archive << anim.movingTexAnim.speedX; + archive << anim.movingTexAnim.speedY; + archive << anim.drawRectAnim.frameRate; + archive << anim.drawRectAnim.frameCount; + archive << anim.drawRectAnim.horizontalFrameCount; + archive << anim.wobbleAnim.amount; + archive << anim.wobbleAnim.speed; + } + } + } diff --git a/WickedEngine/wiSprite.h b/WickedEngine/wiSprite.h index 3b84a9a8c..83d7b8748 100644 --- a/WickedEngine/wiSprite.h +++ b/WickedEngine/wiSprite.h @@ -3,6 +3,8 @@ #include "wiGraphicsDevice.h" #include "wiResourceManager.h" #include "wiRandom.h" +#include "wiArchive.h" +#include "wiECS.h" #include #include @@ -11,17 +13,19 @@ namespace wi { class Sprite { - private: + public: enum FLAGS { EMPTY = 0, HIDDEN = 1 << 0, DISABLE_UPDATE = 1 << 1, + CAMERA_FACING = 1 << 2, + CAMERA_SCALING = 1 << 3, }; uint32_t _flags = EMPTY; std::string textureName, maskName; - public: + Sprite(const std::string& newTexture = "", const std::string& newMask = ""); virtual ~Sprite() = default; @@ -32,12 +36,19 @@ namespace wi constexpr void SetHidden(bool value = true) { if (value) { _flags |= HIDDEN; } else { _flags &= ~HIDDEN; } } constexpr bool IsHidden() const { return _flags & HIDDEN; } constexpr void SetDisableUpdate(bool value = true) { if (value) { _flags |= DISABLE_UPDATE; } else { _flags &= ~DISABLE_UPDATE; } } + constexpr void SetCameraFacing(bool value = true) { if (value) { _flags |= CAMERA_FACING; } else { _flags &= ~CAMERA_FACING; } } + constexpr void SetCameraScaling(bool value = true) { if (value) { _flags |= CAMERA_SCALING; } else { _flags &= ~CAMERA_SCALING; } } + constexpr bool IsDisableUpdate() const { return _flags & DISABLE_UPDATE; } + constexpr bool IsCameraFacing() const { return _flags & CAMERA_FACING; } + constexpr bool IsCameraScaling() const { return _flags & CAMERA_SCALING; } wi::image::Params params; wi::Resource textureResource; wi::Resource maskResource; + const wi::graphics::Texture* GetTexture() const; + struct Anim { struct MovingTexAnim @@ -53,6 +64,13 @@ namespace wi float _elapsedTime = 0; // internal use; you don't need to initialize int _currentFrame = 0; // internal use; you don't need to initialize + XMFLOAT4 originalDrawRect = XMFLOAT4(0, 0, 0, 0); // internal use; you don't need to initialize + + void restart() + { + _elapsedTime = 0; + _currentFrame = 0; + } }; struct WobbleAnim { @@ -104,5 +122,7 @@ namespace wi } return nullptr; } + + void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; } diff --git a/WickedEngine/wiSpriteFont.cpp b/WickedEngine/wiSpriteFont.cpp index fc74e3a03..6d4a707f7 100644 --- a/WickedEngine/wiSpriteFont.cpp +++ b/WickedEngine/wiSpriteFont.cpp @@ -16,6 +16,15 @@ namespace wi if (IsDisableUpdate()) return; + if (fontStyleResource.IsValid()) + { + params.style = fontStyleResource.GetFontStyle(); + } + else + { + params.style = 0; + } + if (anim.typewriter.time > 0) { size_t text_length = text.length(); @@ -48,14 +57,7 @@ namespace wi if (IsHidden()) return; - size_t text_length = text.length(); - - if (anim.typewriter.time > 0) - { - text_length = std::min(text_length, size_t(wi::math::Lerp(float(std::min(text_length, anim.typewriter.character_start)), float(text_length + 1), anim.typewriter.elapsed / anim.typewriter.time))); - } - - wi::font::Draw(text.c_str(), text_length, params, cmd); + wi::font::Draw(text.c_str(), GetCurrentTextLength(), params, cmd); } XMFLOAT2 SpriteFont::TextSize() const @@ -99,4 +101,105 @@ namespace wi return text; } + size_t SpriteFont::GetCurrentTextLength() const + { + size_t text_length = text.length(); + if (anim.typewriter.time > 0) + { + text_length = std::min(text_length, size_t(wi::math::Lerp(float(std::min(text_length, anim.typewriter.character_start)), float(text_length + 1), anim.typewriter.elapsed / anim.typewriter.time))); + } + return text_length; + } + + void SpriteFont::Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri) + { + const std::string& dir = archive.GetSourceDirectory(); + + if (archive.IsReadMode()) + { + archive >> _flags; + std::string textA; + archive >> textA; + SetText(textA); + archive >> fontStyleName; + + archive >> params._flags; + archive >> params.position; + archive >> params.size; + archive >> params.scaling; + archive >> params.rotation; + archive >> params.spacingX; + archive >> params.spacingY; + archive >> *(uint32_t*)¶ms.h_align; + archive >> *(uint32_t*)¶ms.v_align; + archive >> params.color; + archive >> params.shadowColor; + archive >> params.h_wrap; + archive >> params.softness; + archive >> params.bolden; + archive >> params.shadow_softness; + archive >> params.shadow_bolden; + archive >> params.shadow_offset_x; + archive >> params.shadow_offset_y; + archive >> params.hdr_scaling; + archive >> params.intensity; + archive >> params.shadow_intensity; + + archive >> anim.typewriter.time; + archive >> anim.typewriter.looped; + archive >> anim.typewriter.character_start; + + if (!fontStyleName.empty()) + { + wi::jobsystem::Execute(seri.ctx, [&](wi::jobsystem::JobArgs args) { + if (!fontStyleName.empty()) + { + fontStyleName = dir + fontStyleName; + fontStyleResource = wi::resourcemanager::Load(fontStyleName, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA); + params.style = fontStyleResource.GetFontStyle(); + } + }); + } + } + else + { + // If detecting an absolute path in textures, remove it and convert to relative: + if (!dir.empty()) + { + wi::helper::MakePathRelative(dir, fontStyleName); + } + + archive << _flags; + std::string textA = GetTextA(); + archive << textA; + archive << fontStyleName; + + archive << params._flags; + archive << params.position; + archive << params.size; + archive << params.scaling; + archive << params.rotation; + archive << params.spacingX; + archive << params.spacingY; + archive << *(uint32_t*)¶ms.h_align; + archive << *(uint32_t*)¶ms.v_align; + archive << params.color; + archive << params.shadowColor; + archive << params.h_wrap; + archive << params.softness; + archive << params.bolden; + archive << params.shadow_softness; + archive << params.shadow_bolden; + archive << params.shadow_offset_x; + archive << params.shadow_offset_y; + archive << params.hdr_scaling; + archive << params.intensity; + archive << params.shadow_intensity; + + archive << anim.typewriter.time; + archive << anim.typewriter.looped; + archive << anim.typewriter.character_start; + } + } + } diff --git a/WickedEngine/wiSpriteFont.h b/WickedEngine/wiSpriteFont.h index f8926211c..006a2aa8f 100644 --- a/WickedEngine/wiSpriteFont.h +++ b/WickedEngine/wiSpriteFont.h @@ -1,6 +1,9 @@ #pragma once #include "wiFont.h" #include "wiAudio.h" +#include "wiArchive.h" +#include "wiECS.h" +#include "wiResourceManager.h" #include @@ -8,22 +11,29 @@ namespace wi { class SpriteFont { - private: + public: enum FLAGS { EMPTY = 0, HIDDEN = 1 << 0, DISABLE_UPDATE = 1 << 1, + CAMERA_FACING = 1 << 2, + CAMERA_SCALING = 1 << 3, }; uint32_t _flags = EMPTY; - public: std::wstring text; wi::font::Params params; + wi::Resource fontStyleResource; + std::string fontStyleName; SpriteFont() = default; - SpriteFont(const std::string& value, const wi::font::Params& params = wi::font::Params()) :params(params) + SpriteFont(const std::string& value, const wi::font::Params& params = wi::font::Params(), const std::string& fontStyleName = "") :params(params), fontStyleName(fontStyleName) { SetText(value); + if (!fontStyleName.empty()) + { + fontStyleResource = wi::resourcemanager::Load(fontStyleName); + } } virtual ~SpriteFont() = default; @@ -34,7 +44,12 @@ namespace wi constexpr void SetHidden(bool value = true) { if (value) { _flags |= HIDDEN; } else { _flags &= ~HIDDEN; } } constexpr bool IsHidden() const { return _flags & HIDDEN; } constexpr void SetDisableUpdate(bool value = true) { if (value) { _flags |= DISABLE_UPDATE; } else { _flags &= ~DISABLE_UPDATE; } } + constexpr void SetCameraFacing(bool value = true) { if (value) { _flags |= CAMERA_FACING; } else { _flags &= ~CAMERA_FACING; } } + constexpr void SetCameraScaling(bool value = true) { if (value) { _flags |= CAMERA_SCALING; } else { _flags &= ~CAMERA_SCALING; } } + constexpr bool IsDisableUpdate() const { return _flags & DISABLE_UPDATE; } + constexpr bool IsCameraFacing() const { return _flags & CAMERA_FACING; } + constexpr bool IsCameraScaling() const { return _flags & CAMERA_SCALING; } XMFLOAT2 TextSize() const; float TextWidth() const; @@ -48,6 +63,8 @@ namespace wi std::string GetTextA() const; const std::wstring& GetText() const; + size_t GetCurrentTextLength() const; + struct Animation { struct Typewriter @@ -74,5 +91,7 @@ namespace wi } } typewriter; } anim; + + void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 74a760c4a..c23e57927 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 = 316; + const int revision = 317; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); @@ -153,6 +153,7 @@ Patreon supporters - Paul Brain - Connor Greaves - Alexandr +- Lee Bamber )"; return credits;