Particle System Demo 71/293771/4
authorAdam Bialogonski <adam.b@samsung.com>
Fri, 2 Jun 2023 17:09:24 +0000 (18:09 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 2 Jun 2023 17:17:33 +0000 (18:17 +0100)
Change-Id: Ib5cc186c001952c8f2f74c2371575a08d94bb607

24 files changed:
build/tizen/examples/CMakeLists.txt
demo/dali-demo.cpp
examples-reel/dali-examples-reel.cpp
examples/particle-system/effects/fire-ring-effect-modifier.cpp [new file with mode: 0644]
examples/particle-system/effects/fire-ring-effect-modifier.h [new file with mode: 0644]
examples/particle-system/effects/fire-ring-effect-source.cpp [new file with mode: 0644]
examples/particle-system/effects/fire-ring-effect-source.h [new file with mode: 0644]
examples/particle-system/effects/image-effect-modifier.cpp [new file with mode: 0644]
examples/particle-system/effects/image-effect-modifier.h [new file with mode: 0644]
examples/particle-system/effects/image-effect-source.cpp [new file with mode: 0644]
examples/particle-system/effects/image-effect-source.h [new file with mode: 0644]
examples/particle-system/effects/particle-effect.cpp [new file with mode: 0644]
examples/particle-system/effects/particle-effect.h [new file with mode: 0644]
examples/particle-system/effects/sparkles-effect-modifier.cpp [new file with mode: 0644]
examples/particle-system/effects/sparkles-effect-modifier.h [new file with mode: 0644]
examples/particle-system/effects/sparkles-effect-source.cpp [new file with mode: 0644]
examples/particle-system/effects/sparkles-effect-source.h [new file with mode: 0644]
examples/particle-system/particle-system-example.cpp [new file with mode: 0644]
resources/images/blue-part2.png [new file with mode: 0644]
resources/images/particle-image-source.jpg [new file with mode: 0644]
resources/images/sparkle-part1.png [new file with mode: 0644]
resources/po/en_GB.po
resources/po/en_US.po
shared/dali-demo-strings.h

index ad3d815..05ebbdf 100644 (file)
@@ -27,7 +27,7 @@ ENDIF()
 
 FUNCTION(INSTALL_EXAMPLES EXAMPLE)
   SET(PARENT_CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../")
-  FILE(GLOB SRCS "${EXAMPLES_SRC_DIR}/${EXAMPLE}/*.cpp")
+  FILE(GLOB_RECURSE SRCS "${EXAMPLES_SRC_DIR}/${EXAMPLE}/*.cpp")
   SET(SRCS ${SRCS} "${ROOT_SRC_DIR}/shared/resources-location.cpp")
   IF(SHARED)
     ADD_LIBRARY(${EXAMPLE}.example SHARED ${SRCS})
index 60f9653..9baad32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@ int DALI_EXPORT_API main(int argc, char** argv)
   demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR));
   demo.AddExample(Example("page-turn-view.example", DALI_DEMO_STR_TITLE_PAGE_TURN));
   demo.AddExample(Example("particles.example", DALI_DEMO_STR_TITLE_PARTICLES));
+  demo.AddExample(Example("particle-system.example", DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM));
   demo.AddExample(Example("reflection-demo.example", DALI_DEMO_STR_TITLE_REFLECTION));
   demo.AddExample(Example("refraction-effect.example", DALI_DEMO_STR_TITLE_REFRACTION));
   demo.AddExample(Example("renderer-stencil.example", DALI_DEMO_STR_TITLE_RENDERER_STENCIL));
index e86f459..90ac6db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -78,6 +78,7 @@ int DALI_EXPORT_API main(int argc, char** argv)
   demo.AddExample(Example("mesh-morph.example", DALI_DEMO_STR_TITLE_MESH_MORPH));
   demo.AddExample(Example("motion-stretch.example", DALI_DEMO_STR_TITLE_MOTION_STRETCH));
   demo.AddExample(Example("native-image-source.example", DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE));
+  demo.AddExample(Example("particle-system.example", DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM));
   demo.AddExample(Example("popup.example", DALI_DEMO_STR_TITLE_POPUP));
   demo.AddExample(Example("pivot.example", DALI_DEMO_STR_TITLE_PIVOT));
   demo.AddExample(Example("primitive-shapes.example", DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES));
