Graphics: Add Billboard support

This commit is contained in:
SirLynix 2023-11-25 15:52:58 +01:00
parent 4cbb5b91a3
commit 1ac992b5c7
12 changed files with 472 additions and 98 deletions

View File

@ -32,6 +32,7 @@
#include <Nazara/Graphics/AbstractViewer.hpp> #include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Algorithm.hpp> #include <Nazara/Graphics/Algorithm.hpp>
#include <Nazara/Graphics/BakedFrameGraph.hpp> #include <Nazara/Graphics/BakedFrameGraph.hpp>
#include <Nazara/Graphics/Billboard.hpp>
#include <Nazara/Graphics/Camera.hpp> #include <Nazara/Graphics/Camera.hpp>
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/DebugDrawPipelinePass.hpp> #include <Nazara/Graphics/DebugDrawPipelinePass.hpp>

View File

@ -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 <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/InstancedRenderable.hpp>
#include <Nazara/Renderer/RenderPipeline.hpp>
#include <Nazara/Utility/VertexDeclaration.hpp>
#include <Nazara/Utility/VertexStruct.hpp>
#include <array>
namespace Nz
{
class NAZARA_GRAPHICS_API Billboard : public InstancedRenderable
{
public:
Billboard(std::shared_ptr<MaterialInstance> material);
Billboard(std::shared_ptr<MaterialInstance> 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<RenderElementOwner>& elements) const override;
inline const Color& GetColor() const;
inline const Color& GetCornerColor(RectCorner corner) const;
const std::shared_ptr<MaterialInstance>& 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<MaterialInstance> 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<VertexStruct_UV_SizeSinCos_Color, 4> m_vertices;
std::shared_ptr<MaterialInstance> m_material;
Color m_color;
EnumArray<RectCorner, Color> m_cornerColor;
RadianAnglef m_rotation;
Rectf m_textureCoords;
Vector2f m_size;
};
}
#include <Nazara/Graphics/Billboard.inl>
#endif // NAZARA_GRAPHICS_BILLBOARD_HPP

View File

@ -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 <cassert>
#include <Nazara/Graphics/Debug.hpp>
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<MaterialInstance> 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<RectCorner, Vector2f> 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 <Nazara/Graphics/DebugOff.hpp>

View File

@ -390,6 +390,7 @@ namespace Nz
JointWeights, JointWeights,
Normal, Normal,
Position, Position,
SizeSinCos,
Tangent, Tangent,
TexCoord, TexCoord,
Userdata, Userdata,
@ -406,6 +407,7 @@ namespace Nz
enum class VertexLayout enum class VertexLayout
{ {
// Predefined declarations for rendering // Predefined declarations for rendering
UV_SizeSinCos,
XY, XY,
XY_Color, XY_Color,
XY_UV, XY_UV,
@ -416,6 +418,7 @@ namespace Nz
XYZ_Normal_UV, XYZ_Normal_UV,
XYZ_Normal_UV_Tangent, XYZ_Normal_UV_Tangent,
XYZ_Normal_UV_Tangent_Skinning, XYZ_Normal_UV_Tangent_Skinning,
UV_SizeSinCos_Color,
XYZ_UV, XYZ_UV,
// Predefined declarations for instancing // Predefined declarations for instancing

View File

@ -68,6 +68,17 @@ namespace Nz
Vector3f tangent; 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 struct VertexStruct_XYZ_UV : VertexStruct_XYZ
{ {
Vector2f uv; Vector2f uv;

View File

@ -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 <Nazara/Graphics/Billboard.hpp>
#include <Nazara/Graphics/ElementRendererRegistry.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/MaterialInstance.hpp>
#include <Nazara/Graphics/RenderSpriteChain.hpp>
#include <Nazara/Graphics/WorldInstance.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
Billboard::Billboard(std::shared_ptr<MaterialInstance> 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<MaterialInstance> 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<RenderElementOwner>& elements) const
{
const auto& materialPipeline = m_material->GetPipeline(passIndex);
if (!materialPipeline)
return;
MaterialPassFlags passFlags = m_material->GetPassFlags(passIndex);
const std::shared_ptr<VertexDeclaration>& 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<RenderSpriteChain>(GetRenderLayer(), m_material, passFlags, renderPipeline, *elementData.worldInstance, vertexDeclaration, whiteTexture, 1, m_vertices.data(), *elementData.scissorBox));
}
const std::shared_ptr<MaterialInstance>& 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<Texture>* textureOpt = m_material->GetTextureProperty("BaseColorMap"))
{
// Material should always have textures but we're better safe than sorry
if (const std::shared_ptr<Texture>& texture = *textureOpt)
return texture->GetSize();
}
// Couldn't get material pass or texture
return Vector3ui::Unit(); //< prevents division by zero
}
}

