Added particles first implementation

Former-commit-id: 2b98ce2f007927690bdecd4092e211013bf568cb
This commit is contained in:
Lynix 2014-08-08 17:17:58 +02:00
parent 4acd61cf30
commit e9267d7f43
16 changed files with 1071 additions and 4 deletions

View File

@ -43,6 +43,40 @@ enum nzMaterialUniform
nzMaterialUniform_Max = nzMaterialUniform_SpecularMap
};
enum nzParticleComponent
{
nzParticleComponent_Unused = -1,
nzParticleComponent_Color,
nzParticleComponent_Life,
nzParticleComponent_Normal,
nzParticleComponent_Position,
nzParticleComponent_Radius,
nzParticleComponent_Rotation,
nzParticleComponent_Size,
nzParticleComponent_Velocity,
nzParticleComponent_Userdata0,
nzParticleComponent_Userdata1,
nzParticleComponent_Userdata2,
nzParticleComponent_Userdata3,
nzParticleComponent_Userdata4,
nzParticleComponent_Userdata5,
nzParticleComponent_Userdata6,
nzParticleComponent_Userdata7,
nzParticleComponent_Userdata8,
nzParticleComponent_Max = nzParticleComponent_Userdata8
};
enum nzParticleLayout
{
nzParticleLayout_Billboard,
nzParticleLayout_Model,
nzParticleLayout_Sprite,
nzParticleLayout_Max = nzParticleLayout_Sprite
};
enum nzRenderPassType
{
nzRenderPassType_AA,
@ -71,10 +105,11 @@ enum nzRenderTechniqueType
enum nzSceneNodeType
{
nzSceneNodeType_Light, // NzLight
nzSceneNodeType_Model, // NzModel
nzSceneNodeType_Root, // NzSceneRoot
nzSceneNodeType_Sprite, // NzSprite
nzSceneNodeType_Light, // NzLight
nzSceneNodeType_Model, // NzModel
nzSceneNodeType_ParticleEmitter, // NzParticleEmitter
nzSceneNodeType_Root, // NzSceneRoot
nzSceneNodeType_Sprite, // NzSprite
nzSceneNodeType_User,
nzSceneNodeType_Max = nzSceneNodeType_User

View File

@ -0,0 +1,31 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLECONTROLLER_HPP
#define NAZARA_PARTICLECONTROLLER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceRef.hpp>
class NzParticleController;
class NzParticleEmitter;
class NzParticleMapper;
using NzParticleControllerConstRef = NzResourceRef<const NzParticleController>;
using NzParticleControllerRef = NzResourceRef<NzParticleController>;
class NAZARA_API NzParticleController : public NzResource
{
public:
NzParticleController() = default;
NzParticleController(const NzParticleController& controller);
virtual ~NzParticleController();
virtual void Apply(NzParticleEmitter& emitter, NzParticleMapper& mapper, unsigned int offset, unsigned int particleCount, float elapsedTime) = 0;
};
#endif // NAZARA_PARTICLECONTROLLER_HPP

View File

@ -0,0 +1,67 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLEDECLARATION_HPP
#define NAZARA_PARTICLEDECLARATION_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceRef.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Utility/Enums.hpp>
class NzParticleDeclaration;
using NzParticleDeclarationConstRef = NzResourceRef<const NzParticleDeclaration>;
using NzParticleDeclarationRef = NzResourceRef<NzParticleDeclaration>;
class NAZARA_API NzParticleDeclaration : public NzResource
{
friend class NzGraphics;
public:
NzParticleDeclaration();
NzParticleDeclaration(const NzParticleDeclaration& declaration);
~NzParticleDeclaration();
void DisableComponent(nzParticleComponent component);
void EnableComponent(nzParticleComponent component, nzComponentType type, unsigned int offset);
void GetComponent(nzParticleComponent component, bool* enabled, nzComponentType* type, unsigned int* offset) const;
unsigned int GetStride() const;
void SetStride(unsigned int stride);
NzParticleDeclaration& operator=(const NzParticleDeclaration& declaration);
static NzParticleDeclaration* Get(nzParticleLayout layout);
static bool IsTypeSupported(nzComponentType type);
private:
static bool Initialize();
static void Uninitialize();
struct Component
{
nzComponentType type;
bool enabled = false;
unsigned int offset;
/*
** -Lynix:
** Il serait aussi possible de préciser le stride de façon indépendante, ce que je ne permets pas
** pour décomplexifier l'interface en enlevant quelque chose que je juge inutile.
** Si vous pensez que ça peut être utile, n'hésitez pas à me le faire savoir !
*/
};
Component m_components[nzParticleComponent_Max+1];
unsigned int m_stride;
static NzParticleDeclaration s_declarations[nzParticleLayout_Max+1];
};
#endif // NAZARA_PARTICLEDECLARATION_HPP

View File

@ -0,0 +1,91 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLEEMITTER_HPP
#define NAZARA_PARTICLEEMITTER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Updatable.hpp>
#include <Nazara/Graphics/ParticleController.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
#include <Nazara/Graphics/ParticleGenerator.hpp>
#include <Nazara/Graphics/ParticleRenderer.hpp>
#include <Nazara/Graphics/SceneNode.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <memory>
#include <set>
#include <vector>
class NAZARA_API NzParticleEmitter : public NzSceneNode, NzUpdatable
{
public:
NzParticleEmitter(unsigned int maxParticleCount, nzParticleLayout layout);
NzParticleEmitter(unsigned int maxParticleCount, NzParticleDeclaration* declaration);
NzParticleEmitter(const NzParticleEmitter& emitter);
NzParticleEmitter(NzParticleEmitter&& emitter) = default;
~NzParticleEmitter();
void AddController(NzParticleController* controller);
void AddGenerator(NzParticleGenerator* generator);
void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const;
void* CreateParticle();
void* CreateParticles(unsigned int count);
void* GenerateParticle();
void* GenerateParticles(unsigned int count);
const NzBoundingVolumef& GetBoundingVolume() const override;
unsigned int GetEmissionCount() const;
float GetEmissionRate() const;
unsigned int GetMaxParticleCount() const;
unsigned int GetParticleCount() const;
unsigned int GetParticleSize() const;
nzSceneNodeType GetSceneNodeType() const override;
bool IsDrawable() const;
void KillParticle(unsigned int index);
void KillParticles();
void RemoveController(NzParticleController* controller);
void RemoveGenerator(NzParticleGenerator* generator);
void SetEmissionCount(unsigned int count);
void SetEmissionRate(float rate);
void SetRenderer(NzParticleRenderer* renderer);
NzParticleEmitter& operator=(const NzParticleEmitter& emitter);
NzParticleEmitter& operator=(NzParticleEmitter&& emitter);
private:
void GenerateAABB() const;
void Register() override;
void ResizeBuffer();
void Unregister() override;
void UpdateBoundingVolume() const;
void Update() override;
std::set<unsigned int, std::greater<unsigned int>> m_dyingParticles;
mutable std::vector<nzUInt8> m_buffer;
std::vector<NzParticleControllerRef> m_controllers;
std::vector<NzParticleGeneratorRef> m_generators;
mutable NzBoundingVolumef m_boundingVolume;
NzParticleDeclarationConstRef m_declaration;
NzParticleRendererRef m_renderer;
mutable bool m_boundingVolumeUpdated;
bool m_processing;
float m_emissionAccumulator;
float m_emissionRate;
unsigned int m_emissionCount;
unsigned int m_maxParticleCount;
unsigned int m_particleCount;
unsigned int m_particleSize;
};
#endif // NAZARA_PARTICLEEMITTER_HPP

View File

@ -0,0 +1,31 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLEGENERATOR_HPP
#define NAZARA_PARTICLEGENERATOR_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceRef.hpp>
class NzParticleEmitter;
class NzParticleGenerator;
class NzParticleMapper;
using NzParticleGeneratorConstRef = NzResourceRef<const NzParticleGenerator>;
using NzParticleGeneratorRef = NzResourceRef<NzParticleGenerator>;
class NAZARA_API NzParticleGenerator : public NzResource
{
public:
NzParticleGenerator() = default;
NzParticleGenerator(const NzParticleGenerator& generator);
virtual ~NzParticleGenerator();
virtual void Generate(NzParticleEmitter& emitter, NzParticleMapper& mapper, unsigned int offset, unsigned int particleCount) = 0;
};
#endif // NAZARA_PARTICLEGENERATOR_HPP

View File

@ -0,0 +1,31 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLEMAPPER_HPP
#define NAZARA_PARTICLEMAPPER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/SparsePtr.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
class NAZARA_API NzParticleMapper
{
public:
NzParticleMapper(void* buffer, const NzParticleDeclaration* declaration);
~NzParticleMapper();
template<typename T> NzSparsePtr<T> GetComponentPtr(nzParticleComponent component);
template<typename T> NzSparsePtr<const T> GetComponentPtr(nzParticleComponent component) const;
private:
const NzParticleDeclaration* m_declaration;
nzUInt8* m_ptr;
};
#include <Nazara/Graphics/ParticleMapper.inl>
#endif // NAZARA_PARTICLEMAPPER_HPP

View File

@ -0,0 +1,50 @@
// Copyright (C) 2014 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/Error.hpp>
#include <Nazara/Graphics/Debug.hpp>
template <typename T>
NzSparsePtr<T> NzParticleMapper::GetComponentPtr(nzParticleComponent component)
{
// Ensuite le composant qui nous intéresse
bool enabled;
nzComponentType type;
unsigned int offset;
m_declaration->GetComponent(component, &enabled, &type, &offset);
if (enabled)
{
///TODO: Vérifier le rapport entre le type de l'attribut et le type template ?
return NzSparsePtr<T>(m_ptr + offset, m_declaration->GetStride());
}
else
{
NazaraError("Attribute 0x" + NzString::Number(component, 16) + " is not enabled");
return NzSparsePtr<T>();
}
}
template <typename T>
NzSparsePtr<const T> NzParticleMapper::GetComponentPtr(nzParticleComponent component) const
{
// Ensuite le composant qui nous intéresse
bool enabled;
nzComponentType type;
unsigned int offset;
m_declaration->GetComponent(component, &enabled, &type, &offset);
if (enabled)
{
///TODO: Vérifier le rapport entre le type de l'attribut et le type template ?
return NzSparsePtr<const T>(m_ptr + offset, m_declaration->GetStride());
}
else
{
NazaraError("Attribute 0x" + NzString::Number(component, 16) + " is not enabled");
return NzSparsePtr<const T>();
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -0,0 +1,32 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLERENDERER_HPP
#define NAZARA_PARTICLERENDERER_HPP
#include <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceRef.hpp>
class NzAbstractRenderQueue;
class NzParticleEmitter;
class NzParticleMapper;
class NzParticleRenderer;
using NzParticleRendererConstRef = NzResourceRef<const NzParticleRenderer>;
using NzParticleRendererRef = NzResourceRef<NzParticleRenderer>;
class NAZARA_API NzParticleRenderer : public NzResource
{
public:
NzParticleRenderer() = default;
NzParticleRenderer(const NzParticleRenderer& renderer);
virtual ~NzParticleRenderer();
virtual void Render(const NzParticleEmitter& emitter, const NzParticleMapper& mapper, unsigned int offset, unsigned int particleCount, NzAbstractRenderQueue* renderQueue) = 0;
};
#endif // NAZARA_PARTICLERENDERER_HPP

View File

@ -0,0 +1,42 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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_PARTICLESTRUCT_HPP
#define NAZARA_PARTICLESTRUCT_HPP
#include <Nazara/Core/Color.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Math/Vector3.hpp>
struct NzParticleStruct_Billboard
{
NzColor color;
NzVector3f normal;
NzVector3f position;
NzVector3f velocity;
nzUInt32 life;
float rotation;
};
struct NzParticleStruct_Model
{
NzVector3f position;
NzVector3f velocity;
nzUInt32 life;
NzQuaternionf rotation;
};
struct NzParticleStruct_Sprite
{
NzColor color;
NzVector2f position;
NzVector2f velocity;
nzUInt32 life;
float rotation;
};
#endif // NAZARA_PARTICLESTRUCT_HPP

View File

@ -10,6 +10,7 @@
#include <Nazara/Graphics/DeferredRenderTechnique.hpp>
#include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Graphics/ParticleDeclaration.hpp>
#include <Nazara/Graphics/RenderTechniques.hpp>
#include <Nazara/Graphics/SkinningManager.hpp>
#include <Nazara/Graphics/Loaders/Mesh.hpp>
@ -44,6 +45,12 @@ bool NzGraphics::Initialize()
return false;
}
if (!NzParticleDeclaration::Initialize())
{
NazaraError("Failed to initialize particle declarations");
return false;
}
if (!NzSkinningManager::Initialize())
{
NazaraError("Failed to initialize skinning manager");
@ -98,6 +105,7 @@ void NzGraphics::Uninitialize()
NzDeferredRenderTechnique::Uninitialize();
NzMaterial::Uninitialize();
NzParticleDeclaration::Uninitialize();
NzSkinningManager::Uninitialize();
NazaraNotice("Uninitialized: Graphics module");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleController.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleController::NzParticleController(const NzParticleController& controller) :
NzResource()
{
NazaraUnused(controller);
}
NzParticleController::~NzParticleController() = default;

View File

@ -0,0 +1,231 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleDeclaration.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/OffsetOf.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/ParticleStruct.hpp>
#include <Nazara/Utility/Utility.hpp>
#include <cstring>
#include <Nazara/Graphics/Debug.hpp>
NzParticleDeclaration::NzParticleDeclaration() :
m_stride(0)
{
}
NzParticleDeclaration::NzParticleDeclaration(const NzParticleDeclaration& declaration) :
NzResource(),
m_stride(declaration.m_stride)
{
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzParticleComponent_Max+1));
}
NzParticleDeclaration::~NzParticleDeclaration()
{
NotifyDestroy();
}
void NzParticleDeclaration::DisableComponent(nzParticleComponent component)
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == nzParticleComponent_Unused)
{
NazaraError("Cannot disable \"unused\" component");
return;
}
#endif
Component& vertexComponent = m_components[component];
if (vertexComponent.enabled)
{
vertexComponent.enabled = false;
m_stride -= NzUtility::ComponentStride[vertexComponent.type];
}
}
void NzParticleDeclaration::EnableComponent(nzParticleComponent component, nzComponentType type, unsigned int offset)
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
{
NazaraError("Vertex component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (!IsTypeSupported(type))
{
NazaraError("Component type 0x" + NzString::Number(type, 16) + " is not supported by particle declarations");
return;
}
#endif
if (component != nzParticleComponent_Unused)
{
Component& particleComponent = m_components[component];
if (particleComponent.enabled)
m_stride -= NzUtility::ComponentStride[particleComponent.type];
else
particleComponent.enabled = true;
particleComponent.offset = offset;
particleComponent.type = type;
}
m_stride += NzUtility::ComponentStride[type];
}
void NzParticleDeclaration::GetComponent(nzParticleComponent component, bool* enabled, nzComponentType* type, unsigned int* offset) const
{
#ifdef NAZARA_DEBUG
if (component > nzParticleComponent_Max)
{
NazaraError("Particle component out of enum");
return;
}
#endif
#if NAZARA_GRAPHICS_SAFE
if (component == nzParticleComponent_Unused)
{
NazaraError("Cannot get \"unused\" component");
return;
}
#endif
const Component& particleComponent = m_components[component];
if (enabled)
*enabled = particleComponent.enabled;
if (type)
*type = particleComponent.type;
if (offset)
*offset = particleComponent.offset;
}
unsigned int NzParticleDeclaration::GetStride() const
{
return m_stride;
}
void NzParticleDeclaration::SetStride(unsigned int stride)
{
m_stride = stride;
}
NzParticleDeclaration& NzParticleDeclaration::operator=(const NzParticleDeclaration& declaration)
{
std::memcpy(m_components, declaration.m_components, sizeof(Component)*(nzParticleComponent_Max+1));
m_stride = declaration.m_stride;
return *this;
}
NzParticleDeclaration* NzParticleDeclaration::Get(nzParticleLayout layout)
{
#ifdef NAZARA_DEBUG
if (layout > nzParticleLayout_Max)
{
NazaraError("Particle layout out of enum");
return nullptr;
}
#endif
return &s_declarations[layout];
}
bool NzParticleDeclaration::IsTypeSupported(nzComponentType type)
{
switch (type)
{
case nzComponentType_Color:
case nzComponentType_Double1:
case nzComponentType_Double2:
case nzComponentType_Double3:
case nzComponentType_Double4:
case nzComponentType_Float1:
case nzComponentType_Float2:
case nzComponentType_Float3:
case nzComponentType_Float4:
case nzComponentType_Int1:
case nzComponentType_Int2:
case nzComponentType_Int3:
case nzComponentType_Int4:
case nzComponentType_Quaternion:
return true;
}
NazaraError("Component type not handled (0x" + NzString::Number(type, 16) + ')');
return false;
}
bool NzParticleDeclaration::Initialize()
{
try
{
NzErrorFlags flags(nzErrorFlag_Silent | nzErrorFlag_ThrowException);
// Layout : Type
NzParticleDeclaration* declaration;
// nzParticleLayout_Billboard : NzParticleStruct_Billboard
declaration = &s_declarations[nzParticleLayout_Billboard];
declaration->EnableComponent(nzParticleComponent_Color, nzComponentType_Color, NzOffsetOf(NzParticleStruct_Billboard, color));
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Billboard, life));
declaration->EnableComponent(nzParticleComponent_Normal, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, normal));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Float1, NzOffsetOf(NzParticleStruct_Billboard, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Billboard, velocity));
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Billboard), "Invalid stride for declaration nzParticleLayout_Billboard");
// nzParticleLayout_Model : NzParticleStruct_Model
declaration = &s_declarations[nzParticleLayout_Model];
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Model, life));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Model, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Quaternion, NzOffsetOf(NzParticleStruct_Model, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float3, NzOffsetOf(NzParticleStruct_Model, velocity));
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Model), "Invalid stride for declaration nzParticleLayout_Model");
// nzParticleLayout_Sprite : NzParticleStruct_Sprite
declaration = &s_declarations[nzParticleLayout_Sprite];
declaration->EnableComponent(nzParticleComponent_Color, nzComponentType_Color, NzOffsetOf(NzParticleStruct_Sprite, color));
declaration->EnableComponent(nzParticleComponent_Life, nzComponentType_Int1, NzOffsetOf(NzParticleStruct_Sprite, life));
declaration->EnableComponent(nzParticleComponent_Position, nzComponentType_Float2, NzOffsetOf(NzParticleStruct_Sprite, position));
declaration->EnableComponent(nzParticleComponent_Rotation, nzComponentType_Float1, NzOffsetOf(NzParticleStruct_Sprite, rotation));
declaration->EnableComponent(nzParticleComponent_Velocity, nzComponentType_Float2, NzOffsetOf(NzParticleStruct_Sprite, velocity));
NazaraAssert(declaration->GetStride() == sizeof(NzParticleStruct_Sprite), "Invalid stride for declaration nzParticleLayout_Sprite");
}
catch (const std::exception& e)
{
NazaraError("Failed to initialize particle declarations: " + NzString(e.what()));
return false;
}
return true;
}
void NzParticleDeclaration::Uninitialize()
{
// Rien à faire
}
NzParticleDeclaration NzParticleDeclaration::s_declarations[nzParticleLayout_Max+1];

