From 96599d11160d25adc363a297c8eb84ba2c486621 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Tue, 12 Jul 2022 08:45:02 +0200 Subject: [PATCH] Graphics: Add LinearSlicedSprite class --- .../Nazara/Graphics/LinearSlicedSprite.hpp | 91 +++++++++++ .../Nazara/Graphics/LinearSlicedSprite.inl | 142 ++++++++++++++++ src/Nazara/Graphics/LinearSlicedSprite.cpp | 154 ++++++++++++++++++ 3 files changed, 387 insertions(+) create mode 100644 include/Nazara/Graphics/LinearSlicedSprite.hpp create mode 100644 include/Nazara/Graphics/LinearSlicedSprite.inl create mode 100644 src/Nazara/Graphics/LinearSlicedSprite.cpp diff --git a/include/Nazara/Graphics/LinearSlicedSprite.hpp b/include/Nazara/Graphics/LinearSlicedSprite.hpp new file mode 100644 index 000000000..14e522ce1 --- /dev/null +++ b/include/Nazara/Graphics/LinearSlicedSprite.hpp @@ -0,0 +1,91 @@ +// Copyright (C) 2022 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_LINEARSLICEDSPRITE_HPP +#define NAZARA_GRAPHICS_LINEARSLICEDSPRITE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API LinearSlicedSprite : public InstancedRenderable + { + public: + enum class Orientation; + struct Section; + + LinearSlicedSprite(std::shared_ptr material, Orientation orientation); + LinearSlicedSprite(const LinearSlicedSprite&) = delete; + LinearSlicedSprite(LinearSlicedSprite&&) noexcept = default; + ~LinearSlicedSprite() = default; + + inline void AddSection(float size, float textureCoord); + + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) const override; + + inline void Clear(); + + inline const Color& GetColor() const; + const std::shared_ptr& GetMaterial(std::size_t i = 0) const override; + std::size_t GetMaterialCount() const override; + inline Orientation GetOrientation() const; + inline const Section& GetSection(std::size_t sectionIndex) const; + std::size_t GetSectionCount() const; + inline const Rectf& GetTextureCoords() const; + Vector3ui GetTextureSize() const; + + inline void RemoveSection(std::size_t sectionIndex); + + inline void SetColor(const Color& color); + inline void SetMaterial(std::shared_ptr material); + inline void SetSection(std::size_t sectionIndex, float size, float textureCoord); + inline void SetSectionSize(std::size_t sectionIndex, float size); + inline void SetSectionTextureCoord(std::size_t sectionIndex, float textureCoord); + inline void SetSize(const Vector2f& size); + inline void SetTextureCoords(const Rectf& textureCoords); + inline void SetTextureRect(const Rectf& textureRect); + + LinearSlicedSprite& operator=(const LinearSlicedSprite&) = delete; + LinearSlicedSprite& operator=(LinearSlicedSprite&&) noexcept = default; + + enum class Orientation + { + Horizontal, + Vertical + }; + + struct Section + { + float size; + float textureCoord; + }; + + static constexpr std::size_t MaxSection = 5; + + private: + void UpdateVertices(); + + std::array m_sections; + std::array m_vertices; + std::shared_ptr m_material; + std::size_t m_sectionCount; + std::size_t m_spriteCount; + Color m_color; + Orientation m_orientation; + Rectf m_textureCoords; + Vector2f m_size; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_LINEARSLICEDSPRITE_HPP diff --git a/include/Nazara/Graphics/LinearSlicedSprite.inl b/include/Nazara/Graphics/LinearSlicedSprite.inl new file mode 100644 index 000000000..007dccc90 --- /dev/null +++ b/include/Nazara/Graphics/LinearSlicedSprite.inl @@ -0,0 +1,142 @@ +// Copyright (C) 2022 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 + +namespace Nz +{ + inline void LinearSlicedSprite::AddSection(float size, float textureCoord) + { + NazaraAssert(m_sectionCount < m_sections.size(), "too many sections"); + + auto& section = m_sections[m_sectionCount++]; + section.size = size; + section.textureCoord = textureCoord; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::Clear() + { + m_sectionCount = 0; + + UpdateVertices(); + } + + inline const Color& LinearSlicedSprite::GetColor() const + { + return m_color; + } + + inline auto LinearSlicedSprite::GetOrientation() const -> Orientation + { + return m_orientation; + } + + inline auto LinearSlicedSprite::GetSection(std::size_t sectionIndex) const -> const Section& + { + NazaraAssert(sectionIndex < m_sectionCount, "out of range section"); + return m_sections[sectionIndex]; + } + + inline std::size_t LinearSlicedSprite::GetSectionCount() const + { + return m_sectionCount; + } + + inline const Rectf& LinearSlicedSprite::GetTextureCoords() const + { + return m_textureCoords; + } + + inline void LinearSlicedSprite::SetColor(const Color& color) + { + m_color = color; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::RemoveSection(std::size_t sectionIndex) + { + NazaraAssert(sectionIndex < m_sectionCount, "out of range section"); + if (m_sectionCount >= sectionIndex + 1) + std::move(m_sections.begin() + sectionIndex + 1, m_sections.begin() + m_sectionCount, m_sections.begin() + sectionIndex); + + m_sectionCount--; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetMaterial(std::shared_ptr material) + { + assert(material); + + if (m_material != material) + { + OnMaterialInvalidated(this, 0, material); + m_material = std::move(material); + + OnElementInvalidated(this); + } + } + + inline void LinearSlicedSprite::SetSection(std::size_t sectionIndex, float size, float textureCoord) + { + NazaraAssert(sectionIndex < m_sectionCount, "out of range section"); + + auto& section = m_sections[sectionIndex]; + section.size = size; + section.textureCoord = textureCoord; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetSectionSize(std::size_t sectionIndex, float size) + { + NazaraAssert(sectionIndex < m_sectionCount, "out of range section"); + + auto& section = m_sections[sectionIndex]; + section.size = size; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetSectionTextureCoord(std::size_t sectionIndex, float textureCoord) + { + NazaraAssert(sectionIndex < m_sectionCount, "out of range section"); + + auto& section = m_sections[sectionIndex]; + section.textureCoord = textureCoord; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetSize(const Vector2f& size) + { + NazaraAssert(size.x >= 0.f, "width must be positive"); + NazaraAssert(size.y >= 0.f, "height must be positive"); + + m_size = size; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetTextureCoords(const Rectf& textureCoords) + { + m_textureCoords = textureCoords; + + UpdateVertices(); + } + + inline void LinearSlicedSprite::SetTextureRect(const Rectf& textureRect) + { + Vector2ui textureSize(GetTextureSize()); + return SetTextureCoords(textureRect / Vector2f(textureSize)); + } +} + +#include diff --git a/src/Nazara/Graphics/LinearSlicedSprite.cpp b/src/Nazara/Graphics/LinearSlicedSprite.cpp new file mode 100644 index 000000000..fed157901 --- /dev/null +++ b/src/Nazara/Graphics/LinearSlicedSprite.cpp @@ -0,0 +1,154 @@ +// Copyright (C) 2022 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 +{ + LinearSlicedSprite::LinearSlicedSprite(std::shared_ptr material, Orientation orientation) : + m_material(std::move(material)), + m_sectionCount(0), + m_spriteCount(0), + m_color(Color::White), + m_orientation(orientation), + m_textureCoords(0.f, 0.f, 1.f, 1.f), + m_size(64.f, 64.f) + { + UpdateVertices(); + } + + void LinearSlicedSprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements, const Recti& scissorBox) 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(GetRenderLayer(), materialPass, renderPipeline, worldInstance, vertexDeclaration, whiteTexture, m_spriteCount, m_vertices.data(), scissorBox)); + } + + const std::shared_ptr& LinearSlicedSprite::GetMaterial(std::size_t i) const + { + assert(i == 0); + NazaraUnused(i); + + return m_material; + } + + std::size_t LinearSlicedSprite::GetMaterialCount() const + { + return 1; + } + + Vector3ui LinearSlicedSprite::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 texture->GetSize(); + } + } + + // Couldn't get material pass or texture + return Vector3ui::Unit(); //< prevents division by zero + } + + void LinearSlicedSprite::UpdateVertices() + { + VertexStruct_XYZ_Color_UV* vertices = m_vertices.data(); + + Vector3f origin = Vector3f::Zero(); + Vector2f topLeftUV = m_textureCoords.GetCorner(RectCorner::LeftTop); + + m_spriteCount = 0; + + for (std::size_t i = 0; i < m_sectionCount; ++i) + { + const auto& section = m_sections[i]; + if (section.size <= 0.f) + continue; + + Vector2f dir; + Vector2f size; + Vector2f texCoords; + if (m_orientation == Orientation::Horizontal) + { + dir = Vector2(1.f, 0.f); + size = Vector2f(section.size, m_size.y); + texCoords = Vector2f(section.textureCoord, m_textureCoords.height); + } + else + { + dir = Vector2(0.f, 1.f); + size = Vector2f(m_size.x, section.size); + texCoords = Vector2f(m_textureCoords.width, section.textureCoord); + } + + vertices->color = m_color; + vertices->position = origin; + vertices->uv = topLeftUV; + vertices++; + + vertices->color = m_color; + vertices->position = origin + size.x * Vector3f::Right(); + vertices->uv = topLeftUV + Vector2f(texCoords.x, 0.f); + vertices++; + + vertices->color = m_color; + vertices->position = origin + size.y * Vector3f::Up(); + vertices->uv = topLeftUV + Vector2f(0.f, texCoords.y); + vertices++; + + vertices->color = m_color; + vertices->position = origin + size.x * Vector3f::Right() + size.y * Vector3f::Up(); + vertices->uv = topLeftUV + Vector2f(texCoords.x, texCoords.y); + vertices++; + + origin += dir * section.size; + topLeftUV += dir * section.textureCoord; + m_spriteCount++; + } + + Boxf aabb = Boxf::Zero(); + + std::size_t vertexCount = 4 * m_spriteCount; + if (vertexCount > 0) + { + // Reverse texcoords Y + for (std::size_t i = 0; i < vertexCount; ++i) + m_vertices[i].uv.y = m_textureCoords.height - m_vertices[i].uv.y; + + aabb.Set(m_vertices[0].position); + for (std::size_t i = 1; i < vertexCount; ++i) + aabb.ExtendTo(m_vertices[i].position); + } + + UpdateAABB(aabb); + OnElementInvalidated(this); + } +}