View File

@ -146,6 +146,10 @@ namespace Nz
config.optionValues[CRC32("VertexPositionLoc")] = locationIndex; config.optionValues[CRC32("VertexPositionLoc")] = locationIndex;
break; break;
case VertexComponent::SizeSinCos:
config.optionValues[CRC32("VertexSizeRotLocation")] = locationIndex;
break;
case VertexComponent::Tangent: case VertexComponent::Tangent:
config.optionValues[CRC32("VertexTangentLoc")] = locationIndex; config.optionValues[CRC32("VertexTangentLoc")] = locationIndex;
break; break;

View File

@ -15,12 +15,14 @@ namespace Nz
{ {
settings.AddValueProperty<Color>("BaseColor", Color::White()); settings.AddValueProperty<Color>("BaseColor", Color::White());
settings.AddValueProperty<bool>("AlphaTest", false); settings.AddValueProperty<bool>("AlphaTest", false);
settings.AddValueProperty<bool>("Billboard", false);
settings.AddValueProperty<float>("AlphaTestThreshold", 0.2f); settings.AddValueProperty<float>("AlphaTestThreshold", 0.2f);
settings.AddValueProperty<float>("ShadowMapNormalOffset", 0.f); settings.AddValueProperty<float>("ShadowMapNormalOffset", 0.f);
settings.AddValueProperty<float>("ShadowPosScale", 1.f - 0.0025f); settings.AddValueProperty<float>("ShadowPosScale", 1.f - 0.0025f);
settings.AddTextureProperty("BaseColorMap", ImageType::E2D); settings.AddTextureProperty("BaseColorMap", ImageType::E2D);
settings.AddTextureProperty("AlphaMap", ImageType::E2D); settings.AddTextureProperty("AlphaMap", ImageType::E2D);
settings.AddPropertyHandler(std::make_unique<OptionValuePropertyHandler>("AlphaTest", "AlphaTest")); settings.AddPropertyHandler(std::make_unique<OptionValuePropertyHandler>("AlphaTest", "AlphaTest"));
settings.AddPropertyHandler(std::make_unique<OptionValuePropertyHandler>("Billboard", "Billboard"));
settings.AddPropertyHandler(std::make_unique<TexturePropertyHandler>("BaseColorMap", "HasBaseColorTexture")); settings.AddPropertyHandler(std::make_unique<TexturePropertyHandler>("BaseColorMap", "HasBaseColorTexture"));
settings.AddPropertyHandler(std::make_unique<TexturePropertyHandler>("AlphaMap", "HasAlphaTexture")); settings.AddPropertyHandler(std::make_unique<TexturePropertyHandler>("AlphaMap", "HasAlphaTexture"));
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("BaseColor")); settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("BaseColor"));

View File

