diff --git a/Engine/BuiltInShaders/shaders/fs_particle.sc b/Engine/BuiltInShaders/shaders/fs_particle.sc new file mode 100644 index 00000000..d233bb19 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/fs_particle.sc @@ -0,0 +1,13 @@ +$input v_color0, v_texcoord0 +#include "../common/common.sh" + +SAMPLER2D(s_texColor, 0); + +void main() +{ + vec4 rgba = texture2D(s_texColor, v_texcoord0.xy); + + rgba.xyz = rgba.xyz * v_color0.xyz; + rgba.w = rgba.w * v_color0.w; + gl_FragColor = rgba; +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/fs_particleEmitterShape.sc b/Engine/BuiltInShaders/shaders/fs_particleEmitterShape.sc new file mode 100644 index 00000000..b1ed6c51 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/fs_particleEmitterShape.sc @@ -0,0 +1,6 @@ +#include "../common/common.sh" + +void main() +{ + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/fs_particleforcefield.sc b/Engine/BuiltInShaders/shaders/fs_particleforcefield.sc new file mode 100644 index 00000000..197f0bc5 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/fs_particleforcefield.sc @@ -0,0 +1,6 @@ +#include "../common/common.sh" + +void main() +{ + gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/fs_wo_billboardparticle.sc b/Engine/BuiltInShaders/shaders/fs_wo_billboardparticle.sc new file mode 100644 index 00000000..d233bb19 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/fs_wo_billboardparticle.sc @@ -0,0 +1,13 @@ +$input v_color0, v_texcoord0 +#include "../common/common.sh" + +SAMPLER2D(s_texColor, 0); + +void main() +{ + vec4 rgba = texture2D(s_texColor, v_texcoord0.xy); + + rgba.xyz = rgba.xyz * v_color0.xyz; + rgba.w = rgba.w * v_color0.w; + gl_FragColor = rgba; +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/varying.def.sc b/Engine/BuiltInShaders/shaders/varying.def.sc index 07bd3c26..bd82f0c4 100644 --- a/Engine/BuiltInShaders/shaders/varying.def.sc +++ b/Engine/BuiltInShaders/shaders/varying.def.sc @@ -18,4 +18,10 @@ vec2 a_texcoord0 : TEXCOORD0; vec4 a_color0 : COLOR0; vec4 a_color1 : COLOR1; ivec4 a_indices : BLENDINDICES; -vec4 a_weight : BLENDWEIGHT; \ No newline at end of file +vec4 a_weight : BLENDWEIGHT; + +vec4 i_data0 : TEXCOORD7; +vec4 i_data1 : TEXCOORD6; +vec4 i_data2 : TEXCOORD5; +vec4 i_data3 : TEXCOORD4; +vec4 i_data4 : TEXCOORD3; \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/vs_particle.sc b/Engine/BuiltInShaders/shaders/vs_particle.sc new file mode 100644 index 00000000..d2859e35 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/vs_particle.sc @@ -0,0 +1,29 @@ +#if defined(PARTICLEINSTANCE) +$input a_position, a_color0, a_texcoord0, i_data0, i_data1 ,i_data2 ,i_data3 ,i_data4 +#else +$input a_position, a_color0, a_texcoord0 +#endif + +$output v_color0, v_texcoord0 + +#if defined(PARTICLEINSTANCE) +#include "../common/common.sh" +#else +#include "../common/common.sh" +uniform vec4 u_particleColor; +#endif + +void main() +{ + #if defined(PARTICLEINSTANCE) + mat4 model = mtxFromCols(i_data0, i_data1, i_data2, i_data3); + vec4 worldPos = mul(model,vec4(a_position,1.0)); + gl_Position = mul(u_viewProj, worldPos); + v_color0 = a_color0*i_data4; + #else + gl_Position = mul(u_modelViewProj, vec4(a_position,1.0)); + v_color0 = a_color0 * u_particleColor; + #endif + + v_texcoord0 = a_texcoord0; +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/vs_particleEmitterShape.sc b/Engine/BuiltInShaders/shaders/vs_particleEmitterShape.sc new file mode 100644 index 00000000..142fef27 --- /dev/null +++ b/Engine/BuiltInShaders/shaders/vs_particleEmitterShape.sc @@ -0,0 +1,10 @@ +$input a_position//, a_color1 + +#include "../common/common.sh" + +uniform vec4 u_shapeRange; + +void main() +{ + gl_Position = mul(u_modelViewProj, vec4(a_position*u_shapeRange.xyz, 1.0)); +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/vs_particleforcefield.sc b/Engine/BuiltInShaders/shaders/vs_particleforcefield.sc new file mode 100644 index 00000000..c5a18b1e --- /dev/null +++ b/Engine/BuiltInShaders/shaders/vs_particleforcefield.sc @@ -0,0 +1,8 @@ +$input a_position//, a_color1 + +#include "../common/common.sh" + +void main() +{ + gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); +} \ No newline at end of file diff --git a/Engine/BuiltInShaders/shaders/vs_wo_billboardparticle.sc b/Engine/BuiltInShaders/shaders/vs_wo_billboardparticle.sc new file mode 100644 index 00000000..7850436f --- /dev/null +++ b/Engine/BuiltInShaders/shaders/vs_wo_billboardparticle.sc @@ -0,0 +1,56 @@ +#if defined(PARTICLEINSTANCE) +$input a_position, a_color0, a_texcoord0, i_data0, i_data1 ,i_data2 ,i_data3 ,i_data4 +#else +$input a_position, a_color0, a_texcoord0 +#endif +$output v_color0, v_texcoord0 + +#include "../common/common.sh" + +#if defined(PARTICLEINSTANCE) +uniform vec4 u_particlePos; +uniform vec4 u_particleScale; +#else +uniform vec4 u_particleColor; +#endif + +void main() +{ +#if defined(PARTICLEINSTANCE) + mat4 model = mtxFromCols(i_data0, i_data1, i_data2, i_data3); + mat4 billboardMatrix; + billboardMatrix[0] = vec4( + u_view[0][0]*u_particleScale.x, + u_view[1][0]*u_particleScale.x, + u_view[2][0]*u_particleScale.x, + 0.0 + ); + billboardMatrix[1] = vec4( + u_view[0][1]*u_particleScale.y, + u_view[1][1]*u_particleScale.y, + u_view[2][1]*u_particleScale.y, + 0.0 + ); + billboardMatrix[2] = vec4( + u_view[0][2]*u_particleScale.z, + u_view[1][2]*u_particleScale.z, + u_view[2][2]*u_particleScale.z, + 0.0 + ); + billboardMatrix[3] = vec4( + u_particlePos.x, + u_particlePos.y, + u_particlePos.z, + 1.0 + ); + model = mul(model,billboardMatrix); + vec4 worldPos = mul(model,vec4(a_position,1.0)); + gl_Position = mul(u_viewProj, worldPos); + v_color0 = a_color0*i_data4; +#else + gl_Position = mul(u_modelViewProj, vec4(a_position,1.0)); + v_color0 = a_color0 * u_particleColor; +#endif + + v_texcoord0 = a_texcoord0; +} \ No newline at end of file diff --git a/Engine/Source/Editor/ECWorld/ECWorldConsumer.cpp b/Engine/Source/Editor/ECWorld/ECWorldConsumer.cpp index 01fbf15e..c4a94ae4 100644 --- a/Engine/Source/Editor/ECWorld/ECWorldConsumer.cpp +++ b/Engine/Source/Editor/ECWorld/ECWorldConsumer.cpp @@ -74,39 +74,31 @@ void ECWorldConsumer::Execute(const cd::SceneDatabase* pSceneDatabase) } }; - // There are multiple kinds of cases in the SceneDatabase: - // 1. No nodes but have meshes in the SceneDatabase. - // 2. Only a root node with multiple meshes. - // 3. Node hierarchy. - // Another case is that we want to skip Node/Mesh which alreay parsed previously. - std::set parsedMeshIDs; + // Parse particle emitter and skip its mesh shapes. + std::set parsedMeshIDs; + for (auto& particleEmitter : pSceneDatabase->GetParticleEmitters()) + { + engine::Entity emitterEntity = m_pSceneWorld->GetWorld()->CreateEntity(); + const auto& mesh = pSceneDatabase->GetMesh(particleEmitter.GetMeshID().Data()); + AddParticleEmitter(emitterEntity, mesh, m_pSceneWorld->GetParticleMaterialType()->GetRequiredVertexFormat(), particleEmitter); + parsedMeshIDs.insert(mesh.GetID()); + } + + // Parse meshes in normal usage. for (const auto& mesh : pSceneDatabase->GetMeshes()) { if (m_meshMinID > mesh.GetID().Data()) { continue; } - - ParseMesh(mesh.GetID(), cd::Transform::Identity()); - parsedMeshIDs.insert(mesh.GetID().Data()); - } - for (const auto& node : pSceneDatabase->GetNodes()) - { - if (m_nodeMinID > node.GetID().Data()) + if (parsedMeshIDs.contains(mesh.GetID())) { continue; } - for (cd::MeshID meshID : node.GetMeshIDs()) - { - if (parsedMeshIDs.find(meshID.Data()) != parsedMeshIDs.end()) - { - continue; - } - - ParseMesh(meshID, node.GetTransform()); - } + ParseMesh(mesh.GetID(), cd::Transform::Identity()); + parsedMeshIDs.insert(mesh.GetID().Data()); } for (const auto& camera : pSceneDatabase->GetCameras()) @@ -398,4 +390,43 @@ void ECWorldConsumer::AddBlendShape(engine::Entity entity, const cd::Mesh* pMesh blendShapeComponent.Build(); } +void ECWorldConsumer::AddParticleEmitter(engine::Entity entity, const cd::Mesh& mesh, const cd::VertexFormat& vertexFormat, const cd::ParticleEmitter& emitter) +{ + engine::World* pWorld = m_pSceneWorld->GetWorld(); + engine::MaterialType* pMaterialType = m_pSceneWorld->GetParticleMaterialType(); + engine::NameComponent& nameComponent = pWorld->CreateComponent(entity); + nameComponent.SetName(emitter.GetName()); + auto& particleEmitterComponent = pWorld->CreateComponent(entity); + // TODO : Some initialization here. + auto& transformComponent = pWorld->CreateComponent(entity); + cd::Vec3f pos = emitter.GetPosition(); + cd::Vec3f rotation = emitter.GetFixedRotation(); + cd::Vec3f scale = emitter.GetFixedScale(); + auto fixedRotation = cd::Math::RadianToDegree(rotation); + cd::Quaternion rotationQuat = cd::Quaternion::FromPitchYawRoll(fixedRotation.x(), fixedRotation.y(), fixedRotation.z()); + transformComponent.GetTransform().SetTranslation(pos); + transformComponent.GetTransform().SetRotation(rotationQuat); + transformComponent.GetTransform().SetScale(scale); + transformComponent.Build(); + + particleEmitterComponent.SetRequiredVertexFormat(&vertexFormat); + ////const cd::VertexFormat *requriredVertexFormat = emitter.GetVertexFormat(); + ////particleEmitterComponent.SetRequiredVertexFormat(requriredVertexFormat); + ////particleEmitterComponent.GetParticleSystem().Init(); + if (nameof::nameof_enum(emitter.GetType()) == "Sprite") { particleEmitterComponent.SetEmitterParticleType(engine::ParticleType::Sprite); } + else if (nameof::nameof_enum(emitter.GetType()) == "Ribbon") { particleEmitterComponent.SetEmitterParticleType(engine::ParticleType::Ribbon); } + else if (nameof::nameof_enum(emitter.GetType()) == "Ring") { particleEmitterComponent.SetEmitterParticleType(engine::ParticleType::Ring); } + else if (nameof::nameof_enum(emitter.GetType()) == "Model") { particleEmitterComponent.SetEmitterParticleType(engine::ParticleType::Model); } + else if (nameof::nameof_enum(emitter.GetType()) == "Track") { particleEmitterComponent.SetEmitterParticleType(engine::ParticleType::Track); } + + particleEmitterComponent.SetSpawnCount(emitter.GetMaxCount()); + particleEmitterComponent.SetEmitterColor(emitter.GetColor()/255.0f); + particleEmitterComponent.SetEmitterVelocity(emitter.GetVelocity()); + particleEmitterComponent.SetEmitterAcceleration(emitter.GetAccelerate()); + particleEmitterComponent.SetMeshData(&mesh); + particleEmitterComponent.SetMaterialType(pMaterialType); + particleEmitterComponent.ActivateShaderFeature(engine::ShaderFeature::PARTICLE_INSTANCE); + particleEmitterComponent.Build(); +} + } \ No newline at end of file diff --git a/Engine/Source/Editor/ECWorld/ECWorldConsumer.h b/Engine/Source/Editor/ECWorld/ECWorldConsumer.h index 625ede4c..8359055a 100644 --- a/Engine/Source/Editor/ECWorld/ECWorldConsumer.h +++ b/Engine/Source/Editor/ECWorld/ECWorldConsumer.h @@ -25,6 +25,7 @@ class Material; class Mesh; class Morph; class Node; +class ParticleEmitter; class SceneDatabase; class Texture; class VertexFormat; @@ -69,7 +70,7 @@ class ECWorldConsumer final : public cdtools::IConsumer void AddAnimation(engine::Entity entity, const cd::Animation& animation, const cd::SceneDatabase* pSceneDatabase); void AddMaterial(engine::Entity entity, const cd::Material* pMaterial, engine::MaterialType* pMaterialType, const cd::SceneDatabase* pSceneDatabase); void AddBlendShape(engine::Entity entity, const cd::Mesh* pMesh, const cd::BlendShape& blendShape, const cd::SceneDatabase* pSceneDatabase); - + void AddParticleEmitter(engine::Entity entity, const cd::Mesh& mesh, const cd::VertexFormat& vertexFormat, const cd::ParticleEmitter& emitter); private: engine::MaterialType* m_pDefaultMaterialType = nullptr; engine::SceneWorld* m_pSceneWorld = nullptr; diff --git a/Engine/Source/Editor/EditorApp.cpp b/Engine/Source/Editor/EditorApp.cpp index d53ecddc..f38605b8 100644 --- a/Engine/Source/Editor/EditorApp.cpp +++ b/Engine/Source/Editor/EditorApp.cpp @@ -33,6 +33,7 @@ #include "Rendering/ShadowMapRenderer.h" #include "Rendering/TerrainRenderer.h" #include "Rendering/WorldRenderer.h" +#include "Rendering/ParticleForceFieldRenderer.h" #include "Rendering/ParticleRenderer.h" #include "Resources/FileWatcher.h" #include "Resources/ResourceBuilder.h" @@ -260,19 +261,23 @@ void EditorApp::InitMaterialType() constexpr const char* WorldProgram = "WorldProgram"; constexpr const char* AnimationProgram = "AnimationProgram"; constexpr const char* TerrainProgram = "TerrainProgram"; + constexpr const char* ParticleProgram = "ParticleProgram"; constexpr engine::StringCrc WorldProgramCrc{ WorldProgram }; constexpr engine::StringCrc AnimationProgramCrc{ AnimationProgram }; constexpr engine::StringCrc TerrainProgramCrc{ TerrainProgram }; + constexpr engine::StringCrc ParticleProgramCrc{ ParticleProgram}; m_pRenderContext->RegisterShaderProgram(WorldProgramCrc, { "vs_PBR", "fs_PBR" }); m_pRenderContext->RegisterShaderProgram(AnimationProgramCrc, { "vs_animation", "fs_animation" }); m_pRenderContext->RegisterShaderProgram(TerrainProgramCrc, { "vs_terrain", "fs_terrain" }); + m_pRenderContext->RegisterShaderProgram(ParticleProgramCrc, { "vs_particle","fs_particle" }); m_pSceneWorld = std::make_unique(); m_pSceneWorld->CreatePBRMaterialType(WorldProgram, IsAtmosphericScatteringEnable()); m_pSceneWorld->CreateAnimationMaterialType(AnimationProgram); m_pSceneWorld->CreateTerrainMaterialType(TerrainProgram); + m_pSceneWorld->CreateParticleMaterialType(ParticleProgram); } void EditorApp::InitEditorCameraEntity() @@ -554,6 +559,10 @@ void EditorApp::InitEngineRenderers() pParticleRenderer->SetSceneWorld(m_pSceneWorld.get()); AddEngineRenderer(cd::MoveTemp(pParticleRenderer)); + auto pParticleForceFieldRenderer = std::make_unique(m_pRenderContext->CreateView(), pSceneRenderTarget); + pParticleForceFieldRenderer->SetSceneWorld(m_pSceneWorld.get()); + AddEngineRenderer(cd::MoveTemp(pParticleForceFieldRenderer)); + #ifdef ENABLE_DDGI auto pDDGIRenderer = std::make_unique(m_pRenderContext->CreateView(), pSceneRenderTarget); pDDGIRenderer->SetSceneWorld(m_pSceneWorld.get()); diff --git a/Engine/Source/Editor/UILayers/AssetBrowser.cpp b/Engine/Source/Editor/UILayers/AssetBrowser.cpp index 908d0ac4..87593cc7 100644 --- a/Engine/Source/Editor/UILayers/AssetBrowser.cpp +++ b/Engine/Source/Editor/UILayers/AssetBrowser.cpp @@ -13,6 +13,7 @@ #include "Log/Log.h" #include "Material/MaterialType.h" #include "Producers/CDProducer/CDProducer.h" +#include "Producers/EffekseerProducer/EffekseerProducer.h" #include "Rendering/WorldRenderer.h" #include "Rendering/RenderContext.h" #include "Resources/ResourceBuilder.h" @@ -98,6 +99,21 @@ bool IsLightInputFile(const char* pFileExtension) return false; } +bool IsParticleInputFile(const char* pFileExtension) +{ + constexpr const char* pFileExtensions[] = { ".efkefc" }; + constexpr const int fileExtensionsSize = sizeof(pFileExtensions) / sizeof(pFileExtensions[0]); + for (int extensionIndex = 0; extensionIndex < fileExtensionsSize; ++extensionIndex) + { + if (0 == strcmp(pFileExtensions[extensionIndex], pFileExtension)) + { + return true; + } + } + + return false; +} + std::string GetFilePathExtension(const std::string& FileName) { auto pos = FileName.find_last_of('.'); @@ -780,6 +796,10 @@ void AssetBrowser::ImportAssetFile(const char* pFilePath) { m_importOptions.AssetType = IOAssetType::Light; } + else if (IsParticleInputFile(pFileExtension.c_str())) + { + m_importOptions.AssetType = IOAssetType::Particle; + } else { // Still unknown, exit. @@ -852,6 +872,10 @@ void AssetBrowser::ImportAssetFile(const char* pFilePath) { ImportJson(pFilePath); } + else if (IOAssetType::Particle == m_importOptions.AssetType) + { + ImportParticleEffect(pFilePath); + } } void AssetBrowser::ProcessSceneDatabase(cd::SceneDatabase* pSceneDatabase, bool keepMesh, bool keepMaterial, bool keepTexture, bool keepCamera, bool keepLight) @@ -1085,6 +1109,36 @@ void AssetBrowser::ImportJson(const char* pFilePath) } } +void AssetBrowser::ImportParticleEffect(const char* pFilePath) +{ + ////engine::RenderContext* pCurrentRenderContext = GetRenderContext(); + //engine::SceneWorld* pSceneWorld = GetImGuiContextInstance()->GetSceneWorld(); + + //cd::SceneDatabase* pSceneDatabase = pSceneWorld->GetSceneDatabase(); + ////uint32_t oldNodeCount = pSceneDatabase->GetNodeCount(); + ////uint32_t oldMeshCount = pSceneDatabase->GetMeshCount(); + //uint32_t oldParticleEmitterCount = pSceneDatabase->GetParticleEmitterCount(); + + //// Step 1 : Convert model file to cd::SceneDatabase + //std::filesystem::path inputFilePath(pFilePath); + //std::filesystem::path inputFileExtension = inputFilePath.extension(); + //if (0 == inputFileExtension.compare(".efkefc")) + //{ + ///* cdtools::CDProducer cdProducer(pFilePath); + // cd::SceneDatabase newSceneDatabase; + // cdtools::Processor processor(&cdProducer, nullptr, &newSceneDatabase); + // proce qssor.Run(); + // pSceneDatabase->Merge(cd::MoveTemp(newSceneDatabase));*/ + // std::string filePath = pFilePath; + // int size = MultiByteToWideChar(CP_UTF8, 0, filePath.c_str(), -1, nullptr, 0); + // std::wstring wstr(size, 0); + // MultiByteToWideChar(CP_UTF8, 0, filePath.c_str(), -1, &wstr[0], size); + // std::wstring wFilePath = wstr; + // const char16_t* u16_cstr = reinterpret_cast(wFilePath.c_str()); + // cdtools::EffekseerProducer efkProducer(u16_cstr); + //} +} + void AssetBrowser::ExportAssetFile(const char* pFilePath) { engine::SceneWorld* pSceneWorld = GetImGuiContextInstance()->GetSceneWorld(); diff --git a/Engine/Source/Editor/UILayers/AssetBrowser.h b/Engine/Source/Editor/UILayers/AssetBrowser.h index 3fb19549..0538a566 100644 --- a/Engine/Source/Editor/UILayers/AssetBrowser.h +++ b/Engine/Source/Editor/UILayers/AssetBrowser.h @@ -38,6 +38,7 @@ enum class IOAssetType SceneDatabase, Terrain, Light, + Particle, Unknown, }; @@ -95,6 +96,7 @@ class AssetBrowser : public engine::ImGuiBaseLayer private: void ProcessSceneDatabase(cd::SceneDatabase* pSceneDatabase, bool keepMesh, bool keepMaterial, bool keepTexture, bool keepCamera, bool keepLight); void ImportModelFile(const char* pFilePath); + void ImportParticleEffect(const char* pFilePath); void ImportJson(const char* pFilePath); void DrawFolder(const std::shared_ptr& dirInfo, bool defaultOpen = false); void ChangeDirectory(std::shared_ptr& directory); diff --git a/Engine/Source/Editor/UILayers/EntityList.cpp b/Engine/Source/Editor/UILayers/EntityList.cpp index c3ab4566..db7cc06c 100644 --- a/Engine/Source/Editor/UILayers/EntityList.cpp +++ b/Engine/Source/Editor/UILayers/EntityList.cpp @@ -32,6 +32,7 @@ void EntityList::AddEntity(engine::SceneWorld* pSceneWorld) cd::SceneDatabase* pSceneDatabase = pSceneWorld->GetSceneDatabase(); engine::MaterialType* pPBRMaterialType = pSceneWorld->GetPBRMaterialType(); engine::MaterialType* pTerrainMaterialType = pSceneWorld->GetTerrainMaterialType(); + engine::MaterialType* pParticleMaterialType = pSceneWorld->GetParticleMaterialType(); engine::ResourceContext* pResourceContext = GetRenderContext()->GetResourceContext(); auto AddNamedEntity = [&pWorld](std::string defaultName) -> engine::Entity @@ -241,6 +242,26 @@ void EntityList::AddEntity(engine::SceneWorld* pSceneWorld) engine::Entity entity = AddNamedEntity("ParticleEmitter"); auto& particleEmitterComponent = pWorld->CreateComponent(entity); // TODO : Some initialization here. + auto& transformComponent = pWorld->CreateComponent(entity); + transformComponent.SetTransform(cd::Transform::Identity()); + transformComponent.Build(); + particleEmitterComponent.SetRequiredVertexFormat(&pParticleMaterialType->GetRequiredVertexFormat());//to do : modify vertexFormat + particleEmitterComponent.SetMaterialType(pParticleMaterialType); + particleEmitterComponent.ActivateShaderFeature(engine::ShaderFeature::PARTICLE_INSTANCE); + particleEmitterComponent.Build(); + //auto& particleForceFieldComponent = pWorld->CreateComponent(entity); + //particleForceFieldComponent.Build(); + } + else if (ImGui::MenuItem("Add Particle ForceField")) + { + engine::Entity entity = AddNamedEntity("ParticleForceField"); + auto& particleForceFieldComponent = pWorld->CreateComponent(entity); + // TODO : Some initialization here. + auto& transformComponent = pWorld->CreateComponent(entity); + transformComponent.SetTransform(cd::Transform::Identity()); + transformComponent.Build(); + + particleForceFieldComponent.Build(); } } diff --git a/Engine/Source/Editor/UILayers/Inspector.cpp b/Engine/Source/Editor/UILayers/Inspector.cpp index 6990edaa..be0cb447 100644 --- a/Engine/Source/Editor/UILayers/Inspector.cpp +++ b/Engine/Source/Editor/UILayers/Inspector.cpp @@ -600,10 +600,10 @@ void UpdateComponentWidget(engine::SceneWorld* pSceneWorld } template<> -void UpdateComponentWidget(engine::SceneWorld* pSceneWorld, engine::Entity entity) +void UpdateComponentWidget(engine::SceneWorld* pSceneWorld, engine::Entity entity) { - auto* pParticleComponent = pSceneWorld->GetParticleComponent(entity); - if (!pParticleComponent) + auto* pParticleEmitterComponent = pSceneWorld->GetParticleEmitterComponent(entity); + if (!pParticleEmitterComponent) { return; } @@ -614,7 +614,49 @@ void UpdateComponentWidget(engine::SceneWorld* pScene if (isOpen) { + ImGuiUtils::ImGuiEnumProperty("Render Mode", pParticleEmitterComponent->GetRenderMode()); + ImGuiUtils::ImGuiEnumProperty("Particle Type", pParticleEmitterComponent->GetEmitterParticleType()); + //ImGuiUtils::ImGuiEnumProperty("Emitter Shape", pParticleEmitterComponent->GetEmitterShape()); + ImGuiUtils::ImGuiVectorProperty("Emitter Range", pParticleEmitterComponent->GetEmitterShapeRange()); + ImGuiUtils::ImGuiBoolProperty("Random Emit Pos", pParticleEmitterComponent->GetRandomPosState()); + ImGuiUtils::ImGuiIntProperty("Max Count", pParticleEmitterComponent->GetSpawnCount(), cd::Unit::None, 1, 300); + ImGuiUtils::ImGuiVectorProperty("Velocity", pParticleEmitterComponent->GetEmitterVelocity()); + ImGuiUtils::ImGuiVectorProperty("Random Velocity", pParticleEmitterComponent->GetRandomVelocity()); + ImGuiUtils::ImGuiBoolProperty("RandomVelocity", pParticleEmitterComponent->GetRandomVelocityState()); + ImGuiUtils::ImGuiVectorProperty("Acceleration", pParticleEmitterComponent->GetEmitterAcceleration()); + ImGuiUtils::ColorPickerProperty("Color", pParticleEmitterComponent->GetEmitterColor()); + ImGuiUtils::ImGuiFloatProperty("LifeTime", pParticleEmitterComponent->GetLifeTime()); + if (ImGuiUtils::ImGuiBoolProperty("Instance State", pParticleEmitterComponent->GetInstanceState())) + { + pParticleEmitterComponent->ActivateShaderFeature(engine::ShaderFeature::PARTICLE_INSTANCE); + } + else + { + pParticleEmitterComponent->DeactivateShaderFeature(engine::ShaderFeature::PARTICLE_INSTANCE); + } + } + + ImGui::Separator(); + ImGui::PopStyleVar(); +} + +template<> +void UpdateComponentWidget(engine::SceneWorld* pSceneWorld, engine::Entity entity) +{ + auto* pParticleForceFieldComponent = pSceneWorld->GetParticleForceFieldComponent(entity); + if (!pParticleForceFieldComponent) + { + return; + } + bool isOpen = ImGui::CollapsingHeader("ParticleForceField Component", ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_DefaultOpen); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); + ImGui::Separator(); + + if (isOpen) + { + //ImGuiUtils::ImGuiVectorProperty("ForceFieldRange", pParticleForceFieldComponent->GetForceFieldRange()); + ImGuiUtils::ImGuiBoolProperty("RotationForceValue", pParticleForceFieldComponent->GetRotationForce()); } ImGui::Separator(); @@ -660,7 +702,6 @@ void Inspector::Update() } ImGui::BeginChild("Inspector"); - details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); @@ -669,7 +710,8 @@ void Inspector::Update() details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); - details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); + details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); + details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); details::UpdateComponentWidget(pSceneWorld, m_lastSelectedEntity); diff --git a/Engine/Source/Runtime/ECWorld/AllComponentsHeader.h b/Engine/Source/Runtime/ECWorld/AllComponentsHeader.h index 53f38877..7af83610 100644 --- a/Engine/Source/Runtime/ECWorld/AllComponentsHeader.h +++ b/Engine/Source/Runtime/ECWorld/AllComponentsHeader.h @@ -15,5 +15,5 @@ #include "ECWorld/StaticMeshComponent.h" #include "ECWorld/TerrainComponent.h" #include "ECWorld/TransformComponent.h" -#include "ECWorld/ParticleComponent.h" -#include "ECWorld/ParticleEmitterComponent.h" \ No newline at end of file +#include "ECWorld/ParticleEmitterComponent.h" +#include "ECWorld/ParticleForceFieldComponent.h" \ No newline at end of file diff --git a/Engine/Source/Runtime/ECWorld/ParticleComponent.cpp b/Engine/Source/Runtime/ECWorld/ParticleComponent.cpp deleted file mode 100644 index f7e32e89..00000000 --- a/Engine/Source/Runtime/ECWorld/ParticleComponent.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "ParticleComponent.h" diff --git a/Engine/Source/Runtime/ECWorld/ParticleComponent.h b/Engine/Source/Runtime/ECWorld/ParticleComponent.h deleted file mode 100644 index e2be6580..00000000 --- a/Engine/Source/Runtime/ECWorld/ParticleComponent.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Base/Template.h" -#include "Core/StringCrc.h" -#include "Math/Vector.hpp" - -namespace engine -{ - -class ParticleComponent final -{ -public: - static constexpr StringCrc GetClassName() - { - constexpr StringCrc className("ParticleComponent"); - return className; - } - - ParticleComponent() = default; - ParticleComponent(const ParticleComponent&) = default; - ParticleComponent& operator=(const ParticleComponent&) = default; - ParticleComponent(ParticleComponent&&) = default; - ParticleComponent& operator=(ParticleComponent&&) = default; - ~ParticleComponent() = default; - -private: - -}; - -} \ No newline at end of file diff --git a/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.cpp b/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.cpp index 5dcfb9de..32dc1ac7 100644 --- a/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.cpp +++ b/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.cpp @@ -1 +1,336 @@ +#include "Log/Log.h" #include "ParticleEmitterComponent.h" +#include "Rendering/Utility/VertexLayoutUtility.h" +#include "Utilities/MeshUtils.hpp" + +#include + +namespace engine +{ + +void ParticleEmitterComponent::Build() +{ + BuildParticleShape(); + bgfx::VertexLayout vertexLayout; + VertexLayoutUtility::CreateVertexLayout(vertexLayout, m_pRequiredVertexFormat->GetVertexAttributeLayouts()); + + if (m_pMeshData == nullptr) + { + PaddingVertexBuffer(); + PaddingIndexBuffer(); + + m_particleVertexBufferHandle = bgfx::createVertexBuffer(bgfx::makeRef(m_particleVertexBuffer.data(), static_cast(m_particleVertexBuffer.size())), vertexLayout).idx; + m_particleIndexBufferHandle = bgfx::createIndexBuffer(bgfx::makeRef(m_particleIndexBuffer.data(), static_cast(m_particleIndexBuffer.size())), 0U).idx; + } + else + { + m_particleVertexBuffer = cd::BuildVertexBufferForStaticMesh(*m_pMeshData, *m_pRequiredVertexFormat).value(); + for (const auto& optionalVec : cd::BuildIndexBufferesForMesh(*m_pMeshData)) + { + const std::vector& vec = optionalVec.value(); + m_particleIndexBuffer.insert(m_particleIndexBuffer.end(), vec.begin(), vec.end()); + } + m_particleVertexBufferHandle = bgfx::createVertexBuffer(bgfx::makeRef(m_particleVertexBuffer.data(), static_cast(m_particleVertexBuffer.size())), vertexLayout).idx; + m_particleIndexBufferHandle = bgfx::createIndexBuffer(bgfx::makeRef(m_particleIndexBuffer.data(), static_cast(m_particleIndexBuffer.size())), 0U).idx; + } +} + +void ParticleEmitterComponent::PaddingVertexBuffer() +{ + //m_particleVertexBuffer.clear(); + //m_particleVertexBuffer.insert(m_particleVertexBuffer.end(), m_particlePool.GetRenderDataBuffer().begin(), m_particlePool.GetRenderDataBuffer().end()); + m_particleVertexBuffer.clear(); + + const bool containsPosition = m_pRequiredVertexFormat->Contains(cd::VertexAttributeType::Position); + const bool containsColor = m_pRequiredVertexFormat->Contains(cd::VertexAttributeType::Color); + const bool containsUV = m_pRequiredVertexFormat->Contains(cd::VertexAttributeType::UV); + //vertexbuffer + if (m_emitterParticleType == ParticleType::Sprite) + { + constexpr int meshVertexCount = Particle::GetMeshVertexCount(); + const int MAX_VERTEX_COUNT = m_particlePool.GetParticleMaxCount() * meshVertexCount; + size_t vertexCount = MAX_VERTEX_COUNT; + const uint32_t vertexFormatStride = m_pRequiredVertexFormat->GetStride(); + + m_particleVertexBuffer.resize(vertexCount * vertexFormatStride); + + uint32_t currentDataSize = 0U; + auto currentDataPtr = m_particleVertexBuffer.data(); + + std::vector vertexDataBuffer; + vertexDataBuffer.resize(MAX_VERTEX_COUNT); + // pos color uv + // only a picture now + for (int i = 0; i < MAX_VERTEX_COUNT; i += meshVertexCount) + { + vertexDataBuffer[i] = { cd::Vec3f(-1.0f,-1.0f,0.0f),cd::Vec4f(1.0f,1.0f,1.0f,1.0f),cd::Vec2f(1.0f,1.0f) }; + vertexDataBuffer[i + 1] = { cd::Vec3f(1.0f,-1.0f,0.0f),cd::Vec4f(1.0f,1.0f,1.0f,1.0f),cd::Vec2f(0.0f,1.0f) }; + vertexDataBuffer[i + 2] = { cd::Vec3f(1.0f,1.0f,0.0f),cd::Vec4f(1.0f,1.0f,1.0f,1.0f),cd::Vec2f(0.0f,0.0f) }; + vertexDataBuffer[i + 3] = { cd::Vec3f(-1.0f,1.0f,0.0f),cd::Vec4f(1.0f,1.0f,1.0f,1.0f),cd::Vec2f(1.0f,0.0f) }; + } + + for (int i = 0; i < MAX_VERTEX_COUNT; ++i) + { + if (containsPosition) + { + std::memcpy(¤tDataPtr[currentDataSize], &vertexDataBuffer[i].pos, sizeof(cd::Point)); + currentDataSize += sizeof(cd::Point); + } + + if (containsColor) + { + std::memcpy(¤tDataPtr[currentDataSize], &vertexDataBuffer[i].color, sizeof(cd::Color)); + currentDataSize += sizeof(cd::Color); + } + + if (containsUV) + { + std::memcpy(¤tDataPtr[currentDataSize], &vertexDataBuffer[i].uv, sizeof(cd::UV)); + currentDataSize += sizeof(cd::UV); + } + } + } + else if (m_emitterParticleType == engine::ParticleType::Ribbon) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Track) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Ring) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Model) + { + + } +} + +void ParticleEmitterComponent::PaddingIndexBuffer() +{ + m_particleIndexBuffer.clear(); + if (m_emitterParticleType == engine::ParticleType::Sprite) + { + constexpr int meshVertexCount = Particle::GetMeshVertexCount(); + const bool useU16Index = meshVertexCount <= static_cast(std::numeric_limits::max()) + 1U; + const uint32_t indexTypeSize = useU16Index ? sizeof(uint16_t) : sizeof(uint32_t); + const int MAX_VERTEX_COUNT = m_particlePool.GetParticleMaxCount() * meshVertexCount; + int indexCountForOneSprite = 6; + const uint32_t indicesCount = MAX_VERTEX_COUNT / meshVertexCount * indexCountForOneSprite; + m_particleIndexBuffer.resize(indicesCount * indexTypeSize); + /// + /* size_t indexTypeSize = sizeof(uint16_t); + m_particleIndexBuffer.resize(m_particleSystem.GetMaxCount() / 4 * 6 * indexTypeSize);*/ + uint32_t currentDataSize = 0U; + auto currentDataPtr = m_particleIndexBuffer.data(); + + std::vector indexes; + for (uint16_t i = 0; i < MAX_VERTEX_COUNT; i += meshVertexCount) + { + uint16_t vertexIndex = static_cast(i); + indexes.push_back(vertexIndex); + indexes.push_back(vertexIndex + 1); + indexes.push_back(vertexIndex + 2); + indexes.push_back(vertexIndex); + indexes.push_back(vertexIndex + 2); + indexes.push_back(vertexIndex + 3); + } + + for (const auto& index : indexes) + { + std::memcpy(¤tDataPtr[currentDataSize], &index, indexTypeSize); + currentDataSize += static_cast(indexTypeSize); + } + } + else if (m_emitterParticleType == engine::ParticleType::Ribbon) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Track) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Ring) + { + + } + else if (m_emitterParticleType == engine::ParticleType::Model) + { + + } +} + +void ParticleEmitterComponent::BuildParticleShape() +{ + if (m_emitterShape == ParticleEmitterShape::Box) + { + cd::VertexFormat vertexFormat; + vertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::Position, cd::AttributeValueType::Float, 3); + + const uint32_t vertexCount = 8; + std::vector vertexArray + { + cd::Point{-1.0f, -1.0f, 1.0f}, + cd::Point{1.0f, -1.0f, 1.0f}, + cd::Point{1.0f, 1.0f, 1.0f}, + cd::Point{-1.0f, 1.0f, 1.0f}, + cd::Point{-1.0f, -1.0f, -1.0f}, + cd::Point{1.0f, -1.0f, -1.0f}, + cd::Point{1.0f, 1.0f, -1.0f}, + cd::Point{-1.0f, 1.0f, -1.0f}, + }; + m_emitterShapeVertexBuffer.resize(vertexCount * vertexFormat.GetStride()); + uint32_t currentDataSize = 0U; + auto currentDataPtr = m_emitterShapeVertexBuffer.data(); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) + { + //position + const cd::Point& position = vertexArray[vertexIndex]; + constexpr uint32_t posDataSize = cd::Point::Size * sizeof(cd::Point::ValueType); + std::memcpy(¤tDataPtr[currentDataSize], position.begin(), posDataSize); + currentDataSize += posDataSize; + } + + size_t indexTypeSize = sizeof(uint16_t); + m_emitterShapeIndexBuffer.resize(24 * indexTypeSize); + currentDataSize = 0U; + currentDataPtr = m_emitterShapeIndexBuffer.data(); + + std::vector indexes = + { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + }; + + for (const auto& index : indexes) + { + std::memcpy(¤tDataPtr[currentDataSize], &index, indexTypeSize); + currentDataSize += static_cast(indexTypeSize); + } + + bgfx::VertexLayout vertexLayout; + VertexLayoutUtility::CreateVertexLayout(vertexLayout, vertexFormat.GetVertexAttributeLayouts()); + m_emitterShapeVertexBufferHandle = bgfx::createVertexBuffer(bgfx::makeRef(m_emitterShapeVertexBuffer.data(), static_cast(m_emitterShapeVertexBuffer.size())), vertexLayout).idx; + m_emitterShapeIndexBufferHandle = bgfx::createIndexBuffer(bgfx::makeRef(m_emitterShapeIndexBuffer.data(), static_cast(m_emitterShapeIndexBuffer.size())), 0U).idx; + } +} + +void ParticleEmitterComponent::RePaddingShapeBuffer() +{ + if (m_emitterShape == ParticleEmitterShape::Box) + { + cd::VertexFormat vertexFormat; + vertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::Position, cd::AttributeValueType::Float, 3); + + const uint32_t vertexCount = 8; + std::vector vertexArray + { + cd::Point{-m_emitterShapeRange.x(), -m_emitterShapeRange.y(), m_emitterShapeRange.z()}, + cd::Point{m_emitterShapeRange.x(), -m_emitterShapeRange.y(), m_emitterShapeRange.z()}, + cd::Point{m_emitterShapeRange.x(), m_emitterShapeRange.y(), m_emitterShapeRange.z()}, + cd::Point{-m_emitterShapeRange.x(), m_emitterShapeRange.y(), m_emitterShapeRange.z()}, + cd::Point{-m_emitterShapeRange.x(), -m_emitterShapeRange.y(), -m_emitterShapeRange.z()}, + cd::Point{m_emitterShapeRange.x(), -m_emitterShapeRange.y(), -m_emitterShapeRange.z()}, + cd::Point{m_emitterShapeRange.x(), m_emitterShapeRange.y(), -m_emitterShapeRange.z()}, + cd::Point{-m_emitterShapeRange.x(), m_emitterShapeRange.y(), -m_emitterShapeRange.z()}, + }; + m_emitterShapeVertexBuffer.resize(vertexCount * vertexFormat.GetStride()); + uint32_t currentDataSize = 0U; + auto currentDataPtr = m_emitterShapeVertexBuffer.data(); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) + { + //position + const cd::Point& position = vertexArray[vertexIndex]; + constexpr uint32_t posDataSize = cd::Point::Size * sizeof(cd::Point::ValueType); + std::memcpy(¤tDataPtr[currentDataSize], position.begin(), posDataSize); + currentDataSize += posDataSize; + } + + size_t indexTypeSize = sizeof(uint16_t); + m_emitterShapeIndexBuffer.resize(24 * indexTypeSize); + currentDataSize = 0U; + currentDataPtr = m_emitterShapeIndexBuffer.data(); + + std::vector indexes = + { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + }; + + for (const auto& index : indexes) + { + std::memcpy(¤tDataPtr[currentDataSize], &index, indexTypeSize); + currentDataSize += static_cast(indexTypeSize); + } + } +} + +const std::string& ParticleEmitterComponent::GetShaderProgramName() const +{ + return m_pParticleMaterialType->GetShaderSchema().GetShaderProgramName(); +} + +void ParticleEmitterComponent::ActivateShaderFeature(ShaderFeature feature) +{ + if (ShaderFeature::DEFAULT == feature) + { + return; + } + + for (const auto& conflict : m_pParticleMaterialType->GetShaderSchema().GetConflictFeatureSet(feature)) + { + m_shaderFeatures.erase(conflict); + } + + m_shaderFeatures.insert(cd::MoveTemp(feature)); + + m_isShaderFeatureDirty = true; +} + +void ParticleEmitterComponent::DeactivateShaderFeature(ShaderFeature feature) +{ + m_shaderFeatures.erase(feature); + + m_isShaderFeatureDirty = true; +} + +const std::string& ParticleEmitterComponent::GetFeaturesCombine() +{ + if (m_isShaderFeatureDirty == false) + { + return m_featureCombine; + } + + m_featureCombine = m_pParticleMaterialType->GetShaderSchema().GetFeaturesCombine(m_shaderFeatures); + m_isShaderFeatureDirty = false; + + return m_featureCombine; +} + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.h b/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.h index b21634cd..51bc53c4 100644 --- a/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.h +++ b/Engine/Source/Runtime/ECWorld/ParticleEmitterComponent.h @@ -3,9 +3,28 @@ #include "Base/Template.h" #include "Core/StringCrc.h" #include "Math/Vector.hpp" +#include "Math/Transform.hpp" +#include "Material/ShaderSchema.h" +#include "Material/MaterialType.h" +#include "ParticleSystem/ParticlePool.h" +#include "Scene/Mesh.h" +#include "Scene/Types.h" +#include "Scene/VertexFormat.h" namespace engine { +enum class ParticleRenderMode +{ + Billboard, + Mesh, +}; + +enum class ParticleEmitterShape +{ + Sphere, + Hemisphere, + Box +}; class ParticleEmitterComponent final { @@ -23,8 +42,138 @@ class ParticleEmitterComponent final ParticleEmitterComponent& operator=(ParticleEmitterComponent&&) = default; ~ParticleEmitterComponent() = default; + ParticlePool& GetParticlePool() { return m_particlePool; } + + int& GetSpawnCount() { return m_spawnCount; } + void SetSpawnCount(int count) { m_spawnCount = count; } + + ParticleEmitterShape& GetEmitterShape() { return m_emitterShape; } + void SetEmitterShape(ParticleEmitterShape shape) { m_emitterShape = shape; } + + cd::Vec3f& GetEmitterShapeRange() { return m_emitterShapeRange; } + void SetEmitterShapeRange(cd::Vec3f range) { m_emitterShapeRange = range; } + + bool& GetInstanceState() { return m_useInstance; } + void SetInstanceState(bool state) { m_useInstance = state; } + + const engine::MaterialType* GetMaterialType() const { return m_pParticleMaterialType; } + void SetMaterialType(const engine::MaterialType* pMaterialType) { m_pParticleMaterialType = pMaterialType; } + + ParticleRenderMode& GetRenderMode() { return m_renderMode; } + void SetRenderMode(engine::ParticleRenderMode mode) { m_renderMode = mode; } + + ParticleType& GetEmitterParticleType() { return m_emitterParticleType; } + void SetEmitterParticleType(engine::ParticleType type) { m_emitterParticleType = type; } + + //random + bool& GetRandomPosState() { return m_randomPosState; } + cd::Vec3f& GetRandormPos() { return m_randomPos; } + void SetRandomPos(cd::Vec3f randomPos) { m_randomPos = randomPos; } + + bool& GetRandomVelocityState() { return m_randomVelocityState; } + cd::Vec3f& GetRandomVelocity() { return m_randomVelocity; } + void SetRandomVelocity(cd::Vec3f randomVelocity) { m_randomVelocity = randomVelocity; } + + //particle data + cd::Vec3f& GetEmitterVelocity() { return m_emitterVelocity; } + void SetEmitterVelocity(cd::Vec3f velocity) { m_emitterVelocity = velocity; } + + cd::Vec3f& GetEmitterAcceleration() { return m_emitterAcceleration; } + void SetEmitterAcceleration(cd::Vec3f accleration) { m_emitterAcceleration = accleration; } + + cd::Vec4f& GetEmitterColor() { return m_emitterColor; } + void SetEmitterColor(cd::Vec4f fillcolor) { m_emitterColor = fillcolor; } + + float& GetLifeTime() { return m_emitterLifeTime; } + void SetEmitterLifeTime(float lifetime) { m_emitterLifeTime = lifetime; } + + uint16_t& GetParticleVertexBufferHandle() { return m_particleVertexBufferHandle; } + uint16_t& GetParticleIndexBufferHandle() { return m_particleIndexBufferHandle; } + + std::vector& GetVertexBuffer() { return m_particleVertexBuffer; } + std::vector& GetIndexBuffer() { return m_particleIndexBuffer; } + + uint16_t& GetEmitterShapeVertexBufferHandle() { return m_emitterShapeVertexBufferHandle; } + uint16_t& GetEmitterShapeIndexBufferHandle() { return m_emitterShapeIndexBufferHandle; } + + std::vector& GetEmitterShapeVertexBuffer() { return m_emitterShapeVertexBuffer; } + std::vector& GetEmitterShapeIndexBuffer() { return m_emitterShapeIndexBuffer; } + + const cd::Mesh* GetMeshData() const { return m_pMeshData; } + void SetMeshData(const cd::Mesh* pMeshData) { m_pMeshData = pMeshData; } + + void Build(); + + void SetRequiredVertexFormat(const cd::VertexFormat* pVertexFormat) { m_pRequiredVertexFormat = pVertexFormat; } + + //void UpdateBuffer(); + void PaddingVertexBuffer(); + void PaddingIndexBuffer(); + + void BuildParticleShape(); + void RePaddingShapeBuffer(); + + // Uber shader data. + const std::string& GetShaderProgramName() const; + void ActivateShaderFeature(ShaderFeature feature); + void DeactivateShaderFeature(ShaderFeature feature); + void SetShaderFeatures(std::set options) { m_shaderFeatures = cd::MoveTemp(m_shaderFeatures); } + std::set& GetShaderFeatures() { return m_shaderFeatures; } + const std::set& GetShaderFeatures() const { return m_shaderFeatures; } + const std::string& GetFeaturesCombine(); + private: + //ParticleSystem m_particleSystem; + ParticlePool m_particlePool; + + engine::ParticleType m_emitterParticleType; + + //emitter data + int m_spawnCount = 75; + cd::Vec3f m_emitterVelocity {20.0f, 20.0f, 0.0f}; + cd::Vec3f m_emitterAcceleration; + cd::Vec4f m_emitterColor = cd::Vec4f::One(); + float m_emitterLifeTime = 6.0f; + + // random emitter data + bool m_randomPosState; + cd::Vec3f m_randomPos; + bool m_randomVelocityState; + cd::Vec3f m_randomVelocity; + + //instancing + bool m_useInstance = false; + + //Uber shader + const engine::MaterialType* m_pParticleMaterialType = nullptr; + bool m_isShaderFeatureDirty = false; + std::set m_shaderFeatures; + std::string m_featureCombine; + + //render mode mesh/billboard/ribbon + ParticleRenderMode m_renderMode = ParticleRenderMode::Mesh; + const cd::Mesh* m_pMeshData = nullptr; + + //particle vertex/index + struct VertexData + { + cd::Vec3f pos; + cd::Vec4f color; + cd::UV uv; + }; + const cd::VertexFormat* m_pRequiredVertexFormat = nullptr; + std::vector m_particleVertexBuffer; + std::vector m_particleIndexBuffer; + uint16_t m_particleVertexBufferHandle = UINT16_MAX; + uint16_t m_particleIndexBufferHandle = UINT16_MAX; + //emitter shape vertex/index + ParticleEmitterShape m_emitterShape = ParticleEmitterShape::Box; + cd::Vec3f m_emitterShapeRange {10.0f ,5.0f ,10.0f}; + std::vector m_emitterShapeVertexBuffer; + std::vector m_emitterShapeIndexBuffer; + uint16_t m_emitterShapeVertexBufferHandle = UINT16_MAX; + uint16_t m_emitterShapeIndexBufferHandle = UINT16_MAX; }; } \ No newline at end of file diff --git a/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.cpp b/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.cpp new file mode 100644 index 00000000..8d70bf43 --- /dev/null +++ b/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.cpp @@ -0,0 +1,77 @@ +#include "ParticleForceFieldComponent.h" +#include "Rendering/Utility/VertexLayoutUtility.h" +#include "Scene/Types.h" +#include "Scene/VertexFormat.h" + +namespace engine +{ +void ParticleForceFieldComponent::Build() +{ + cd::VertexFormat vertexFormat; + vertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::Position, cd::AttributeValueType::Float, 3); + + const uint32_t vertexCount = 8; + std::vector vertexArray + { + cd::Point{-m_forcefieldRange.x(), -m_forcefieldRange.y(), m_forcefieldRange.z()}, + cd::Point{m_forcefieldRange.x(), -m_forcefieldRange.y(), m_forcefieldRange.z()}, + cd::Point{m_forcefieldRange.x(), m_forcefieldRange.y(), m_forcefieldRange.z()}, + cd::Point{-m_forcefieldRange.x(), m_forcefieldRange.y(), m_forcefieldRange.z()}, + cd::Point{-m_forcefieldRange.x(), -m_forcefieldRange.y(), -m_forcefieldRange.z()}, + cd::Point{m_forcefieldRange.x(), -m_forcefieldRange.y(), -m_forcefieldRange.z()}, + cd::Point{m_forcefieldRange.x(), m_forcefieldRange.y(), -m_forcefieldRange.z()}, + cd::Point{-m_forcefieldRange.x(), m_forcefieldRange.y(), -m_forcefieldRange.z()}, + }; + m_vertexBuffer.resize(vertexCount * vertexFormat.GetStride()); + uint32_t currentDataSize = 0U; + auto currentDataPtr = m_vertexBuffer.data(); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) + { + //position + const cd::Point& position = vertexArray[vertexIndex]; + constexpr uint32_t posDataSize = cd::Point::Size * sizeof(cd::Point::ValueType); + std::memcpy(¤tDataPtr[currentDataSize], position.begin(), posDataSize); + currentDataSize += posDataSize; + } + + size_t indexTypeSize = sizeof(uint16_t); + m_indexBuffer.resize(24 * indexTypeSize); + currentDataSize = 0U; + currentDataPtr = m_indexBuffer.data(); + + std::vector indexes = + { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + }; + + for (const auto& index : indexes) + { + std::memcpy(¤tDataPtr[currentDataSize], &index, indexTypeSize); + currentDataSize += static_cast(indexTypeSize); + } + + bgfx::VertexLayout vertexLayout; + VertexLayoutUtility::CreateVertexLayout(vertexLayout, vertexFormat.GetVertexAttributeLayouts()); + m_vertexBufferHandle = bgfx::createVertexBuffer(bgfx::makeRef(m_vertexBuffer.data(), static_cast(m_vertexBuffer.size())), vertexLayout).idx; + m_indexBufferHandle = bgfx::createIndexBuffer(bgfx::makeRef(m_indexBuffer.data(), static_cast(m_indexBuffer.size())), 0U).idx; +} + +bool ParticleForceFieldComponent::IfWithinTheRange(ForceFieldType type, float m_startRange, float m_endRange) +{ + return false; +} + +} diff --git a/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.h b/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.h new file mode 100644 index 00000000..e1e0a78e --- /dev/null +++ b/Engine/Source/Runtime/ECWorld/ParticleForceFieldComponent.h @@ -0,0 +1,79 @@ +#pragma once + +#include "Base/Template.h" +#include "Core/StringCrc.h" +#include "Math/Vector.hpp" + +#include +namespace engine +{ +enum ForceFieldType +{ + ForceFieldSphere, + ForceFieldHemisphere, + ForceFieldCylinder, + ForceFieldBox +}; + +class ParticleForceFieldComponent final +{ +public: + static constexpr StringCrc GetClassName() + { + constexpr StringCrc className("ParticleForceFieldComponent"); + return className; + } + + ParticleForceFieldComponent() = default; + ParticleForceFieldComponent(const ParticleForceFieldComponent&) = default; + ParticleForceFieldComponent& operator=(const ParticleForceFieldComponent&) = default; + ParticleForceFieldComponent(ParticleForceFieldComponent&&) = default; + ParticleForceFieldComponent& operator=(ParticleForceFieldComponent&&) = default; + ~ParticleForceFieldComponent() = default; + + cd::Vec3f& GetForceFieldRange() { return m_forcefieldRange; } + void SetForceFieldRange(cd::Vec3f range) { m_forcefieldRange = range; } + + bool& GetRotationForce() { return m_rotationForce; } + void SetRotationForce(bool force) { m_rotationForce = force; } + + uint16_t& GetVertexBufferHandle() { return m_vertexBufferHandle; } + uint16_t& GetIndexBufferHandle() { return m_indexBufferHandle; } + + std::vector& GetVertexBuffer() { return m_vertexBuffer; } + std::vector& GetIndexBuffer() { return m_indexBuffer; } + + void Build(); + + bool IfWithinTheRange(ForceFieldType type, float m_startRange, float m_endRange); + +private: + ForceFieldType m_foceFieldType = ForceFieldType::ForceFieldBox; + //size + cd::Vec3f m_forcefieldRange{3.0f, 3.0f,3.0f}; + + cd::Vec3f m_direction; + + float m_gravityStrength; + float m_gravityFocus; + + bool m_rotationForce = false; + float m_rotationSpeed; + float m_rotationAttraction; + float m_rotationRandomness; + + float m_dragStrength; + bool m_dragMultiplyBySize; + bool m_dragMultiplyByVelocity; + + //TODO: Vector Field + + std::vector m_vertexBuffer; + std::vector m_indexBuffer; + uint16_t m_vertexBufferHandle = UINT16_MAX; + uint16_t m_indexBufferHandle = UINT16_MAX; + + +}; + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ECWorld/SceneWorld.cpp b/Engine/Source/Runtime/ECWorld/SceneWorld.cpp index 556049ea..54cb4aae 100644 --- a/Engine/Source/Runtime/ECWorld/SceneWorld.cpp +++ b/Engine/Source/Runtime/ECWorld/SceneWorld.cpp @@ -35,8 +35,8 @@ SceneWorld::SceneWorld() m_pNameComponentStorage = m_pWorld->Register(); m_pSkyComponentStorage = m_pWorld->Register(); m_pStaticMeshComponentStorage = m_pWorld->Register(); - m_pParticleComponentStorage = m_pWorld->Register(); m_pParticleEmitterComponentStorage = m_pWorld->Register(); + m_pParticleForceFieldComponentStorage = m_pWorld->Register(); m_pTerrainComponentStorage = m_pWorld->Register(); m_pTransformComponentStorage = m_pWorld->Register(); @@ -112,6 +112,24 @@ void SceneWorld::CreateTerrainMaterialType(std::string shaderProgramName) m_pTerrainMaterialType->SetRequiredVertexFormat(cd::MoveTemp(terrainVertexFormat)); } +void SceneWorld::CreateParticleMaterialType(std::string shaderProgramName) +{ + m_pParticleMaterialType = std::make_unique(); + m_pParticleMaterialType->SetMaterialName("CD_Particle"); + + ShaderSchema shaderSchema; + shaderSchema.SetShaderProgramName(cd::MoveTemp(shaderProgramName)); + shaderSchema.AddFeatureSet({ ShaderFeature::PARTICLE_INSTANCE }); + shaderSchema.Build(); + m_pParticleMaterialType->SetShaderSchema(cd::MoveTemp(shaderSchema)); + + cd::VertexFormat particleVertexFormat; + particleVertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::Position, cd::GetAttributeValueType(), cd::Point::Size); + particleVertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::Color, cd::GetAttributeValueType(), cd::Color::Size); + particleVertexFormat.AddVertexAttributeLayout(cd::VertexAttributeType::UV, cd::GetAttributeValueType(), cd::UV::Size); + m_pParticleMaterialType->SetRequiredVertexFormat(cd::MoveTemp(particleVertexFormat)); +} + #ifdef ENABLE_DDGI void SceneWorld::CreateDDGIMaterialType(std::string shaderProgramName) { diff --git a/Engine/Source/Runtime/ECWorld/SceneWorld.h b/Engine/Source/Runtime/ECWorld/SceneWorld.h index 8e6531ea..7ee6025c 100644 --- a/Engine/Source/Runtime/ECWorld/SceneWorld.h +++ b/Engine/Source/Runtime/ECWorld/SceneWorld.h @@ -40,8 +40,8 @@ class SceneWorld DEFINE_COMPONENT_STORAGE_WITH_APIS(Name); DEFINE_COMPONENT_STORAGE_WITH_APIS(Sky); DEFINE_COMPONENT_STORAGE_WITH_APIS(StaticMesh); - DEFINE_COMPONENT_STORAGE_WITH_APIS(Particle); DEFINE_COMPONENT_STORAGE_WITH_APIS(ParticleEmitter); + DEFINE_COMPONENT_STORAGE_WITH_APIS(ParticleForceField); DEFINE_COMPONENT_STORAGE_WITH_APIS(Terrain); DEFINE_COMPONENT_STORAGE_WITH_APIS(Transform); @@ -98,8 +98,8 @@ class SceneWorld DeleteNameComponent(entity); DeleteSkyComponent(entity); DeleteStaticMeshComponent(entity); - DeleteParticleComponent(entity); DeleteParticleEmitterComponent(entity); + DeleteParticleForceFieldComponent(entity); DeleteTerrainComponent(entity); DeleteTransformComponent(entity); } @@ -113,6 +113,9 @@ class SceneWorld void CreateTerrainMaterialType(std::string shaderProgramName); CD_FORCEINLINE engine::MaterialType* GetTerrainMaterialType() const { return m_pTerrainMaterialType.get(); } + void CreateParticleMaterialType(std::string shaderProgramName); + CD_FORCEINLINE engine::MaterialType* GetParticleMaterialType() const { return m_pParticleMaterialType.get(); } + #ifdef ENABLE_DDGI void CreateDDGIMaterialType(std::string shaderProgramName); CD_FORCEINLINE engine::MaterialType* GetDDGIMaterialType() const { return m_pDDGIMaterialType.get(); } @@ -136,6 +139,7 @@ class SceneWorld std::unique_ptr m_pAnimationMaterialType; std::unique_ptr m_pTerrainMaterialType; std::unique_ptr m_pDDGIMaterialType; + std::unique_ptr m_pParticleMaterialType; // TODO : wrap them into another class? engine::Entity m_selectedEntity = engine::INVALID_ENTITY; diff --git a/Engine/Source/Runtime/ImGui/ImGuiUtils.hpp b/Engine/Source/Runtime/ImGui/ImGuiUtils.hpp index cbdc4ef7..fe0fd95b 100644 --- a/Engine/Source/Runtime/ImGui/ImGuiUtils.hpp +++ b/Engine/Source/Runtime/ImGui/ImGuiUtils.hpp @@ -378,4 +378,4 @@ static void ColorPickerProperty(const char* pName, T& color) ImGui::PopID(); } -} +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ParticleSystem/Particle.cpp b/Engine/Source/Runtime/ParticleSystem/Particle.cpp new file mode 100644 index 00000000..ce629c62 --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/Particle.cpp @@ -0,0 +1,51 @@ +#include "Particle.h" + +namespace engine +{ + +void Particle::Reset() +{ + m_particlePos = cd::Vec3f::Zero(); + m_particleSpeed = cd::Vec3f::Zero(); + m_particleSpeed = cd::Vec3f::Zero(); + + m_isActive = false; + m_currentTime = 0.0f; + m_lifeTime = 6.0f; + + m_particleColor = cd::Vec4f::One(); +} + +void Particle::Update(float deltaTime) +{ + if (m_currentTime >= m_lifeTime) + { + m_isActive = false; + return; + } + + m_particlePos.x() = m_particlePos.x() + m_particleSpeed.x() * deltaTime + 0.5f * m_particleAcceleration.x() * deltaTime * deltaTime; + m_particlePos.y() = m_particlePos.y() + m_particleSpeed.y() * deltaTime + 0.5f * m_particleAcceleration.y() * deltaTime * deltaTime; + m_particlePos.z() = m_particlePos.z() + m_particleSpeed.z() * deltaTime + 0.5f * m_particleAcceleration.z() * deltaTime * deltaTime; + + + m_particleSpeed.x() += m_particleAcceleration.x() * deltaTime; + m_particleSpeed.y() += m_particleAcceleration.y() * deltaTime; + m_particleSpeed.z() += m_particleAcceleration.z() * deltaTime; + + if (m_rotationForceField) + { + if ((m_particlePos.x() < m_rotationForceFieldRange.x() && m_particlePos.x() > -m_rotationForceFieldRange.x()) && + (m_particlePos.y() < m_rotationForceFieldRange.y() && m_particlePos.y() > -m_rotationForceFieldRange.y()) && + (m_particlePos.z() < m_rotationForceFieldRange.z() && m_particlePos.z() > -m_rotationForceFieldRange.z())) + { + cd::Vec3f zForward{0.0f, 0.0f, 1.0f}; + cd::Vec3f CentripetalV = zForward.Cross(m_particleSpeed); + m_particleAcceleration -= CentripetalV*0.5; + } + } + + m_currentTime += deltaTime; +} + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ParticleSystem/Particle.h b/Engine/Source/Runtime/ParticleSystem/Particle.h new file mode 100644 index 00000000..54f7c50f --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/Particle.h @@ -0,0 +1,92 @@ +#pragma once + +#include "Base/Template.h" +#include "Core/StringCrc.h" +#include "Math/Vector.hpp" +#include "Scene/Types.h" + +#include + +namespace engine +{ + +enum class ParticleType +{ + Sprite, + Ribbon, + Track, + Ring, + Model +}; + +class Particle final +{ +public: + template + static constexpr int GetMeshVertexCount() + { + if constexpr (ParticleType::Sprite == PT) + { + return 4; + } + else if constexpr (ParticleType::Ribbon == PT) + { + return 2; + } + else if constexpr (ParticleType::Track == PT) + { + return 3; + } + else if constexpr (ParticleType::Ring == PT) + { + return 8; + } + + return 3; + } + +public: + Particle() { Reset(); } + Particle(const Particle&) = default; + Particle& operator=(const Particle&) = default; + Particle(Particle&&) = default; + Particle& operator=(Particle&&) = default; + ~Particle() = default; + + cd::Vec3f& GetPos() { return m_particlePos; } + void SetPos(cd::Vec3f pos) { m_particlePos = pos; } + + cd::Vec3f GetSpeed() { return m_particleSpeed; } + void SetSpeed(cd::Vec3f speed) { m_particleSpeed = speed; } + + void SetAcceleration(cd::Vec3f acceleration) { m_particleAcceleration = acceleration; } + void SetColor(cd::Vec4f color) { m_particleColor = color; } + + void SetLifeTime(float lifeTime) { m_lifeTime = lifeTime; } + + void SetRotationForceField(bool value) { m_rotationForceField = value; } + void SetRotationForceFieldRange(cd::Vec3f range) { m_rotationForceFieldRange = range; } + + void Active() { m_isActive = true; } + bool isActive() const { return m_isActive; } + + void Reset(); + + void Update(float deltaTime); + +private: + cd::Vec3f m_particlePos; + cd::Vec3f m_particleSpeed; + cd::Vec3f m_particleAcceleration; + + bool m_rotationForceField = false; + cd::Vec3f m_rotationForceFieldRange; + + bool m_isActive; + float m_currentTime; + float m_lifeTime; + + cd::Color m_particleColor; +}; + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ParticleSystem/ParticlePool.cpp b/Engine/Source/Runtime/ParticleSystem/ParticlePool.cpp new file mode 100644 index 00000000..e75f4703 --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/ParticlePool.cpp @@ -0,0 +1,71 @@ +#include "ParticlePool.h" + +namespace engine +{ + +int ParticlePool::AllocateParticleIndex() +{ + int particleIndex = -1; + if (!m_freeParticleIndexes.empty()) + { + int index = m_freeParticleIndexes.back(); + m_freeParticleIndexes.pop_back(); + particleIndex = index; + } + else + { + ++m_currentParticleCount; + if (m_currentParticleCount >= m_maxParticleCount) + { + m_currentParticleCount = 0; + } + + if (m_particles[m_currentParticleCount].isActive()) + { + particleIndex = -1; + } + else + { + particleIndex = m_currentParticleCount; + } + } + + if (particleIndex != -1) + { + m_particles[particleIndex].Reset(); + m_particles[particleIndex].Active(); + } + + return particleIndex; +} + +void ParticlePool::Update(float deltaTime) +{ + m_currentActiveCount = 0; + for (int i = 0; i < m_maxParticleCount; ++i) + { + if (!m_particles[i].isActive()) + { + continue; + } + + m_particles[i].Update(deltaTime); + if (!m_particles[i].isActive()) + { + m_freeParticleIndexes.push_back(i); + } + else + { + ++m_currentActiveCount; + } + } +} + +void ParticlePool::AllParticlesReset() +{ + m_particles.resize(m_maxParticleCount); + m_freeParticleIndexes.clear(); + m_freeParticleIndexes.reserve(m_maxParticleCount); +} + +} diff --git a/Engine/Source/Runtime/ParticleSystem/ParticlePool.h b/Engine/Source/Runtime/ParticleSystem/ParticlePool.h new file mode 100644 index 00000000..54bc1657 --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/ParticlePool.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Base/Template.h" +#include "Core/StringCrc.h" +#include "Math/Vector.hpp" + +#include + +#include "Particle.h" + +namespace engine +{ + +class ParticlePool final +{ +public: + ParticlePool() = default; + ParticlePool(const ParticlePool&) = default; + ParticlePool& operator=(const ParticlePool&) = default; + ParticlePool(ParticlePool&&) = default; + ParticlePool& operator=(ParticlePool&&) = default; + ~ParticlePool() = default; + + int AllocateParticleIndex(); + Particle& GetParticle(int index) { return m_particles[index]; } + int GetParticleCount() { return m_currentActiveCount; } + int& GetParticleMaxCount() { return m_maxParticleCount; } + void SetParticleMaxCount(int count) { m_maxParticleCount = count; } + + void Update(float deltaTime); + void AllParticlesReset(); + +private: + int m_maxParticleCount = 75; + int m_currentActiveCount = 0; + int m_currentParticleCount = 0; + std::vector m_particles; + std::vector m_freeParticleIndexes; +}; + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/ParticleSystem/ParticleUtil.cpp b/Engine/Source/Runtime/ParticleSystem/ParticleUtil.cpp new file mode 100644 index 00000000..60cc1554 --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/ParticleUtil.cpp @@ -0,0 +1,30 @@ +#include "ParticleUtil.h" +#include + +namespace engine +{ +// Catmull-Rom Spline + cd::Vec3f GetRibbonPoint(std::vector points ,float t) + { + int p0, p1, p2, p3; + p1 = (int)t + 1; + p2 = p1 + 1; + p3 = p2 + 1; + p0 = p1 - 1; + + float tt = t * t; + float ttt = tt * t; + + float q1 = -ttt + 2.0f * tt - t; + float q2 = 3.0f * ttt - 5.0f * tt + 2.0f; + float q3 = -3.0f * ttt + 4.0f * tt + t; + float q4 = ttt - tt; + + float tx = 0.5f * (points[p0].x() * q1 + points[p1].x() * q2 + points[p2].x() * q3 + points[p3].x() * q4); + float ty = 0.5f * (points[p0].y() * q1 + points[p1].y() * q2 + points[p2].y() * q3 + points[p3].y() * q4); + float tz = 0.5f * (points[p0].z() * q1 + points[p1].z() * q2 + points[p2].z() * q3 + points[p3].z() * q4); + + return cd::Vec3f(tx, ty, tz); + } + +} diff --git a/Engine/Source/Runtime/ParticleSystem/ParticleUtil.h b/Engine/Source/Runtime/ParticleSystem/ParticleUtil.h new file mode 100644 index 00000000..552f70bc --- /dev/null +++ b/Engine/Source/Runtime/ParticleSystem/ParticleUtil.h @@ -0,0 +1,12 @@ +#include "Base/Template.h" +#include "Core/StringCrc.h" +#include "Math/Vector.hpp" + +#include + +namespace engine +{ +// Catmull-Rom Spline + cd::Vec3f GetRibbonPoint(std::vector points, float t); + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.cpp b/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.cpp new file mode 100644 index 00000000..2cfc3199 --- /dev/null +++ b/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.cpp @@ -0,0 +1,51 @@ +#include "ParticleForceFieldRenderer.h" + +#include "ECWorld/CameraComponent.h" +#include "ECWorld/SceneWorld.h" +#include "ECWorld/TransformComponent.h" +#include "Rendering/RenderContext.h" + +namespace engine +{ + +void ParticleForceFieldRenderer::Init() +{ + constexpr StringCrc programCrc = StringCrc("ParticleForceFieldProgram"); + GetRenderContext()->RegisterShaderProgram(programCrc, { "vs_particleforcefield", "fs_particleforcefield" }); + + bgfx::setViewName(GetViewID(), "ParticleForceFieldRenderer"); +} + +void ParticleForceFieldRenderer::Warmup() +{ + GetRenderContext()->UploadShaderProgram("ParticleForceFieldProgram"); +} + +void ParticleForceFieldRenderer::UpdateView(const float* pViewMatrix, const float* pProjectionMatrix) +{ + UpdateViewRenderTarget(); + bgfx::setViewTransform(GetViewID(), pViewMatrix, pProjectionMatrix); +} + +void ParticleForceFieldRenderer::Render(float deltaTime) +{ + for (Entity entity : m_pCurrentSceneWorld->GetParticleForceFieldEntities()) + { + if (auto* pTransformComponent = m_pCurrentSceneWorld->GetTransformComponent(entity)) + { + pTransformComponent->Build(); + bgfx::setTransform(pTransformComponent->GetWorldMatrix().begin()); + } + auto* pParticleForceFieldComponent = m_pCurrentSceneWorld->GetParticleForceFieldComponent(entity); + bgfx::setVertexBuffer(0, bgfx::VertexBufferHandle{ pParticleForceFieldComponent->GetVertexBufferHandle() }); + bgfx::setIndexBuffer(bgfx::IndexBufferHandle{ pParticleForceFieldComponent->GetIndexBufferHandle() }); + + constexpr uint64_t state = BGFX_STATE_WRITE_MASK | BGFX_STATE_MSAA | BGFX_STATE_DEPTH_TEST_LESS | + BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_PT_LINES; + bgfx::setState(state); + + GetRenderContext()->Submit(GetViewID(), "ParticleForceFieldProgram"); + } +} + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.h b/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.h new file mode 100644 index 00000000..2b021bfc --- /dev/null +++ b/Engine/Source/Runtime/Rendering/ParticleForceFieldRenderer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Renderer.h" + +namespace engine +{ + +class SceneWorld; + +class ParticleForceFieldRenderer final : public Renderer +{ +public: + using Renderer::Renderer; + + virtual void Init() override; + virtual void Warmup() override; + virtual void UpdateView(const float* pViewMatrix, const float* pProjectionMatrix) override; + virtual void Render(float deltaTime) override; + + void SetSceneWorld(SceneWorld* pSceneWorld) { m_pCurrentSceneWorld = pSceneWorld; } + +private: + SceneWorld* m_pCurrentSceneWorld = nullptr; +}; + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ParticleRenderer.cpp b/Engine/Source/Runtime/Rendering/ParticleRenderer.cpp index c0c42474..ed72070b 100644 --- a/Engine/Source/Runtime/Rendering/ParticleRenderer.cpp +++ b/Engine/Source/Runtime/Rendering/ParticleRenderer.cpp @@ -1,20 +1,57 @@ +#include "Log/Log.h" #include "ParticleRenderer.h" - #include "ECWorld/CameraComponent.h" #include "ECWorld/SceneWorld.h" +#include "ECWorld/ParticleForceFieldComponent.h" #include "ECWorld/TransformComponent.h" #include "Rendering/RenderContext.h" namespace engine { +namespace +{ +constexpr const char* particlePos = "u_particlePos"; +constexpr const char* particleScale = "u_particleScale"; +constexpr const char* shapeRange = "u_shapeRange"; +constexpr const char* particleColor = "u_particleColor"; + +uint64_t state_tristrip = BGFX_STATE_WRITE_MASK | BGFX_STATE_MSAA | BGFX_STATE_DEPTH_TEST_LESS | +BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_PT_TRISTRIP; + +uint64_t state_lines = BGFX_STATE_WRITE_MASK | BGFX_STATE_MSAA | BGFX_STATE_DEPTH_TEST_LESS | +BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_PT_LINES; + +constexpr const char* ParticleProgram = "ParticleProgram"; +constexpr const char* ParticleEmitterShapeProgram = "ParticleEmitterShapeProgram"; +constexpr const char* WO_BillboardParticleProgram = "WO_BillboardParticleProgram"; + +constexpr StringCrc ParticleProgramCrc = StringCrc{ "ParticleProgram" }; +constexpr StringCrc ParticleEmitterShapeProgramCrc = StringCrc{ "ParticleEmitterShapeProgram" }; +constexpr StringCrc WO_BillboardParticleProgramCrc = StringCrc{ "WO_BillboardParticleProgram" }; +} + void ParticleRenderer::Init() { + GetRenderContext()->RegisterShaderProgram(ParticleProgramCrc, { "vs_particle", "fs_particle" }); + GetRenderContext()->RegisterShaderProgram(ParticleEmitterShapeProgramCrc, {"vs_particleEmitterShape", "fs_particleEmitterShape"}); + GetRenderContext()->RegisterShaderProgram(WO_BillboardParticleProgramCrc, { "vs_wo_billboardparticle","fs_wo_billboardparticle" }); + bgfx::setViewName(GetViewID(), "ParticleRenderer"); } void ParticleRenderer::Warmup() { + constexpr const char* particleTexture = "Textures/textures/Particle.png"; + m_particleTextureHandle = GetRenderContext()->CreateTexture(particleTexture); + GetRenderContext()->CreateUniform("s_texColor", bgfx::UniformType::Sampler); + GetRenderContext()->CreateUniform(particlePos, bgfx::UniformType::Vec4, 1); + GetRenderContext()->CreateUniform(particleScale, bgfx::UniformType::Vec4, 1); + GetRenderContext()->CreateUniform(shapeRange, bgfx::UniformType::Vec4, 1); + GetRenderContext()->CreateUniform(particleColor, bgfx::UniformType::Vec4, 1); + GetRenderContext()->UploadShaderProgram(ParticleProgram); + GetRenderContext()->UploadShaderProgram(ParticleEmitterShapeProgram); + GetRenderContext()->UploadShaderProgram(WO_BillboardParticleProgram); } void ParticleRenderer::UpdateView(const float* pViewMatrix, const float* pProjectionMatrix) @@ -25,7 +62,182 @@ void ParticleRenderer::UpdateView(const float* pViewMatrix, const float* pProjec void ParticleRenderer::Render(float deltaTime) { - const cd::Transform& cameraTransform = m_pCurrentSceneWorld->GetTransformComponent(m_pCurrentSceneWorld->GetMainCameraEntity())->GetTransform(); + for (Entity entity : m_pCurrentSceneWorld->GetParticleForceFieldEntities()) + { + ParticleForceFieldComponent* pForceFieldComponent = m_pCurrentSceneWorld->GetParticleForceFieldComponent(entity); + const cd::Transform& forcefieldTransform = m_pCurrentSceneWorld->GetTransformComponent(entity)->GetTransform(); + SetForceFieldRotationForce(pForceFieldComponent); + SetForceFieldRange(pForceFieldComponent, forcefieldTransform.GetScale()); + } + + Entity pMainCameraEntity = m_pCurrentSceneWorld->GetMainCameraEntity(); + for (Entity entity : m_pCurrentSceneWorld->GetParticleEmitterEntities()) + { + const cd::Transform& particleTransform = m_pCurrentSceneWorld->GetTransformComponent(entity)->GetTransform(); + const cd::Quaternion& particleRotation = m_pCurrentSceneWorld->GetTransformComponent(entity)->GetTransform().GetRotation(); + ParticleEmitterComponent* pEmitterComponent = m_pCurrentSceneWorld->GetParticleEmitterComponent(entity); + + const cd::Transform& pMainCameraTransform = m_pCurrentSceneWorld->GetTransformComponent(pMainCameraEntity)->GetTransform(); + //const cd::Quaternion& cameraRotation = pMainCameraTransform.GetRotation(); + //Not include particle attribute + pEmitterComponent->GetParticlePool().SetParticleMaxCount(pEmitterComponent->GetSpawnCount()); + pEmitterComponent->GetParticlePool().AllParticlesReset(); + int particleIndex = pEmitterComponent->GetParticlePool().AllocateParticleIndex(); + + //Random value + cd::Vec3f randomPos(getRandomValue(-pEmitterComponent->GetEmitterShapeRange().x(), pEmitterComponent->GetEmitterShapeRange().x()), + getRandomValue(-pEmitterComponent->GetEmitterShapeRange().y(), pEmitterComponent->GetEmitterShapeRange().y()), + getRandomValue(-pEmitterComponent->GetEmitterShapeRange().z(), pEmitterComponent->GetEmitterShapeRange().z())); + cd::Vec3f randomVelocity(getRandomValue(-pEmitterComponent->GetRandomVelocity().x(), pEmitterComponent->GetRandomVelocity().x()), + getRandomValue(-pEmitterComponent->GetRandomVelocity().y(), pEmitterComponent->GetRandomVelocity().y()), + getRandomValue(-pEmitterComponent->GetRandomVelocity().z(), pEmitterComponent->GetRandomVelocity().z())); + pEmitterComponent->SetRandomPos(randomPos); + + //particle + if (particleIndex != -1) + { + Particle& particle = pEmitterComponent->GetParticlePool().GetParticle(particleIndex); + if (pEmitterComponent->GetRandomPosState()) + { + particle.SetPos(particleTransform.GetTranslation()+ pEmitterComponent->GetRandormPos()); + } + else + { + particle.SetPos(particleTransform.GetTranslation()); + } + if (pEmitterComponent->GetRandomVelocityState()) + { + particle.SetSpeed(pEmitterComponent->GetEmitterVelocity()+ randomVelocity); + } + else + { + particle.SetSpeed(pEmitterComponent->GetEmitterVelocity()); + } + particle.SetRotationForceField(m_forcefieldRotationFoce); + particle.SetRotationForceFieldRange(m_forcefieldRange); + particle.SetAcceleration(pEmitterComponent->GetEmitterAcceleration()); + particle.SetColor(pEmitterComponent->GetEmitterColor()); + particle.SetLifeTime(pEmitterComponent->GetLifeTime()); + } + + pEmitterComponent->GetParticlePool().Update(1.0f/60.0f); + + if (pEmitterComponent->GetInstanceState()) + { + //Particle Emitter Instance + const uint16_t instanceStride = 80; + // to total number of instances to draw + uint32_t totalSprites; + totalSprites = pEmitterComponent->GetParticlePool().GetParticleMaxCount(); + uint32_t drawnSprites = bgfx::getAvailInstanceDataBuffer(totalSprites, instanceStride); + + bgfx::InstanceDataBuffer idb; + bgfx::allocInstanceDataBuffer(&idb, drawnSprites, instanceStride); + + uint8_t* data = idb.data; + for (uint32_t ii = 0; ii < drawnSprites; ++ii) + { + float* mtx = (float*)data; + bx::mtxSRT(mtx, particleTransform.GetScale().x(), particleTransform.GetScale().y(), particleTransform.GetScale().z(), + particleRotation.Pitch(), particleRotation.Yaw(), particleRotation.Roll(), + pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().x(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().y(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().z()); + + float* color = (float*)&data[64]; + color[0] = pEmitterComponent->GetEmitterColor().x(); + color[1] = pEmitterComponent->GetEmitterColor().y(); + color[2] = pEmitterComponent->GetEmitterColor().z(); + color[3] = pEmitterComponent->GetEmitterColor().w(); + + data += instanceStride; + } + + //Billboard particlePos particleScale + constexpr StringCrc particlePosCrc(particlePos); + bgfx::setUniform(GetRenderContext()->GetUniform(particlePosCrc), &particleTransform.GetTranslation(), 1); + constexpr StringCrc ParticleScaleCrc(particleScale); + bgfx::setUniform(GetRenderContext()->GetUniform(ParticleScaleCrc), &particleTransform.GetScale(), 1); + + constexpr StringCrc ParticleSampler("s_texColor"); + bgfx::setTexture(0, GetRenderContext()->GetUniform(ParticleSampler), m_particleTextureHandle); + bgfx::setVertexBuffer(0, bgfx::VertexBufferHandle{ pEmitterComponent->GetParticleVertexBufferHandle() }); + bgfx::setIndexBuffer(bgfx::IndexBufferHandle{ pEmitterComponent->GetParticleIndexBufferHandle() }); + + + bgfx::setInstanceDataBuffer(&idb); + + bgfx::setState(state_tristrip); + + if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Mesh) + { + GetRenderContext()->Submit(GetViewID(), ParticleProgram); + } + else if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Billboard) + { + GetRenderContext()->Submit(GetViewID(), WO_BillboardParticleProgram); + } + } + else + { + constexpr StringCrc particleColorCrc(particleColor); + bgfx::setUniform(GetRenderContext()->GetUniform(particleColorCrc), &pEmitterComponent->GetEmitterColor(), 1); + + uint32_t drawnSprites = pEmitterComponent->GetParticlePool().GetParticleMaxCount(); + for (uint32_t ii = 0; ii < drawnSprites; ++ii) + { + float mtx[16]; + if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Mesh) + { + bx::mtxSRT(mtx, particleTransform.GetScale().x(), particleTransform.GetScale().y(), particleTransform.GetScale().z(), + particleRotation.Pitch(), particleRotation.Yaw(), particleRotation.Roll(), + pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().x(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().y(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().z()); + } + else if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Billboard) + { + auto up = particleTransform.GetRotation().ToMatrix3x3() * cd::Vec3f(0, 1, 0); + auto vec = pMainCameraTransform.GetTranslation() - pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos(); + auto right = up.Cross(vec); + float yaw = atan2f(right.z(), right.x()); + float pitch = atan2f(vec.y(), sqrtf(vec.x() * vec.x() + vec.z() * vec.z())); + float roll = atan2f(right.x(), -right.y()); + bx::mtxSRT(mtx, particleTransform.GetScale().x(), particleTransform.GetScale().y(), particleTransform.GetScale().z(), + pitch, yaw, roll, + pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().x(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().y(), pEmitterComponent->GetParticlePool().GetParticle(ii).GetPos().z()); + } + + bgfx::setTransform(mtx); + + constexpr StringCrc ParticleSampler("s_texColor"); + bgfx::setTexture(0, GetRenderContext()->GetUniform(ParticleSampler), m_particleTextureHandle); + bgfx::setVertexBuffer(0, bgfx::VertexBufferHandle{ pEmitterComponent->GetParticleVertexBufferHandle() }); + bgfx::setIndexBuffer(bgfx::IndexBufferHandle{ pEmitterComponent->GetParticleIndexBufferHandle() }); + + bgfx::setState(state_tristrip); + + if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Mesh) + { + GetRenderContext()->Submit(GetViewID(), ParticleProgram); + } + else if (pEmitterComponent->GetRenderMode() == engine::ParticleRenderMode::Billboard) + { + GetRenderContext()->Submit(GetViewID(), WO_BillboardParticleProgram); + } + } + } + + //pEmitterComponent->RePaddingShapeBuffer(); + //const bgfx::Memory* pParticleVertexBuffer = bgfx::makeRef(pEmitterComponent->GetEmitterShapeVertexBuffer().data(), static_cast(pEmitterComponent->GetEmitterShapeVertexBuffer().size())); + //const bgfx::Memory* pParticleIndexBuffer = bgfx::makeRef(pEmitterComponent->GetEmitterShapeIndexBuffer().data(), static_cast(pEmitterComponent->GetEmitterShapeIndexBuffer().size())); + //bgfx::update(bgfx::DynamicVertexBufferHandle{ pEmitterComponent->GetEmitterShapeVertexBufferHandle()}, 0, pParticleVertexBuffer); + //bgfx::update(bgfx::DynamicIndexBufferHandle{pEmitterComponent->GetEmitterShapeIndexBufferHandle()}, 0, pParticleIndexBuffer); + constexpr StringCrc emitShapeRangeCrc(shapeRange); + bgfx::setUniform(GetRenderContext()->GetUniform(emitShapeRangeCrc), &pEmitterComponent->GetEmitterShapeRange(), 1); + bgfx::setTransform(m_pCurrentSceneWorld->GetTransformComponent(entity)->GetWorldMatrix().begin()); + bgfx::setVertexBuffer(1, bgfx::VertexBufferHandle{ pEmitterComponent->GetEmitterShapeVertexBufferHandle() }); + bgfx::setIndexBuffer(bgfx::IndexBufferHandle{ pEmitterComponent->GetEmitterShapeIndexBufferHandle() }); + bgfx::setState(state_lines); + + GetRenderContext()->Submit(GetViewID(), ParticleEmitterShapeProgram); + } } } \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ParticleRenderer.h b/Engine/Source/Runtime/Rendering/ParticleRenderer.h index 36e8e277..6766fb78 100644 --- a/Engine/Source/Runtime/Rendering/ParticleRenderer.h +++ b/Engine/Source/Runtime/Rendering/ParticleRenderer.h @@ -1,7 +1,12 @@ #pragma once #include "Renderer.h" - +#include "ECWorld/CameraComponent.h" +#include "ECWorld/SceneWorld.h" +#include "ECWorld/ParticleForceFieldComponent.h" +#include "ECWorld/TransformComponent.h" +#include "RenderContext.h" +#include "Rendering/Utility/VertexLayoutUtility.h" namespace engine { @@ -18,9 +23,16 @@ class ParticleRenderer final : public Renderer virtual void Render(float deltaTime) override; void SetSceneWorld(SceneWorld* pSceneWorld) { m_pCurrentSceneWorld = pSceneWorld; } - + float getRandomValue(float min, float max) { return min + static_cast(rand()) / (RAND_MAX / (max - min)); } + void SetForceFieldRotationForce(ParticleForceFieldComponent* forcefield) { m_forcefieldRotationFoce = forcefield->GetRotationForce(); } + void SetForceFieldRange(ParticleForceFieldComponent* forcefield ,cd::Vec3f scale) { m_forcefieldRange = forcefield->GetForceFieldRange()*scale; } private: SceneWorld* m_pCurrentSceneWorld = nullptr; + bgfx::TextureHandle m_particleTextureHandle; + ParticleType m_currentType = ParticleType::Sprite; + + bool m_forcefieldRotationFoce = false; + cd::Vec3f m_forcefieldRange; }; } \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ShaderFeature.h b/Engine/Source/Runtime/Rendering/ShaderFeature.h index 1cbb0240..c91ba474 100644 --- a/Engine/Source/Runtime/Rendering/ShaderFeature.h +++ b/Engine/Source/Runtime/Rendering/ShaderFeature.h @@ -22,6 +22,7 @@ enum class ShaderFeature : uint32_t // Techniques IBL, + PARTICLE_INSTANCE, ATM, AREAL_LIGHT, @@ -37,6 +38,7 @@ constexpr const char* ShaderFeatureNames[] = "ORMMAP;", "EMISSIVEMAP;", "IBL;", + "PARTICLEINSTANCE;", "ATM;", "AREALLIGHT;", }; diff --git a/Engine/Source/ThirdParty/AssetPipeline b/Engine/Source/ThirdParty/AssetPipeline index e9315cd1..4a5644f7 160000 --- a/Engine/Source/ThirdParty/AssetPipeline +++ b/Engine/Source/ThirdParty/AssetPipeline @@ -1 +1 @@ -Subproject commit e9315cd11f1a0eecb465c14bc775934f93828d30 +Subproject commit 4a5644f700720e95e425a415620d058a32dcf47a diff --git a/Projects/Test/Textures/textures/Particle.png b/Projects/Test/Textures/textures/Particle.png new file mode 100644 index 00000000..ab451be9 Binary files /dev/null and b/Projects/Test/Textures/textures/Particle.png differ