From db88f0ca0d033acabe78df3c83bdcaf4dc456f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Sun, 28 Nov 2021 20:19:59 +0100 Subject: [PATCH] Graphics: Add SlicedSprite class --- include/Nazara/Graphics.hpp | 1 + include/Nazara/Graphics/SlicedSprite.hpp | 73 ++++++++++ include/Nazara/Graphics/SlicedSprite.inl | 91 +++++++++++++ src/Nazara/Graphics/SlicedSprite.cpp | 161 +++++++++++++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 include/Nazara/Graphics/SlicedSprite.hpp create mode 100644 include/Nazara/Graphics/SlicedSprite.inl create mode 100644 src/Nazara/Graphics/SlicedSprite.cpp diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index 1821f9319..b39dbddde 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/SlicedSprite.hpp b/include/Nazara/Graphics/SlicedSprite.hpp new file mode 100644 index 000000000..125a5a22f --- /dev/null +++ b/include/Nazara/Graphics/SlicedSprite.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_SLICEDSPRITE_HPP +#define NAZARA_GRAPHICS_SLICEDSPRITE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API SlicedSprite : public InstancedRenderable + { + public: + struct Corner; + + SlicedSprite(std::shared_ptr material); + SlicedSprite(const SlicedSprite&) = delete; + SlicedSprite(SlicedSprite&&) noexcept = default; + ~SlicedSprite() = default; + + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const override; + + inline const Color& GetColor() const; + inline const Corner& GetBottomRightCorner() const; + const std::shared_ptr& GetMaterial(std::size_t i = 0) const override; + std::size_t GetMaterialCount() const override; + inline const Corner& GetTopLeftCorner() const; + inline const Rectf& GetTextureCoords() const; + Vector3ui GetTextureSize() const; + + inline void SetColor(const Color& color); + inline void SetCorners(const Corner& topLeftCorner, const Corner& bottomRightCorner); + inline void SetCornersSize(const Vector2f& topLeftSize, const Vector2f& bottomRightSize); + inline void SetCornersTextureCoords(const Vector2f& topLeftTextureCoords, const Vector2f& bottomRightTextureCoords); + inline void SetMaterial(std::shared_ptr material); + inline void SetSize(const Vector2f& size); + inline void SetTextureCoords(const Rectf& textureCoords); + inline void SetTextureRect(const Rectf& textureRect); + + SlicedSprite& operator=(const SlicedSprite&) = delete; + SlicedSprite& operator=(SlicedSprite&&) noexcept = default; + + struct Corner + { + Vector2f textureCoords = Vector2f(0.125f, 0.125f); + Vector2f size = Vector2f(16.f, 16.f); + }; + + private: + void UpdateVertices(); + + std::array m_vertices; + std::shared_ptr m_material; + Color m_color; + Corner m_topLeftCorner; + Corner m_bottomRightCorner; + Rectf m_textureCoords; + Vector2f m_size; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_SLICEDSPRITE_HPP diff --git a/include/Nazara/Graphics/SlicedSprite.inl b/include/Nazara/Graphics/SlicedSprite.inl new file mode 100644 index 000000000..d385de620 --- /dev/null +++ b/include/Nazara/Graphics/SlicedSprite.inl @@ -0,0 +1,91 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline const Color& SlicedSprite::GetColor() const + { + return m_color; + } + + inline auto SlicedSprite::GetBottomRightCorner() const -> const Corner& + { + return m_bottomRightCorner; + } + + inline const Rectf& SlicedSprite::GetTextureCoords() const + { + return m_textureCoords; + } + + inline void SlicedSprite::SetColor(const Color& color) + { + m_color = color; + + UpdateVertices(); + } + + inline void SlicedSprite::SetCorners(const Corner& topLeftCorner, const Corner& bottomRightCorner) + { + m_topLeftCorner = topLeftCorner; + m_bottomRightCorner = bottomRightCorner; + + UpdateVertices(); + } + + inline void SlicedSprite::SetCornersSize(const Vector2f& topLeftSize, const Vector2f& bottomRightSize) + { + m_topLeftCorner.size = topLeftSize; + m_bottomRightCorner.size = bottomRightSize; + + UpdateVertices(); + } + + inline void SlicedSprite::SetCornersTextureCoords(const Vector2f& topLeftTextureCoords, const Vector2f& bottomRightTextureCoords) + { + m_topLeftCorner.textureCoords = topLeftTextureCoords; + m_bottomRightCorner.textureCoords = bottomRightTextureCoords; + + UpdateVertices(); + } + + inline void SlicedSprite::SetMaterial(std::shared_ptr material) + { + assert(material); + + if (m_material != material) + { + OnMaterialInvalidated(this, 0, material); + m_material = std::move(material); + + OnElementInvalidated(this); + } + } + + inline void SlicedSprite::SetSize(const Vector2f& size) + { + m_size = size; + + UpdateVertices(); + } + + inline void SlicedSprite::SetTextureCoords(const Rectf& textureCoords) + { + m_textureCoords = textureCoords; + + UpdateVertices(); + } + + inline void SlicedSprite::SetTextureRect(const Rectf& textureRect) + { + Vector2ui textureSize(GetTextureSize()); + return SetTextureCoords(textureRect / Vector2f(textureSize)); + } +} + +#include diff --git a/src/Nazara/Graphics/SlicedSprite.cpp b/src/Nazara/Graphics/SlicedSprite.cpp new file mode 100644 index 000000000..700137126 --- /dev/null +++ b/src/Nazara/Graphics/SlicedSprite.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + SlicedSprite::SlicedSprite(std::shared_ptr material) : + m_material(std::move(material)), + m_color(Color::White), + m_textureCoords(0.f, 0.f, 1.f, 1.f), + m_size(64.f, 64.f) + { + UpdateVertices(); + } + + void SlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const + { + const auto& materialPass = m_material->GetPass(passIndex); + if (!materialPass) + return; + + const std::shared_ptr& vertexDeclaration = VertexDeclaration::Get(VertexLayout::XYZ_Color_UV); + + std::vector vertexBufferData = { + { + { + 0, + vertexDeclaration + } + } + }; + const auto& renderPipeline = materialPass->GetPipeline()->GetRenderPipeline(vertexBufferData); + + const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)]; + + elements.emplace_back(std::make_unique(0, materialPass, renderPipeline, worldInstance, vertexDeclaration, whiteTexture, 9, m_vertices.data())); + } + + const std::shared_ptr& SlicedSprite::GetMaterial(std::size_t i) const + { + assert(i == 0); + NazaraUnused(i); + + return m_material; + } + + std::size_t SlicedSprite::GetMaterialCount() const + { + return 1; + } + + inline auto SlicedSprite::GetTopLeftCorner() const -> const Corner& + { + return m_topLeftCorner; + } + + Vector3ui SlicedSprite::GetTextureSize() const + { + assert(m_material); + + //TODO: Cache index in registry? + if (const auto& material = m_material->FindPass("ForwardPass")) + { + BasicMaterial mat(*material); + if (mat.HasDiffuseMap()) + { + // Material should always have textures but we're better safe than sorry + if (const auto& texture = mat.GetDiffuseMap()) + return mat.GetDiffuseMap()->GetSize(); + } + } + + // Couldn't get material pass or texture + return Vector3ui::Unit(); //< prevents division by zero + } + + void SlicedSprite::UpdateVertices() + { + VertexStruct_XYZ_Color_UV* vertices = m_vertices.data(); + + std::array heights = { + m_topLeftCorner.size.y, + m_size.y - m_topLeftCorner.size.y - m_bottomRightCorner.size.y, + m_bottomRightCorner.size.y + }; + + std::array widths = { + m_topLeftCorner.size.x, + m_size.x - m_topLeftCorner.size.x - m_bottomRightCorner.size.x, + m_bottomRightCorner.size.x + }; + + std::array texCoordsX = { + m_topLeftCorner.textureCoords.x * m_textureCoords.width, + m_textureCoords.width - m_topLeftCorner.textureCoords.x * m_textureCoords.width - m_bottomRightCorner.textureCoords.x * m_textureCoords.width, + m_bottomRightCorner.textureCoords.x * m_textureCoords.width + }; + + std::array texCoordsY = { + m_topLeftCorner.textureCoords.y * m_textureCoords.height, + m_textureCoords.height - m_topLeftCorner.textureCoords.y * m_textureCoords.height - m_bottomRightCorner.textureCoords.y * m_textureCoords.height, + m_bottomRightCorner.textureCoords.y * m_textureCoords.height + }; + + Vector3f origin = Vector3f::Zero(); + Vector2f topLeftUV = m_textureCoords.GetCorner(RectCorner::LeftTop); + + for (std::size_t y = 0; y < 3; ++y) + { + for (std::size_t x = 0; x < 3; ++x) + { + vertices->color = m_color; + vertices->position = origin; + vertices->uv = topLeftUV; + vertices++; + + vertices->color = m_color; + vertices->position = origin + widths[x] * Vector3f::Right(); + vertices->uv = topLeftUV + Vector2f(texCoordsX[x], 0.f); + vertices++; + + vertices->color = m_color; + vertices->position = origin + heights[y] * Vector3f::Up(); + vertices->uv = topLeftUV + Vector2f(0.f, texCoordsY[y]); + vertices++; + + vertices->color = m_color; + vertices->position = origin + widths[x] * Vector3f::Right() + heights[y] * Vector3f::Up(); + vertices->uv = topLeftUV + Vector2f(texCoordsX[x], texCoordsY[y]); + vertices++; + + origin.x += widths[x]; + topLeftUV.x += texCoordsX[x]; + } + + origin.x = 0; + origin.y += heights[y]; + + topLeftUV.x = m_textureCoords.x; + topLeftUV.y += texCoordsY[y]; + } + + // Reverse texcoords Y + for (auto& vertex : m_vertices) + vertex.uv.y = m_textureCoords.height - vertex.uv.y; + + Boxf aabb; + aabb.Set(m_vertices[0].position); + for (std::size_t i = 1; i < m_vertices.size(); ++i) + aabb.ExtendTo(m_vertices[i].position); + + UpdateAABB(aabb); + OnElementInvalidated(this); + } +}