@ -18,14 +18,12 @@ option AlphaTest: bool = false;
// Billboard related options // Billboard related options
option Billboard: bool = false; option Billboard: bool = false;
option BillboardCenterLocation: i32 = -1;
option BillboardColorLocation: i32 = -1;
option BillboardSizeRotLocation: i32 = -1;
// Vertex declaration related options // Vertex declaration related options
option VertexColorLoc: i32 = -1; option VertexColorLoc: i32 = -1;
option VertexNormalLoc: i32 = -1; option VertexNormalLoc: i32 = -1;
option VertexPositionLoc: i32; option VertexPositionLoc: i32 = -1;
option VertexSizeRotLocation: i32 = -1;
option VertexUvLoc: i32 = -1; option VertexUvLoc: i32 = -1;
option VertexJointIndicesLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1;
@ -34,7 +32,8 @@ option VertexJointWeightsLoc: i32 = -1;
const HasNormal = (VertexNormalLoc >= 0); const HasNormal = (VertexNormalLoc >= 0);
const HasVertexColor = (VertexColorLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0);
const HasColor = (HasVertexColor || Billboard); const HasColor = (HasVertexColor || Billboard);
const HasUV = (VertexUvLoc >= 0); const HasVertexUV = (VertexUvLoc >= 0);
const HasUV = (HasVertexUV);
const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0);
[layout(std140)] [layout(std140)]
@ -152,6 +151,7 @@ fn FragDepth(input: VertOut) -> FragOut
fn FragDepthNoAlpha() {} //< dummy fn FragDepthNoAlpha() {} //< dummy
// Vertex stage // Vertex stage
[cond(!Billboard)]
struct VertIn struct VertIn
{ {
[location(VertexPositionLoc)] [location(VertexPositionLoc)]
@ -163,55 +163,68 @@ struct VertIn
[cond(HasNormal), location(VertexNormalLoc)] [cond(HasNormal), location(VertexNormalLoc)]
normal: vec3[f32], normal: vec3[f32],
[cond(HasUV), location(VertexUvLoc)] [cond(HasVertexUV), location(VertexUvLoc)]
uv: vec2[f32], uv: vec2[f32],
[cond(HasSkinning), location(VertexJointIndicesLoc)] [cond(HasSkinning), location(VertexJointIndicesLoc)]
jointIndices: vec4[i32], jointIndices: vec4[i32],
[cond(HasSkinning), location(VertexJointWeightsLoc)] [cond(HasSkinning), location(VertexJointWeightsLoc)]
jointWeights: vec4[f32], 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]
} }
[entry(vert), cond(Billboard)] [cond(Billboard)]
fn VertBillboard(input: VertIn) -> VertOut struct BillboardVertIn
{ {
let size = input.billboardSizeRot.xy; [builtin(vertex_index)] vertIndex: i32,
let sinCos = input.billboardSizeRot.zw;
[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]( let rotatedPosition = vec2[f32](
input.pos.x * sinCos.y - input.pos.y * sinCos.x, position.x * sinCos.y - position.y * sinCos.x,
input.pos.y * sinCos.y + input.pos.x * sinCos.x position.y * sinCos.y + position.x * sinCos.x
); );
rotatedPosition *= size; rotatedPosition *= size;
let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); 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 cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]);
let vertexPos = input.billboardCenter; let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz);
vertexPos += cameraRight * rotatedPosition.x; worldPosition += cameraRight * rotatedPosition.x;
vertexPos += cameraUp * rotatedPosition.y; worldPosition += cameraUp * rotatedPosition.y;
let worldPosition = instanceData.worldMatrix * vec4[f32](vertexPos, 1.0);
let output: VertOut; let output: VertOut;
output.worldPos = worldPosition.xyz; output.worldPos = worldPosition;
output.position = viewerData.viewProjMatrix * worldPosition; output.position = viewerData.viewProjMatrix * vec4[f32](worldPosition, 1.0);
const if (HasVertexUV)
output.uv = input.uv;
const if (HasColor) const if (HasColor)
output.color = input.billboardColor; output.color = input.color;
const if (HasUV)
output.uv = input.pos.xy + vec2[f32](0.5, 0.5);
return output; return output;
} }
@ -252,7 +265,7 @@ fn VertMain(input: VertIn) -> VertOut
const if (HasColor) const if (HasColor)
output.color = input.color; output.color = input.color;
const if (HasUV) const if (HasVertexUV)
output.uv = input.uv; output.uv = input.uv;
return output; return output;

View File