diff --git a/examples/particle-system/effects/fire-ring-effect-modifier.cpp b/examples/particle-system/effects/fire-ring-effect-modifier.cpp
new file mode 100644 (file)
index 0000000..56f7e5b
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "fire-ring-effect-modifier.h"
+#include "fire-ring-effect-source.h"
+
+namespace Dali::ParticleEffect
+{
+FireModifier::FireModifier(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+  // initialize gradient with flame colors
+  mFireGradient.PushColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f), 1.0f - 1.0f);
+  mFireGradient.PushColor(Vector4(0.975, 0.955, 0.476, 1.0f), 1.0f - 0.947f);
+  mFireGradient.PushColor(Vector4(0.999, 0.550, 0.194, 1.0f), 1.0f - 0.800f);
+  mFireGradient.PushColor(Vector4(0.861, 0.277, 0.094, 1.0f), 1.0f - 0.670f);
+  mFireGradient.PushColor(Vector4(0.367, 0.0, 0.0, 1.0f), 1.0f - 0.456f);
+  mFireGradient.PushColor(Vector4(0.3, 0.3, 0.3, 1.0f), 1.0f - 0.400f);
+  mFireGradient.PushColor(Vector4(0.3, 0.2, 0.2, 1.0f), 1.0f - 0.200f);
+  mFireGradient.PushColor(Vector4(0.2, 0.1, 0.1, 1.0f), 1.0f - 0.150f);
+  mFireGradient.PushColor(Vector4(0.1, 0.0, 0.0, 1.0f), 1.0f- 0.100f);
+  mFireGradient.PushColor(Vector4(0.0, 0.0, 0.0, 0.5f), 1.0f-0.050f);
+  mFireGradient.PushColor(Vector4(0.0, 0.0, 0.0, 0.2f), 1.0f);
+}
+
+bool FireModifier::IsMultiThreaded()
+{
+  return false;
+}
+
+void FireModifier::Update(ParticleList& particleList, uint32_t first, uint32_t count)
+{
+  // If no acive particles return
+  if(!particleList.GetActiveParticleCount())
+  {
+    return;
+  }
+
+  mAngle = ((mAngle + 2) % 360);
+
+  // Retrieve the Source and get the stream
+  if(!mStreamBasePos)
+  {
+    mStreamBasePos = static_cast<FireSource*>(&mEmitter.GetSource().GetSourceCallback())->mStreamBasePos;
+  }
+
+  // Missing stream, return!
+  if(!mStreamBasePos)
+  {
+    return;
+  }
+
+  auto& activeParticles = particleList.GetActiveParticles();
+
+  auto it = activeParticles.begin();
+  std::advance(it, first);
+
+  int i = 0;
+  for(; count; ++it, count--)
+  {
+    i += 1;
+
+    // Acquire stream data
+    auto&                  particle    = *it;
+    auto&                  position    = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+    auto&                  velocity    = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+    auto&                  color       = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+    auto&                  baseLifetime = particle.Get<float>(ParticleStream::LIFETIME_BASE_STREAM_BIT);
+    auto&                  scale        = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+
+    // Get base positions
+    auto& basePos = particle.GetByIndex<Vector3>(mStreamBasePos);
+
+    float                  lifetime = particle.Get<float>(ParticleStream::LIFETIME_STREAM_BIT);
+    position.y += -fabs(velocity.y);
+    position.x  = basePos.x + 5.0f * sin((((mAngle + i)%360)*M_PI)/180.f );
+
+    velocity *= 0.990f;
+    auto newColor = mFireGradient.GetColorAt((baseLifetime - lifetime) / baseLifetime);
+    float normalizedTime = (lifetime / baseLifetime);
+    newColor.a = normalizedTime * normalizedTime;
+
+    scale = Vector3(64.0f*(normalizedTime * normalizedTime * normalizedTime * normalizedTime), 64.0f*(normalizedTime * normalizedTime * normalizedTime * normalizedTime), 1.0);
+
+    color = newColor;
+  }
+}
+}
\ No newline at end of file
diff --git a/examples/particle-system/effects/fire-ring-effect-modifier.h b/examples/particle-system/effects/fire-ring-effect-modifier.h
new file mode 100644 (file)
index 0000000..3f9f6ea
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef DALI_PROJECT_FIRE_RING_EFFECT_MODIFIER_H
+#define DALI_PROJECT_FIRE_RING_EFFECT_MODIFIER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+class FireModifier : public ParticleModifierInterface
+{
+public:
+
+  struct ColorGradient
+  {
+    std::vector<Vector4> colors;
+    std::vector<float>   position;
+
+    void PushColor(const Vector4& color, float pos)
+    {
+      colors.emplace_back(color);
+      position.emplace_back(pos);
+    }
+
+    Vector4 GetColorAt(float pos)
+    {
+      if(pos >= 1.0f)
+      {
+        return colors.back();
+      }
+      else if(pos <= 0.0f)
+      {
+        return colors[0];
+      }
+      for(auto i = 0u; i < position.size() - 1; ++i)
+      {
+        if(pos >= position[i] && pos < position[i + 1])
+        {
+          auto colorDiff = colors[i + 1] - colors[i];
+          return colors[i] + (colorDiff * ((pos - position[i]) / (position[i + 1] - position[i])));
+        }
+      }
+      return colors[0];
+    }
+  };
+
+
+  explicit FireModifier(ParticleEmitter& emitter);
+
+  bool IsMultiThreaded() override;
+
+  void Update(ParticleList& particleList, uint32_t first, uint32_t count) override;
+
+  ColorGradient mFireGradient;
+  ParticleEmitter mEmitter;
+  uint32_t mStreamBasePos{0u};
+  uint32_t mAngle{0u};
+};
+
+
+
+}
+
+#endif // DALI_PROJECT_FIRE_RING_EFFECT_MODIFIER_H
diff --git a/examples/particle-system/effects/fire-ring-effect-source.cpp b/examples/particle-system/effects/fire-ring-effect-source.cpp
new file mode 100644 (file)
index 0000000..0ff0652
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "fire-ring-effect-source.h"
+#include <random>
+
+namespace Dali::ParticleEffect
+{
+static float LIFETIME = 5.0f;
+FireSource::FireSource(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+  std::time_t result = std::time(nullptr);
+  srand(result);
+  mRadius = Vector2::ONE;
+}
+
+FireSource::FireSource(ParticleEmitter& emitter, Dali::Vector2 ringRadius) :
+mEmitter(emitter)
+{
+  std::time_t result = std::time(nullptr);
+  srand(result);
+  mRadius = ringRadius;
+
+}
+
+void FireSource::Init()
+{
+  mStreamBasePos = mEmitter.GetParticleList().AddLocalStream<Vector3>(Vector3::ZERO);
+}
+
+uint32_t FireSource::Update(ParticleList& particleList, uint32_t count)
+{
+  while(count--)
+  {
+    auto particle = particleList.NewParticle(LIFETIME  * (float(std::rand() % 1000)/1000.0f) + 1.0f );
+    if(!particle)
+    {
+      return 0u;
+    }
+
+    auto& basePosition = particle.GetByIndex<Vector3>(mStreamBasePos);
+
+    auto& position = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+    auto& color    = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+    auto& velocity = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+    auto& scale    = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+    UpdateParticle(position, basePosition, color, velocity, scale);
+  }
+
+  return 0;
+}
+
+void FireSource::UpdateParticle(Vector3& position, Vector3& basePosition, Vector4& color, Vector3& velocity, Vector3& scale)
+{
+  float posRadians   = ((rand() % 360) * M_PI) / 180.0f;
+
+  basePosition.x = position.x = mRadius.x * sin(posRadians);
+  basePosition.y = position.y = mRadius.y * cos(posRadians);
+  color                       = Dali::Color::WHITE; // white color when emitted
+
+  // angle of motion
+  float radians    = ((rand() % 360) * M_PI) / 180.0f;
+  float speed = ((rand() % 5) + 5);
+  velocity.x       = sin(radians) * speed;
+  velocity.y       = cos(radians) * speed;
+
+  // Random initial scale
+  float currentScale = float(rand() % 32) + 32;
+  scale   = Vector3(currentScale, currentScale, 1);
+}
+
+} // namespace Dali::ParticleEffect
\ No newline at end of file
diff --git a/examples/particle-system/effects/fire-ring-effect-source.h b/examples/particle-system/effects/fire-ring-effect-source.h
new file mode 100644 (file)
index 0000000..28f78ef
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef DALI_FIRE_RING_EFFECT_SOURCE_H
+#define DALI_FIRE_RING_EFFECT_SOURCE_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+class FireSource : public Toolkit::ParticleSystem::ParticleSourceInterface
+{
+public:
+
+  explicit FireSource(ParticleEmitter& emitter);
+
+  explicit FireSource(ParticleEmitter& emitter, Dali::Vector2 ringRadius);
+
+  uint32_t Update(ParticleList& particleList, uint32_t count) override;
+
+  void Init() override;
+
+  void UpdateParticle(Vector3& position, Vector3& basePosition, Vector4& color, Vector3& velocity, Vector3& scale);
+
+  ParticleEmitter mEmitter;
+
+  Dali::Vector2 mRadius;
+
+  uint32_t mStreamBasePos{0u};
+
+};
+
+}
+#endif // DALI_FIRE_RING_EFFECT_SOURCE_H
diff --git a/examples/particle-system/effects/image-effect-modifier.cpp b/examples/particle-system/effects/image-effect-modifier.cpp
new file mode 100644 (file)
index 0000000..395040c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "image-effect-modifier.h"
+#include "image-effect-source.h"
+namespace Dali::ParticleEffect
+{
+
+#define RAD(x) (float(x)*M_PI/180.0f)
+
+ImageExplodeEffectModifier::ImageExplodeEffectModifier(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+
+}
+
+bool ImageExplodeEffectModifier::IsMultiThreaded()
+{
+  return false;
+}
+
+void ImageExplodeEffectModifier::Update(ParticleList& particleList, uint32_t first, uint32_t count)
+{
+  // If no acive particles return
+  if(!particleList.GetActiveParticleCount())
+  {
+    return;
+  }
+
+  // Retrieve the Source and get the stream
+  if(!mStreamBasePos)
+  {
+    mStreamBasePos = static_cast<ImageExplodeEffectSource*>(&mEmitter.GetSource().GetSourceCallback())->mStreamBasePos;
+  }
+
+  // Missing stream, return!
+  if(!mStreamBasePos)
+  {
+    return;
+  }
+
+  auto& activeParticles = particleList.GetActiveParticles();
+
+  auto it = activeParticles.begin();
+  std::advance(it, first);
+
+  mAngle += 5.0f;
+
+  for(; count; ++it, count--)
+  {
+    // Acquire stream data
+    auto&                  particle = *it;
+    auto&                  position = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+    auto&                  color    = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+
+    // Get base positions
+    auto& basePos = particle.GetByIndex<Vector3>(mStreamBasePos);
+    position.z = 200.f * sin(RAD(mAngle+basePos.x));
+    color.a = position.z < 0.0f ? 1.0f : 1.0f - position.z/500.0f;
+    position.z = 500 + position.z;
+  }
+}
+}
\ No newline at end of file
diff --git a/examples/particle-system/effects/image-effect-modifier.h b/examples/particle-system/effects/image-effect-modifier.h
new file mode 100644 (file)
index 0000000..8a0781e
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef DALI_IMAGE_EFFECT_MODIFIER_H
+#define DALI_IMAGE_EFFECT_MODIFIER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+class ImageExplodeEffectModifier : public ParticleModifierInterface
+{
+public:
+
+  explicit ImageExplodeEffectModifier(ParticleEmitter& emitter);
+
+  bool IsMultiThreaded() override;
+
+  void Update(ParticleList& particleList, uint32_t first, uint32_t count) override;
+
+  ParticleEmitter mEmitter;
+  uint32_t mStreamBasePos{0u};
+  float mAngle{0.0f};
+
+};
+
+
+
+}
+
+#endif // DALI_IMAGE_EFFECT_MODIFIER_H
diff --git a/examples/particle-system/effects/image-effect-source.cpp b/examples/particle-system/effects/image-effect-source.cpp
new file mode 100644 (file)
index 0000000..2e52a1e
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "image-effect-source.h"
+#include <dali/devel-api/rendering/texture-devel.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <random>
+
+namespace Dali::ParticleEffect
+{
+namespace
+{
+Vector4 GetColorAt(uint32_t x, uint32_t y, Devel::PixelBuffer& buffer)
+{
+  if(buffer.GetPixelFormat() == Pixel::Format::RGBA8888)
+  {
+    const auto ptr   = reinterpret_cast<uint32_t*>(buffer.GetBuffer());
+    auto       value = *(ptr + x + (y * buffer.GetHeight()));
+    auto       rgba  = reinterpret_cast<uint8_t*>(&value);
+    return Vector4(float(rgba[0]) / 255.0f, float(rgba[1]) / 255.0f, float(rgba[2]) / 255.0f, 1.0f);
+  }
+  else
+  {
+    auto       rgba  = reinterpret_cast<uint8_t*>(buffer.GetBuffer() + (y*buffer.GetWidth()*3) + (x*3));
+    return Vector4(float(rgba[0]) / 255.0f, float(rgba[1]) / 255.0f, float(rgba[2]) / 255.0f, 1.0f);
+  }
+}
+}
+
+static float LIFETIME = 50000.0f; // we need infinite lifetime?
+ImageExplodeEffectSource::ImageExplodeEffectSource(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+}
+
+ImageExplodeEffectSource::ImageExplodeEffectSource(ParticleEmitter& emitter, const std::string& imageFileName, uint32_t width, uint32_t height) :
+mEmitter(emitter)
+{
+
+  // Create texture
+  std::string filePath(DEMO_IMAGE_DIR);
+  filePath += imageFileName;
+  ImageDimensions dimensions(width, height);
+  // Pixel buffer will be used as a source of pixels (populating colors of particles based on image pixels)
+  Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromFile(filePath, dimensions, FittingMode::SHRINK_TO_FIT, SamplingMode::DEFAULT, false);
+  mImageWidth = pixelBuffer.GetWidth();
+  mImageHeight = pixelBuffer.GetHeight();
+  mPixelBuffer = pixelBuffer;
+}
+
+void ImageExplodeEffectSource::Init()
+{
+  mStreamBasePos = mEmitter.GetParticleList().AddLocalStream<Vector3>(Vector3::ZERO);
+}
+
+uint32_t ImageExplodeEffectSource::Update(ParticleList& particleList, uint32_t count)
+{
+  if(!mShouldEmit)
+  {
+    return 0;
+  }
+
+  if(mPixelBuffer.GetPixelFormat() != Dali::Pixel::RGBA8888 &&
+     mPixelBuffer.GetPixelFormat() != Dali::Pixel::RGB888)
+  {
+    return 0;
+  }
+
+  auto i = 0u;
+  float particleScale = 4.0f;
+  float pixelSize = 2.0f;
+
+  uint32_t halfWidth = (mImageWidth/2) * particleScale;
+  uint32_t halfHeight = (mImageHeight/2) * particleScale;
+
+  for(auto y = 0u ; y < mImageHeight; ++y)
+  {
+    for(auto x = 0u; x < mImageWidth; ++x)
+    {
+      if(i < particleList.GetCapacity())
+      {
+        // Ignore count, populating all pixels instantly (emitter must account for all the points)
+        auto particle = particleList.NewParticle(LIFETIME);
+
+        auto& basePosition = particle.GetByIndex<Vector3>(mStreamBasePos);
+        auto& position     = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+        auto& color        = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+        auto& velocity     = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+        auto& scale        = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+        color              = GetColorAt(x, y, mPixelBuffer);
+        // Set basePosition
+        position = basePosition = Vector3(x* particleScale -halfWidth, y* particleScale -halfHeight, 0);
+        scale                   = Vector3(pixelSize, pixelSize, 1);
+        velocity                = Vector3::ZERO;
+      }
+      ++i;
+    }
+  }
+
+  mShouldEmit = false;
+  return mImageWidth * mImageHeight;
+}
+
+
+} // namespace Dali::ParticleEffect
\ No newline at end of file
diff --git a/examples/particle-system/effects/image-effect-source.h b/examples/particle-system/effects/image-effect-source.h
new file mode 100644 (file)
index 0000000..49d84c9
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef DALI_IMAGE_EFFECT_SOURCE_H
+#define DALI_IMAGE_EFFECT_SOURCE_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+/**
+ * Image source will use 2D image to populate points for the emitter
+ * This particular implementation populates points only once.
+ */
+class ImageExplodeEffectSource : public Toolkit::ParticleSystem::ParticleSourceInterface
+{
+public:
+
+  explicit ImageExplodeEffectSource(ParticleEmitter& emitter);
+
+  explicit ImageExplodeEffectSource(ParticleEmitter& emitter, const std::string& imageFileName, uint32_t width, uint32_t height);
+
+  uint32_t Update(ParticleList& particleList, uint32_t count) override;
+
+  void Init() override;
+
+  ParticleEmitter mEmitter;
+
+  uint32_t mImageWidth{0u};
+  uint32_t mImageHeight{0u};
+
+  uint32_t mStreamBasePos{0u};
+
+  Devel::PixelBuffer mPixelBuffer;
+
+  bool mShouldEmit {true};
+};
+
+}
+#endif // DALI_IMAGE_EFFECT_SOURCE_H
diff --git a/examples/particle-system/effects/particle-effect.cpp b/examples/particle-system/effects/particle-effect.cpp
new file mode 100644 (file)
index 0000000..e4f2c30
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "particle-effect.h"
+#include "fire-ring-effect-source.h"
+#include "sparkles-effect-source.h"
+#include "image-effect-source.h"
+#include "fire-ring-effect-modifier.h"
+#include "sparkles-effect-modifier.h"
+#include "image-effect-modifier.h"
+
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <functional>
+
+#ifndef DEMO_IMAGE_DIR
+#define DEMO_IMAGE_DIR ""
+#endif
+
+namespace Dali::ParticleEffect
+{
+using ParticleEmitter = Dali::Toolkit::ParticleSystem::ParticleEmitter;
+using ParticleSource = Dali::Toolkit::ParticleSystem::ParticleSource;
+using ParticleModifier = Dali::Toolkit::ParticleSystem::ParticleModifier;
+
+struct FunctorReturn
+{
+  ParticleEmitter emitter;
+  ParticleSource source;
+  ParticleModifier modifier;
+};
+
+static std::vector<FunctorReturn(*)(const ParticleEffectParams&)> gEffectInitializers =
+{
+  [](const ParticleEffectParams& params){
+      ParticleEmitter emitter = ParticleEmitter::New();
+      return FunctorReturn{emitter, ParticleSource::New<FireSource>(emitter, params.sourceSize), ParticleModifier::New<FireModifier>(emitter) };
+    },
+  [](const ParticleEffectParams& params){
+      ParticleEmitter emitter = ParticleEmitter::New();
+      return FunctorReturn{emitter, ParticleSource::New<SparklesSource>(emitter), ParticleModifier::New<SparklesModifier>(emitter) };
+    },
+  [](const ParticleEffectParams& params){
+      ParticleEmitter emitter = ParticleEmitter::New();
+      return FunctorReturn{emitter, ParticleSource::New<ImageExplodeEffectSource>(emitter,
+                                                                                  params.strImageSourceName,
+                                                                                  uint32_t(params.sourceSize.width),
+                                                                                  uint32_t(params.sourceSize.height)
+                                                                                  ), ParticleModifier::New<ImageExplodeEffectModifier>(emitter) };
+    },
+};
+
+ParticleEffect::ParticleEffect() = default;
+
+ParticleEffect::~ParticleEffect() = default;
+
+Dali::Toolkit::ParticleSystem::ParticleEmitter ParticleEffect::CreateEffectEmitter( EffectType effectType, Actor parentActor, const ParticleEffectParams& params )
+{
+  auto retval = gEffectInitializers[int(effectType)](params);
+  auto emitter = retval.emitter;
+
+  ParticleRenderer renderer = ParticleRenderer::New();
+
+  if(!params.strTexture.empty())
+  {
+    // Create texture
+    std::string filename(DEMO_IMAGE_DIR);
+    filename += params.strTexture;
+    Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load(filename);
+    auto texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
+    texture.Upload(pixelData);
+    renderer.SetTexture(texture);
+  }
+
+  emitter.AttachTo(std::move(parentActor));
+  emitter.SetEmissionRate( params.emissionRate ); // 20 particles emitted per second
+  emitter.SetParticleCount( params.particleCount );
+  emitter.SetSource( retval.source );
+  emitter.SetDomain( ParticleDomain::New() );
+  emitter.AddModifier(retval.modifier);
+  emitter.SetRenderer( renderer );
+  renderer.SetBlendingMode(Dali::Toolkit::ParticleSystem::BlendingMode::SCREEN);
+  emitter.SetInitialParticleCount( params.initialParticleCount );
+
+  return emitter;
+}
+
+
+}
+
diff --git a/examples/particle-system/effects/particle-effect.h b/examples/particle-system/effects/particle-effect.h
new file mode 100644 (file)
index 0000000..a1277ef
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef DALI_PARTICLE_EFFECT_H
+#define DALI_PARTICLE_EFFECT_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <memory>
+
+namespace Dali::ParticleEffect
+{
+
+using EffectIndex = uint32_t;
+
+enum class EffectType
+{
+  FIRE_RING,
+  SPARKLES,
+  IMAGE_EXPLOSION
+};
+
+struct ParticleEffectParams
+{
+  uint32_t emissionRate;
+  uint32_t particleCount;
+  uint32_t initialParticleCount;
+  Vector2 sourceSize;
+  std::string strTexture;
+  std::string strImageSourceName;
+};
+
+/**
+ * Simple manager to spawn and control partcile emitters
+ */
+class ParticleEffect
+{
+public:
+
+  ParticleEffect();
+
+  ~ParticleEffect();
+
+  Dali::Toolkit::ParticleSystem::ParticleEmitter CreateEffectEmitter( EffectType effectType, Actor parentActor, const ParticleEffectParams& params );
+
+private:
+
+
+};
+}
+
+#endif  // DALI_PARTICLE_MANAGER_H
\ No newline at end of file
diff --git a/examples/particle-system/effects/sparkles-effect-modifier.cpp b/examples/particle-system/effects/sparkles-effect-modifier.cpp
new file mode 100644 (file)
index 0000000..dda93da
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "sparkles-effect-modifier.h"
+#include "sparkles-effect-source.h"
+
+namespace Dali::ParticleEffect
+{
+static float LIFETIME = 3.0f;
+SparklesModifier::SparklesModifier(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+}
+
+bool SparklesModifier::IsMultiThreaded()
+{
+  return true;
+}
+
+void SparklesModifier::Update(ParticleList& particleList, uint32_t first, uint32_t count)
+{
+  // If no acive particles return
+  if(!particleList.GetActiveParticleCount())
+  {
+    return;
+  }
+
+  mAngle = ((mAngle + 2) % 360);
+
+  // Retrieve the Source and get the stream
+  if(!mStreamBasePos)
+  {
+    mStreamBasePos = static_cast<SparklesSource*>(&mEmitter.GetSource().GetSourceCallback())->mStreamBasePos;
+  }
+  if(!mStreamBaseAngle)
+  {
+    mStreamBaseAngle = static_cast<SparklesSource*>(&mEmitter.GetSource().GetSourceCallback())->mStreamBaseAngle;
+  }
+
+  // Missing stream, return!
+  if(!mStreamBasePos)
+  {
+    return;
+  }
+
+  auto& activeParticles = particleList.GetActiveParticles();
+
+  auto it = activeParticles.begin();
+  std::advance(it, first);
+
+  for(; count; ++it, count--)
+  {
+    // Acquire stream data
+    auto&                  particle = *it;
+    auto&                  position = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+    auto&                  velocity = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+    auto&                  color    = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+    auto&                  scale    = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+
+    // Get base positions
+    [[maybe_unused]] auto& basePos = particle.GetByIndex<Vector3>(mStreamBasePos);
+
+    auto angle = particle.GetByIndex<float>(mStreamBaseAngle);
+    auto                   radians  = ((angle * M_PI)/180.f);
+    float                  lifetime = particle.Get<float>(ParticleStream::LIFETIME_STREAM_BIT);
+    position.y += velocity.y *sin(radians);
+    position.x += velocity.x * cos(radians);
+
+    velocity *= 0.990f;
+    float normalizedTime = (lifetime / LIFETIME);
+    color.a = normalizedTime;
+    scale      = Vector3(64.0f*(normalizedTime * normalizedTime * normalizedTime * normalizedTime), 64.0f*(normalizedTime * normalizedTime * normalizedTime * normalizedTime), 1.0);
+  }
+}
+}
\ No newline at end of file
diff --git a/examples/particle-system/effects/sparkles-effect-modifier.h b/examples/particle-system/effects/sparkles-effect-modifier.h
new file mode 100644 (file)
index 0000000..1c64bd6
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef DALI_PARTICLES_SPARKLES_EFFECT_MODIFIER_H
+#define DALI_PARTICLES_SPARKLES_EFFECT_MODIFIER_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+class SparklesModifier : public ParticleModifierInterface
+{
+public:
+
+  explicit SparklesModifier(ParticleEmitter& emitter);
+
+  bool IsMultiThreaded() override;
+
+  void Update(ParticleList& particleList, uint32_t first, uint32_t count) override;
+
+  ParticleEmitter mEmitter;
+  uint32_t mStreamBasePos{0u};
+  uint32_t mStreamBaseAngle{0u};
+  uint32_t mAngle{0u};
+};
+
+
+
+}
+
+#endif // DALI_PARTICLES_SPARKLES_EFFECT_MODIFIER_H
diff --git a/examples/particle-system/effects/sparkles-effect-source.cpp b/examples/particle-system/effects/sparkles-effect-source.cpp
new file mode 100644 (file)
index 0000000..56eb298
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "sparkles-effect-source.h"
+
+namespace Dali::ParticleEffect
+{
+static float LIFETIME = 3.0f;
+SparklesSource::SparklesSource(ParticleEmitter& emitter)
+: mEmitter(emitter)
+{
+  std::time_t result = std::time(nullptr);
+  srand(result);
+  mRadius = Vector2::ONE;
+}
+
+SparklesSource::SparklesSource(ParticleEmitter& emitter, Dali::Vector2 ringRadius) :
+mEmitter(emitter)
+{
+  std::time_t result = std::time(nullptr);
+  srand(result);
+  mRadius = ringRadius;
+
+}
+
+void SparklesSource::Init()
+{
+  mStreamBasePos = mEmitter.GetParticleList().AddLocalStream<Vector3>(Vector3::ZERO);
+  mStreamBaseAngle = mEmitter.GetParticleList().AddLocalStream<float>(0.0f);
+}
+
+uint32_t SparklesSource::Update(ParticleList& particleList, uint32_t count)
+{
+  while(count--)
+  {
+    auto particle = particleList.NewParticle(LIFETIME);
+    if(!particle)
+    {
+      return 0u;
+    }
+
+    auto& basePosition = particle.GetByIndex<Vector3>(mStreamBasePos);
+    auto& angle = particle.GetByIndex<float>(mStreamBaseAngle);
+    auto& position     = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+    auto& color        = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+    auto& velocity     = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+    auto& scale        = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+
+
+    UpdateParticle(position, basePosition, color, velocity, scale, angle);
+  }
+
+  return 0;
+}
+
+void SparklesSource::UpdateParticle(Vector3& position, Vector3& basePosition, Vector4& color, Vector3& velocity, Vector3& scale, float& angle)
+{
+  static uint32_t a = 0.0f;
+  float posRadians   = ((rand() % 360) * M_PI) / 180.0f;
+
+  basePosition.x = position.x = mRadius.x * sin(posRadians);
+  basePosition.y = position.y = mRadius.y * cos(posRadians);
+  color                       = Dali::Color::WHITE;
+
+  angle = float(a);
+  a = ((a+5)%360);
+  float rad   = ((rand() % 360) * M_PI) / 180.0f;
+  float speed = ((rand() % 5) + 5);
+  velocity.x       = sin(rad) * speed;
+  velocity.y       = cos(rad) * speed;
+
+  // Random initial scale
+  float initialScale = float(rand() % 32) + 32;
+  scale   = Vector3(initialScale, initialScale, 1);
+}
+
+} // namespace Dali::ParticleEffect
\ No newline at end of file
diff --git a/examples/particle-system/effects/sparkles-effect-source.h b/examples/particle-system/effects/sparkles-effect-source.h
new file mode 100644 (file)
index 0000000..466c49b
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef DALI_PARTICLES_SPARKLES_EFFECT_SOURCE_H
+#define DALI_PARTICLES_SPARKLES_EFFECT_SOURCE_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <ctime>
+
+namespace Dali::ParticleEffect
+{
+using namespace Dali::Toolkit::ParticleSystem;
+
+class SparklesSource : public Toolkit::ParticleSystem::ParticleSourceInterface
+{
+public:
+
+  explicit SparklesSource(ParticleEmitter& emitter);
+
+  explicit SparklesSource(ParticleEmitter& emitter, Dali::Vector2 ringRadius);
+
+  uint32_t Update(ParticleList& particleList, uint32_t count) override;
+
+  void Init() override;
+
+  void UpdateParticle(Vector3& position, Vector3& basePosition, Vector4& color, Vector3& velocity, Vector3& scale, float& angle);
+
+  ParticleEmitter mEmitter;
+
+  Dali::Vector2 mRadius;
+
+  uint32_t mStreamBasePos{0u};
+  uint32_t mStreamBaseAngle{0u};
+
+};
+
+}
+#endif // DALI_PARTICLES_SPARKLES_EFFECT_SOURCE_H
diff --git a/examples/particle-system/particle-system-example.cpp b/examples/particle-system/particle-system-example.cpp
new file mode 100644 (file)
index 0000000..7d6a889
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-toolkit/dali-toolkit.h>
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+#include <utility>
+#include <ctime>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <unistd.h>
+#include <map>
+
+#include "effects/particle-effect.h"
+
+using namespace Dali;
+using namespace Dali::Toolkit::ParticleSystem;
+using namespace Dali::Toolkit;
+using Dali::Toolkit::TextLabel;
+
+using namespace Dali::ParticleEffect;
+
+/**
+ * This example shows Particle System feature
+ */
+class ParticleEffectController : public ConnectionTracker
+{
+public:
+
+  ParticleEffectController(Application& application)
+  : mApplication(application)
+  {
+    // Connect to the Application's Init signal
+    mApplication.InitSignal().Connect(this, &ParticleEffectController::Create);
+  }
+
+  ~ParticleEffectController() = default; // Nothing to do in destructor
+
+  template<class ButtonType>
+  ButtonType MakeButton( std::string title,
+                        Vector2 position,
+                        Vector2 size,
+                        bool toggleable,
+                        std::function<bool(Button)> onClick )
+  {
+    ButtonType button = ButtonType::New();
+    button.SetProperty( Button::Property::LABEL, title);
+    button.SetProperty( Actor::Property::POSITION, position);
+    button.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+    button.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+    button.SetProperty(Button::Property::TOGGLABLE, toggleable);
+    static std::map<RefObject*, std::function<bool(Button)>> callbackMap;
+    struct OnClick
+    {
+      static bool Slot(Button btn)
+      {
+        auto ptr = btn.GetObjectPtr();
+        return callbackMap[ptr](btn);
+      }
+    };
+
+    mUILastControlPosition = position;
+    mUILastControlSize = (size == Vector2::ZERO ? Vector2(button.GetNaturalSize()) : size);
+
+    callbackMap[button.GetObjectPtr()] = onClick;
+    button.ClickedSignal().Connect(OnClick::Slot);
+    return button;
+  }
+
+  // The Init signal is received once (only) during the Application lifetime
+  void Create(Application& application)
+  {
+    using namespace Dali::ParticleEffect;
+
+    // Get a handle to the window
+    Window window = application.GetWindow();
+    window.SetBackgroundColor(Color::BLACK);
+    {
+      Actor emitterActor = Actor::New();
+      emitterActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+      emitterActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+      emitterActor.SetProperty(Actor::Property::POSITION, Vector2(0.0, 0.0f));
+      emitterActor.SetProperty(Actor::Property::SIZE, Vector2(1.0, 1.0f));
+      window.Add(emitterActor);
+
+      mEmitterActor = emitterActor;
+      PushButton lastButton;
+      window.Add(
+        MakeButton<PushButton>("Fire Effect", Vector2::ZERO, {}, true, [&](Button button){
+
+          if(mCurrentEmitter)
+          {
+            mCurrentEmitter.Stop();
+            mCurrentEmitter.Reset();
+          }
+
+          ParticleEffectParams params{};
+          params.particleCount = 5000;
+          params.emissionRate = 1000;
+          params.initialParticleCount = 0;
+          params.sourceSize = Vector2(200, 10);
+          params.strTexture = "sparkle-part1.png";
+
+          mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::FIRE_RING, mEmitterActor, params );
+          mCurrentEmitter.Start();
+          return true;
+        })
+      );
+
+      window.Add(
+        MakeButton<PushButton>("Sparkle Effect", Vector2(0.0f, mUILastControlSize.height), {}, true, [&](Button button){
+          if(mCurrentEmitter)
+          {
+            mCurrentEmitter.Stop();
+            mCurrentEmitter.Reset();
+          }
+
+          ParticleEffectParams params{};
+          params.particleCount = 10000;
+          params.emissionRate = 500;
+          params.initialParticleCount = 0;
+          params.sourceSize = Vector2(10, 10);
+          params.strTexture = "blue-part2.png";
+
+          mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::SPARKLES, mEmitterActor, params );
+          mCurrentEmitter.Start();
+          return true;
+        })
+      );
+
+      window.Add(
+        MakeButton<PushButton>("Image Source Effect", Vector2(0.0f, mUILastControlPosition.y + mUILastControlSize.height), {}, true, [&](Button button){
+          if(mCurrentEmitter)
+          {
+            mCurrentEmitter.Stop();
+            mCurrentEmitter.Reset();
+          }
+
+          ParticleEffectParams params{};
+          params.particleCount = 20000;
+          params.emissionRate = 0;
+          params.initialParticleCount = 10;
+          params.sourceSize = Vector2(64, 64);
+          params.strImageSourceName = "particle-image-source.jpg";
+
+          mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::IMAGE_EXPLOSION, mEmitterActor, params );
+          mCurrentEmitter.Start();
+          return true;
+        })
+      );
+      window.Add(
+        MakeButton<PushButton>("Quit", Vector2(0.0f, mUILastControlPosition.y + mUILastControlSize.height * 2), {}, true, [&](Button button){
+          if(mCurrentEmitter)
+          {
+            mCurrentEmitter.Stop();
+            mCurrentEmitter.Reset();
+          }
+          mApplication.Quit();
+          return true;
+        })
+      );
+    }
+
+    // Respond to key events
+    window.KeyEventSignal().Connect(this, &ParticleEffectController::OnKeyEvent);
+  }
+
+  void OnKeyEvent(const KeyEvent& event)
+  {
+    if(event.GetState() == KeyEvent::DOWN)
+    {
+      if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
+      {
+        mApplication.Quit();
+      }
+    }
+  }
+
+private:
+
+  Application& mApplication;
+  std::unique_ptr<Dali::ParticleEffect::ParticleEffect> mParticleSystem;
+  ParticleEmitter mCurrentEmitter;
+  Actor mEmitterActor;
+
+  // Needed for buttons
+  Vector2 mUILastControlPosition;
+  Vector2 mUILastControlSize;
+
+};
+
+int DALI_EXPORT_API main(int argc, char** argv)
+{
+  Application          application = Application::New(&argc, &argv);
+  ParticleEffectController test(application);
+  application.MainLoop();
+  return 0;
+}
diff --git a/resources/images/blue-part2.png b/resources/images/blue-part2.png
new file mode 100644 (file)
index 0000000..3f41b43
Binary files /dev/null and b/resources/images/blue-part2.png differ
diff --git a/resources/images/particle-image-source.jpg b/resources/images/particle-image-source.jpg
new file mode 100644 (file)
index 0000000..05e41f8
Binary files /dev/null and b/resources/images/particle-image-source.jpg differ
diff --git a/resources/images/sparkle-part1.png b/resources/images/sparkle-part1.png
new file mode 100644 (file)
index 0000000..d6c1764
Binary files /dev/null and b/resources/images/sparkle-part1.png differ
index 3211603..35c782c 100755 (executable)
@@ -178,6 +178,9 @@ msgstr "Creation View Performance"
 msgid "DALI_DEMO_STR_TITLE_POINT_MESH"
 msgstr "Point Mesh"
 