View File

@ -0,0 +1,361 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleEmitter.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Graphics/ParticleMapper.hpp>
#include <cstdlib>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>
NzParticleEmitter::NzParticleEmitter(unsigned int maxParticleCount, nzParticleLayout layout) :
NzParticleEmitter(maxParticleCount, NzParticleDeclaration::Get(layout))
{
}
NzParticleEmitter::NzParticleEmitter(unsigned int maxParticleCount, NzParticleDeclaration* declaration) :
m_declaration(declaration),
m_boundingVolumeUpdated(false),
m_emissionAccumulator(0.f),
m_emissionRate(0.f),
m_emissionCount(1),
m_maxParticleCount(maxParticleCount),
m_particleCount(0)
{
// En cas d'erreur, un constructeur ne peut que lancer une exception
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
m_particleSize = m_declaration->GetStride(); // La taille de chaque particule
ResizeBuffer();
}
NzParticleEmitter::NzParticleEmitter(const NzParticleEmitter& emitter) :
NzSceneNode(emitter),
m_controllers(emitter.m_controllers),
m_generators(emitter.m_generators),
m_boundingVolume(emitter.m_boundingVolume),
m_declaration(emitter.m_declaration),
m_renderer(emitter.m_renderer),
m_boundingVolumeUpdated(emitter.m_boundingVolumeUpdated),
m_processing(false),
m_emissionAccumulator(0.f),
m_emissionRate(emitter.m_emissionRate),
m_emissionCount(emitter.m_emissionCount),
m_maxParticleCount(emitter.m_maxParticleCount),
m_particleCount(emitter.m_particleCount),
m_particleSize(emitter.m_particleSize)
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
ResizeBuffer();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), emitter.m_buffer.data(), emitter.m_particleCount*m_particleSize);
}
NzParticleEmitter::~NzParticleEmitter() = default;
void NzParticleEmitter::AddController(NzParticleController* controller)
{
m_controllers.emplace_back(controller);
}
void NzParticleEmitter::AddGenerator(NzParticleGenerator* generator)
{
m_generators.emplace_back(generator);
}
void NzParticleEmitter::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const
{
///FIXME: Vérifier le renderer
NzParticleMapper mapper(m_buffer.data(), m_declaration);
m_renderer->Render(*this, mapper, 0, m_particleCount, renderQueue);
}
void* NzParticleEmitter::CreateParticle()
{
return CreateParticles(1);
}
void* NzParticleEmitter::CreateParticles(unsigned int count)
{
if (m_particleCount+count > m_maxParticleCount)
return nullptr;
unsigned int particlesIndex = m_particleCount;
m_particleCount += count;
return &m_buffer[particlesIndex*m_particleSize];
}
void* NzParticleEmitter::GenerateParticle()
{
return GenerateParticles(1);
}
void* NzParticleEmitter::GenerateParticles(unsigned int count)
{
void* ptr = CreateParticles(count);
if (!ptr)
return nullptr;
NzParticleMapper mapper(ptr, m_declaration);
for (NzParticleGenerator* generator : m_generators)
generator->Generate(*this, mapper, 0, m_particleCount);
return ptr;
}
const NzBoundingVolumef& NzParticleEmitter::GetBoundingVolume() const
{
if (!m_boundingVolumeUpdated)
UpdateBoundingVolume();
return m_boundingVolume;
}
unsigned int NzParticleEmitter::GetEmissionCount() const
{
return m_emissionCount;
}
float NzParticleEmitter::GetEmissionRate() const
{
return m_emissionRate;
}
unsigned int NzParticleEmitter::GetMaxParticleCount() const
{
return m_maxParticleCount;
}
unsigned int NzParticleEmitter::GetParticleCount() const
{
return m_particleCount;
}
unsigned int NzParticleEmitter::GetParticleSize() const
{
return m_particleSize;
}
nzSceneNodeType NzParticleEmitter::GetSceneNodeType() const
{
return nzSceneNodeType_ParticleEmitter;
}
bool NzParticleEmitter::IsDrawable() const
{
return true;
}
void NzParticleEmitter::KillParticle(unsigned int index)
{
///FIXME: Vérifier index
if (m_processing)
{
// Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste de secours
m_dyingParticles.insert(index);
return;
}
// On déplace la dernière particule vivante à la place de celle-ci
if (--m_particleCount > 0)
std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize);
}
void NzParticleEmitter::KillParticles()
{
m_particleCount = 0;
}
void NzParticleEmitter::RemoveController(NzParticleController* controller)
{
auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
if (it != m_controllers.end())
m_controllers.erase(it);
}
void NzParticleEmitter::RemoveGenerator(NzParticleGenerator* generator)
{
auto it = std::find(m_generators.begin(), m_generators.end(), generator);
if (it != m_generators.end())
m_generators.erase(it);
}
void NzParticleEmitter::SetEmissionCount(unsigned int count)
{
m_emissionCount = count;
}
void NzParticleEmitter::SetEmissionRate(float rate)
{
m_emissionRate = rate;
}
void NzParticleEmitter::SetRenderer(NzParticleRenderer* renderer)
{
m_renderer = renderer;
}
NzParticleEmitter& NzParticleEmitter::operator=(const NzParticleEmitter& emitter)
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
NzSceneNode::operator=(emitter);
m_boundingVolume = emitter.m_boundingVolume;
m_boundingVolumeUpdated = emitter.m_boundingVolumeUpdated;
m_controllers = emitter.m_controllers;
m_declaration = emitter.m_declaration;
m_emissionCount = emitter.m_emissionCount;
m_emissionRate = emitter.m_emissionRate;
m_generators = emitter.m_generators;
m_maxParticleCount = emitter.m_maxParticleCount;
m_particleCount = emitter.m_particleCount;
m_particleSize = emitter.m_particleSize;
m_renderer = emitter.m_renderer;
// La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier
m_dyingParticles.clear();
m_emissionAccumulator = 0.f;
m_processing = false;
m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose
ResizeBuffer();
// On ne copie que les particules vivantes
std::memcpy(m_buffer.data(), emitter.m_buffer.data(), emitter.m_particleCount*m_particleSize);
return *this;
}
NzParticleEmitter& NzParticleEmitter::operator=(NzParticleEmitter&& emitter)
{
NzErrorFlags flags(nzErrorFlag_ThrowException, true);
NzSceneNode::operator=(emitter);
m_boundingVolume = std::move(emitter.m_boundingVolume);
m_boundingVolumeUpdated = std::move(emitter.m_boundingVolumeUpdated);
m_buffer = std::move(emitter.m_buffer);
m_controllers = std::move(emitter.m_controllers);
m_declaration = std::move(emitter.m_declaration);
m_dyingParticles = std::move(emitter.m_dyingParticles);
m_emissionAccumulator = std::move(emitter.m_emissionAccumulator);
m_emissionCount = std::move(emitter.m_emissionCount);
m_emissionRate = std::move(emitter.m_emissionRate);
m_generators = std::move(emitter.m_generators);
m_maxParticleCount = std::move(emitter.m_maxParticleCount);
m_particleCount = std::move(emitter.m_particleCount);
m_particleSize = std::move(emitter.m_particleSize);
m_processing = std::move(emitter.m_processing);
m_renderer = std::move(emitter.m_renderer);
return *this;
}
void NzParticleEmitter::GenerateAABB() const
{
m_boundingVolume.MakeInfinite();
}
void NzParticleEmitter::Register()
{
m_scene->RegisterForUpdate(this);
}
void NzParticleEmitter::ResizeBuffer()
{
// Histoire de décrire un peu mieux l'erreur en cas d'échec
try
{
m_buffer.resize(m_maxParticleCount*m_particleSize);
}
catch (const std::exception& e)
{
NzStringStream stream;
stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;
NazaraError(stream.ToString());
}
}
void NzParticleEmitter::Unregister()
{
m_scene->UnregisterForUpdate(this);
}
void NzParticleEmitter::UpdateBoundingVolume() const
{
if (m_boundingVolume.IsNull())
GenerateAABB();
if (!m_transformMatrixUpdated)
UpdateTransformMatrix();
m_boundingVolume.Update(m_transformMatrix);
m_boundingVolumeUpdated = true;
}
void NzParticleEmitter::Update()
{
float elapsedTime = m_scene->GetUpdateTime();
if (m_emissionRate > 0.f)
{
// On accumule la partie réelle (pour éviter qu'un taux d'update élevé empêche des particules de se former)
m_emissionAccumulator += elapsedTime*m_emissionRate;
float emissionCount = std::floor(m_emissionAccumulator); // Le nombre d'émissions de cette mise à jour
m_emissionAccumulator -= emissionCount; // On enlève la partie entière
if (emissionCount >= 1.f)
{
// On calcule le nombre maximum de particules pouvant être émises cette fois-ci
unsigned int maxParticleCount = static_cast<unsigned int>(emissionCount)*m_emissionCount;
// On récupère le nombre de particules qu'il est possible de créer selon l'espace libre
unsigned int particleCount = std::min(maxParticleCount, m_maxParticleCount - m_particleCount);
// Et on émet nos particules
GenerateParticles(particleCount);
}
}
NzParticleMapper mapper(m_buffer.data(), m_declaration);
m_processing = true;
// Pour éviter un verrouillage en cas d'exception
NzCallOnExit onExit([this]()
{
m_processing = false;
});
for (NzParticleController* controller : m_controllers)
controller->Apply(*this, mapper, 0, m_particleCount, elapsedTime);
m_processing = false;
onExit.Reset();
// On tue maintenant les particules mortes durant la mise à jour
if (m_dyingParticles.size() < m_particleCount)
{
// On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater
// La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer,
// sans cette solution certaines particules pourraient échapper à la mort
for (unsigned int index : m_dyingParticles)
KillParticle(index);
}
else
KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide
m_dyingParticles.clear();
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleGenerator.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleGenerator::NzParticleGenerator(const NzParticleGenerator& generator) :
NzResource()
{
NazaraUnused(generator);
}
NzParticleGenerator::~NzParticleGenerator() = default;

View File

@ -0,0 +1,15 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleMapper.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleMapper::NzParticleMapper(void* buffer, const NzParticleDeclaration* declaration) :
m_declaration(declaration),
m_ptr(static_cast<nzUInt8*>(buffer))
{
}
NzParticleMapper::~NzParticleMapper() = default;

View File

@ -0,0 +1,14 @@
// Copyright (C) 2014 Jérôme Leclercq
// 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/ParticleRenderer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzParticleRenderer::NzParticleRenderer(const NzParticleRenderer& renderer) :
NzResource()
{
NazaraUnused(renderer);
}
NzParticleRenderer::~NzParticleRenderer() = default;