@ -27,14 +27,12 @@ option HasSpecularTexture: bool = false;
// Billboard related options // Billboard related options
option Billboard: bool = false; option Billboard: bool = false;
option BillboardCenterLocation: i32 = -1;
option BillboardColorLocation: i32 = -1;
option BillboardSizeRotLocation: i32 = -1;
// Vertex declaration related options // Vertex declaration related options
option VertexColorLoc: i32 = -1; option VertexColorLoc: i32 = -1;
option VertexNormalLoc: i32 = -1; option VertexNormalLoc: i32 = -1;
option VertexPositionLoc: i32; option VertexPositionLoc: i32 = -1;
option VertexSizeRotLocation: i32 = -1;
option VertexTangentLoc: i32 = -1; option VertexTangentLoc: i32 = -1;
option VertexUvLoc: i32 = -1; option VertexUvLoc: i32 = -1;
@ -47,7 +45,8 @@ const HasNormal = (VertexNormalLoc >= 0);
const HasVertexColor = (VertexColorLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0);
const HasColor = (HasVertexColor || Billboard); const HasColor = (HasVertexColor || Billboard);
const HasTangent = (VertexTangentLoc >= 0); const HasTangent = (VertexTangentLoc >= 0);
const HasUV = (VertexUvLoc >= 0); const HasVertexUV = (VertexUvLoc >= 0);
const HasUV = (HasVertexUV);
const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass;
const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0);
const HasLighting = HasNormal && !DepthPass; const HasLighting = HasNormal && !DepthPass;
@ -295,6 +294,7 @@ fn FragDepth(input: VertOut) -> FragOut
fn FragDepthNoAlpha() {} //< dummy fn FragDepthNoAlpha() {} //< dummy
// Vertex stage // Vertex stage
[cond(!Billboard)]
struct VertIn struct VertIn
{ {
[location(VertexPositionLoc)] [location(VertexPositionLoc)]
@ -303,7 +303,7 @@ struct VertIn
[cond(HasVertexColor), location(VertexColorLoc)] [cond(HasVertexColor), location(VertexColorLoc)]
color: vec4[f32], color: vec4[f32],
[cond(HasUV), location(VertexUvLoc)] [cond(HasVertexUV), location(VertexUvLoc)]
uv: vec2[f32], uv: vec2[f32],
[cond(HasNormal), location(VertexNormalLoc)] [cond(HasNormal), location(VertexNormalLoc)]
@ -316,45 +316,61 @@ struct VertIn
jointIndices: vec4[i32], jointIndices: vec4[i32],
[cond(HasSkinning), location(VertexJointWeightsLoc)] [cond(HasSkinning), location(VertexJointWeightsLoc)]
jointWeights: vec4[f32], 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]
} }
[entry(vert), cond(Billboard)] [cond(Billboard)]
fn billboardMain(input: VertIn) -> VertOut struct BillboardVertIn
{ {
let size = input.billboardSizeRot.xy; [builtin(vertex_index)] vertIndex: i32,
let sinCos = input.billboardSizeRot.zw;
[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]( let rotatedPosition = vec2[f32](
input.pos.x * sinCos.y - input.pos.y * sinCos.x, position.x * sinCos.y - position.y * sinCos.x,
input.pos.y * sinCos.y + input.pos.x * sinCos.x position.y * sinCos.y + position.x * sinCos.x
); );
rotatedPosition *= size; rotatedPosition *= size;
let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); 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 cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]);
let vertexPos = input.billboardCenter; let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz);
vertexPos += cameraRight * rotatedPosition.x; worldPosition += cameraRight * rotatedPosition.x;
vertexPos += cameraUp * rotatedPosition.y; worldPosition += cameraUp * rotatedPosition.y;
let output: VertOut; 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) const if (HasColor)
output.color = input.billboardColor; output.color = input.color;
const if (HasUV)
output.uv = input.pos.xy + vec2[f32](0.5, 0.5);
return output; return output;
} }
@ -414,7 +430,7 @@ fn main(input: VertIn) -> VertOut
const if (HasNormal) const if (HasNormal)
output.normal = rotationMatrix * normal; output.normal = rotationMatrix * normal;
const if (HasUV) const if (HasVertexUV)
output.uv = input.uv; output.uv = input.uv;
const if (HasNormalMapping) const if (HasNormalMapping)

View File

