From 1ac992b5c781cdd15c13b25f0ddd51bd52389360 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 25 Nov 2023 15:52:58 +0100 Subject: [PATCH] Graphics: Add Billboard support --- include/Nazara/Graphics.hpp | 1 + include/Nazara/Graphics/Billboard.hpp | 66 ++++++++++ include/Nazara/Graphics/Billboard.inl | 117 ++++++++++++++++++ include/Nazara/Utility/Enums.hpp | 3 + include/Nazara/Utility/VertexStruct.hpp | 11 ++ src/Nazara/Graphics/Billboard.cpp | 88 +++++++++++++ src/Nazara/Graphics/Material.cpp | 4 + src/Nazara/Graphics/PredefinedMaterials.cpp | 2 + .../Resources/Shaders/BasicMaterial.nzsl | 83 +++++++------ .../Resources/Shaders/PhongMaterial.nzsl | 80 +++++++----- .../Shaders/PhysicallyBasedMaterial.nzsl | 78 +++++++----- src/Nazara/Utility/VertexDeclaration.cpp | 37 ++++++ 12 files changed, 472 insertions(+), 98 deletions(-) create mode 100644 include/Nazara/Graphics/Billboard.hpp create mode 100644 include/Nazara/Graphics/Billboard.inl create mode 100644 src/Nazara/Graphics/Billboard.cpp diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index f2417732f..8241fa52f 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/Billboard.hpp b/include/Nazara/Graphics/Billboard.hpp new file mode 100644 index 000000000..c043b0ce2 --- /dev/null +++ b/include/Nazara/Graphics/Billboard.hpp @@ -0,0 +1,66 @@ +// Copyright (C) 2023 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_BILLBOARD_HPP +#define NAZARA_GRAPHICS_BILLBOARD_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API Billboard : public InstancedRenderable + { + public: + Billboard(std::shared_ptr material); + Billboard(std::shared_ptr material, const Vector2f& size); + Billboard(const Billboard&) = delete; + Billboard(Billboard&&) noexcept = default; + ~Billboard() = default; + + void BuildElement(ElementRendererRegistry& registry, const ElementData& elementData, std::size_t passIndex, std::vector& elements) const override; + + inline const Color& GetColor() const; + inline const Color& GetCornerColor(RectCorner corner) const; + const std::shared_ptr& GetMaterial(std::size_t i = 0) const override; + std::size_t GetMaterialCount() const override; + inline RadianAnglef GetRotation() const; + inline const Vector2f& GetSize() const; + inline const Rectf& GetTextureCoords() const; + Vector3ui GetTextureSize() const; + + inline void SetColor(const Color& color); + inline void SetCornerColor(RectCorner corner, const Color& color); + inline void SetMaterial(std::shared_ptr material); + inline void SetRotation(RadianAnglef rotation); + inline void SetSize(const Vector2f& size); + inline void SetTextureCoords(const Rectf& textureCoords); + inline void SetTextureRect(const Rectf& textureRect); + + Billboard& operator=(const Billboard&) = delete; + Billboard& operator=(Billboard&&) noexcept = default; + + private: + inline void UpdateVertices(); + + std::array m_vertices; + std::shared_ptr m_material; + Color m_color; + EnumArray m_cornerColor; + RadianAnglef m_rotation; + Rectf m_textureCoords; + Vector2f m_size; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_BILLBOARD_HPP diff --git a/include/Nazara/Graphics/Billboard.inl b/include/Nazara/Graphics/Billboard.inl new file mode 100644 index 000000000..99d8458de --- /dev/null +++ b/include/Nazara/Graphics/Billboard.inl @@ -0,0 +1,117 @@ +// Copyright (C) 2023 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 + +namespace Nz +{ + inline const Color& Billboard::GetColor() const + { + return m_color; + } + + inline const Color& Billboard::GetCornerColor(RectCorner corner) const + { + return m_cornerColor[corner]; + } + + inline RadianAnglef Billboard::GetRotation() const + { + return m_rotation; + } + + inline const Rectf& Billboard::GetTextureCoords() const + { + return m_textureCoords; + } + + inline const Vector2f& Billboard::GetSize() const + { + return m_size; + } + + inline void Billboard::SetColor(const Color& color) + { + m_color = color; + + UpdateVertices(); + } + + inline void Billboard::SetCornerColor(RectCorner corner, const Color& color) + { + m_cornerColor[corner] = color; + + UpdateVertices(); + } + + inline void Billboard::SetMaterial(std::shared_ptr material) + { + assert(material); + + if (m_material != material) + { + OnMaterialInvalidated(this, 0, material); + m_material = std::move(material); + + OnElementInvalidated(this); + } + } + + inline void Billboard::SetRotation(RadianAnglef rotation) + { + m_rotation = rotation; + + UpdateVertices(); + } + + inline void Billboard::SetSize(const Vector2f& size) + { + m_size = size; + + UpdateVertices(); + } + + inline void Billboard::SetTextureCoords(const Rectf& textureCoords) + { + m_textureCoords = textureCoords; + + UpdateVertices(); + } + + inline void Billboard::SetTextureRect(const Rectf& textureRect) + { + Vector2f invTextureSize = 1.f / Vector2f(Vector2ui(GetTextureSize())); + return SetTextureCoords(Rectf(textureRect.x * invTextureSize.x, textureRect.y * invTextureSize.y, textureRect.width * invTextureSize.x, textureRect.height * invTextureSize.y)); + } + + inline void Billboard::UpdateVertices() + { + VertexStruct_UV_SizeSinCos_Color* vertices = m_vertices.data(); + + EnumArray cornerExtent; + cornerExtent[RectCorner::LeftBottom] = Vector2f(0.f, 0.f); + cornerExtent[RectCorner::RightBottom] = Vector2f(1.f, 0.f); + cornerExtent[RectCorner::LeftTop] = Vector2f(0.f, 1.f); + cornerExtent[RectCorner::RightTop] = Vector2f(1.f, 1.f); + + auto [sin, cos] = m_rotation.GetSinCos(); + + for (RectCorner corner : { RectCorner::LeftBottom, RectCorner::RightBottom, RectCorner::LeftTop, RectCorner::RightTop }) + { + vertices->color = m_color * m_cornerColor[corner]; + vertices->sizeSinCos = Vector4f(m_size.x, m_size.y, sin, cos); + vertices->uv = m_textureCoords.GetCorner(corner); + + vertices++; + } + + Vector2f halfSize = m_size * 0.5f; + float maxExtent = std::max(halfSize.x, halfSize.y); + UpdateAABB(Boxf(-maxExtent, -maxExtent, -maxExtent, maxExtent * 2.0f, maxExtent * 2.0f, maxExtent * 2.0f)); + OnElementInvalidated(this); + } +} + +#include diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 43deebe57..0a510aad9 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -390,6 +390,7 @@ namespace Nz JointWeights, Normal, Position, + SizeSinCos, Tangent, TexCoord, Userdata, @@ -406,6 +407,7 @@ namespace Nz enum class VertexLayout { // Predefined declarations for rendering + UV_SizeSinCos, XY, XY_Color, XY_UV, @@ -416,6 +418,7 @@ namespace Nz XYZ_Normal_UV, XYZ_Normal_UV_Tangent, XYZ_Normal_UV_Tangent_Skinning, + UV_SizeSinCos_Color, XYZ_UV, // Predefined declarations for instancing diff --git a/include/Nazara/Utility/VertexStruct.hpp b/include/Nazara/Utility/VertexStruct.hpp index afcb72728..023dca46e 100644 --- a/include/Nazara/Utility/VertexStruct.hpp +++ b/include/Nazara/Utility/VertexStruct.hpp @@ -68,6 +68,17 @@ namespace Nz Vector3f tangent; }; + struct VertexStruct_UV_SizeSinCos + { + Vector2f uv; + Vector4f sizeSinCos; //< width, height, sin, cos + }; + + struct VertexStruct_UV_SizeSinCos_Color : VertexStruct_UV_SizeSinCos + { + Color color; + }; + struct VertexStruct_XYZ_UV : VertexStruct_XYZ { Vector2f uv; diff --git a/src/Nazara/Graphics/Billboard.cpp b/src/Nazara/Graphics/Billboard.cpp new file mode 100644 index 000000000..99bf956b9 --- /dev/null +++ b/src/Nazara/Graphics/Billboard.cpp @@ -0,0 +1,88 @@ +// Copyright (C) 2023 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 +#include +#include + +namespace Nz +{ + Billboard::Billboard(std::shared_ptr material) : + m_material(std::move(material)), + m_color(Color::White()), + m_rotation(RadianAnglef::Zero()), + m_textureCoords(0.f, 0.f, 1.f, 1.f) + { + m_cornerColor.fill(Color::White()); + m_size = Vector2f(Vector2ui(GetTextureSize())); + + UpdateVertices(); + } + + Billboard::Billboard(std::shared_ptr material, const Vector2f& size) : + m_material(std::move(material)), + m_color(Color::White()), + m_rotation(RadianAnglef::Zero()), + m_textureCoords(0.f, 0.f, 1.f, 1.f) + { + m_cornerColor.fill(Color::White()); + m_size = size; + + UpdateVertices(); + } + + void Billboard::BuildElement(ElementRendererRegistry& registry, const ElementData& elementData, std::size_t passIndex, std::vector& elements) const + { + const auto& materialPipeline = m_material->GetPipeline(passIndex); + if (!materialPipeline) + return; + + MaterialPassFlags passFlags = m_material->GetPassFlags(passIndex); + + const std::shared_ptr& vertexDeclaration = VertexDeclaration::Get(VertexLayout::UV_SizeSinCos_Color); + + RenderPipelineInfo::VertexBufferData vertexBufferData = { + 0, + vertexDeclaration + }; + const auto& renderPipeline = materialPipeline->GetRenderPipeline(&vertexBufferData, 1); + + const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[ImageType::E2D]; + + elements.emplace_back(registry.AllocateElement(GetRenderLayer(), m_material, passFlags, renderPipeline, *elementData.worldInstance, vertexDeclaration, whiteTexture, 1, m_vertices.data(), *elementData.scissorBox)); + } + + const std::shared_ptr& Billboard::GetMaterial(std::size_t i) const + { + assert(i == 0); + NazaraUnused(i); + + return m_material; + } + + std::size_t Billboard::GetMaterialCount() const + { + return 1; + } + + Vector3ui Billboard::GetTextureSize() const + { + assert(m_material); + + //TODO: Cache index in registry? + if (const std::shared_ptr* textureOpt = m_material->GetTextureProperty("BaseColorMap")) + { + // Material should always have textures but we're better safe than sorry + if (const std::shared_ptr& texture = *textureOpt) + return texture->GetSize(); + } + + // Couldn't get material pass or texture + return Vector3ui::Unit(); //< prevents division by zero + } +} diff --git a/src/Nazara/Graphics/Material.cpp b/src/Nazara/Graphics/Material.cpp index 242abd807..f082a185b 100644 --- a/src/Nazara/Graphics/Material.cpp +++ b/src/Nazara/Graphics/Material.cpp @@ -146,6 +146,10 @@ namespace Nz config.optionValues[CRC32("VertexPositionLoc")] = locationIndex; break; + case VertexComponent::SizeSinCos: + config.optionValues[CRC32("VertexSizeRotLocation")] = locationIndex; + break; + case VertexComponent::Tangent: config.optionValues[CRC32("VertexTangentLoc")] = locationIndex; break; diff --git a/src/Nazara/Graphics/PredefinedMaterials.cpp b/src/Nazara/Graphics/PredefinedMaterials.cpp index acdc7f399..049dd0924 100644 --- a/src/Nazara/Graphics/PredefinedMaterials.cpp +++ b/src/Nazara/Graphics/PredefinedMaterials.cpp @@ -15,12 +15,14 @@ namespace Nz { settings.AddValueProperty("BaseColor", Color::White()); settings.AddValueProperty("AlphaTest", false); + settings.AddValueProperty("Billboard", false); settings.AddValueProperty("AlphaTestThreshold", 0.2f); settings.AddValueProperty("ShadowMapNormalOffset", 0.f); settings.AddValueProperty("ShadowPosScale", 1.f - 0.0025f); settings.AddTextureProperty("BaseColorMap", ImageType::E2D); settings.AddTextureProperty("AlphaMap", ImageType::E2D); settings.AddPropertyHandler(std::make_unique("AlphaTest", "AlphaTest")); + settings.AddPropertyHandler(std::make_unique("Billboard", "Billboard")); settings.AddPropertyHandler(std::make_unique("BaseColorMap", "HasBaseColorTexture")); settings.AddPropertyHandler(std::make_unique("AlphaMap", "HasAlphaTexture")); settings.AddPropertyHandler(std::make_unique("BaseColor")); diff --git a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl index 30ce0a87c..f67708df1 100644 --- a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl @@ -18,14 +18,12 @@ option AlphaTest: bool = false; // Billboard related options option Billboard: bool = false; -option BillboardCenterLocation: i32 = -1; -option BillboardColorLocation: i32 = -1; -option BillboardSizeRotLocation: i32 = -1; // Vertex declaration related options option VertexColorLoc: i32 = -1; option VertexNormalLoc: i32 = -1; -option VertexPositionLoc: i32; +option VertexPositionLoc: i32 = -1; +option VertexSizeRotLocation: i32 = -1; option VertexUvLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1; @@ -34,7 +32,8 @@ option VertexJointWeightsLoc: i32 = -1; const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); -const HasUV = (VertexUvLoc >= 0); +const HasVertexUV = (VertexUvLoc >= 0); +const HasUV = (HasVertexUV); const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); [layout(std140)] @@ -152,6 +151,7 @@ fn FragDepth(input: VertOut) -> FragOut fn FragDepthNoAlpha() {} //< dummy // Vertex stage +[cond(!Billboard)] struct VertIn { [location(VertexPositionLoc)] @@ -163,55 +163,68 @@ struct VertIn [cond(HasNormal), location(VertexNormalLoc)] normal: vec3[f32], - [cond(HasUV), location(VertexUvLoc)] + [cond(HasVertexUV), location(VertexUvLoc)] uv: vec2[f32], [cond(HasSkinning), location(VertexJointIndicesLoc)] jointIndices: vec4[i32], [cond(HasSkinning), location(VertexJointWeightsLoc)] - jointWeights: vec4[f32], - - [cond(Billboard), location(BillboardCenterLocation)] - billboardCenter: vec3[f32], - - [cond(Billboard), location(BillboardSizeRotLocation)] - billboardSizeRot: vec4[f32], //< width,height,sin,cos - - [cond(Billboard), location(BillboardColorLocation)] - billboardColor: vec4[f32] + jointWeights: vec4[f32] } -[entry(vert), cond(Billboard)] -fn VertBillboard(input: VertIn) -> VertOut +[cond(Billboard)] +struct BillboardVertIn { - let size = input.billboardSizeRot.xy; - let sinCos = input.billboardSizeRot.zw; + [builtin(vertex_index)] vertIndex: i32, + + [location(VertexSizeRotLocation)] + sizeRot: vec4[f32], //< width,height,sin,cos + + [cond(HasVertexUV), location(VertexUvLoc)] + uv: vec2[f32], + + [cond(HasVertexColor), location(VertexColorLoc)] + color: vec4[f32] +} + +const billboardPos = array[vec2[f32]]( + vec2[f32](-0.5, -0.5), + vec2[f32]( 0.5, -0.5), + vec2[f32](-0.5, 0.5), + vec2[f32]( 0.5, 0.5) +); + +[entry(vert), cond(Billboard)] +fn VertBillboard(input: BillboardVertIn) -> VertOut +{ + let position = billboardPos[input.vertIndex % 4]; + + let size = input.sizeRot.xy; + let sinCos = input.sizeRot.zw; let rotatedPosition = vec2[f32]( - input.pos.x * sinCos.y - input.pos.y * sinCos.x, - input.pos.y * sinCos.y + input.pos.x * sinCos.x + position.x * sinCos.y - position.y * sinCos.x, + position.y * sinCos.y + position.x * sinCos.x ); rotatedPosition *= size; let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); let cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]); - let vertexPos = input.billboardCenter; - vertexPos += cameraRight * rotatedPosition.x; - vertexPos += cameraUp * rotatedPosition.y; - - let worldPosition = instanceData.worldMatrix * vec4[f32](vertexPos, 1.0); + let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz); + worldPosition += cameraRight * rotatedPosition.x; + worldPosition += cameraUp * rotatedPosition.y; let output: VertOut; - output.worldPos = worldPosition.xyz; - output.position = viewerData.viewProjMatrix * worldPosition; - + output.worldPos = worldPosition; + output.position = viewerData.viewProjMatrix * vec4[f32](worldPosition, 1.0); + + const if (HasVertexUV) + output.uv = input.uv; + const if (HasColor) - output.color = input.billboardColor; - - const if (HasUV) - output.uv = input.pos.xy + vec2[f32](0.5, 0.5); + output.color = input.color; return output; } @@ -252,7 +265,7 @@ fn VertMain(input: VertIn) -> VertOut const if (HasColor) output.color = input.color; - const if (HasUV) + const if (HasVertexUV) output.uv = input.uv; return output; diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index 29ea98df1..bb2fb916a 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -27,14 +27,12 @@ option HasSpecularTexture: bool = false; // Billboard related options option Billboard: bool = false; -option BillboardCenterLocation: i32 = -1; -option BillboardColorLocation: i32 = -1; -option BillboardSizeRotLocation: i32 = -1; // Vertex declaration related options option VertexColorLoc: i32 = -1; option VertexNormalLoc: i32 = -1; -option VertexPositionLoc: i32; +option VertexPositionLoc: i32 = -1; +option VertexSizeRotLocation: i32 = -1; option VertexTangentLoc: i32 = -1; option VertexUvLoc: i32 = -1; @@ -47,7 +45,8 @@ const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); const HasTangent = (VertexTangentLoc >= 0); -const HasUV = (VertexUvLoc >= 0); +const HasVertexUV = (VertexUvLoc >= 0); +const HasUV = (HasVertexUV); const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); const HasLighting = HasNormal && !DepthPass; @@ -295,6 +294,7 @@ fn FragDepth(input: VertOut) -> FragOut fn FragDepthNoAlpha() {} //< dummy // Vertex stage +[cond(!Billboard)] struct VertIn { [location(VertexPositionLoc)] @@ -303,7 +303,7 @@ struct VertIn [cond(HasVertexColor), location(VertexColorLoc)] color: vec4[f32], - [cond(HasUV), location(VertexUvLoc)] + [cond(HasVertexUV), location(VertexUvLoc)] uv: vec2[f32], [cond(HasNormal), location(VertexNormalLoc)] @@ -316,45 +316,61 @@ struct VertIn jointIndices: vec4[i32], [cond(HasSkinning), location(VertexJointWeightsLoc)] - jointWeights: vec4[f32], - - [cond(Billboard), location(BillboardCenterLocation)] - billboardCenter: vec3[f32], - - [cond(Billboard), location(BillboardSizeRotLocation)] - billboardSizeRot: vec4[f32], //< width,height,sin,cos - - [cond(Billboard), location(BillboardColorLocation)] - billboardColor: vec4[f32] + jointWeights: vec4[f32] } -[entry(vert), cond(Billboard)] -fn billboardMain(input: VertIn) -> VertOut +[cond(Billboard)] +struct BillboardVertIn { - let size = input.billboardSizeRot.xy; - let sinCos = input.billboardSizeRot.zw; + [builtin(vertex_index)] vertIndex: i32, + + [location(VertexSizeRotLocation)] + sizeRot: vec4[f32], //< width,height,sin,cos + + [cond(HasVertexUV), location(VertexUvLoc)] + uv: vec2[f32], + + [cond(HasVertexColor), location(VertexColorLoc)] + color: vec4[f32] +} + +const billboardPos = array[vec2[f32]]( + vec2[f32](-0.5, -0.5), + vec2[f32]( 0.5, -0.5), + vec2[f32](-0.5, 0.5), + vec2[f32]( 0.5, 0.5) +); + +[entry(vert), cond(Billboard)] +fn VertBillboard(input: BillboardVertIn) -> VertOut +{ + let position = billboardPos[input.vertIndex % 4]; + + let size = input.sizeRot.xy; + let sinCos = input.sizeRot.zw; let rotatedPosition = vec2[f32]( - input.pos.x * sinCos.y - input.pos.y * sinCos.x, - input.pos.y * sinCos.y + input.pos.x * sinCos.x + position.x * sinCos.y - position.y * sinCos.x, + position.y * sinCos.y + position.x * sinCos.x ); rotatedPosition *= size; let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); let cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]); - let vertexPos = input.billboardCenter; - vertexPos += cameraRight * rotatedPosition.x; - vertexPos += cameraUp * rotatedPosition.y; + let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz); + worldPosition += cameraRight * rotatedPosition.x; + worldPosition += cameraUp * rotatedPosition.y; let output: VertOut; - output.position = viewerData.viewProjMatrix * instanceData.worldMatrix * vec4[f32](vertexPos, 1.0); - + output.worldPos = worldPosition; + output.position = viewerData.viewProjMatrix * vec4[f32](worldPosition, 1.0); + + const if (HasVertexUV) + output.uv = input.uv; + const if (HasColor) - output.color = input.billboardColor; - - const if (HasUV) - output.uv = input.pos.xy + vec2[f32](0.5, 0.5); + output.color = input.color; return output; } @@ -414,7 +430,7 @@ fn main(input: VertIn) -> VertOut const if (HasNormal) output.normal = rotationMatrix * normal; - const if (HasUV) + const if (HasVertexUV) output.uv = input.uv; const if (HasNormalMapping) diff --git a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl index 73f1b419d..5cd56218a 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl @@ -29,14 +29,12 @@ option HasSpecularTexture: bool = false; // Billboard related options option Billboard: bool = false; -option BillboardCenterLocation: i32 = -1; -option BillboardColorLocation: i32 = -1; -option BillboardSizeRotLocation: i32 = -1; // Vertex declaration related options option VertexColorLoc: i32 = -1; option VertexNormalLoc: i32 = -1; -option VertexPositionLoc: i32; +option VertexPositionLoc: i32 = -1; +option VertexSizeRotLocation: i32 = -1; option VertexTangentLoc: i32 = -1; option VertexUvLoc: i32 = -1; @@ -49,7 +47,8 @@ const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); const HasTangent = (VertexTangentLoc >= 0); -const HasUV = (VertexUvLoc >= 0); +const HasVertexUV = (VertexUvLoc >= 0); +const HasUV = (HasVertexUV); const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); @@ -292,6 +291,7 @@ fn FragDepth(input: VertOut) -> FragOut fn FragDepthNoAlpha() {} //< dummy // Vertex stage +[cond(!Billboard)] struct VertIn { [location(VertexPositionLoc)] @@ -300,7 +300,7 @@ struct VertIn [cond(HasVertexColor), location(VertexColorLoc)] color: vec4[f32], - [cond(HasUV), location(VertexUvLoc)] + [cond(HasVertexUV), location(VertexUvLoc)] uv: vec2[f32], [cond(HasNormal), location(VertexNormalLoc)] @@ -313,45 +313,61 @@ struct VertIn jointIndices: vec4[i32], [cond(HasSkinning), location(VertexJointWeightsLoc)] - jointWeights: vec4[f32], - - [cond(Billboard), location(BillboardCenterLocation)] - billboardCenter: vec3[f32], - - [cond(Billboard), location(BillboardSizeRotLocation)] - billboardSizeRot: vec4[f32], //< width,height,sin,cos - - [cond(Billboard), location(BillboardColorLocation)] - billboardColor: vec4[f32] + jointWeights: vec4[f32] } -[entry(vert), cond(Billboard)] -fn VertBillboard(input: VertIn) -> VertOut +[cond(Billboard)] +struct BillboardVertIn { - let size = input.billboardSizeRot.xy; - let sinCos = input.billboardSizeRot.zw; + [builtin(vertex_index)] vertIndex: i32, + + [location(VertexSizeRotLocation)] + sizeRot: vec4[f32], //< width,height,sin,cos + + [cond(HasVertexUV), location(VertexUvLoc)] + uv: vec2[f32], + + [cond(HasVertexColor), location(VertexColorLoc)] + color: vec4[f32] +} + +const billboardPos = array[vec2[f32]]( + vec2[f32](-0.5, -0.5), + vec2[f32]( 0.5, -0.5), + vec2[f32](-0.5, 0.5), + vec2[f32]( 0.5, 0.5) +); + +[entry(vert), cond(Billboard)] +fn VertBillboard(input: BillboardVertIn) -> VertOut +{ + let position = billboardPos[input.vertIndex % 4]; + + let size = input.sizeRot.xy; + let sinCos = input.sizeRot.zw; let rotatedPosition = vec2[f32]( - input.pos.x * sinCos.y - input.pos.y * sinCos.x, - input.pos.y * sinCos.y + input.pos.x * sinCos.x + position.x * sinCos.y - position.y * sinCos.x, + position.y * sinCos.y + position.x * sinCos.x ); rotatedPosition *= size; let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); let cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]); - let vertexPos = input.billboardCenter; - vertexPos += cameraRight * rotatedPosition.x; - vertexPos += cameraUp * rotatedPosition.y; + let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz); + worldPosition += cameraRight * rotatedPosition.x; + worldPosition += cameraUp * rotatedPosition.y; let output: VertOut; - output.position = viewerData.viewProjMatrix * instanceData.worldMatrix * vec4[f32](vertexPos, 1.0); + output.worldPos = worldPosition; + output.position = viewerData.viewProjMatrix * vec4[f32](worldPosition, 1.0); + + const if (HasVertexUV) + output.uv = input.uv; const if (HasColor) - output.color = input.billboardColor; - - const if (HasUV) - output.uv = input.pos.xy + vec2[f32](0.5, 0.5); + output.color = input.color; return output; } @@ -411,7 +427,7 @@ fn VertMain(input: VertIn) -> VertOut const if (HasNormal) output.normal = rotationMatrix * input.normal; - const if (HasUV) + const if (HasVertexUV) output.uv = input.uv; const if (HasNormalMapping) diff --git a/src/Nazara/Utility/VertexDeclaration.cpp b/src/Nazara/Utility/VertexDeclaration.cpp index b73eaa652..f8144437f 100644 --- a/src/Nazara/Utility/VertexDeclaration.cpp +++ b/src/Nazara/Utility/VertexDeclaration.cpp @@ -292,6 +292,43 @@ namespace Nz }); NazaraAssert(s_declarations[VertexLayout::XYZ_Normal_UV_Tangent_Skinning]->GetStride() == sizeof(VertexStruct_XYZ_Normal_UV_Tangent_Skinning), "Invalid stride for declaration VertexLayout::XYZ_Normal_UV_Tangent_Skinning"); + + // VertexLayout::XYZ_SizeRot : VertexStruct_XYZ_SizeRot + s_declarations[VertexLayout::UV_SizeSinCos] = NewDeclaration(VertexInputRate::Vertex, { + { + VertexComponent::TexCoord, + ComponentType::Float2, + 0 + }, + { + VertexComponent::SizeSinCos, + ComponentType::Float4, + 0 + } + }); + + NazaraAssert(s_declarations[VertexLayout::UV_SizeSinCos]->GetStride() == sizeof(VertexStruct_UV_SizeSinCos), "Invalid stride for declaration VertexLayout::UV_SizeSinCos"); + + // VertexLayout::XYZ_SizeRot_Color : VertexStruct_XYZ_SizeRot_Color + s_declarations[VertexLayout::UV_SizeSinCos_Color] = NewDeclaration(VertexInputRate::Vertex, { + { + VertexComponent::TexCoord, + ComponentType::Float2, + 0 + }, + { + VertexComponent::SizeSinCos, + ComponentType::Float4, + 0 + }, + { + VertexComponent::Color, + ComponentType::Color, + 0 + } + }); + + NazaraAssert(s_declarations[VertexLayout::UV_SizeSinCos_Color]->GetStride() == sizeof(VertexStruct_UV_SizeSinCos_Color), "Invalid stride for declaration VertexLayout::UV_SizeSinCos_Color"); // VertexLayout::XYZ_UV : VertexStruct_XYZ_UV s_declarations[VertexLayout::XYZ_UV] = NewDeclaration(VertexInputRate::Vertex, {