+msgid "DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM"
+msgstr "Particle System"
+
 msgid "DALI_DEMO_STR_TITLE_POPUP"
 msgstr "Popup"
 
index a460366..6d1a91f 100755 (executable)
@@ -178,6 +178,9 @@ msgstr "Page Turn"
 msgid "DALI_DEMO_STR_TITLE_PARTICLES"
 msgstr "Particles"
 
+msgid "DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM"
+msgstr "Particle System"
+
 msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL"
 msgstr "Scrolling Performance"
 
index da60352..1d6377f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -97,6 +97,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE")
 #define DALI_DEMO_STR_TITLE_PAGE_TURN dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PAGE_TURN")
 #define DALI_DEMO_STR_TITLE_PARTICLES dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PARTICLES")
+#define DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM")
 #define DALI_DEMO_STR_TITLE_PBR dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PBR")
 #define DALI_DEMO_STR_TITLE_PERF_SCROLL dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PERF_SCROLL")
 #define DALI_DEMO_STR_TITLE_PERF_VIEW_CREATION dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PERF_VIEW_CREATION")
@@ -211,6 +212,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE "Negotiate Size"
 #define DALI_DEMO_STR_TITLE_PAGE_TURN "Page Turn"
 #define DALI_DEMO_STR_TITLE_PARTICLES "Particles"
+#define DALI_DEMO_STR_TITLE_PARTICLE_SYSTEM "Particle System"
 #define DALI_DEMO_STR_TITLE_PBR "PBR"
 #define DALI_DEMO_STR_TITLE_PERF_SCROLL "Scrolling Performance"
 #define DALI_DEMO_STR_TITLE_PERF_VIEW_CREATION "Creation View Performance"