@ -29,14 +29,12 @@ option HasSpecularTexture: bool = false;
// Billboard related options // Billboard related options
option Billboard: bool = false; option Billboard: bool = false;
option BillboardCenterLocation: i32 = -1;
option BillboardColorLocation: i32 = -1;
option BillboardSizeRotLocation: i32 = -1;
// Vertex declaration related options // Vertex declaration related options
option VertexColorLoc: i32 = -1; option VertexColorLoc: i32 = -1;
option VertexNormalLoc: i32 = -1; option VertexNormalLoc: i32 = -1;
option VertexPositionLoc: i32; option VertexPositionLoc: i32 = -1;
option VertexSizeRotLocation: i32 = -1;
option VertexTangentLoc: i32 = -1; option VertexTangentLoc: i32 = -1;
option VertexUvLoc: i32 = -1; option VertexUvLoc: i32 = -1;
@ -49,7 +47,8 @@ const HasNormal = (VertexNormalLoc >= 0);
const HasVertexColor = (VertexColorLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0);
const HasColor = (HasVertexColor || Billboard); const HasColor = (HasVertexColor || Billboard);
const HasTangent = (VertexTangentLoc >= 0); const HasTangent = (VertexTangentLoc >= 0);
const HasUV = (VertexUvLoc >= 0); const HasVertexUV = (VertexUvLoc >= 0);
const HasUV = (HasVertexUV);
const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass;
const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0);
@ -292,6 +291,7 @@ fn FragDepth(input: VertOut) -> FragOut
fn FragDepthNoAlpha() {} //< dummy fn FragDepthNoAlpha() {} //< dummy
// Vertex stage // Vertex stage
[cond(!Billboard)]
struct VertIn struct VertIn
{ {
[location(VertexPositionLoc)] [location(VertexPositionLoc)]
@ -300,7 +300,7 @@ struct VertIn
[cond(HasVertexColor), location(VertexColorLoc)] [cond(HasVertexColor), location(VertexColorLoc)]
color: vec4[f32], color: vec4[f32],
[cond(HasUV), location(VertexUvLoc)] [cond(HasVertexUV), location(VertexUvLoc)]
uv: vec2[f32], uv: vec2[f32],
[cond(HasNormal), location(VertexNormalLoc)] [cond(HasNormal), location(VertexNormalLoc)]
@ -313,45 +313,61 @@ struct VertIn
jointIndices: vec4[i32], jointIndices: vec4[i32],
[cond(HasSkinning), location(VertexJointWeightsLoc)] [cond(HasSkinning), location(VertexJointWeightsLoc)]
jointWeights: vec4[f32], 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]
} }
[entry(vert), cond(Billboard)] [cond(Billboard)]
fn VertBillboard(input: VertIn) -> VertOut struct BillboardVertIn
{ {
let size = input.billboardSizeRot.xy; [builtin(vertex_index)] vertIndex: i32,
let sinCos = input.billboardSizeRot.zw;
[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]( let rotatedPosition = vec2[f32](
input.pos.x * sinCos.y - input.pos.y * sinCos.x, position.x * sinCos.y - position.y * sinCos.x,
input.pos.y * sinCos.y + input.pos.x * sinCos.x position.y * sinCos.y + position.x * sinCos.x
); );
rotatedPosition *= size; rotatedPosition *= size;
let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]); 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 cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]);
let vertexPos = input.billboardCenter; let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz);
vertexPos += cameraRight * rotatedPosition.x; worldPosition += cameraRight * rotatedPosition.x;
vertexPos += cameraUp * rotatedPosition.y; worldPosition += cameraUp * rotatedPosition.y;
let output: VertOut; 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) const if (HasColor)
output.color = input.billboardColor; output.color = input.color;
const if (HasUV)
output.uv = input.pos.xy + vec2[f32](0.5, 0.5);
return output; return output;
} }
@ -411,7 +427,7 @@ fn VertMain(input: VertIn) -> VertOut
const if (HasNormal) const if (HasNormal)
output.normal = rotationMatrix * input.normal; output.normal = rotationMatrix * input.normal;
const if (HasUV) const if (HasVertexUV)
output.uv = input.uv; output.uv = input.uv;
const if (HasNormalMapping) const if (HasNormalMapping)

View File

@ -293,6 +293,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"); 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 // VertexLayout::XYZ_UV : VertexStruct_XYZ_UV
s_declarations[VertexLayout::XYZ_UV] = NewDeclaration(VertexInputRate::Vertex, { s_declarations[VertexLayout::XYZ_UV] = NewDeclaration(VertexInputRate::Vertex, {
{ {