DALI_TEST_EQUALS(child2, modelNode2, TEST_LOCATION);
END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliModelSizeChange(void)
+{
+ tet_infoline(" UtcDaliModelSizeChange.");
+
+ ToolkitTestApplication application;
+
+ Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ model.SetProperty(Dali::Actor::Property::SIZE, Vector3(300, 300, 300));
+ application.GetScene().Add(model);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(model.GetChildCount(), 1u, TEST_LOCATION);
+ Vector3 scale = model.GetChildAt(0u).GetProperty<Vector3>(Dali::Actor::Property::SCALE);
+
+ model.SetProperty(Dali::Actor::Property::SIZE, Vector3(600, 600, 600));
+ Vector3 scale2 = model.GetChildAt(0u).GetProperty<Vector3>(Dali::Actor::Property::SCALE);
+
+ DALI_TEST_NOT_EQUALS(scale, scale2, 0.1f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliModelSizeChange2(void)
+{
+ tet_infoline(" UtcDaliModelSizeChange2.");
+
+ ToolkitTestApplication application;
+
+ Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+ model.SetProperty(Dali::Actor::Property::SIZE, Vector3(300, 300, 300));
+ application.GetScene().Add(model);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(model.GetChildCount(), 1u, TEST_LOCATION);
+ Vector3 scale = model.GetChildAt(0u).GetProperty<Vector3>(Dali::Actor::Property::SCALE);
+
+ Animation animation = Animation::New(0.5f);
+ animation.AnimateTo(Dali::Property(model, Dali::Actor::Property::SIZE), Vector3(600, 600, 600));
+ animation.Play();
+
+ application.SendNotification();
+ application.Render(250);
+
+ application.SendNotification();
+
+ Vector3 scale2 = model.GetChildAt(0u).GetProperty<Vector3>(Dali::Actor::Property::SCALE);
+ DALI_TEST_NOT_EQUALS(scale, scale2, 0.1f, TEST_LOCATION);
+
+ END_TEST;
+}
*
*/
-#include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
-#include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
#include <dali-test-suite-utils.h>
#include <dlfcn.h>
+#include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
+#include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
using namespace Dali;
using namespace Dali::Scene3D::Algorithm;
using namespace Dali::Scene3D::Loader;
}
}
- void SetReturnValue( R value, uint32_t n )
+ void SetReturnValue(R value, uint32_t n)
{
if(overrideEnabled)
{
tet_infoline("Warning! Overriding return value is already enabled! Ignoring!\n");
return;
}
- result = value;
+ result = value;
overrideCounter = n;
overrideEnabled = true;
}
}
std::string funcNameStr;
- R result{R{}};
- F* func{nullptr};
- uint32_t overrideCounter = 0;
- bool overrideEnabled = false;
+ R result{R{}};
+ F* func{nullptr};
+ uint32_t overrideCounter = 0;
+ bool overrideEnabled = false;
};
// Override fseek()
static thread_local SysOverride<int, decltype(fseek)> call_fseek("fseek");
-extern "C" int fseek (FILE *s, long int o, int w)
+extern "C" int fseek(FILE* s, long int o, int w)
{
- return call_fseek.Invoke( s, o, w );
+ return call_fseek.Invoke(s, o, w);
}
// Override ftell()
static thread_local SysOverride<int, decltype(ftell)> call_ftell("ftell");
-extern "C" long int ftell(FILE *s)
+extern "C" long int ftell(FILE* s)
{
- return call_ftell.Invoke( s );
+ return call_ftell.Invoke(s);
}
// Override fread()
static thread_local SysOverride<int, decltype(fread)> call_fread("fread");
-extern "C" size_t fread (void *__restrict p, size_t s, size_t n, FILE *__restrict st)
+extern "C" size_t fread(void* __restrict p, size_t s, size_t n, FILE* __restrict st)
{
return call_fread.Invoke(p, s, n, st);
}
{
tet_infoline("UtcDaliNavigationMeshCreateFromBufferP: Creates navigation mesh using binary buffer");
- auto fin = fopen("resources/navmesh-test.bin", "rb");
- [[maybe_unused]] auto err = fseek(fin, 0, SEEK_END);
- auto length = ftell(fin);
- fseek( fin, 0, SEEK_SET);
+ auto fin = fopen("resources/navmesh-test.bin", "rb");
+ [[maybe_unused]] auto err = fseek(fin, 0, SEEK_END);
+ auto length = ftell(fin);
+ fseek(fin, 0, SEEK_SET);
std::vector<uint8_t> buffer;
buffer.resize(length);
- fread( buffer.data(), 1, length, fin );
+ fread(buffer.data(), 1, length, fin);
fclose(fin);
- auto result = NavigationMeshFactory::CreateFromBuffer( buffer );
+ auto result = NavigationMeshFactory::CreateFromBuffer(buffer);
DALI_TEST_CHECK(result != nullptr);
END_TEST;
DALI_TEST_CHECK(result != nullptr);
auto vertexCount = result->GetVertexCount();
- auto edgeCount = result->GetEdgeCount();
- auto faceCount = result->GetFaceCount();
+ auto edgeCount = result->GetEdgeCount();
+ auto faceCount = result->GetFaceCount();
- DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
- DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
- DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
+ DALI_TEST_EQUALS(vertexCount, 132, TEST_LOCATION);
+ DALI_TEST_EQUALS(edgeCount, 300, TEST_LOCATION);
+ DALI_TEST_EQUALS(faceCount, 165, TEST_LOCATION);
END_TEST;
}
auto vertexCount = navmesh->GetVertexCount();
- DALI_TEST_EQUALS( vertexCount, 132, TEST_LOCATION );
+ DALI_TEST_EQUALS(vertexCount, 132, TEST_LOCATION);
// List of coords, must be verified with Blender exporter
// clang-format off
// clang-format on
auto j = 0;
- for( auto i = 0u; i < 132; i+= 10, j+= 3)
+ for(auto i = 0u; i < 132; i += 10, j += 3)
{
const auto* vertex = navmesh->GetVertex(i);
- Vector3 v0(vertex->co);
- Vector3 v1(vertexData[j], vertexData[j+1], vertexData[j+2]);
- DALI_TEST_EQUALS( v0, v1, TEST_LOCATION);
+ Vector3 v0(vertex->coordinates);
+ Vector3 v1(vertexData[j], vertexData[j + 1], vertexData[j + 2]);
+ DALI_TEST_EQUALS(v0, v1, TEST_LOCATION);
}
END_TEST;
auto edgeCount = navmesh->GetEdgeCount();
- DALI_TEST_EQUALS( edgeCount, 300, TEST_LOCATION );
+ DALI_TEST_EQUALS(edgeCount, 300, TEST_LOCATION);
// List of coords, must be verified with Blender exporter
// clang-format off
- std::vector<uint16_t> edgeData = {
+ std::vector<EdgeIndex> edgeData = {
2, 65535, 8, 1,
8, 109, 124, 108,
10, 158, 32, 35,
};
// clang-format on
auto j = 0;
- for( auto i = 0u; i < 300; i+= 30, j+= 4)
+ for(auto i = 0u; i < 300; i += 30, j += 4)
{
const auto* edge = navmesh->GetEdge(i);
- auto e0 = edge->face[0];
- auto e1 = edge->face[1];
- auto v0 = edge->vertex[0];
- auto v1 = edge->vertex[1];
-
- DALI_TEST_EQUALS(e0, edgeData[j+0], TEST_LOCATION);
- DALI_TEST_EQUALS(e1, edgeData[j+1], TEST_LOCATION);
- DALI_TEST_EQUALS(v0, edgeData[j+2], TEST_LOCATION);
- DALI_TEST_EQUALS(v1, edgeData[j+3], TEST_LOCATION);
+ auto e0 = edge->face[0];
+ auto e1 = edge->face[1];
+ auto v0 = edge->vertex[0];
+ auto v1 = edge->vertex[1];
+
+ DALI_TEST_EQUALS(e0, edgeData[j + 0], TEST_LOCATION);
+ DALI_TEST_EQUALS(e1, edgeData[j + 1], TEST_LOCATION);
+ DALI_TEST_EQUALS(v0, edgeData[j + 2], TEST_LOCATION);
+ DALI_TEST_EQUALS(v1, edgeData[j + 3], TEST_LOCATION);
}
END_TEST;
auto faceCount = navmesh->GetFaceCount();
- DALI_TEST_EQUALS( faceCount, 165, TEST_LOCATION );
+ DALI_TEST_EQUALS(faceCount, 165, TEST_LOCATION);
// List of coords, must be verified with Blender exporter
// clang-format off
};
// clang-format on
auto j = 0;
- for( auto i = 0u; i < 165; i+= 16, j++)
+ for(auto i = 0u; i < 165; i += 16, j++)
{
const auto* face = navmesh->GetFace(i);
- Vector3 n0(face->normal);
- Vector3 c0(face->center);
+ Vector3 n0(face->normal);
+ Vector3 c0(face->center);
Vector3 n1(faceData[j].normal);
Vector3 c1(faceData[j].center);
- DALI_TEST_EQUALS( n0, n1, TEST_LOCATION);
- DALI_TEST_EQUALS( c0, c1, TEST_LOCATION);
+ DALI_TEST_EQUALS(n0, n1, TEST_LOCATION);
+ DALI_TEST_EQUALS(c0, c1, TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].vertex[0], face->vertex[0], TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].vertex[1], face->vertex[1], TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].vertex[2], face->vertex[2], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].vertex[0], face->vertex[0], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].vertex[1], face->vertex[1], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].vertex[2], face->vertex[2], TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].edge[0], face->edge[0], TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].edge[1], face->edge[1], TEST_LOCATION);
- DALI_TEST_EQUALS( faceData[j].edge[2], face->edge[2], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].edge[0], face->edge[0], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].edge[1], face->edge[1], TEST_LOCATION);
+ DALI_TEST_EQUALS(faceData[j].edge[2], face->edge[2], TEST_LOCATION);
}
END_TEST;
}
-
int UtcDaliNavigationGetGravityP(void)
{
tet_infoline("UtcDaliNavigationGetGravityP: Tests gravity vector");
auto gravity = navmesh->GetGravityVector();
// navmesh-test.bin is exported in Blender and the default gravity is Z = -1
- Dali::Vector3 expectedGravity( 0.0f, 0.0f, -1.0f );
+ Dali::Vector3 expectedGravity(0.0f, 0.0f, -1.0f);
- DALI_TEST_EQUALS( gravity, expectedGravity, TEST_LOCATION);
+ DALI_TEST_EQUALS(gravity, expectedGravity, TEST_LOCATION);
END_TEST;
}
Matrix matrix;
matrix.SetIdentity();
- Quaternion q = Quaternion( Radian(Degree(-90)), Vector3(1.0, 0.0, 0.0));
- Matrix newMatrix;
- matrix.Multiply( newMatrix, matrix, q); // Rotate matrix along X-axis
+ Quaternion q = Quaternion(Radian(Degree(-90)), Vector3(1.0, 0.0, 0.0));
+ Matrix newMatrix;
+ matrix.Multiply(newMatrix, matrix, q); // Rotate matrix along X-axis
navmesh->SetSceneTransform(newMatrix);
auto gravityVector = navmesh->GetGravityVector();
// 'point' should be turned into the gravity vector after transforming into the local space
- DALI_TEST_EQUALS( navMeshLocalSpace, gravityVector, TEST_LOCATION);
+ DALI_TEST_EQUALS(navMeshLocalSpace, gravityVector, TEST_LOCATION);
navMeshParentSpace = navmesh->PointLocalToScene(gravityVector);
// The gravity should be transformed back into point
- DALI_TEST_EQUALS( navMeshParentSpace, point, TEST_LOCATION);
+ DALI_TEST_EQUALS(navMeshParentSpace, point, TEST_LOCATION);
END_TEST;
}
// All calculations in the navmesh local space
navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
- std::vector<Vector3> inPositions;
- std::vector<Vector3> expectedPositions;
- std::vector<uint32_t> expectedFaceIndex;
- std::vector<bool> expectedResult;
+ std::vector<Vector3> inPositions;
+ std::vector<Vector3> expectedPositions;
+ std::vector<FaceIndex> expectedFaceIndex;
+ std::vector<bool> expectedResult;
// Lift slightly over the floor level
auto upFromGravity = navmesh->GetGravityVector() * (0.05f);
auto size = navmesh->GetFaceCount();
- for( auto i = 0u; i < size; ++i)
+ for(auto i = 0u; i < size; ++i)
{
const auto* face = navmesh->GetFace(i);
Vector3(face->center);
inPositions.emplace_back(Vector3(face->center));
- inPositions.back() -= Vector3( upFromGravity );
+ inPositions.back() -= Vector3(upFromGravity);
expectedResult.emplace_back(true);
expectedPositions.emplace_back(face->center);
// Middle 'circle' of scene
inPositions.emplace_back(Vector3(-0.048838f, 0.039285f, 0.013085f));
expectedPositions.emplace_back();
- expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedFaceIndex.emplace_back(NavigationMesh::NULL_FACE);
expectedResult.emplace_back(false);
// Triangle under stairs
inPositions.emplace_back(Vector3(0.44365f, -1.787f, 0.13085f));
expectedPositions.emplace_back();
- expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedFaceIndex.emplace_back(NavigationMesh::NULL_FACE);
expectedResult.emplace_back(false);
// Outside area
inPositions.emplace_back(Vector3(0.77197f, -3.8596f, 0.13085f));
expectedPositions.emplace_back();
- expectedFaceIndex.emplace_back( NavigationMesh::NULL_FACE );
+ expectedFaceIndex.emplace_back(NavigationMesh::NULL_FACE);
expectedResult.emplace_back(false);
- for( auto i = 0u; i < inPositions.size(); ++i )
+ for(auto i = 0u; i < inPositions.size(); ++i)
{
- Vector3 outPosition;
- uint32_t faceIndex {NavigationMesh::NULL_FACE};
- auto result = navmesh->FindFloor(inPositions[i], outPosition, faceIndex);
- DALI_TEST_EQUALS( bool(result), bool(expectedResult[i]), TEST_LOCATION);
- DALI_TEST_EQUALS( faceIndex, expectedFaceIndex[i], TEST_LOCATION);
- DALI_TEST_EQUALS( outPosition, expectedPositions[i], TEST_LOCATION);
+ Vector3 outPosition;
+ FaceIndex faceIndex{NavigationMesh::NULL_FACE};
+ auto result = navmesh->FindFloor(inPositions[i], outPosition, faceIndex);
+ DALI_TEST_EQUALS(bool(result), bool(expectedResult[i]), TEST_LOCATION);
+ DALI_TEST_EQUALS(faceIndex, expectedFaceIndex[i], TEST_LOCATION);
+ DALI_TEST_EQUALS(outPosition, expectedPositions[i], TEST_LOCATION);
}
END_TEST;
navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
{
- auto faceIndex = 0u;
- auto position = Vector3( 0, 0, 0);
+ auto faceIndex = FaceIndex(0u);
+ auto position = Vector3(0, 0, 0);
auto dontCheckNeighbours = true;
- auto outPosition = Vector3();
- auto expectedPosition = Vector3();
- bool result = false;
+ auto outPosition = Vector3();
+ auto expectedPosition = Vector3();
+ bool result = false;
{
// test 1. position lies within selected triangle
- faceIndex = 137;
- position = Vector3(-6.0767f, -1.7268f, 4.287f);
- expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
+ faceIndex = 137;
+ position = Vector3(-6.0767f, -1.7268f, 4.287f);
+ expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
dontCheckNeighbours = true;
- result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
- DALI_TEST_EQUALS( result, true, TEST_LOCATION);
- DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(outPosition, expectedPosition, TEST_LOCATION);
}
{
// test 2. position lies outside selected triangle, not checking neighbours
- faceIndex = 137;
- position = Vector3(-5.3073f, -0.6023f, 4.287f);
- expectedPosition = Vector3::ZERO;
- outPosition = Vector3::ZERO;
+ faceIndex = 137;
+ position = Vector3(-5.3073f, -0.6023f, 4.287f);
+ expectedPosition = Vector3::ZERO;
+ outPosition = Vector3::ZERO;
dontCheckNeighbours = true;
- result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
- DALI_TEST_EQUALS( result, false, TEST_LOCATION);
- DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ DALI_TEST_EQUALS(result, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(outPosition, expectedPosition, TEST_LOCATION);
}
{
// test 3. position lies outside selected triangle but this time checking neighbours
- faceIndex = 137;
- position = Vector3(-5.3073f, -0.6023f, 4.287f);
- expectedPosition = Vector3(-5.3073, -0.6023, 3.83);
- outPosition = Vector3::ZERO;
+ faceIndex = 137;
+ position = Vector3(-5.3073f, -0.6023f, 4.287f);
+ expectedPosition = Vector3(-5.3073, -0.6023, 3.83);
+ outPosition = Vector3::ZERO;
dontCheckNeighbours = false;
- result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
+ result = navmesh->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
- DALI_TEST_EQUALS( result, true, TEST_LOCATION);
- DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(outPosition, expectedPosition, TEST_LOCATION);
}
}
navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
{
- [[maybe_unused]] auto faceIndex = 0u;
- auto position = Vector3( 0, 0, 0);
- auto dontCheckNeighbours = true;
- auto outPosition = Vector3();
- auto expectedPosition = Vector3();
- bool result = false;
+ [[maybe_unused]] auto faceIndex = 0u;
+ auto position = Vector3(0, 0, 0);
+ auto dontCheckNeighbours = true;
+ auto outPosition = Vector3();
+ auto expectedPosition = Vector3();
+ bool result = false;
{
// test 4. position lies within a triangle but this time full search forced,
// the navmesh must have no previous searches (mCurrentFace shouldn't be set)
- faceIndex = 137;
- position = Vector3(-6.0767f, -1.7268f, 4.287f);
- expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
+ faceIndex = 137;
+ position = Vector3(-6.0767f, -1.7268f, 4.287f);
+ expectedPosition = Vector3(-6.0767f, -1.7268f, 3.83f);
dontCheckNeighbours = true;
- result = navmesh->FindFloorForFace(position, NavigationMesh::NULL_FACE, dontCheckNeighbours, outPosition);
+ result = navmesh->FindFloorForFace(position, NavigationMesh::NULL_FACE, dontCheckNeighbours, outPosition);
- DALI_TEST_EQUALS( result, true, TEST_LOCATION);
- DALI_TEST_EQUALS( outPosition, expectedPosition, TEST_LOCATION);
+ DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(outPosition, expectedPosition, TEST_LOCATION);
}
-
}
END_TEST;
*
*/
+#include <dali-test-suite-utils.h>
#include "dali-scene3d/public-api/algorithm/navigation-mesh.h"
#include "dali-scene3d/public-api/algorithm/path-finder.h"
#include "dali-scene3d/public-api/loader/navigation-mesh-factory.h"
-#include <dali-test-suite-utils.h>
using namespace Dali;
using namespace Dali::Scene3D::Algorithm;
using namespace Dali::Scene3D::Loader;
-bool CompareResults( const std::vector<uint32_t>& nodes, const WayPointList& waypoints )
+bool CompareResults(const std::vector<FaceIndex>& nodes, const WayPointList& waypoints)
{
if(nodes.size() != waypoints.size())
{
oss << waypoint.GetNavigationMeshFaceIndex() << ", ";
}
oss << "]\n";
- tet_printf("%s\n",oss.str().c_str());
+ tet_printf("%s\n", oss.str().c_str());
return false;
}
- for(auto i = 0u; i < nodes.size(); ++i )
+ for(auto i = 0u; i < nodes.size(); ++i)
{
if(nodes[i] != waypoints[i].GetNavigationMeshFaceIndex())
{
oss << waypoint.GetNavigationMeshFaceIndex() << ", ";
}
oss << "]\n";
- tet_printf("%s\n",oss.str().c_str());
+ tet_printf("%s\n", oss.str().c_str());
return false;
}
}
int UtcDaliPathFinderNewP(void)
{
- auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
- auto pathfinder = PathFinder::New( *navmesh, PathFinderAlgorithm::DEFAULT );
+ auto pathfinder = PathFinder::New(*navmesh, PathFinderAlgorithm::DEFAULT);
DALI_TEST_CHECK(navmesh);
DALI_TEST_CHECK(pathfinder);
int UtcDaliPathFinderNewFail(void)
{
- auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
- auto pathfinder = PathFinder::New( *navmesh, static_cast<PathFinderAlgorithm>(-1) );
+ auto pathfinder = PathFinder::New(*navmesh, static_cast<PathFinderAlgorithm>(-1));
DALI_TEST_CHECK(navmesh);
DALI_TEST_CHECK(!pathfinder);
END_TEST;
}
-void printWaypointForPython( WayPointList& waypoints)
+void printWaypointForPython(WayPointList& waypoints)
{
- tet_printf( "size: %d\n", waypoints.size());
- tet_printf( "[");
+ tet_printf("size: %d\n", waypoints.size());
+ tet_printf("[");
for(auto& wp : waypoints)
{
auto index = wp.GetNavigationMeshFaceIndex();
tet_printf("%d, ", index);
}
- tet_printf( "]");
+ tet_printf("]");
}
int UtcDaliPathFinderFindShortestPath0(void)
{
- auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
std::vector<PathFinderAlgorithm> testAlgorithms = {
PathFinderAlgorithm::DJIKSTRA_SHORTEST_PATH,
for(const auto& algorithm : testAlgorithms)
{
tet_printf("Test algorithm type : %d\n", static_cast<int>(algorithm));
- auto pathfinder = PathFinder::New( *navmesh, algorithm );
+ auto pathfinder = PathFinder::New(*navmesh, algorithm);
DALI_TEST_CHECK(navmesh);
DALI_TEST_CHECK(pathfinder);
DALI_TEST_NOT_EQUALS(int(waypoints.size()), 0, 0, TEST_LOCATION);
// Results are verified in the Blender
- std::vector<uint32_t> expectedResults =
+ std::vector<FaceIndex> expectedResults =
{18, 97, 106, 82, 50, 139};
DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
//printWaypointForPython(waypoints);
// Results are verified in the Blender
- std::vector<uint32_t> expectedResults =
+ std::vector<FaceIndex> expectedResults =
{18, 97, 106, 82, 50, 6, 89, 33, 157};
DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
-
}
}
int UtcDaliPathFinderFindShortestPath1(void)
{
- auto navmesh = NavigationMeshFactory::CreateFromFile( "resources/navmesh-test.bin");
+ auto navmesh = NavigationMeshFactory::CreateFromFile("resources/navmesh-test.bin");
// All coordinates in navmesh local space
- navmesh->SetSceneTransform( Matrix(Matrix::IDENTITY));
+ navmesh->SetSceneTransform(Matrix(Matrix::IDENTITY));
std::vector<PathFinderAlgorithm> testAlgorithms = {
PathFinderAlgorithm::DJIKSTRA_SHORTEST_PATH,
for(const auto& algorithm : testAlgorithms)
{
tet_printf("Test algorithm type : %d\n", static_cast<int>(algorithm));
- auto pathfinder = PathFinder::New( *navmesh, algorithm );
+ auto pathfinder = PathFinder::New(*navmesh, algorithm);
DALI_TEST_CHECK(navmesh);
DALI_TEST_CHECK(pathfinder);
{
Vector3 from(-6.0767, -1.7268, 0.1438); // ground floor
- Vector3 to(-6.0767, -1.7268, 4.287); // first floor
+ Vector3 to(-6.0767, -1.7268, 4.287); // first floor
auto waypoints = pathfinder->FindPath(from, to);
DALI_TEST_NOT_EQUALS(int(waypoints.size()), 0, 0, TEST_LOCATION);
// Results are verified in the Blender
- std::vector<uint32_t> expectedResults =
- {154, 58, 85, 106, 128, 132, 137};
+ std::vector<FaceIndex> expectedResults =
+ {154, 58, 85, 106, 128, 132, 137};
DALI_TEST_EQUALS(CompareResults(expectedResults, waypoints), true, TEST_LOCATION);
// Verify last and first points by finding floor points
{
- Vector3 verifyPos = Vector3::ZERO;
- uint32_t verifyIndex = NavigationMesh::NULL_FACE;
- auto result = navmesh->FindFloor(from, verifyPos, verifyIndex);
+ Vector3 verifyPos = Vector3::ZERO;
+ FaceIndex verifyIndex = NavigationMesh::NULL_FACE;
+ auto result = navmesh->FindFloor(from, verifyPos, verifyIndex);
DALI_TEST_EQUALS(result, true, TEST_LOCATION);
DALI_TEST_EQUALS(verifyPos, waypoints[0].GetScenePosition(), TEST_LOCATION);
}
{
- Vector3 verifyPos = Vector3::ZERO;
- uint32_t verifyIndex = NavigationMesh::NULL_FACE;
- auto result = navmesh->FindFloor(to, verifyPos, verifyIndex);
+ Vector3 verifyPos = Vector3::ZERO;
+ FaceIndex verifyIndex = NavigationMesh::NULL_FACE;
+ auto result = navmesh->FindFloor(to, verifyPos, verifyIndex);
DALI_TEST_EQUALS(result, true, TEST_LOCATION);
DALI_TEST_EQUALS(verifyPos, waypoints.back().GetScenePosition(), TEST_LOCATION);
#include <dali-toolkit/internal/texture-manager/texture-upload-observer.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
#include <dali-toolkit/internal/visuals/visual-factory-impl.h> ///< For VisualFactory's member TextureManager.
+#include <dali-toolkit/public-api/image-loader/image-url.h>
+#include <dali-toolkit/public-api/image-loader/image.h>
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#include <test-encoded-image-buffer.h>
END_TEST;
}
+int UtcTextureManagerExternalTexture(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcTextureManagerExternalTexture check TextureManager using external texture works well");
+
+ auto visualFactory = Toolkit::VisualFactory::Get();
+ auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); // Use VisualFactory's texture manager
+
+ TestObserver observer1;
+ TestObserver observer2;
+
+ auto textureId1(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId2(TextureManager::INVALID_TEXTURE_ID);
+ std::string maskname(TEST_MASK_FILE_NAME);
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ maskInfo.reset(new TextureManager::MaskingData());
+ maskInfo->mAlphaMaskUrl = maskname;
+ maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo->mCropToMask = true;
+ maskInfo->mContentScaleFactor = 1.0f;
+ Vector4 atlasRect(0.f, 0.f, 1.f, 1.f);
+ Dali::ImageDimensions atlasRectSize(0, 0);
+ bool synchronousLoading(false);
+ bool atlasingStatus(false);
+ bool loadingStatus(false);
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+ ImageAtlasManagerPtr atlasManager = nullptr;
+ Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+ uint32_t width(64);
+ uint32_t height(64);
+ uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+ PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+ DALI_TEST_CHECK(pixelData);
+
+ Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+ std::string url = imageUrl.GetUrl();
+
+ TextureSet texture1 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ TextureSet texture2 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(textureId1 == textureId2, true, TEST_LOCATION);
+
+ texture1 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ texture2 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(textureId1 == textureId2, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(texture1 != texture2, true, TEST_LOCATION);
+
+ END_TEST;
+}
+
int UtcTextureManagerEncodedImageBufferReferenceCount(void)
{
ToolkitTestApplication application;
utc-Dali-JsonParser.cpp
utc-Dali-KeyInputFocusManager.cpp
utc-Dali-PageTurnView.cpp
+ utc-Dali-ParticleSystem.cpp
utc-Dali-Scene3dView.cpp
utc-Dali-ScrollBar.cpp
utc-Dali-ScrollView.cpp
ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES})
TARGET_LINK_LIBRARIES(${EXEC_NAME}
${${CAPI_LIB}_LIBRARIES}
- -lpthread --coverage
+ -lpthread --coverage -ldl
)
INSTALL(PROGRAMS ${EXEC_NAME}
ToolkitTestApplication application;
tet_infoline("UtcDaliAnimatedVectorImageVisualPlayRangeMarker");
+ // Set 1 marker as array
Property::Array array;
array.PushBack(VECTOR_ANIMATION_MARKER_NAME_1);
DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_START_FRAME_1, resultStartFrame, TEST_LOCATION);
DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_END_FRAME_1, resultEndFrame, TEST_LOCATION);
+ // Set 1 marker as string
+ array.Clear();
+
+ attributes.Clear();
+ attributes.Add(DevelImageVisual::Property::PLAY_RANGE, VECTOR_ANIMATION_MARKER_NAME_1);
+ DevelControl::DoAction(actor, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+ // To make event trigger
+ actor.SetProperty(Actor::Property::SIZE, Vector2(40.0f, 40.0f));
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ map = actor.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+ value = map.Find(DevelImageVisual::Property::PLAY_RANGE);
+
+ result = value->GetArray();
+ result->GetElementAt(0).Get(resultStartFrame);
+ result->GetElementAt(1).Get(resultEndFrame);
+
+ DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_START_FRAME_1, resultStartFrame, TEST_LOCATION);
+ DALI_TEST_EQUALS(VECTOR_ANIMATION_MARKER_END_FRAME_1, resultEndFrame, TEST_LOCATION);
+
// Set 2 markers
array.Clear();
array.PushBack(VECTOR_ANIMATION_MARKER_NAME_1);
ToolkitTestApplication application;
tet_infoline("Use FrameBuffer as url");
- uint32_t width(64);
- uint32_t height(64);
+ uint32_t width(64);
+ uint32_t height(64);
FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
DALI_TEST_CHECK(frameBuffer);
ToolkitTestApplication application;
tet_infoline("Use FrameBuffer as url");
- uint32_t width(64);
- uint32_t height(64);
+ uint32_t width(64);
+ uint32_t height(64);
FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
DALI_TEST_CHECK(frameBuffer);
END_TEST;
}
+int UtcDaliImageVisualWithPixelDataMasking(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Load external texture with mask");
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+
+ uint32_t width(64);
+ uint32_t height(64);
+ uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+ PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+ DALI_TEST_CHECK(pixelData);
+
+ ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+ std::string url = imageUrl.GetUrl();
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK(factory);
+
+ Property::Map propertyMap;
+ propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, url);
+ propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+ DALI_TEST_CHECK(visual);
+
+ Property::Map testMap;
+ visual.CreatePropertyMap(testMap);
+ DALI_TEST_EQUALS(*testMap.Find(ImageVisual::Property::ALPHA_MASK_URL), Property::Value(TEST_MASK_IMAGE_FILE_NAME), TEST_LOCATION);
+
+ DummyControl actor = DummyControl::New();
+ DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+ dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+ DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+ application.GetScene().Add(actor);
+ application.SendNotification();
+ application.Render(16);
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+ dummyImpl.UnregisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1);
+ DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliImageVisualWithPixelDataMaskingSynchronously(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Load synchronously external texture with mask");
+
+ uint32_t width(64);
+ uint32_t height(64);
+ uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+ PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+ DALI_TEST_CHECK(pixelData);
+
+ ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+ std::string url = imageUrl.GetUrl();
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK(factory);
+
+ Property::Map propertyMap;
+ propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, url);
+ propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+ propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+ DALI_TEST_CHECK(visual);
+
+ Property::Map testMap;
+ visual.CreatePropertyMap(testMap);
+ DALI_TEST_EQUALS(*testMap.Find(ImageVisual::Property::ALPHA_MASK_URL), Property::Value(TEST_MASK_IMAGE_FILE_NAME), TEST_LOCATION);
+
+ DummyControl actor = DummyControl::New();
+ DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+ dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+ DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+ application.GetScene().Add(actor);
+
+ DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render(16);
+
+ END_TEST;
+}
+
int UtcDaliImageVisualWithNativeImage(void)
{
ToolkitTestApplication application;
--- /dev/null
+/*
+ * 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-domain.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+#include <dali-test-suite-utils.h>
+
+#include <dlfcn.h>
+
+#include <future>
+
+using namespace Dali;
+using namespace Dali::Toolkit::ParticleSystem;
+
+/**
+ * Helper function to invoke next function in the call chain
+ */
+template<class R, class T, class... Args>
+R InvokeNext(T* pObj, Args... args)
+{
+ Dl_info info;
+ dladdr(__builtin_return_address(0), &info);
+ using Func = R(*)(T*,Args...);
+ auto sym = (Func)(dlsym(RTLD_NEXT, info.dli_sname));
+ return sym(pObj, args...);
+}
+
+// Create fake time getter
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+struct ParticleEmitter
+{
+ [[nodiscard]] std::chrono::milliseconds GetCurrentTimeMillis() const;
+
+ static std::chrono::milliseconds currentTime;
+
+ static void AdvanceTimeByMs( uint32_t ms)
+ {
+ currentTime += std::chrono::milliseconds(ms);
+ }
+};
+
+std::chrono::milliseconds ParticleEmitter::currentTime(1u);
+
+std::chrono::milliseconds ParticleEmitter::GetCurrentTimeMillis() const
+{
+ [[maybe_unused]] auto value = InvokeNext<std::chrono::milliseconds>(this);
+ return std::chrono::milliseconds(currentTime);
+}
+}
+
+using ParticleEmitterWrapper = Dali::Toolkit::ParticleSystem::Internal::ParticleEmitter;
+
+Texture CreateTexture()
+{
+ Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, 100, 100);
+ uint8_t* data = reinterpret_cast<uint8_t*>(malloc(100*100*4));
+ PixelData pixelData = PixelData::New(data, 100*100*4, 100, 100, Pixel::Format::RGBA8888, PixelData::FREE);
+ texture.Upload(pixelData);
+ return texture;
+}
+
+
+/**
+ * Test particle source
+ */
+class TestSource : public ParticleSourceInterface
+{
+public:
+
+ TestSource(ParticleEmitter* emitter)
+ {
+ mEmitter = *emitter;
+ }
+
+ void NewFrame()
+ {
+ mPromise = std::promise<uint32_t>();
+ mFuture = mPromise.get_future();
+ }
+
+ uint32_t Update(ParticleList& outList, uint32_t count) override
+ {
+ mPromise.set_value(count);
+ auto i = count;
+ while(--i)
+ {
+ outList.NewParticle(1.0f);
+ }
+ return count;
+ }
+
+ void Init() override
+ {
+ // calls initialized
+ mInitialized = true;
+ }
+
+ bool mInitialized{false};
+ std::future<uint32_t> mFuture;
+ std::promise<uint32_t> mPromise;
+ ParticleEmitter mEmitter;
+};
+
+class TestSource2 : public ParticleSourceInterface
+{
+public:
+
+ TestSource2(ParticleEmitter* emitter)
+ {
+ mEmitter = *emitter;
+ }
+
+ void NewFrame()
+ {
+ mPromise = std::promise<uint32_t>();
+ mFuture = mPromise.get_future();
+ }
+
+ uint32_t Update(ParticleList& outList, uint32_t count) override
+ {
+ mPromise.set_value(count);
+
+ while(count--)
+ {
+ auto particle = outList.NewParticle(1.0);
+ if(!particle)
+ {
+ return 0u;
+ }
+
+ [[maybe_unused]] auto& pos = particle.GetByIndex<Vector3>(mStreamBasePos);
+
+ [[maybe_unused]] auto& gpos = particle.Get<Vector3>(ParticleStream::POSITION_STREAM_BIT);
+ [[maybe_unused]] auto& col = particle.Get<Vector4>(ParticleStream::COLOR_STREAM_BIT);
+ [[maybe_unused]] auto& vel = particle.Get<Vector3>(ParticleStream::VELOCITY_STREAM_BIT);
+ [[maybe_unused]] auto& sca = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+ //auto& basePos = particle.Get<Vector3>(ParticleStream::SCALE_STREAM_BIT);
+ }
+
+ return count;
+ }
+
+ void Init() override
+ {
+ // calls initialized
+ mStreamBasePos = mEmitter.GetParticleList().AddLocalStream<Vector3>(Vector3::ZERO);
+ mInitialized = true;
+ }
+
+ bool mInitialized{false};
+ std::future<uint32_t> mFuture;
+ std::promise<uint32_t> mPromise;
+ uint32_t mStreamBasePos{0u};
+ ParticleEmitter mEmitter;
+
+};
+/**
+ * Sample of FlameModifier
+ */
+struct TestModifier : public ParticleModifierInterface
+{
+ void Update(ParticleList& particleList, uint32_t firstParticleIndex, uint32_t particleCount) override
+ {
+
+ }
+};
+
+struct TestModifierMT : public ParticleModifierInterface
+{
+ void Update(ParticleList& particleList, uint32_t firstParticleIndex, uint32_t particleCount) override
+ {
+
+ }
+
+ bool IsMultiThreaded() override
+ {
+ return true;
+ }
+};
+
+/**
+ * Another modifier to test modifier stack
+ */
+struct TestModifier2 : public ParticleModifierInterface
+{
+ void Update(ParticleList& particleList, uint32_t firstParticleIndex, uint32_t particleCount) override
+ {
+
+ }
+};
+
+struct EmitterGroup
+{
+ ParticleEmitter emitter;
+ ParticleRenderer renderer;
+ ParticleModifier modifier;
+ ParticleSource source;
+};
+
+// Helper function to create emitter (every test will be doing that)
+template<class SOURCE, class MODIFIER>
+ParticleEmitter CreateEmitter(EmitterGroup* output = nullptr)
+{
+ auto emitter = ParticleEmitter::New();
+
+ bool result = (emitter != nullptr);
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION );
+
+ // Create test source
+ auto source = ParticleSource::New<SOURCE>(&emitter);
+
+ {
+ BaseHandle handle(source);
+ auto newHandle = ParticleSource::DownCast(handle);
+ DALI_TEST_EQUALS(newHandle, source, TEST_LOCATION);
+ }
+
+ // Create test renderer
+ auto renderer = ParticleRenderer::New();
+
+ {
+ BaseHandle handle(renderer);
+ auto newHandle = ParticleRenderer::DownCast(handle);
+ DALI_TEST_EQUALS(newHandle, renderer, TEST_LOCATION);
+ }
+
+ // Create modifier
+ auto modifier = ParticleModifier::New<MODIFIER>();
+
+ {
+ BaseHandle handle(modifier);
+ auto newHandle = ParticleModifier::DownCast(handle);
+ DALI_TEST_EQUALS(newHandle, modifier, TEST_LOCATION);
+ }
+
+ auto domain = ParticleDomain::New();
+
+ {
+ BaseHandle handle(domain);
+ auto newHandle = ParticleDomain::DownCast(handle);
+ DALI_TEST_EQUALS(newHandle, domain, TEST_LOCATION);
+ }
+
+ // Test emitter readiness
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::INCOMPLETE, TEST_LOCATION);
+
+ // Attach all components to the emitter
+ emitter.SetSource( source );
+ emitter.SetRenderer( renderer );
+ emitter.AddModifier( modifier );
+ emitter.SetDomain( domain );
+
+ auto domain0 = emitter.GetDomain();
+ auto renderer0 = emitter.GetRenderer();
+
+ DALI_TEST_EQUALS( renderer0, renderer, TEST_LOCATION);
+ DALI_TEST_EQUALS( domain0, domain, TEST_LOCATION);
+
+ if(output)
+ {
+ output->emitter = emitter;
+ output->renderer = renderer;
+ output->modifier = modifier;
+ output->source = source;
+ }
+
+ return emitter;
+}
+
+int UtcDaliParticleSystemEmitterNew(void)
+{
+ // create particle emitter
+ auto emitter = ParticleEmitter::New();
+
+ bool result = (emitter != nullptr);
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION );
+
+ // Create test source
+ auto source = ParticleSource::New<TestSource>(&emitter);
+
+ // Create test renderer
+ auto renderer = ParticleRenderer::New();
+
+ // Create modifier
+ auto modifier = ParticleModifier::New<TestModifier>();
+
+ // Create domain
+ auto domain = ParticleDomain::New();
+
+ // Test emitter readiness
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::INCOMPLETE, TEST_LOCATION);
+
+ // Attach all components to the emitter
+ emitter.SetSource( source );
+ emitter.SetRenderer( renderer );
+ emitter.AddModifier( modifier );
+ emitter.SetDomain( domain );
+
+ // test status again (domain is optional);
+ ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemEmitterModifierStack(void)
+{
+ // create particle emitter
+ auto emitter = ParticleEmitter::New();
+
+ bool result = (emitter != nullptr);
+ DALI_TEST_EQUALS( result, true, TEST_LOCATION );
+
+ // Create test source
+ auto source = ParticleSource::New<TestSource>(&emitter);
+
+ // Create test renderer
+ auto renderer = ParticleRenderer::New();
+
+ // Create modifier
+ auto modifier0 = ParticleModifier::New<TestModifier>();
+ auto modifier1 = ParticleModifier::New<TestModifier>();
+ auto modifier2 = ParticleModifier::New<TestModifier>();
+
+ // Create domain
+ auto domain = ParticleDomain::New();
+
+ // Test emitter readiness
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::INCOMPLETE, TEST_LOCATION);
+
+ // Attach all components to the emitter
+ emitter.SetSource( source );
+ emitter.SetRenderer( renderer );
+ emitter.AddModifier( modifier0 );
+ emitter.AddModifier( modifier1 );
+ emitter.AddModifier( modifier2 );
+
+ emitter.SetDomain( domain );
+
+ // test status again (domain is optional);
+ ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ auto modifier = emitter.GetModifierAt(1);
+ DALI_TEST_EQUALS(modifier, modifier1, TEST_LOCATION);
+
+ emitter.RemoveModifierAt(0);
+ modifier = emitter.GetModifierAt(0);
+ DALI_TEST_EQUALS(modifier, modifier1, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTest(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ auto emitter = CreateEmitter<TestSource, TestModifier>();
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Set initial parameters of system
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 5000 );
+
+ // Test getters
+ auto initialParticleCount = emitter.GetInitialParticleCount();
+ auto activeParticlesLimit = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS(initialParticleCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(activeParticlesLimit, 5000, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = dynamic_cast<TestSource&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 1000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTestWithTextureScreen(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ EmitterGroup group;
+
+ auto emitter = CreateEmitter<TestSource, TestModifier>(&group);
+
+ // Blending mode with screen
+ auto texture = CreateTexture();
+ group.renderer.SetTexture( texture );
+ group.renderer.SetBlendingMode( BlendingMode::SCREEN );
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Set initial parameters of system
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 5000 );
+
+ // Test getters
+ auto initialParticleCount = emitter.GetInitialParticleCount();
+ auto activeParticlesLimit = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS(initialParticleCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(activeParticlesLimit, 5000, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = dynamic_cast<TestSource&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 1000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTestWithTextureAdd(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ EmitterGroup group;
+
+ auto emitter = CreateEmitter<TestSource, TestModifier>(&group);
+
+ // Blending mode with screen
+ auto texture = CreateTexture();
+ group.renderer.SetTexture( texture );
+ group.renderer.SetBlendingMode( BlendingMode::DEFAULT );
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Set initial parameters of system
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 5000 );
+
+ // Test getters
+ auto initialParticleCount = emitter.GetInitialParticleCount();
+ auto activeParticlesLimit = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS(initialParticleCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(activeParticlesLimit, 5000, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = dynamic_cast<TestSource&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 1000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTestInitialSetup(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ EmitterGroup group;
+
+ auto emitter = CreateEmitter<TestSource, TestModifier>(&group);
+
+ emitter.SetEmissionRate( 1000 );
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 10000 );
+
+ auto emissionRate = emitter.GetEmissionRate();
+ auto initialCount = emitter.GetInitialParticleCount();
+ auto activeCount = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS( emissionRate, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS( initialCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS( activeCount, 10000, TEST_LOCATION);
+
+ // Blending mode with screen
+ auto texture = CreateTexture();
+ group.renderer.SetTexture( texture );
+ group.renderer.SetBlendingMode( BlendingMode::DEFAULT );
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Set initial parameters of system
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 5000 );
+
+ // Test getters
+ auto initialParticleCount = emitter.GetInitialParticleCount();
+ auto activeParticlesLimit = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS(initialParticleCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(activeParticlesLimit, 5000, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = dynamic_cast<TestSource&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 1000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTestMT(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ EmitterGroup group;
+
+ auto emitter = CreateEmitter<TestSource, TestModifierMT>(&group);
+
+ emitter.SetEmissionRate( 10000 );
+ emitter.SetInitialParticleCount( 10000 );
+ emitter.SetActiveParticlesLimit( 20000 );
+ emitter.SetParticleCount( 300000 );
+
+ auto emissionRate = emitter.GetEmissionRate();
+ auto initialCount = emitter.GetInitialParticleCount();
+ auto activeCount = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS( emissionRate, 10000, TEST_LOCATION);
+ DALI_TEST_EQUALS( initialCount, 10000, TEST_LOCATION);
+ DALI_TEST_EQUALS( activeCount, 20000, TEST_LOCATION);
+
+ emitter.EnableParallelProcessing(true);
+
+ auto mtEnabled = emitter.IsParallelProcessingEnabled();
+ DALI_TEST_EQUALS(mtEnabled, true, TEST_LOCATION);
+
+ // Blending mode with screen
+ auto texture = CreateTexture();
+ group.renderer.SetTexture( texture );
+ group.renderer.SetBlendingMode( BlendingMode::DEFAULT );
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = dynamic_cast<TestSource&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 10000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliParticleSystemTestParticleSource(void)
+{
+ TestApplication application;
+
+ // Create actor to be used with emitter
+ Actor actor = Actor::New();
+ application.GetScene().Add(actor);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(100, 100));
+
+ EmitterGroup group;
+
+ auto emitter = CreateEmitter<TestSource2, TestModifier>(&group);
+
+ emitter.SetEmissionRate( 1000 );
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 10000 );
+
+ auto emissionRate = emitter.GetEmissionRate();
+ auto initialCount = emitter.GetInitialParticleCount();
+ auto activeCount = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS( emissionRate, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS( initialCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS( activeCount, 10000, TEST_LOCATION);
+
+ emitter.EnableParallelProcessing(true);
+
+ // Blending mode with screen
+ auto texture = CreateTexture();
+ group.renderer.SetTexture( texture );
+ group.renderer.SetBlendingMode( BlendingMode::DEFAULT );
+
+ // test status again (domain is optional);
+ auto ready = emitter.GetStatus();
+
+ // Emitter should return status incomplete
+ DALI_TEST_EQUALS(ready, ParticleEmitter::Status::READY, TEST_LOCATION);
+
+ // Set initial parameters of system
+ emitter.SetInitialParticleCount( 1000 );
+ emitter.SetActiveParticlesLimit( 5000 );
+
+ // Test getters
+ auto initialParticleCount = emitter.GetInitialParticleCount();
+ auto activeParticlesLimit = emitter.GetActiveParticlesLimit();
+
+ DALI_TEST_EQUALS(initialParticleCount, 1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(activeParticlesLimit, 5000, TEST_LOCATION);
+
+ // Attach emitter to actor
+ emitter.AttachTo(actor);
+
+ // Start emitter
+ emitter.Start();
+
+ auto status = emitter.GetStatus();
+ DALI_TEST_EQUALS(status, ParticleEmitter::Status::STARTED, TEST_LOCATION);
+
+ auto& sourceCallback = static_cast<TestSource2&>(emitter.GetSource().GetSourceCallback());
+
+ // Run simulation
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // First call into source callback should emit initial number of particles
+ auto emittedParticleCount = sourceCallback.mFuture.get();
+ DALI_TEST_EQUALS(emittedParticleCount, 1000, TEST_LOCATION);
+
+ // Run 3 more frames advancing by 1000ms which should
+ // emit particles based on emission rate
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ ParticleEmitterWrapper::AdvanceTimeByMs(1000);
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ // Stop the emitter
+ emitter.Stop();
+
+ sourceCallback.NewFrame();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
\ No newline at end of file
DESTINATION bin)
ENDIF()
-SET(BUILT_IN_SHADER_GEN_CPP "${GENERATED_SHADER_DIR}/generated/builtin-shader-gen.cpp")
+SET(BUILT_IN_SHADER_GEN_CPP "${GENERATED_SHADER_DIR}/generated/builtin-shader-gen.cpp" )
+
+FILE(GLOB SHADERS_SRC "${SHADER_SOURCE_DIR}/*.vert" "${SHADER_SOURCE_DIR}/*.frag" )
ADD_CUSTOM_COMMAND(OUTPUT ${BUILT_IN_SHADER_GEN_CPP}
- DEPENDS ${SHADER_GENERATOR_NAME}
+ DEPENDS ${SHADER_GENERATOR_NAME} ${SHADERS_SRC}
COMMAND ${SHADER_GENERATOR_BINARY} ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR})
SET(SOURCES ${SOURCES} ${BUILT_IN_SHADER_GEN_CPP})
SET(SHADER_GENERATOR_BINARY ${CMAKE_CURRENT_BINARY_DIR}/../${SHADER_GENERATOR_NAME})
ENDIF()
+FILE(GLOB SHADERS_SRC "${SHADER_SOURCE_DIR}/*.vert" "${SHADER_SOURCE_DIR}/*.frag")
SET( BUILT_IN_SHADER_GEN_CPP "${GENERATED_SHADER_DIR}/generated/builtin-shader-gen.cpp")
ADD_CUSTOM_COMMAND(OUTPUT ${BUILT_IN_SHADER_GEN_CPP}
- DEPENDS ${SHADER_GENERATOR_BINARY}
+ DEPENDS ${SHADER_GENERATOR_BINARY} ${SHADERS_SRC}
COMMAND ${SHADER_GENERATOR_BINARY} ${SHADER_SOURCE_DIR} ${SHADER_GENERATED_DIR})
SET( scene3d_src_files ${scene3d_src_files} ${BUILT_IN_SHADER_GEN_CPP} )
return mHeader.vertexCount;
}
-bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
+bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, FaceIndex faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
{
if(faceIndex == ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE)
{
// Ray direction matches gravity direction
ray.direction = Vector3(mHeader.gravityVector);
- IntersectResult result = NavigationRayFaceIntersection(ray, *GetFace(uint16_t(faceIndex)));
+ IntersectResult result = NavigationRayFaceIntersection(ray, *GetFace(faceIndex));
if(result.result)
{
outPosition = PointLocalToScene(result.point);
}
// Test neighbouring by edges
- const auto& poly = GetFace(uint16_t(faceIndex));
+ const auto& poly = GetFace(faceIndex);
// collect faces sharing edges
- std::vector<uint32_t> faces;
+ std::vector<FaceIndex> neighbourFaces;
- for(unsigned short i : poly->edge)
+ for(auto edgeIndex : poly->edge)
{
- const auto& edge = *GetEdge(i);
+ const auto& edge = *GetEdge(edgeIndex);
// store faces
- for(unsigned short j : edge.face)
+ for(auto edgeFaceIndex : edge.face)
{
- if(j != ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE && j != faceIndex)
+ if(edgeFaceIndex != ::Dali::Scene3D::Algorithm::NavigationMesh::NULL_FACE && edgeFaceIndex != faceIndex)
{
- faces.emplace_back(j);
+ neighbourFaces.emplace_back(edgeFaceIndex);
}
}
}
- if(faces.empty())
+ if(neighbourFaces.empty())
{
return false;
}
- for(const auto& p : faces)
+ for(const auto& neighbourFaceIndex : neighbourFaces)
{
- if(FindFloorForFace(position, p, true, outPosition))
+ if(FindFloorForFace(position, neighbourFaceIndex, true, outPosition))
{
- mCurrentFace = p;
+ mCurrentFace = neighbourFaceIndex;
return true;
}
}
*/
bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition)
{
- uint32_t polyIndex = 0;
- return FindFloor(position, outPosition, polyIndex);
+ FaceIndex faceIndex = 0u;
+ return FindFloor(position, outPosition, faceIndex);
}
-bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex)
+bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, FaceIndex& faceIndex)
{
[[maybe_unused]] auto newPos = PointSceneToLocal(Dali::Vector3(position));
const auto POLY_COUNT = GetFaceCount();
std::vector<IntersectResult> results;
- for(auto i = 0u; i < POLY_COUNT; ++i)
+ for(auto faceIndex = 0u; faceIndex < POLY_COUNT; ++faceIndex)
{
- auto result = NavigationRayFaceIntersection(ray, *GetFace(i));
+ auto result = NavigationRayFaceIntersection(ray, *GetFace(faceIndex));
if(result.result)
{
- result.faceIndex = i;
+ result.faceIndex = faceIndex;
results.emplace_back(result);
}
}
return true;
}
-const Poly* NavigationMesh::GetFace(int index) const
+const Poly* NavigationMesh::GetFace(FaceIndex index) const
{
auto* basePtr = reinterpret_cast<const Poly*>(mBuffer.data() + mHeader.dataOffset + mHeader.polyDataOffset);
return &basePtr[index];
}
-const Edge* NavigationMesh::GetEdge(int index) const
+const Edge* NavigationMesh::GetEdge(EdgeIndex index) const
{
auto* basePtr = reinterpret_cast<const Edge*>(mBuffer.data() + mHeader.dataOffset + mHeader.edgeDataOffset);
return &basePtr[index];
}
-const Vertex* NavigationMesh::GetVertex(int index) const
+const Vertex* NavigationMesh::GetVertex(VertexIndex index) const
{
auto* basePtr = reinterpret_cast<const Vertex*>(mBuffer.data() + mHeader.dataOffset + mHeader.vertexDataOffset);
return &basePtr[index];
}
-NavigationMesh::IntersectResult NavigationMesh::NavigationRayFaceIntersection(NavigationRay& ray, const Face& face)
+NavigationMesh::IntersectResult NavigationMesh::NavigationRayFaceIntersection(NavigationRay& ray, const Face& face) const
{
auto vi0 = *GetVertex(face.vertex[0]);
auto vi1 = *GetVertex(face.vertex[1]);
transform.InvertTransform(mTransformInverse);
}
-Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point)
+Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point) const
{
// Transform point into navmesh space
Dali::Vector4 invNewPos = mTransformInverse * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
return Dali::Vector3(invNewPos.AsFloat());
}
-Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point)
+Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point) const
{
// Transform point into scene transform space
Dali::Vector4 newPos = mTransform * Dali::Vector4(point.x, -point.y, point.z, 1.0f);
{
class NavigationRay;
+// Make each to change each index value's type here.
+using VertexIndex = Dali::Scene3D::Algorithm::VertexIndex;
+using EdgeIndex = Dali::Scene3D::Algorithm::EdgeIndex;
+using FaceIndex = Dali::Scene3D::Algorithm::FaceIndex;
+
/**
* @class NavigationMesh
*/
{
Dali::Vector3 point;
float distance;
- uint16_t faceIndex;
+ FaceIndex faceIndex;
bool result;
};
[[nodiscard]] uint32_t GetVertexCount() const;
/**
- * Looks for floor only within the face
- * @param[in] position Position to be projected onto the face
- * @param[in] faceIndex Face index
- * @param[in] dontCheckNeighbours states whether to traverse onto neighbouring faces
- * @param[out] outPosition Output position
- *
- * @return true if success
+ * @copydoc Dali::Scene3D::Algorithm::NavigationMesh::FindFloorForFace()
*/
- bool FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
+ bool FindFloorForFace(const Dali::Vector3& position, FaceIndex faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::FindFloor()
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::FindFloor()
*/
- bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex);
+ bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, FaceIndex& faceIndex);
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetFace()
*/
- [[nodiscard]] const Face* GetFace(int index) const;
+ [[nodiscard]] const Face* GetFace(FaceIndex index) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetEdge()
*/
- [[nodiscard]] const Edge* GetEdge(int index) const;
+ [[nodiscard]] const Edge* GetEdge(EdgeIndex index) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetVertex()
*/
- [[nodiscard]] const Vertex* GetVertex(int index) const;
+ [[nodiscard]] const Vertex* GetVertex(VertexIndex index) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::SetSceneTransform()
/**
* Tests intersection between navigation ray and face
*/
- IntersectResult NavigationRayFaceIntersection(NavigationRay& ray, const Face& face);
+ IntersectResult NavigationRayFaceIntersection(NavigationRay& ray, const Face& face) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::PointSceneToLocal()
*/
- Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point);
+ Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::PointLocalToScene()
*/
- Dali::Vector3 PointLocalToScene(const Dali::Vector3& point);
+ Dali::Vector3 PointLocalToScene(const Dali::Vector3& point) const;
/**
* @copydoc Dali::Scene3D::Algorithm::NavigationMesh::GetGravityVector()
private:
std::vector<uint8_t> mBuffer; //< Data buffer
NavigationMeshHeader_V10 mHeader; //< Navigation mesh header
- uint16_t mCurrentFace; //< Current face (last floor position)
+ FaceIndex mCurrentFace; //< Current face (last floor position)
Dali::Matrix mTransform; //< Transform matrix
Dali::Matrix mTransformInverse; //< Inverse of the transform matrix
};
Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo)
{
Dali::Vector3 outPosFrom;
- uint32_t polyIndexFrom;
+ FaceIndex polyIndexFrom;
auto result = mNavigationMesh->FindFloor(positionFrom, outPosFrom, polyIndexFrom);
Scene3D::Algorithm::WayPointList waypoints;
if(result)
{
Dali::Vector3 outPosTo;
- uint32_t polyIndexTo;
+ FaceIndex polyIndexTo;
result = mNavigationMesh->FindFloor(positionTo, outPosTo, polyIndexTo);
if(result)
return waypoints;
}
-Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex)
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmDjikstra::FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex)
{
- auto nodeCount = uint32_t(mNodes.size());
- std::vector<float> dist;
- std::vector<uint32_t> prev;
+ auto nodeCount = uint32_t(mNodes.size());
+ std::vector<float> dist;
+ std::vector<FaceIndex> prev;
dist.resize(mNodes.size());
prev.resize(mNodes.size());
} while(removeCount != nodeCount);
// Compute distances for each node back to the source
- auto u = targetPolyIndex;
- std::list<uint32_t> q;
+ auto u = targetPolyIndex;
+ std::list<FaceIndex> q;
if(prev[u] != Scene3D::Algorithm::NavigationMesh::NULL_FACE || u == sourcePolyIndex)
{
while(u != Scene3D::Algorithm::NavigationMesh::NULL_FACE)
mNodes.resize(faceCount);
// for each face build the list
+ // TODO : Currently, we are assume that FaceNodeIndex is matched with FaceIndex 1:1. This might be changed in future.
for(auto i = 0u; i < faceCount; ++i)
{
auto& node = mNodes[i];
* @param[in] polyIndexTo Index of end polygon
* @return List of waypoints for path
*/
- Scene3D::Algorithm::WayPointList FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex) override;
+ Scene3D::Algorithm::WayPointList FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex) override;
- [[nodiscard]] inline const NavigationMesh::Face* Face(uint32_t i) const
+ [[nodiscard]] inline const NavigationMesh::Face* Face(FaceIndex faceIndex) const
{
- return mNavigationMesh->GetFace(i);
+ return mNavigationMesh->GetFace(faceIndex);
}
/**
*/
struct FaceNode
{
- uint32_t faceIndex; ///< Index of face which is associated with node
-
// neighbours
- uint32_t faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
- uint32_t edges[3]; ///< List of edges (max 3 for a triangle)
- float weight[3]; ///< List of weights (by distance) to each neighbour
+ FaceIndex faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
+ EdgeIndex edges[3]; ///< List of edges (max 3 for a triangle)
+ float weight[3]; ///< List of weights (by distance) to each neighbour
};
+ using FaceNodeIndex = FaceIndex;
NavigationMesh* mNavigationMesh; ///< Pointer to a valid NavigationMesh
- std::vector<FaceNode> mNodes; ///< List of nodes
+ std::vector<FaceNode> mNodes; ///< List of nodes
};
} // namespace Dali::Scene3D::Internal::Algorithm
#endif // DALI_SCENE3D_INTERNAL_PATH_FINDER_DJIKSTRA_H
#include <dali-scene3d/internal/algorithm/path-finder-waypoint-data.h>
#include <dali-scene3d/public-api/algorithm/path-finder-waypoint.h>
-using WayPointList = Dali::Scene3D::Algorithm::WayPointList;
+using WayPointList = Dali::Scene3D::Algorithm::WayPointList;
+using FaceNodeIndex = Dali::Scene3D::Internal::Algorithm::PathFinderAlgorithmSPFADoubleWay::FaceNodeIndex;
namespace
{
*
* @param[in,out] components Container of components id stored.
* @param[in] index index what we want to get components's id.
- * @return uint32_t top-value of this components.
+ * @return FaceIndex top-value of this components.
*/
-uint32_t GetComponentId(std::vector<uint32_t>& components, uint32_t index)
+FaceNodeIndex GetComponentId(std::vector<FaceNodeIndex>& components, FaceNodeIndex index)
{
if(components[index] == index)
{
return index;
}
// Get my parent's components id, and update myself.
- uint32_t ret = GetComponentId(components, components[index]);
+ FaceNodeIndex ret = GetComponentId(components, components[index]);
return components[index] = ret;
}
* @param[in] index0 index of components what we want to be combined.
* @param[in] index1 index of components what we want to be combined.
*/
-void ComponentsCombine(std::vector<uint32_t>& components, std::vector<uint32_t>& componentsLevel, uint32_t index0, uint32_t index1)
+void ComponentsCombine(std::vector<FaceNodeIndex>& components, std::vector<FaceNodeIndex>& componentsLevel, FaceNodeIndex index0, FaceNodeIndex index1)
{
- uint32_t p0 = GetComponentId(components, index0);
- uint32_t p1 = GetComponentId(components, index1);
- if(p0 == p1)
+ FaceNodeIndex ancestor0 = GetComponentId(components, index0);
+ FaceNodeIndex ancestor1 = GetComponentId(components, index1);
+ if(ancestor0 == ancestor1)
{
return;
}
- if(componentsLevel[p0] < componentsLevel[p1])
+ if(componentsLevel[ancestor0] < componentsLevel[ancestor1])
{
- components[p0] = p1;
+ components[ancestor0] = ancestor1;
}
else
{
- components[p1] = p0;
- if(componentsLevel[p0] == componentsLevel[p1])
+ components[ancestor1] = ancestor0;
+ if(componentsLevel[ancestor0] == componentsLevel[ancestor1])
{
- ++componentsLevel[p0];
+ ++componentsLevel[ancestor0];
}
}
}
PathFinderAlgorithmSPFADoubleWay::~PathFinderAlgorithmSPFADoubleWay() = default;
-float PathFinderAlgorithmSPFADoubleWay::DistancePanaltyCalculate(uint32_t index) const noexcept
+float PathFinderAlgorithmSPFADoubleWay::DistancePanaltyCalculate(FaceIndex index) const noexcept
{
return dist[index] - priority[index] * PRIORITY_SCALE_FACTOR;
}
Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFADoubleWay::FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo)
{
Dali::Vector3 outPosFrom;
- uint32_t polyIndexFrom;
+ FaceIndex polyIndexFrom;
auto result = mNavigationMesh->FindFloor(positionFrom, outPosFrom, polyIndexFrom);
Scene3D::Algorithm::WayPointList waypoints;
if(result)
{
Dali::Vector3 outPosTo;
- uint32_t polyIndexTo;
+ FaceIndex polyIndexTo;
result = mNavigationMesh->FindFloor(positionTo, outPosTo, polyIndexTo);
if(result)
return waypoints;
}
-Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFADoubleWay::FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex)
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFADoubleWay::FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex)
{
// Fast return if source and target index is same.
if(sourcePolyIndex == targetPolyIndex)
}
// pair<navimesh FaceIndex, is backward direction>
- using queueItem = std::pair<uint32_t, uint8_t>;
+ using queueItem = std::pair<FaceIndex, uint8_t>;
std::list<queueItem> nodeQueue;
- std::unordered_set<uint32_t> usedPolyIndexs[2];
+ std::unordered_set<FaceIndex> usedPolyIndexs[2];
// Set distance of source and target
dist[sourcePolyIndex] = 0.0f;
usedPolyIndexs[0].insert(sourcePolyIndex);
usedPolyIndexs[1].insert(targetPolyIndex);
- bool foundPath = false;
- uint32_t forwardEndIndex = Scene3D::Algorithm::NavigationMesh::NULL_FACE;
- uint32_t backwardStartIndex = Scene3D::Algorithm::NavigationMesh::NULL_FACE;
+ bool foundPath = false;
+ FaceIndex forwardEndIndex = Scene3D::Algorithm::NavigationMesh::NULL_FACE;
+ FaceIndex backwardStartIndex = Scene3D::Algorithm::NavigationMesh::NULL_FACE;
const auto sourcePos = Dali::Vector3(Face(sourcePolyIndex)->center);
const auto targetPos = Dali::Vector3(Face(targetPolyIndex)->center);
}
// Build path of face index
- std::list<uint32_t> q;
+ std::list<FaceIndex> q;
{
- uint32_t u = forwardEndIndex;
+ FaceIndex u = forwardEndIndex;
while(u != Scene3D::Algorithm::NavigationMesh::NULL_FACE)
{
q.push_front(u);
}
}
{
- uint32_t u = backwardStartIndex;
+ FaceIndex u = backwardStartIndex;
while(u != Scene3D::Algorithm::NavigationMesh::NULL_FACE)
{
q.push_back(u);
queued.resize(faceCount);
// Temperal container for components level. It will be used for Union-Find algorithm.
- std::vector<uint32_t> componentLevels(faceCount);
+ std::vector<FaceNodeIndex> componentLevels(faceCount);
// Initialize path informations.
- for(auto i = 0u; i < faceCount; ++i)
+ for(FaceNodeIndex i = 0u; i < faceCount; ++i)
{
dist[i] = std::numeric_limits<float>::infinity();
priority[i] = -1.0f; // Initialize by negative value, that we didn't calculate yet.
}
// for each face build the list
- for(auto i = 0u; i < faceCount; ++i)
+ // TODO : Currently, we are assume that FaceNodeIndex is matched with FaceIndex 1:1. This might be changed in future.
+ for(FaceNodeIndex i = 0u; i < faceCount; ++i)
{
auto& node = mNodes[i];
const auto* face = mNavigationMesh->GetFace(i);
* @param[in] polyIndexTo Index of end polygon
* @return List of waypoints for path
*/
- Scene3D::Algorithm::WayPointList FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex) override;
+ Scene3D::Algorithm::WayPointList FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex) override;
- [[nodiscard]] inline const NavigationMesh::Face* Face(uint32_t i) const
+ [[nodiscard]] inline const NavigationMesh::Face* Face(FaceIndex faceIndex) const
{
- return mNavigationMesh->GetFace(i);
+ return mNavigationMesh->GetFace(faceIndex);
}
/**
*
* @param[in] index index of node what we want to calculate panantly.
*/
- float DistancePanaltyCalculate(uint32_t index) const noexcept;
+ float DistancePanaltyCalculate(FaceIndex index) const noexcept;
/**
* Structure describes single node of pathfinding algorithm
*/
struct FaceNode
{
- uint32_t faceIndex; ///< Index of face which is associated with node
-
// neighbours
- uint32_t faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
- uint32_t edges[3]; ///< List of edges (max 3 for a triangle)
- float weight[3]; ///< List of weights (by distance) to each neighbour
+ FaceIndex faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
+ EdgeIndex edges[3]; ///< List of edges (max 3 for a triangle)
+ float weight[3]; ///< List of weights (by distance) to each neighbour
};
+ using FaceNodeIndex = FaceIndex;
NavigationMesh* mNavigationMesh; ///< Pointer to a valid NavigationMesh
std::vector<FaceNode> mNodes; ///< List of nodes
private:
- std::vector<float> dist;
- std::vector<float> priority; ///< Cached priority value. It will be calculated by source & target poly index per every queries.
- std::vector<uint32_t> prevForward;
- std::vector<uint32_t> prevBackward;
- std::vector<uint32_t> componentIds; ///< Id of connected components per each nodes. It should be one of node's index.
- std::vector<bool> queued;
+ std::vector<float> dist;
+ std::vector<float> priority; ///< Cached priority value. It will be calculated by source & target poly index per every queries.
+ std::vector<FaceIndex> prevForward;
+ std::vector<FaceIndex> prevBackward;
+ std::vector<FaceNodeIndex> componentIds; ///< Id of connected components per each nodes. It should be one of node's index.
+ std::vector<bool> queued;
};
} // namespace Dali::Scene3D::Internal::Algorithm
#endif // DALI_SCENE3D_INTERNAL_PATH_FINDER_SPFA_DOUBLE_WAY_H
Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFA::FindPath(const Dali::Vector3& positionFrom, const Dali::Vector3& positionTo)
{
Dali::Vector3 outPosFrom;
- uint32_t polyIndexFrom;
+ FaceIndex polyIndexFrom;
auto result = mNavigationMesh->FindFloor(positionFrom, outPosFrom, polyIndexFrom);
Scene3D::Algorithm::WayPointList waypoints;
if(result)
{
Dali::Vector3 outPosTo;
- uint32_t polyIndexTo;
+ FaceIndex polyIndexTo;
result = mNavigationMesh->FindFloor(positionTo, outPosTo, polyIndexTo);
if(result)
return waypoints;
}
-Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFA::FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex)
+Scene3D::Algorithm::WayPointList PathFinderAlgorithmSPFA::FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex)
{
- auto nodeCount = uint32_t(mNodes.size());
- std::vector<float> dist;
- std::vector<uint32_t> prev;
- std::vector<bool> queued;
+ auto nodeCount = uint32_t(mNodes.size());
+ std::vector<float> dist;
+ std::vector<FaceIndex> prev;
+ std::vector<bool> queued;
dist.resize(mNodes.size());
prev.resize(mNodes.size());
queued.resize(mNodes.size());
- std::list<uint32_t> nodeQueue;
+ std::list<FaceIndex> nodeQueue;
[[maybe_unused]] auto sourcePos = Dali::Vector3(Face(sourcePolyIndex)->center);
}
// Compute distances for each node back to the source
- auto u = targetPolyIndex;
- std::list<uint32_t> q;
+ auto u = targetPolyIndex;
+ std::list<FaceIndex> q;
if(prev[u] != Scene3D::Algorithm::NavigationMesh::NULL_FACE || u == sourcePolyIndex)
{
while(u != Scene3D::Algorithm::NavigationMesh::NULL_FACE)
mNodes.resize(faceCount);
// for each face build the list
- for(auto i = 0u; i < faceCount; ++i)
+ // TODO : Currently, we are assume that FaceNodeIndex is matched with FaceIndex 1:1. This might be changed in future.
+ for(FaceNodeIndex i = 0u; i < faceCount; ++i)
{
auto& node = mNodes[i];
const auto* face = mNavigationMesh->GetFace(i);
* @param[in] polyIndexTo Index of end polygon
* @return List of waypoints for path
*/
- Scene3D::Algorithm::WayPointList FindPath(uint32_t sourcePolyIndex, uint32_t targetPolyIndex) override;
+ Scene3D::Algorithm::WayPointList FindPath(FaceIndex sourcePolyIndex, FaceIndex targetPolyIndex) override;
- [[nodiscard]] inline const NavigationMesh::Face* Face(uint32_t i) const
+ [[nodiscard]] inline const NavigationMesh::Face* Face(FaceIndex faceIndex) const
{
- return mNavigationMesh->GetFace(i);
+ return mNavigationMesh->GetFace(faceIndex);
}
/**
*/
struct FaceNode
{
- uint32_t faceIndex; ///< Index of face which is associated with node
-
// neighbours
- uint32_t faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
- uint32_t edges[3]; ///< List of edges (max 3 for a triangle)
- float weight[3]; ///< List of weights (by distance) to each neighbour
+ FaceIndex faces[3]; ///< List of neighbouring faces (max 3 for a triangle)
+ EdgeIndex edges[3]; ///< List of edges (max 3 for a triangle)
+ float weight[3]; ///< List of weights (by distance) to each neighbour
};
+ using FaceNodeIndex = FaceIndex;
NavigationMesh* mNavigationMesh; ///< Pointer to a valid NavigationMesh
std::vector<FaceNode> mNodes; ///< List of nodes
DALI_TYPE_REGISTRATION_END()
static constexpr Vector3 Y_DIRECTION(1.0f, -1.0f, 1.0f);
+static constexpr float SIZE_STEP_CONDITION = 0.1f;
static constexpr bool DEFAULT_MODEL_CHILDREN_SENSITIVE = false;
static constexpr bool DEFAULT_MODEL_CHILDREN_FOCUSABLE = false;
}
NotifyResourceReady();
+
+ mSizeNotification = Self().AddPropertyNotification(Actor::Property::SIZE, StepCondition(SIZE_STEP_CONDITION));
+ mSizeNotification.NotifySignal().Connect(this, &Model::OnSizeNotification);
Control::OnSceneConnection(depth);
}
GetImpl(sceneView).UnregisterSceneItem(this);
mParentSceneView.Reset();
}
+
+ mSizeNotification.NotifySignal().Disconnect(this, &Model::OnSizeNotification);
+ Self().RemovePropertyNotification(mSizeNotification);
+ mSizeNotification.Reset();
+
Control::OnSceneDisconnection();
}
+void Model::OnSizeSet(const Vector3& size)
+{
+ ScaleModel(false);
+}
+
Vector3 Model::GetNaturalSize()
{
if(!mModelRoot)
void Model::OnRelayout(const Vector2& size, RelayoutContainer& container)
{
Control::OnRelayout(size, container);
- ScaleModel();
+ ScaleModel(false);
}
bool Model::IsResourceReady() const
Self().Add(mModelRoot);
}
-void Model::ScaleModel()
+void Model::ScaleModel(bool useCurrentSize)
{
if(!mModelRoot)
{
}
float scale = 1.0f;
- Vector3 size = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+ Vector3 size = (useCurrentSize) ? Self().GetCurrentProperty<Vector3>(Dali::Actor::Property::SIZE) : Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
if(size.x > 0.0f && size.y > 0.0f)
{
scale = MAXFLOAT;
NotifyResourceReady();
}
+void Model::OnSizeNotification(Dali::PropertyNotification& source)
+{
+ ScaleModel(true);
+}
+
void Model::ResetResourceTasks()
{
if(!Dali::Adaptor::IsAvailable())
Self().SetProperty(Dali::Actor::Property::SIZE, mNaturalSize);
}
FitModelPosition();
- ScaleModel();
+ ScaleModel(false);
}
void Model::CreateAnimations(Dali::Scene3D::Loader::SceneDefinition& scene)
#include <dali/public-api/actors/camera-actor.h>
#include <dali/public-api/actors/layer.h>
#include <dali/public-api/animation/animation.h>
+#include <dali/public-api/object/property-notification.h>
#include <dali/public-api/object/weak-handle.h>
#include <dali/public-api/rendering/texture.h>
void OnSceneDisconnection() override;
/**
+ * @copydoc CustomActorImpl::OnSizeSet( const Vector3& size )
+ */
+ void OnSizeSet(const Vector3& size) override;
+
+ /**
* @copydoc Toolkit::Control::GetNaturalSize
*/
Vector3 GetNaturalSize() override;
/**
* @brief Scales the model to fit the control or to return to original size.
*/
- void ScaleModel();
+ void ScaleModel(bool useCurrentSize);
/**
* @brief Changes model anchor point to set the model at center or returns to the original model pivot.
void OnIblLoadComplete();
/**
+ * @brief Update model root scale when Model size property is updated.
+ */
+ void OnSizeNotification(Dali::PropertyNotification& source);
+
+ /**
* @brief Reset Resource loading tasks.
*/
void ResetResourceTasks();
void ResetCameraParameters();
private:
- std::string mModelUrl;
- std::string mResourceDirectoryUrl;
- Scene3D::ModelNode mModelRoot;
- std::vector<AnimationData> mAnimations;
- std::vector<CameraData> mCameraParameters;
- WeakHandle<Scene3D::SceneView> mParentSceneView;
+ std::string mModelUrl;
+ std::string mResourceDirectoryUrl;
+ Scene3D::ModelNode mModelRoot;
+ std::vector<AnimationData> mAnimations;
+ std::vector<CameraData> mCameraParameters;
+ WeakHandle<Scene3D::SceneView> mParentSceneView;
+ Dali::PropertyNotification mSizeNotification;
// Asynchronous loading variable
ModelLoadTaskPtr mModelLoadTask;
uniform samplerCube sDiffuseEnvSampler;
uniform samplerCube sSpecularEnvSampler;
uniform float uIblIntensity;
-uniform vec3 uYDirection;
+uniform mediump vec3 uYDirection;
uniform float uMaxLOD;
// For Alpha Mode.
in vec4 aWeights;
#define MAX_BONES 64
uniform mat4 uBone[MAX_BONES];
+ uniform mediump vec3 uYDirection;
#endif
#ifdef MORPH
uBone[int(aJoints.z)] * aWeights.z +
uBone[int(aJoints.w)] * aWeights.w;
position = bone * position;
- normal = (bone * vec4(normal, 0.0)).xyz;
- tangent = (bone * vec4(tangent, 0.0)).xyz;
- normal = normalize(normal);
- tangent = normalize(tangent);
+ normal = uYDirection * (bone * vec4(normal, 0.0)).xyz;
+ tangent = uYDirection * (bone * vec4(tangent, 0.0)).xyz;
highp vec4 positionW = position;
#else
vPositionToCamera = transpose(mat3(uViewMatrix)) * -vec3(positionV.xyz / positionV.w);
+ normal = normalize(normal);
+ tangent = normalize(tangent);
vec3 bitangent = cross(normal, tangent);
#ifdef VEC4_TANGENT
bitangent *= aTangent.w;
continue;
}
- Dali::Shader shader = renderer.GetShader();
- if(!shader)
- {
- continue;
- }
-
if(boneData.constraint)
{
boneData.constraint.Remove();
boneData.constraint.Reset();
}
- if(shader.GetPropertyIndex(boneData.propertyName) == Property::INVALID_INDEX)
+ auto propBoneXform = renderer.GetPropertyIndex(boneData.propertyName);
+ if(propBoneXform == Property::INVALID_INDEX)
{
- auto propBoneXform = shader.RegisterProperty(boneData.propertyName, Matrix{false});
+ propBoneXform = renderer.RegisterProperty(boneData.propertyName, Matrix{false});
+ }
- Matrix inverseMatrix = boneData.inverseMatrix;
- // Constrain bone matrix to joint transform.
- boneData.constraint = Constraint::New<Matrix>(shader, propBoneXform, [inverseMatrix](Matrix& output, const PropertyInputContainer& inputs)
- { Matrix::Multiply(output, inverseMatrix, inputs[0]->GetMatrix()); });
+ Matrix inverseMatrix = boneData.inverseMatrix;
+ // Constrain bone matrix to joint transform.
+ boneData.constraint = Constraint::New<Matrix>(renderer, propBoneXform, [inverseMatrix](Matrix& output, const PropertyInputContainer& inputs)
+ { Matrix::Multiply(output, inverseMatrix, inputs[0]->GetMatrix()); });
- Actor joint = Self();
- boneData.constraint.AddSource(Source{joint, Actor::Property::WORLD_MATRIX});
- boneData.constraint.ApplyPost();
- }
+ Actor joint = Self();
+ boneData.constraint.AddSource(Source{joint, Actor::Property::WORLD_MATRIX});
+ boneData.constraint.ApplyPost();
break;
}
}
namespace Dali::Scene3D::Algorithm
{
-
-NavigationMesh::NavigationMesh( NavigationMeshImpl* impl )
+NavigationMesh::NavigationMesh(NavigationMeshImpl* impl)
{
- mImpl.reset( impl );
+ mImpl.reset(impl);
}
NavigationMesh::~NavigationMesh() = default;
return mImpl->GetVertexCount();
}
-bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& polyIndex)
+bool NavigationMesh::FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, FaceIndex& faceIndex)
{
- return mImpl->FindFloor(position, outPosition, polyIndex);
+ return mImpl->FindFloor(position, outPosition, faceIndex);
}
-bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
+bool NavigationMesh::FindFloorForFace(const Dali::Vector3& position, FaceIndex faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition)
{
return mImpl->FindFloorForFace(position, faceIndex, dontCheckNeighbours, outPosition);
}
-[[nodiscard]] const NavigationMesh::Face* NavigationMesh::GetFace(int index) const
+[[nodiscard]] const NavigationMesh::Face* NavigationMesh::GetFace(FaceIndex index) const
{
return mImpl->GetFace(index);
}
-[[nodiscard]] const NavigationMesh::Edge* NavigationMesh::GetEdge(int index) const
+[[nodiscard]] const NavigationMesh::Edge* NavigationMesh::GetEdge(EdgeIndex index) const
{
return mImpl->GetEdge(index);
}
-[[nodiscard]] const NavigationMesh::Vertex* NavigationMesh::GetVertex(int index) const
+[[nodiscard]] const NavigationMesh::Vertex* NavigationMesh::GetVertex(VertexIndex index) const
{
return mImpl->GetVertex(index);
}
mImpl->SetTransform(transform);
}
-Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point)
+Dali::Vector3 NavigationMesh::PointSceneToLocal(const Dali::Vector3& point) const
{
return mImpl->PointSceneToLocal(point);
}
-Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point)
+Dali::Vector3 NavigationMesh::PointLocalToScene(const Dali::Vector3& point) const
{
return mImpl->PointLocalToScene(point);
}
return mImpl->GetGravityVector();
}
-}
\ No newline at end of file
+} // namespace Dali::Scene3D::Algorithm
\ No newline at end of file
#include <cinttypes>
#include <cstdio>
+#include <limits>
#include <memory>
// INTERNAL INCLUDES
// Using PImpling but not usual DALi handles as this object isn't supposed to be refcounted
using NavigationMeshImpl = Dali::Scene3D::Internal::Algorithm::NavigationMesh;
+// Make each to change each index value's type here.
+using VertexIndex = uint16_t;
+using EdgeIndex = uint16_t;
+using FaceIndex = uint16_t;
+
/**
* @class NavigationMesh
*
/**
* @struct Face
*
- * Describes a single polygon
+ * Describes a single polygon's face
*/
struct Face
{
- uint16_t vertex[NAVIGATION_MESH_MAX_VERTICES_PER_FACE]; ///< Vertices per face
- uint16_t edge[NAVIGATION_MESH_MAX_EDGES_PER_FACE]; ///< Edges per face
- float normal[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Normal vector
- float center[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Barycentric coordinates
+ VertexIndex vertex[NAVIGATION_MESH_MAX_VERTICES_PER_FACE]; ///< Vertices per face
+ EdgeIndex edge[NAVIGATION_MESH_MAX_EDGES_PER_FACE]; ///< Edges per face
+ float normal[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Normal vector
+ float center[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Barycentric coordinates
};
/**
*/
struct Edge
{
- uint16_t vertex[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Vertices making the edge
- uint16_t face[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Faces on both sides of edge
+ VertexIndex vertex[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Vertices making the edge
+ FaceIndex face[NAVIGATION_MESH_MAX_COMPONENTS_2D]; ///< Faces on both sides of edge
};
/**
{
union
{
- float co[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Coordinates of vertex
+ float coordinates[NAVIGATION_MESH_MAX_COMPONENTS_3D]; ///< Coordinates of vertex
struct
{
float x, y, z;
* @brief Looks for the floor under specified position
* @param[in] position Position to investigate
* @param[in] outPosition Position on the floor in found
- * @param[in] faceIndex Index of NavigationMesh face associated with floor
+ * @param[out] faceIndex Index of NavigationMesh face associated with floor
*
* @return True if floor has been found, False otherwise
*/
- bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, uint32_t& faceIndex);
+ bool FindFloor(const Dali::Vector3& position, Dali::Vector3& outPosition, FaceIndex& faceIndex);
/**
* @brief Looks for a floor starting from specified face
*
* @return True on success, false otherwise
*/
- bool FindFloorForFace(const Dali::Vector3& position, uint32_t faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
+ bool FindFloorForFace(const Dali::Vector3& position, FaceIndex faceIndex, bool dontCheckNeighbours, Dali::Vector3& outPosition);
/**
* @brief Returns pointer to Face structure
* @param[in] index Index of face to retrieve
* @return Pointer to valid Face structure or nullptr
*/
- [[nodiscard]] const Face* GetFace(int index) const;
+ [[nodiscard]] const Face* GetFace(FaceIndex index) const;
/**
* @brief Returns edge structure
* @param[in] index Index of edge to retrieve
* @return Pointer to valid Edge structure or nullptr
*/
- [[nodiscard]] const Edge* GetEdge(int index) const;
+ [[nodiscard]] const Edge* GetEdge(EdgeIndex index) const;
/**
* @brief Returns vertex structure
* @param[in] index Index of vertex to retrieve
* @return Pointer to valid Vertex structure or nullptr
*/
- [[nodiscard]] const Vertex* GetVertex(int index) const;
+ [[nodiscard]] const Vertex* GetVertex(VertexIndex index) const;
/**
* @brief Sets static transform for the navigation mesh object
* @param[in] point Point to transform
* @return Point transformed to the local space
*/
- Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point);
+ Dali::Vector3 PointSceneToLocal(const Dali::Vector3& point) const;
/**
* @brief Transforms point into the parent transform space
* @param[in] point Point to transform
* @return Point transformed into the parent space
*/
- Dali::Vector3 PointLocalToScene(const Dali::Vector3& point);
+ Dali::Vector3 PointLocalToScene(const Dali::Vector3& point) const;
/**
* @brief Returns direction of the gravity vector
*/
Dali::Vector3 GetGravityVector() const;
- static constexpr uint16_t NULL_FACE{0xffff}; ///< Represents null polygon
- static constexpr uint16_t NULL_EDGE{0xffff}; ///< represents null edge
+ static constexpr FaceIndex NULL_FACE{std::numeric_limits<FaceIndex>::max()}; ///< Represents null face
+ static constexpr EdgeIndex NULL_EDGE{std::numeric_limits<EdgeIndex>::max()}; ///< Represents null edge
public:
DALI_INTERNAL explicit NavigationMesh(NavigationMeshImpl* impl);
return mImpl->FindPath(positionFrom, positionTo);
}
-WayPointList PathFinder::FindPath(uint32_t polyIndexFrom, uint32_t polyIndexTo)
+WayPointList PathFinder::FindPath(FaceIndex polyIndexFrom, FaceIndex polyIndexTo)
{
return mImpl->FindPath(polyIndexFrom, polyIndexTo);
}
* @param[in] polyIndexTo Index of end polygon
* @return List of waypoints for path or empty vector if no success
*/
- virtual WayPointList FindPath(uint32_t polyIndexFrom, uint32_t polyIndexTo) = 0;
+ virtual WayPointList FindPath(FaceIndex polyIndexFrom, FaceIndex polyIndexTo) = 0;
};
/**
* @param[in] faceIndexTo Target face index
* @return List of waypoints for path or empty list on failure
*/
- WayPointList FindPath(uint32_t faceIndexFrom, uint32_t faceIndexTo);
+ WayPointList FindPath(FaceIndex faceIndexFrom, FaceIndex faceIndexTo);
private:
PathFinder() = delete;
std::vector<uint8_t> buffer(bufferSize);
std::vector<uint32_t> sparseIndices;
- if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data()), &sparseIndices)
+ if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
{
blendShape.tangents.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
{
mesh.first.mModelPrimitive = ModelPrimitive::New();
- auto primitive = mesh.first.mModelPrimitive;
+ auto primitive = mesh.first.mModelPrimitive;
GetImplementation(primitive).SetRenderer(renderer);
- Index envIndex = resources.mMaterials[mMaterialIdx].first.mEnvironmentIdx;
+ Index envIndex = resources.mMaterials[mMaterialIdx].first.mEnvironmentIdx;
uint32_t specularMipmap = resources.mEnvironmentMaps[envIndex].second.mSpecularMipmapLevels;
GetImplementation(primitive).SetImageBasedLightTexture(resources.mEnvironmentMaps[envIndex].second.mDiffuse,
resources.mEnvironmentMaps[envIndex].second.mSpecular,
renderer.RegisterProperty("uMask", mask);
renderer.RegisterProperty("uAlphaThreshold", alphaCutoff);
- Index envIndex = matDef.mEnvironmentIdx;
+ Index envIndex = matDef.mEnvironmentIdx;
uint32_t specularMipmap = resources.mEnvironmentMaps[envIndex].second.mSpecularMipmapLevels;
renderer.RegisterProperty(IBL_MAXLOD.data(), static_cast<float>(specularMipmap));
renderer.RegisterProperty(IBL_INTENSITY_STRING.data(), resources.mEnvironmentMaps[envIndex].first.mIblIntensity);
node.SetProperty(Actor::Property::COLOR, mColor);
{
- matDef.mMaterial = Material::New();
- auto material = matDef.mMaterial;
+ matDef.mMaterial = Material::New();
+ auto material = matDef.mMaterial;
uint32_t textureIndexOffset = (mesh.second.blendShapeGeometry) ? 1 : 0;
uint32_t textureIndex = 0;
for(uint32_t i = 0; i < MAX_NUMBER_OF_MATERIAL_TEXTURE; ++i)
bool operator<(const SkinningShaderConfigurationRequest& other) const
{
- return mShader < other.mShader;
+ return mShader < other.mShader || (mShader == other.mShader && mPrimitive < other.mPrimitive);
}
};
bool operator<(const BlendshapeShaderConfigurationRequest& other) const
{
- return mShader < other.mShader;
+ return mShader < other.mShader || (mShader == other.mShader && mPrimitive < other.mPrimitive);
}
};
{
namespace
{
-
const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)>& GetConstraintFactory()
{
static const std::map<Property::Type, Constraint (*)(Actor&, Property::Index)> sConstraintFactory = {
{Property::Type::BOOLEAN,
[](Actor& a, Property::Index i) {
- return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
- current = inputs[0]->GetBoolean();
- });
+ return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) { current = inputs[0]->GetBoolean(); });
}},
{Property::Type::INTEGER,
[](Actor& a, Property::Index i) {
- return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
- current = inputs[0]->GetInteger();
- });
+ return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) { current = inputs[0]->GetInteger(); });
}},
{Property::Type::FLOAT,
[](Actor& a, Property::Index i) {
private:
NodeDefinition::CreateParams& mCreationContext;
- std::vector<ModelNode> mActorStack;
- ModelNode mRoot;
+ std::vector<ModelNode> mActorStack;
+ ModelNode mRoot;
};
-void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationRequest>& requests)
+template<typename RequestType>
+void SortAndDeduplicateRequests(std::vector<RequestType>& requests)
{
- // Sort requests by shaders.
+ // Sort requests by shaders and primitives.
std::sort(requests.begin(), requests.end());
// Remove duplicates.
- auto i = requests.begin();
- auto iEnd = requests.end();
- Shader s = i->mShader;
- Index skeletonIdx = i->mSkeletonIdx;
- ++i;
+ auto iter = requests.begin();
+ auto iterEnd = requests.end();
+
+ Shader shader = iter->mShader;
+ ModelPrimitive modelPrimitive = iter->mPrimitive;
+ ++iter;
do
{
- // Multiple identical shader instances are removed.
- while(i != iEnd && i->mShader == s)
+ // Multiple identical shader and primitive instances are removed.
+ while(iter != iterEnd && iter->mShader == shader && iter->mPrimitive == modelPrimitive)
{
- // Cannot have multiple skeletons input to the same shader.
- // NOTE: DliModel now makes sure this doesn't happen.
- DALI_ASSERT_ALWAYS(i->mSkeletonIdx == skeletonIdx &&
- "Skinning shader must not be shared between different skeletons.");
-
- i->mShader = Shader();
- ++i;
+ // Mark as removed
+ iter->mShader = Shader();
+ ++iter;
}
- if(i == iEnd)
+ if(iter == iterEnd)
{
break;
}
- s = i->mShader;
- skeletonIdx = i->mSkeletonIdx;
- ++i;
+ shader = iter->mShader;
+ modelPrimitive = iter->mPrimitive;
+ ++iter;
} while(true);
- requests.erase(std::remove_if(requests.begin(), requests.end(), [](const SkinningShaderConfigurationRequest& sscr) {
- return !sscr.mShader;
- }),
+ requests.erase(std::remove_if(requests.begin(), requests.end(), [](const RequestType& sscr) { return !sscr.mShader; }),
requests.end());
}
{
auto iBegin = mNodes.begin();
auto iEnd = mNodes.end();
- auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
- return nd->mName == name;
- });
+ auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; });
auto result = iFind != iEnd ? iFind->get() : nullptr;
if(result && outIndex)
{
auto iBegin = mNodes.begin();
auto iEnd = mNodes.end();
- auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
- return nd->mName == name;
- });
+ auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; });
auto result = iFind != iEnd ? iFind->get() : nullptr;
if(result && outIndex)
{
auto iBegin = mNodes.begin();
auto iEnd = mNodes.end();
- auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
- return n.get() == &node;
- });
+ auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) { return n.get() == &node; });
return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
}
return;
}
- SortAndDeduplicateSkinningRequests(requests);
+ SortAndDeduplicateRequests(requests);
for(auto& request : requests)
{
Index boneIdx = 0;
for(auto& joint : skeleton.mJoints)
{
- auto node = GetNode(joint.mNodeIdx);
+ auto node = GetNode(joint.mNodeIdx);
ModelNode modelNode = ModelNode::DownCast(rootActor.FindChildByName(node->mName));
if(!modelNode)
{
return true;
}
- // Sort requests by shaders.
- std::sort(requests.begin(), requests.end());
-
- // Remove duplicates.
- auto i = requests.begin();
- auto iEnd = requests.end();
- Shader s = i->mShader;
- ++i;
- do
- {
- // Multiple identical shader instances are removed.
- while(i != iEnd && i->mShader == s)
- {
- i->mShader = Shader();
- ++i;
- }
-
- if(i == iEnd)
- {
- break;
- }
- s = i->mShader;
- ++i;
- } while(true);
+ SortAndDeduplicateRequests(requests);
// Configure the rest.
bool ok = true;
- for(auto& i : requests)
+ for(auto& request : requests)
{
Index iNode;
- if(FindNode(i.mNodeName, &iNode))
+ if(FindNode(request.mNodeName, &iNode))
{
const auto& node = GetNode(iNode);
- const auto& mesh = resources.mMeshes[i.mMeshIdx];
+ const auto& mesh = resources.mMeshes[request.mMeshIdx];
if(mesh.first.HasBlendShapes())
{
- Actor actor = rootActor.FindChildByName(node->mName);
- Scene3D::ModelNode node = Scene3D::ModelNode::DownCast(actor);
+ Actor actor = rootActor.FindChildByName(node->mName);
+ Scene3D::ModelNode node = Scene3D::ModelNode::DownCast(actor);
if(!node)
{
continue;
{
data.unnormalizeFactors.push_back(factor);
}
- data.version = mesh.first.mBlendShapeVersion;
+ data.version = mesh.first.mBlendShapeVersion;
data.bufferOffset = mesh.second.blendShapeBufferOffset;
- data.mActor = actor;
- Internal::GetImplementation(node).SetBlendShapeData(data, i.mPrimitive);
+ data.mActor = actor;
+ Internal::GetImplementation(node).SetBlendShapeData(data, request.mPrimitive);
}
}
}
// We're searching from the end assuming a higher probability of operations targeting
// recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
// the first to be added, is index 0.)
- auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) {
- return nd->mName == name;
- }).base();
+ auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(), [&name](const std::unique_ptr<NodeDefinition>& nd) { return nd->mName == name; })
+ .base();
const bool success = iFind != mNodes.begin();
if(success && result)
states[State::HIGHLIGHTABLE] = self.GetProperty<bool>(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE);
states[State::HIGHLIGHTED] = GetCurrentlyHighlightedActor() == self;
states[State::ENABLED] = true;
- states[State::SENSITIVE] = true;
+ states[State::SENSITIVE] = self.GetProperty<bool>(Actor::Property::SENSITIVE);
states[State::VISIBLE] = self.GetProperty<bool>(Actor::Property::VISIBLE);
states[State::SHOWING] = IsShowing();
states[State::DEFUNCT] = !self.GetProperty(Dali::DevelActor::Property::CONNECTED_TO_SCENE).Get<bool>();
#define DALI_TOOLKIT_DEVEL_API_VISUALS_IMAGE_VISUAL_PROPERTIES_DEVEL_H
/*
- * 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.
* Animation will play between the start frame and the end frame of the marker if one marker is specified.
* Or animation will play between the start frame of the first marker and the end frame of the second marker if two markers are specified.
*
- * @details Name "playRange", Type Property::ARRAY of Property::INTEGER or Property::ARRAY of Property::STRING.
+ * @details Name "playRange", Type Property::ARRAY of Property::INTEGER or Property::ARRAY of Property::STRING or Property::STRING (one marker).
* @note Default 0 and the total frame number.
*/
PLAY_RANGE = ORIENTATION_CORRECTION + 4,
/**
* @brief Whether to apply mask in loading time or rendering time.
* @details Name "maskingType", type PlayState::Type (Property::INTEGER).
+ * In general, MASKING_ON_LOADING is the default behavior.
+ * However, if the visual uses an external texture, only MASKING_ON_RENDERING is possible.
+ * So we change its value to MASKING_ON_RENDERING even if the visual sets the MASKING_TYPE as MASKING_ON_LOADING when it uses external texture.
* @note It is used in the ImageVisual and AnimatedImageVisual. The default is MASKING_ON_LOADING.
*/
MASKING_TYPE = ORIENTATION_CORRECTION + 12
${toolkit_src_dir}/builder/style.cpp
${toolkit_src_dir}/builder/tree-node-manipulator.cpp
${toolkit_src_dir}/builder/replacement.cpp
+ ${toolkit_src_dir}/particle-system/particle-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-domain-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-emitter-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-list-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-modifier-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-renderer-impl.cpp
+ ${toolkit_src_dir}/particle-system/particle-source-impl.cpp
${toolkit_src_dir}/texture-manager/texture-async-loading-helper.cpp
${toolkit_src_dir}/texture-manager/texture-cache-manager.cpp
${toolkit_src_dir}/texture-manager/texture-manager-impl.cpp
{
return;
}
+ // If mClearFocusOnTouch is false, do not clear the focus indicator even if user touch the screen.
+ if(mClearFocusOnTouch)
+ {
+ ClearFocusIndicator();
+ }
+
// If KEYBOARD_FOCUSABLE and TOUCH_FOCUSABLE is true, set focus actor
if(hitActor && hitActor.GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE) && hitActor.GetProperty<bool>(DevelActor::Property::TOUCH_FOCUSABLE))
{
- // If mClearFocusOnTouch is false, do not clear the focus
- if(mClearFocusOnTouch)
- {
- ClearFocus();
- }
SetCurrentFocusActor(hitActor);
}
- else
- {
- // If mClearFocusOnTouch is false, do not clear the focus indicator even if user touch the screen.
- if(mClearFocusOnTouch)
- {
- ClearFocusIndicator();
- }
- }
}
}
uniform mediump float borderlineOffset;
#endif
#ifdef IS_REQUIRED_ROUNDED_CORNER
-uniform mediump vec4 cornerRadius;
+uniform highp vec4 cornerRadius;
uniform mediump float cornerRadiusPolicy;
#endif
uniform mediump vec2 extraSize;
uniform mediump float borderlineOffset;
#endif
#ifdef IS_REQUIRED_ROUNDED_CORNER
-uniform mediump vec4 cornerRadius;
+uniform highp vec4 cornerRadius;
uniform mediump float cornerRadiusPolicy;
#endif
uniform mediump float borderlineOffset;
#endif
#ifdef IS_REQUIRED_ROUNDED_CORNER
-uniform mediump vec4 cornerRadius;
+uniform highp vec4 cornerRadius;
uniform mediump float cornerRadiusPolicy;
#endif
#ifdef IS_REQUIRED_ALPHA_MASKING
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/particle-system/particle-domain-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+// TODO: This needs physics modifier to be implemented (no use-case yet)
+}
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_DOMAIN_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_DOMAIN_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-object.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleDomain : public Dali::BaseObject
+{
+};
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+
+namespace Dali::Toolkit::ParticleSystem
+{
+inline Internal::ParticleDomain& GetImplementation(ParticleSystem::ParticleDomain& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleDomain handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Internal::ParticleDomain&>(handle);
+}
+
+inline const Internal::ParticleDomain& GetImplementation(const ParticleSystem::ParticleDomain& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleDomain handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Internal::ParticleDomain&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_DOMAIN_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/particle-system/particle-emitter-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/particle-system/particle-list-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-modifier-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-renderer-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-source-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/stage-devel.h>
+#include <dali/devel-api/update/frame-callback-interface.h>
+#include <memory>
+#include <utility>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+
+constexpr uint32_t DEFAULT_PARTICLE_COUNT = 100u; ///< Default number of particles in system if not set by user
+
+/**
+ * Particle system frame callback to run modifiers and sources
+ */
+class FrameCallback : public Dali::FrameCallbackInterface
+{
+public:
+ /**
+ * @brief Constructor.
+ */
+ FrameCallback(Internal::ParticleEmitter* emitter)
+ : mEmitter(emitter)
+ {
+ }
+
+ ~FrameCallback() = default;
+
+private:
+ void Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override
+ {
+ mEmitter->Update();
+ }
+
+ Internal::ParticleEmitter* mEmitter;
+};
+
+ParticleSystem::ParticleSource ParticleEmitter::GetSource() const
+{
+ return mParticleSource;
+}
+
+void ParticleEmitter::SetSource(const ParticleSystem::ParticleSource& source)
+{
+ mParticleStatusBits |= SOURCE_SET_STATUS_BIT;
+ mParticleSource = source;
+
+ // call the init function of source
+ GetImplementation(mParticleSource).GetUpdater().Init();
+}
+
+void ParticleEmitter::SetDomain(const ParticleSystem::ParticleDomain& domain)
+{
+ mParticleStatusBits |= DOMAIN_SET_STATUS_BIT;
+ mParticleDomain = domain;
+}
+
+void ParticleEmitter::SetRenderer(const ParticleSystem::ParticleRenderer& renderer)
+{
+ mParticleStatusBits |= RENDERER_SET_STATUS_BIT;
+ mParticleRenderer = renderer;
+ GetImplementation(mParticleRenderer).SetEmitter(this);
+}
+
+void ParticleEmitter::SetParticleCount(uint32_t maxParticleCount)
+{
+ // Default particle list has no data streams, it will replace old list
+ mParticleList = ParticleSystem::ParticleList::New(maxParticleCount,
+ ParticleStream::POSITION_STREAM_BIT |
+ ParticleStream::COLOR_STREAM_BIT |
+ ParticleStream::VELOCITY_STREAM_BIT |
+ ParticleStream::SCALE_STREAM_BIT |
+ ParticleStream::LIFETIME_STREAM_BIT);
+}
+
+ParticleSystem::ParticleList& ParticleEmitter::GetParticleList()
+{
+ return mParticleList;
+}
+
+uint32_t ParticleEmitter::AddModifier(const ParticleSystem::ParticleModifier& modifier)
+{
+ mModifiers.emplace_back(modifier);
+ return mModifiers.size() - 1;
+}
+
+ParticleSystem::ParticleDomain ParticleEmitter::GetDomain() const
+{
+ return mParticleDomain;
+}
+
+ParticleSystem::ParticleRenderer ParticleEmitter::GetRenderer() const
+{
+ return mParticleRenderer;
+}
+
+ParticleSystem::ParticleModifier ParticleEmitter::GetModifierAt(uint32_t index)
+{
+ return index < mModifiers.size() ? mModifiers[index] : ParticleSystem::ParticleModifier();
+}
+
+void ParticleEmitter::RemoveModifierAt(uint32_t index)
+{
+ mModifiers.erase(mModifiers.begin()+index);
+}
+
+void ParticleEmitter::Start()
+{
+ if(mActor && IsComplete() && !(mParticleStatusBits & SIMULATION_STARTED_STATUS_BIT))
+ {
+ if(mFrameCallback)
+ {
+ Stop();
+ }
+
+ GetImplementation(mParticleRenderer).Initialize();
+
+ mSystemStarted = true;
+ mParticleStatusBits &= ~(SIMULATION_STOPPED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT);
+ mParticleStatusBits |= SIMULATION_STARTED_STATUS_BIT;
+ mFrameCallback = std::make_unique<FrameCallback>(this);
+
+ // Attach renderer to an actor
+ auto renderer = GetImplementation(mParticleRenderer).GetRenderer();
+ mActor.AddRenderer(renderer);
+ DevelStage::AddFrameCallback(Stage::GetCurrent(), *mFrameCallback, mActor);
+ }
+}
+
+void ParticleEmitter::Stop()
+{
+ if(mActor && IsComplete() && (mParticleStatusBits & SIMULATION_STARTED_STATUS_BIT))
+ {
+ mSystemStarted = false;
+ mParticleStatusBits &= ~(SIMULATION_STARTED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT);
+ mParticleStatusBits |= SIMULATION_STOPPED_STATUS_BIT;
+ auto renderer = GetImplementation(mParticleRenderer).GetRenderer();
+ mActor.RemoveRenderer(renderer);
+ DevelStage::RemoveFrameCallback(Stage::GetCurrent(), *mFrameCallback);
+ }
+}
+
+std::chrono::milliseconds ParticleEmitter::GetCurrentTimeMillis() const
+{
+ std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch());
+ return ms;
+}
+
+void ParticleEmitter::Update()
+{
+ // Do not update if emitter setup isn't complete
+ if(!IsComplete())
+ {
+ return;
+ }
+
+ auto ms = GetCurrentTimeMillis();
+
+ if(mCurrentMilliseconds.count() == 0)
+ {
+ mCurrentMilliseconds = ms;
+ }
+
+ if(mLastUpdateMs.count() == 0)
+ {
+ mLastUpdateMs = ms;
+ }
+
+ float emissionDelta = 1.0f / float(mEmissionRatePerSecond); // time per one particle emission (TODO: add some randomness to it)
+
+ auto diffTime = double((ms - mCurrentMilliseconds).count()) / 1000.0;
+
+ uint32_t emissionCount = 0u;
+ if(diffTime >= emissionDelta)
+ {
+ emissionCount = round(diffTime / emissionDelta);
+ mCurrentMilliseconds = ms;
+ }
+
+ // Update lifetimes and discard dead particles
+ auto& particles = mParticleList.GetActiveParticles();
+ auto dt = ms - mLastUpdateMs;
+ if(dt.count())
+ {
+ std::vector<int> toErase;
+ int n = 0;
+ for(auto& p : particles)
+ {
+ auto& lifetime = p.Get<float>(ParticleStream::LIFETIME_STREAM_BIT);
+ lifetime -= (float(dt.count()) / 1000.0f);
+ if(lifetime <= 0.0f)
+ {
+ toErase.emplace_back(n);
+ }
+ ++n;
+ }
+
+ if(!toErase.empty())
+ {
+ int indexShift = 0;
+ for(auto& v : toErase)
+ {
+ GetImplementation(mParticleList).ReleaseParticle(v - indexShift);
+ ++indexShift;
+ }
+ }
+ }
+ mLastUpdateMs = ms;
+
+ // apply initial emission count
+ if(mSystemStarted)
+ {
+ emissionCount = mEmissionCountOnStart;
+ mSystemStarted = false;
+ }
+
+ // Update source if there are any particles to be emitted
+ if(emissionCount)
+ {
+ // Apply active particles limiter
+ if(mActiveParticlesLimit && mParticleList.GetActiveParticleCount() + emissionCount > mActiveParticlesLimit)
+ {
+ emissionCount = mActiveParticlesLimit - mParticleList.GetActiveParticleCount();
+ }
+ UpdateSource(emissionCount);
+ }
+
+ // Update modifier stack
+ for(auto& modifier : mModifiers)
+ {
+ if(modifier)
+ {
+ // Parallel processing must be enabled in order to use MT mode
+ bool mt = GetImplementation(modifier).GetUpdater().IsMultiThreaded() && mParallelProcessing;
+
+ if(!mt) // single-threaded, update all particles in one go
+ {
+ GetImplementation(modifier).Update(mParticleList, 0, mParticleList.GetActiveParticleCount());
+ }
+ else
+ {
+ UpdateModifierMT(modifier);
+ }
+ }
+ }
+
+ UpdateDomain();
+}
+
+void ParticleEmitter::AttachTo(Actor actor)
+{
+ mActor = std::move(actor);
+}
+
+Actor ParticleEmitter::GetActor() const
+{
+ return mActor;
+}
+
+void ParticleEmitter::UpdateSource(uint32_t count)
+{
+ GetImplementation(mParticleSource).Update(mParticleList, count);
+}
+
+void ParticleEmitter::UpdateModifierMT(Dali::Toolkit::ParticleSystem::ParticleModifier& modifier)
+{
+ auto& threadPool = GetThreadPool();
+ auto workerThreads = threadPool.GetWorkerCount();
+ auto activeCount = mParticleList.GetActiveParticleCount();
+
+ // at least 10 particles per worker thread (should be parametrized)
+ // If less, continue ST
+ if(activeCount < workerThreads * 10)
+ {
+ GetImplementation(modifier).Update(mParticleList, 0, activeCount);
+ return;
+ }
+
+ auto partial = mParticleList.GetActiveParticleCount() / workerThreads;
+
+ // make tasks
+ struct UpdateTask
+ {
+ UpdateTask(Internal::ParticleModifier& modifier, ParticleSystem::ParticleList& list, uint32_t first, uint32_t count)
+ : mModifier(modifier),
+ mList(list),
+ mFirst(first),
+ mCount(count)
+ {
+ }
+
+ Internal::ParticleModifier& mModifier;
+ ParticleSystem::ParticleList& mList;
+ uint32_t mFirst;
+ uint32_t mCount;
+
+ void Update()
+ {
+ mModifier.Update(mList, mFirst, mCount);
+ }
+ };
+
+ std::vector<UpdateTask> updateTasks;
+ updateTasks.reserve(workerThreads);
+ std::vector<Task> tasks;
+
+ for(auto i = 0u; i < workerThreads; ++i)
+ {
+ auto index = i * partial;
+ auto count = partial;
+ if(i == workerThreads - 1 && index + count < activeCount)
+ {
+ count = activeCount - index;
+ }
+
+ updateTasks.emplace_back(GetImplementation(modifier), mParticleList, index, count);
+ tasks.emplace_back([&task = updateTasks.back()](uint32_t n)
+ {
+ //printf("Updating modifier: %d\n", n);
+ task.Update(); });
+ }
+
+ auto future = threadPool.SubmitTasks(tasks, 0);
+ future->Wait();
+}
+
+void ParticleEmitter::UpdateDomain()
+{
+ // TODO
+}
+
+void ParticleEmitter::SetEmissionRate(uint32_t ratePerSecond)
+{
+ mEmissionRatePerSecond = ratePerSecond;
+}
+
+uint32_t ParticleEmitter::GetEmissionRate() const
+{
+ return mEmissionRatePerSecond;
+}
+
+void ParticleEmitter::EnableParallelProcessing(bool enabled)
+{
+ mParallelProcessing = enabled;
+}
+
+bool ParticleEmitter::IsParallelProcessingEnabled() const
+{
+ return mParallelProcessing;
+}
+
+void ParticleEmitter::SetInitialParticleCount(uint32_t count)
+{
+ mEmissionCountOnStart = count;
+}
+
+uint32_t ParticleEmitter::GetInitialParticleCount() const
+{
+ return mEmissionCountOnStart;
+}
+
+void ParticleEmitter::SetActiveParticlesLimit(uint32_t count)
+{
+ mActiveParticlesLimit = count;
+}
+
+uint32_t ParticleEmitter::GetActiveParticlesLimit() const
+{
+ return mActiveParticlesLimit;
+}
+
+ParticleSystem::ParticleEmitter::Status ParticleEmitter::GetStatus() const
+{
+ auto statusMask = SIMULATION_STARTED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT | SIMULATION_STOPPED_STATUS_BIT;
+ auto status = (mParticleStatusBits & statusMask);
+
+ if(status & SIMULATION_PAUSED_STATUS_BIT)
+ {
+ return ParticleSystem::ParticleEmitter::Status::PAUSED;
+ }
+ else if(status & SIMULATION_STOPPED_STATUS_BIT)
+ {
+ return ParticleSystem::ParticleEmitter::Status::STOPPED;
+ }
+ else if(status & SIMULATION_STARTED_STATUS_BIT)
+ {
+ return ParticleSystem::ParticleEmitter::Status::STARTED;
+ }
+ else
+ {
+ return !IsComplete() ? ParticleSystem::ParticleEmitter::Status::INCOMPLETE : ParticleSystem::ParticleEmitter::Status::READY;
+ }
+}
+
+ParticleEmitter::ParticleEmitter()
+{
+ // Necessary to be called to initialize internal ParticleList
+ SetParticleCount(DEFAULT_PARTICLE_COUNT);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+namespace Dali::Toolkit::ParticleSystem
+{
+Dali::ThreadPool& GetThreadPool()
+{
+ static std::unique_ptr<Dali::ThreadPool> gThreadPool{nullptr};
+ static std::once_flag onceFlag;
+
+ // Intialize thread pool if not there yet, make sure it happens once and it's synchronized!,
+ // NOTE: this function shouldn't be called from multiple thread anyway
+ if(!gThreadPool)
+ {
+ std::call_once(onceFlag, [&threadPool = gThreadPool]
+ { threadPool = std::make_unique<Dali::ThreadPool>();
+ threadPool->Initialize(4u); });
+ }
+
+ return *gThreadPool;
+}
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_EMITTER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_EMITTER_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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/timer.h>
+#include <dali/public-api/object/base-object.h>
+#include <chrono>
+#include <ctime>
+#include <memory>
+
+// For multithreading update
+#include <dali/devel-api/threading/thread-pool.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class FrameCallback;
+class ParticleEmitter : public Dali::BaseObject, public Dali::ConnectionTracker
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ ParticleEmitter();
+
+ /**
+ * @brief Destructor
+ */
+ ~ParticleEmitter() override = default;
+
+ /**
+ * @brief Tests whether emitter is complete (ready for simulation)
+ *
+ * @return True if emitter is complete, false otherwise
+ */
+ [[nodiscard]] bool IsComplete() const
+ {
+ return (mParticleStatusBits & STATUS_COMPLETE_BITS) == STATUS_COMPLETE_BITS;
+ }
+
+ [[nodiscard]] ParticleSystem::ParticleSource GetSource() const;
+
+ void SetSource(const ParticleSystem::ParticleSource& source);
+
+ [[nodiscard]] ParticleSystem::ParticleDomain GetDomain() const;
+
+ void SetDomain(const ParticleSystem::ParticleDomain& domain);
+
+ [[nodiscard]] ParticleSystem::ParticleRenderer GetRenderer() const;
+
+ [[nodiscard]] ParticleSystem::ParticleModifier GetModifierAt(uint32_t index);
+
+ void RemoveModifierAt(uint32_t index);
+
+ void SetRenderer(const ParticleSystem::ParticleRenderer& renderer);
+
+ void SetParticleCount(uint32_t maxParticleCount);
+
+ ParticleSystem::ParticleList& GetParticleList();
+
+ uint32_t AddModifier(const ParticleSystem::ParticleModifier& modifier);
+
+ void AttachTo(Actor actor);
+
+ [[nodiscard]] Actor GetActor() const;
+
+ void Update();
+
+ void UpdateSource(uint32_t count);
+
+ void UpdateModifierMT(Dali::Toolkit::ParticleSystem::ParticleModifier& modifier);
+
+ void UpdateDomain();
+
+ void SetEmissionRate(uint32_t ratePerSecond);
+
+ [[nodiscard]] uint32_t GetEmissionRate() const;
+
+ void SetInitialParticleCount(uint32_t count);
+
+ [[nodiscard]] uint32_t GetInitialParticleCount() const;
+
+ void Start();
+
+ void Stop();
+
+ void EnableParallelProcessing(bool enabled);
+
+ [[nodiscard]] bool IsParallelProcessingEnabled() const;
+
+ void SetActiveParticlesLimit(uint32_t count);
+
+ [[nodiscard]] uint32_t GetActiveParticlesLimit() const;
+
+ [[nodiscard]] ParticleSystem::ParticleEmitter::Status GetStatus() const;
+
+ [[nodiscard]] std::chrono::milliseconds GetCurrentTimeMillis() const;
+
+ // All these bits must be set in order to consider emitter COMPLETE
+ const uint32_t SOURCE_SET_STATUS_BIT = 1 << 0;
+ const uint32_t RENDERER_SET_STATUS_BIT = 1 << 1;
+ const uint32_t DOMAIN_SET_STATUS_BIT = 1 << 2;
+
+ // 1. Only one of these flags can be set at a time
+ // 2. They are invalid as long as emitter is INCOMPLETE
+ const uint32_t SIMULATION_STARTED_STATUS_BIT = 1 << 3;
+ const uint32_t SIMULATION_PAUSED_STATUS_BIT = 1 << 4;
+ const uint32_t SIMULATION_STOPPED_STATUS_BIT = 1 << 5;
+
+ const uint32_t STATUS_COMPLETE_BITS = SOURCE_SET_STATUS_BIT | RENDERER_SET_STATUS_BIT | DOMAIN_SET_STATUS_BIT;
+
+ ParticleSystem::ParticleSource mParticleSource; ///< Current particle source object
+ ParticleSystem::ParticleDomain mParticleDomain; ///< Current particle domain object
+
+ uint8_t mParticleStatusBits{0u}; ///< Current status of the emitter
+
+ // List of particles
+ ParticleSystem::ParticleList mParticleList;
+
+ std::vector<ParticleSystem::ParticleModifier> mModifiers;
+
+ ParticleSystem::ParticleRenderer mParticleRenderer;
+
+ Actor mActor;
+
+ uint32_t mEmissionRatePerSecond{1u};
+ std::atomic<uint32_t> mEmissionCountOnStart{0u};
+ std::atomic<uint32_t> mActiveParticlesLimit{0u}; ///< 0 - unlimited
+ std::atomic<bool> mSystemStarted{false};
+ std::chrono::milliseconds mCurrentMilliseconds{0};
+ std::chrono::milliseconds mLastUpdateMs{0};
+
+ bool mParallelProcessing{false};
+ std::unique_ptr<FrameCallback> mFrameCallback;
+};
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+
+namespace Dali::Toolkit::ParticleSystem
+{
+// Returns thread pool shared by whole particle system
+Dali::ThreadPool& GetThreadPool();
+
+inline Internal::ParticleEmitter& GetImplementation(ParticleSystem::ParticleEmitter& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleEmitter handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Internal::ParticleEmitter&>(handle);
+}
+
+inline const Internal::ParticleEmitter& GetImplementation(const ParticleSystem::ParticleEmitter& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleEmitter handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Internal::ParticleEmitter&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_EMITTER_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/particle-system/particle-impl.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+
+Particle::Particle(Internal::ParticleList& ownerList, uint32_t index)
+: mOwnerList(ownerList),
+ mIndex(index)
+{
+}
+
+void* Particle::Get(ParticleStreamTypeFlagBit streamBit)
+{
+ auto streamIndex = mOwnerList.GetDefaultStreamIndex(streamBit);
+ auto dataSize = mOwnerList.GetStreamDataTypeSize(streamIndex);
+ return reinterpret_cast<uint8_t*>(mOwnerList.GetDefaultStream(streamBit)) + (mIndex * dataSize);
+}
+
+void* Particle::GetByIndex(uint32_t streamIndex)
+{
+ auto dataSize = mOwnerList.GetStreamDataTypeSize(streamIndex);
+ auto* ptr = reinterpret_cast<uint8_t*>(mOwnerList.GetRawStream(streamIndex));
+ return reinterpret_cast<uint8_t*>(ptr + (mIndex * dataSize));
+}
+
+uint32_t Particle::GetIndex() const
+{
+ return mIndex;
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/particle-system/particle-list-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/math/quaternion.h>
+#include <dali/public-api/math/vector3.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class Particle : public Dali::BaseObject
+{
+public:
+ ~Particle() = default;
+
+ Particle(Internal::ParticleList& ownerList, uint32_t index);
+
+ void* Get(ParticleStreamTypeFlagBit streamBit);
+
+ void* GetByIndex(uint32_t streamIndex);
+
+ uint32_t GetIndex() const;
+
+private:
+ Internal::ParticleList& mOwnerList;
+ uint32_t mIndex;
+};
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+
+namespace Dali::Toolkit::ParticleSystem
+{
+inline Internal::Particle& GetImplementation(ParticleSystem::Particle& particle)
+{
+ DALI_ASSERT_ALWAYS(particle && "Particle handle is empty");
+
+ BaseObject& handle = particle.GetBaseObject();
+
+ return static_cast<Internal::Particle&>(handle);
+}
+
+inline const Internal::Particle& GetImplementation(const ParticleSystem::Particle& particle)
+{
+ DALI_ASSERT_ALWAYS(particle && "Particle handle is empty");
+
+ const BaseObject& handle = particle.GetBaseObject();
+
+ return static_cast<const Internal::Particle&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/particle-system/particle-list-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/particle-system/particle-impl.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector3>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT3;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector4>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT4;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector2>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT2;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<float>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT;
+}
+
+ParticleList::ParticleList(uint32_t capacity, ParticleSystem::ParticleList::ParticleStreamTypeFlags streamFlags)
+{
+ // capacity makes for max immutable particle count
+ mMaxParticleCount = capacity;
+
+ // initialize built-in streams and build map (to optimize later)
+ if(streamFlags & ParticleStream::POSITION_STREAM_BIT)
+ {
+ AddStream(Vector3::ZERO, "aStreamPosition", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::POSITION_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::ROTATION_STREAM_BIT)
+ {
+ AddStream(Vector4::ZERO, "aStreamRotation", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::ROTATION_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::SCALE_STREAM_BIT)
+ {
+ AddStream(Vector3::ONE, "aStreamScale", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::SCALE_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::VELOCITY_STREAM_BIT)
+ {
+ AddStream(Vector3::ZERO, "aStreamVelocity", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::VELOCITY_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::COLOR_STREAM_BIT)
+ {
+ AddStream(Color::YELLOW, "aStreamColor", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::COLOR_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::OPACITY_STREAM_BIT)
+ {
+ AddStream(0.0f, "aStreamOpacity", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::OPACITY_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+ if(streamFlags & ParticleStream::LIFETIME_STREAM_BIT)
+ {
+ AddStream(0.0f, "aStreamLifetime", false);
+ mBuiltInStreamMap[uint32_t(ParticleStream::LIFETIME_STREAM_BIT)] = mDataStreams.size() - 1;
+ }
+
+ // create free chain
+ mFreeChain.resize(capacity);
+ for(auto i = 0u; i < mFreeChain.size(); ++i)
+ {
+ mFreeChain[i] = i + 1;
+ }
+ mFreeChain[mFreeChain.size() - 1] = 0;
+ mFreeIndex = 0;
+}
+
+ParticleList::~ParticleList() = default;
+
+uint32_t ParticleList::AddStream(uint32_t sizeOfDataType, const void* defaultValue, ParticleStream::StreamDataType dataType, const char* streamName, bool localStream)
+{
+ mDataStreams.emplace_back(new ParticleDataStream(mMaxParticleCount, sizeOfDataType, defaultValue, dataType));
+ if(streamName)
+ {
+ mDataStreams.back()->SetStreamName(streamName);
+ }
+
+ mDataStreams.back()->SetStreamLocal(localStream);
+
+ // Update element size
+ mParticleStreamElementSize = 0;
+ mParticleStreamElementSizeWithLocal = 0;
+ for(auto& ds : mDataStreams)
+ {
+ if(!ds->localStream)
+ {
+ mParticleStreamElementSize += ds->dataSize;
+ }
+ mParticleStreamElementSizeWithLocal += ds->dataSize;
+ }
+
+ return mDataStreams.size() - 1;
+}
+
+void* ParticleList::GetRawStream(uint32_t index)
+{
+ if(index < mDataStreams.size() && mDataStreams[index])
+ {
+ return mDataStreams[index]->data.data();
+ }
+ return nullptr;
+}
+
+uint32_t ParticleList::GetStreamCount() const
+{
+ return mDataStreams.size();
+}
+
+uint32_t ParticleList::GetParticleCount() const
+{
+ return mMaxParticleCount;
+}
+
+uint32_t ParticleList::GetActiveParticleCount() const
+{
+ return mParticles.size();
+}
+
+ParticleStream::StreamDataType ParticleList::GetStreamDataType(uint32_t streamIndex)
+{
+ return mDataStreams[streamIndex]->type;
+}
+
+const std::string& ParticleList::GetStreamName(uint32_t streamIndex) const
+{
+ return mDataStreams[streamIndex]->streamName;
+}
+
+bool ParticleList::IsStreamLocal(uint32_t streamIndex) const
+{
+ return mDataStreams[streamIndex]->localStream;
+}
+
+uint32_t ParticleList::GetStreamDataTypeSize(uint32_t streamIndex) const
+{
+ return mDataStreams[streamIndex]->dataSize;
+}
+
+ParticleSystem::Particle ParticleList::NewParticle(float lifetime)
+{
+ if(mParticles.size() < mMaxParticleCount)
+ {
+ auto newIndex = int32_t(mFreeIndex);
+ mFreeIndex = int32_t(mFreeChain[mFreeIndex]);
+ mAliveParticleCount++;
+
+ // Add particle
+ mParticles.emplace_back(new Internal::Particle(*this, newIndex));
+
+ // Set particle lifetime
+ auto& particle = mParticles.back();
+
+ particle.Get<float>(ParticleStream::LIFETIME_STREAM_BIT) = lifetime;
+
+ return mParticles.back();
+ }
+ return {nullptr};
+}
+
+uint32_t ParticleList::GetStreamElementSize(bool includeLocalStream)
+{
+ if(includeLocalStream)
+ {
+ return mParticleStreamElementSizeWithLocal;
+ }
+ else
+ {
+ return mParticleStreamElementSize;
+ }
+}
+
+void ParticleList::ReleaseParticle(uint32_t particleIndex)
+{
+ auto it = mParticles.begin();
+ std::advance(it, particleIndex);
+
+ // Point at this slot of memory as next free slot
+ auto& p = *it;
+ if(mFreeIndex)
+ {
+ mFreeChain[p.GetIndex()] = mFreeIndex;
+ mFreeIndex = p.GetIndex();
+ }
+
+ // Remove particle from the list
+ mParticles.erase(it);
+ mAliveParticleCount--;
+}
+
+void* ParticleList::GetDefaultStream(ParticleStreamTypeFlagBit streamBit)
+{
+ return GetRawStream(mBuiltInStreamMap[streamBit]);
+}
+
+uint32_t ParticleList::GetDefaultStreamIndex(ParticleStreamTypeFlagBit streamBit)
+{
+ return mBuiltInStreamMap[uint32_t(streamBit)];
+}
+
+std::list<ParticleSystem::Particle>& ParticleList::GetParticles()
+{
+ return mParticles;
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_LIST_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_LIST_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/common/list-wrapper.h>
+#include <dali/devel-api/common/map-wrapper.h>
+#include <algorithm>
+#include <memory>
+
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+template<class T>
+struct StreamDataTypeWrapper
+{
+ static ParticleStream::StreamDataType GetType()
+ {
+ return {};
+ }
+};
+
+struct ParticleDataStream
+{
+ ~ParticleDataStream() = default;
+ template<class T>
+ ParticleDataStream(uint32_t capacity, const T& defaultValue, ParticleStream::StreamDataType dataType)
+ : ParticleDataStream(capacity, sizeof(T), &defaultValue, dataType)
+ {
+ }
+
+ /**
+ * Creates new stream of requested capacity and (optionally) fills with default data
+ */
+ ParticleDataStream(uint32_t capacity, uint32_t dataSize, const void* defaultValue, ParticleStream::StreamDataType dataType)
+ {
+ this->capacity = capacity;
+ data.resize(capacity * dataSize);
+ if(defaultValue)
+ {
+ for(auto i = 0u; i < capacity; ++i)
+ {
+ auto dstPtr = data.data() + (i * dataSize);
+ std::copy(reinterpret_cast<const uint8_t*>(defaultValue), reinterpret_cast<const uint8_t*>(defaultValue) + dataSize, dstPtr);
+ }
+ }
+ type = dataType;
+ alive = 0u;
+ this->dataSize = dataSize;
+ }
+
+ void SetStreamName(const char* name)
+ {
+ streamName = name;
+ }
+
+ void SetStreamLocal(bool local)
+ {
+ localStream = local;
+ }
+
+ /**
+ * Converts raw data into requested type (does not guarantee compatibility)
+ */
+ template<class T>
+ T* GetAs()
+ {
+ return reinterpret_cast<T*>(data.data());
+ }
+
+ ParticleStream::StreamDataType type;
+ std::vector<uint8_t> data;
+ std::string streamName;
+ uint32_t alive{0u};
+ uint32_t capacity;
+ uint32_t dataSize;
+ bool localStream{true};
+};
+
+/**
+ * Particle list stores particle-specific data and manages the particles memory
+ * It can return a sub-list.
+ *
+ * ParticleList manages the storage memory.
+ *
+ *
+ */
+class ParticleList : public Dali::BaseObject
+{
+public:
+
+ ParticleList(uint32_t capacity, ParticleSystem::ParticleList::ParticleStreamTypeFlags streamFlags);
+
+ ~ParticleList();
+
+ /**
+ * Returns raw pointer to the stream data
+ */
+ void* GetRawStream(uint32_t index);
+
+ /**
+ * Returns number of available data streams
+ * @return
+ */
+ uint32_t GetStreamCount() const;
+
+ /**
+ * Returns number of particles per list
+ * @return
+ */
+ uint32_t GetParticleCount() const;
+
+ /**
+ * Returns number of currently active particles
+ * @return
+ */
+ uint32_t GetActiveParticleCount() const;
+
+ /**
+ * Returns stream data-type
+ * @param streamIndex
+ * @return
+ */
+ ParticleStream::StreamDataType GetStreamDataType(uint32_t streamIndex);
+
+ /**
+ * Returns stream data type size
+ * @param streamIndex
+ * @return
+ */
+ [[nodiscard]] uint32_t GetStreamDataTypeSize(uint32_t streamIndex) const;
+
+ [[nodiscard]] const std::string& GetStreamName(uint32_t streamIndex) const;
+
+ [[nodiscard]] bool IsStreamLocal(uint32_t streamIndex) const;
+
+ /**
+ * Allocates new particle in the streams
+ * @param lifetime
+ * @return
+ */
+ ParticleSystem::Particle NewParticle(float lifetime);
+
+ void* GetDefaultStream(ParticleStreamTypeFlagBit streamBit);
+
+ uint32_t GetDefaultStreamIndex(ParticleStreamTypeFlagBit streamBit);
+
+ std::list<ParticleSystem::Particle>& GetParticles();
+
+ void ReleaseParticle(uint32_t particleIndex);
+
+ uint32_t GetStreamElementSize(bool includeLocalStream);
+
+private:
+ template<class T>
+ uint32_t AddStream(const T& defaultValue, const char* streamName, bool localStream)
+ {
+ return AddStream(sizeof(T), &defaultValue, StreamDataTypeWrapper<T>::GetType(), streamName, localStream);
+ }
+
+public:
+ /**
+ * Adds new stream and returns index
+ */
+ uint32_t AddStream(uint32_t sizeOfDataType, const void* defaultValue, ParticleStream::StreamDataType dataType, const char* streamName, bool localStream);
+
+private:
+ std::vector<char> mBuffer[2];
+
+ uint32_t mAliveParticleCount{0u};
+ uint32_t mMaxParticleCount;
+
+ // Data storage
+ std::vector<std::unique_ptr<ParticleDataStream>> mDataStreams;
+
+ std::vector<uint32_t> mFreeChain;
+ int32_t mFreeIndex{0u};
+
+ std::map<uint32_t, uint32_t> mBuiltInStreamMap;
+
+ std::list<ParticleSystem::Particle> mParticles;
+
+ uint32_t mParticleStreamElementSizeWithLocal{0u};
+ uint32_t mParticleStreamElementSize{0u};
+};
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+namespace Dali::Toolkit::ParticleSystem
+{
+inline Internal::ParticleList& GetImplementation(ParticleSystem::ParticleList& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleList handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Internal::ParticleList&>(handle);
+}
+
+inline const Internal::ParticleList& GetImplementation(const ParticleSystem::ParticleList& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleList handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Internal::ParticleList&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_LIST_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-modifier-impl.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+ParticleModifier::ParticleModifier(std::unique_ptr<ParticleModifierInterface>&& updater)
+{
+ mUpdater = std::move(updater);
+}
+
+void ParticleModifier::Update(ParticleSystem::ParticleList& list, uint32_t first, uint32_t count)
+{
+ mUpdater->Update(list, first, count);
+}
+
+ParticleModifierInterface& ParticleModifier::GetUpdater()
+{
+ return *mUpdater;
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_MODIFIER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_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-modifier.h>
+#include <dali/public-api/object/base-object.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleList;
+class ParticleEmitter;
+class ParticleModifier : public Dali::BaseObject
+{
+public:
+ ParticleModifier(std::unique_ptr<ParticleModifierInterface>&& updater);
+
+ void Update(ParticleSystem::ParticleList& list, uint32_t first, uint32_t count);
+
+ ParticleModifierInterface& GetUpdater();
+
+private:
+ std::unique_ptr<ParticleModifierInterface> mUpdater;
+};
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+
+namespace Dali::Toolkit::ParticleSystem
+{
+inline Internal::ParticleModifier& GetImplementation(ParticleSystem::ParticleModifier& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleModifier handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Internal::ParticleModifier&>(handle);
+}
+
+inline const Internal::ParticleModifier& GetImplementation(const ParticleSystem::ParticleModifier& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleModifier handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Internal::ParticleModifier&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_MODIFIER_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-emitter-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-list-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-renderer-impl.h>
+#include <dali/devel-api/rendering/renderer-devel.h>
+
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/common/capabilities.h>
+#include <dali/graphics-api/graphics-buffer.h>
+#include <dali/graphics-api/graphics-controller.h>
+#include <dali/graphics-api/graphics-program.h>
+#include <dali/graphics-api/graphics-shader.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+ParticleRenderer::ParticleRenderer()
+{
+ mStreamBufferUpdateCallback = Dali::VertexBufferUpdateCallback::New(this, &ParticleRenderer::OnStreamBufferUpdate);
+}
+
+void ParticleRenderer::SetEmitter(ParticleSystem::Internal::ParticleEmitter* emitter)
+{
+ mEmitter = emitter;
+}
+
+void ParticleRenderer::SetTexture(const Dali::Texture& texture)
+{
+ mTexture = texture;
+}
+
+void ParticleRenderer::SetBlendingMode(BlendingMode blendingMode)
+{
+ mBlendingMode = blendingMode;
+}
+
+void ParticleRenderer::CreateShader()
+{
+ // Create shader dynamically
+ auto& list = GetImplementation(mEmitter->GetParticleList());
+ auto streamCount = list.GetStreamCount();
+
+ static const char* ATTR_GLSL_TYPES[] =
+ {
+ "float", "vec2", "vec3", "vec4", "int", "ivec2", "ivec3", "ivec4"};
+
+ static const Property::Type ATTR_TYPES[] =
+ {
+ Property::Type::FLOAT,
+ Property::Type::VECTOR2,
+ Property::Type::VECTOR3,
+ Property::Type::VECTOR4,
+ Property::Type::INTEGER,
+ Property::Type::VECTOR2, // This represents floats but by binary write it shouldn't matter (?)
+ Property::Type::VECTOR3,
+ Property::Type::VECTOR4,
+ };
+
+ struct Vertex2D
+ {
+ Vertex2D(const Vector2& _co, const Vector2& _uv)
+ : co(_co),
+ uv(_uv)
+ {
+ }
+ Dali::Vector2 co{};
+ Dali::Vector2 uv{};
+ };
+
+ uint32_t streamElementSize = 0u;
+ Property::Map streamAtttributes;
+
+ std::stringstream ss;
+ for(auto i = 0u; i < streamCount; ++i)
+ {
+ // Don't add local streams to the shader
+ if(!list.IsStreamLocal(i))
+ {
+ uint32_t dataTypeSize = list.GetStreamDataTypeSize(i);
+ auto dataTypeIndex = uint32_t(list.GetStreamDataType(i));
+ const auto& streamName = list.GetStreamName(i);
+ streamElementSize += dataTypeSize;
+ char key[256];
+ if(streamName.empty())
+ {
+ sprintf(key, "aStreamAttr_%d", i);
+ }
+ else
+ {
+ sprintf(key, "%s", streamName.c_str());
+ }
+ streamAtttributes.Add(key, ATTR_TYPES[dataTypeIndex]);
+
+ // Add shader attribute line
+ ss << "INPUT mediump " << ATTR_GLSL_TYPES[dataTypeIndex] << " " << key << ";\n";
+ }
+ }
+
+ auto streamAttributesStr = ss.str();
+
+ /**
+ * - The MVP comes from the Actor that the particle renderer is attached to
+ * - Attributes are added dynamically based on the particle system properties
+ * - There are two buffers bound
+ * * Geometry buffer (in this instance, a quad)
+ * * ParticleSystem stream buffer with interleaved data
+ * - ParticleSystem buffer is being updated every frame
+ */
+ std::string vertexShaderCode = streamAttributesStr + std::string(
+
+ "INPUT mediump vec2 aPosition;\n\
+ INPUT mediump vec2 aTexCoords;\n\
+ \n\
+ uniform mediump mat4 uMvpMatrix;\n\
+ uniform mediump vec3 uSize;\n\
+ uniform lowp vec4 uColor;\n\
+ \
+ OUTPUT mediump vec2 vTexCoord;\n\
+ OUTPUT mediump vec4 vColor;\n\
+ \n\
+ void main()\n\
+ {\n\
+ vec4 pos = vec4(aPosition, 0.0, 1.0) * vec4(aStreamScale, 1.0);\n\
+ vec4 position = pos + vec4(aStreamPosition, 0.0);\n\
+ vTexCoord = aTexCoords;\n\
+ vColor = uColor * aStreamColor;\n\
+ gl_Position = uMvpMatrix * position ;\n\
+ }\n");
+
+ std::string fragmentShaderCode =
+ {
+ "INPUT mediump vec2 vTexCoord;\n\
+ INPUT mediump vec4 vColor;\n\
+ uniform sampler2D sTexture;\n\
+ \n\
+ void main()\n\
+ {\n\
+ lowp vec4 col = TEXTURE(sTexture, vTexCoord) * vColor;\n\
+ if(col.a < 0.1) { discard; }\
+ fragColor = col;\n\
+ }\n"};
+
+ mShader = Shader::New(Dali::Shader::GetVertexShaderPrefix() + vertexShaderCode, Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderCode);
+ mGeometry = Geometry::New();
+
+ // Configure geometry attributes
+ Property::Map geometryMap;
+ geometryMap.Add("aPosition", Dali::Property::VECTOR2);
+ geometryMap.Add("aTexCoords", Dali::Property::VECTOR2);
+
+ // One vertex buffer with geometry
+ VertexBuffer vertexBuffer0 = VertexBuffer::New(geometryMap);
+
+ // fill the buffer entirely
+ // 2D quad
+ const static Vector2 C(0.5f, 0.5f);
+ struct Quad2D
+ {
+ Vertex2D a0{Vector2(0.0f, 0.0f) - C, Vector2(0.0f, 0.0f)};
+ Vertex2D a1{Vector2(1.0f, 0.0f) - C, Vector2(1.0f, 0.0f)};
+ Vertex2D a2{Vector2(1.0f, 1.0f) - C, Vector2(1.0f, 1.0f)};
+ Vertex2D a3{Vector2(0.0f, 0.0f) - C, Vector2(0.0f, 0.0f)};
+ Vertex2D a4{Vector2(1.0f, 1.0f) - C, Vector2(1.0f, 1.0f)};
+ Vertex2D a5{Vector2(0.0f, 1.0f) - C, Vector2(0.0f, 1.0f)};
+ } QUAD;
+
+ std::vector<Quad2D> quads;
+ quads.resize(mEmitter->GetParticleList().GetCapacity());
+ std::fill(quads.begin(), quads.end(), QUAD);
+ vertexBuffer0.SetData(quads.data(), 6u * quads.size());
+
+ // Second vertex buffer with stream data
+ VertexBuffer vertexBuffer1 = VertexBuffer::New(streamAtttributes);
+
+ /**
+ * For more efficient stream management we need to support glVertexAttribDivisor() function.
+ * This will allow step 1 attribute per 4 vertices (GLES3+). Problem: DALi doesn't support instancing
+ *
+ * For older GLES2 we need to duplicate stream data (4x more memory in case of using a quad geometry)
+ *
+ * Point-sprites may be of use in the future (problem: point sprites use screen space)
+ */
+
+ // Based on the particle system, populate buffer
+ mGeometry.AddVertexBuffer(vertexBuffer0);
+ mGeometry.AddVertexBuffer(vertexBuffer1);
+
+ mGeometry.SetType(Geometry::TRIANGLES);
+
+ mVertexBuffer = vertexBuffer0;
+ mStreamBuffer = vertexBuffer1;
+
+ // Set some initial data for streambuffer to force initialization
+ std::vector<uint8_t> data;
+ // Resize using only-non local streams
+ auto elementSize = mEmitter->GetParticleList().GetParticleDataSize(false);
+ data.resize(elementSize *
+ mEmitter->GetParticleList().GetCapacity() * 6u);
+ mStreamBuffer.SetData(data.data(), mEmitter->GetParticleList().GetCapacity() * 6u); // needed to initialize
+
+ // Sets up callback
+ mStreamBuffer.SetVertexBufferUpdateCallback(std::move(mStreamBufferUpdateCallback));
+
+ mRenderer = Renderer::New(mGeometry, mShader);
+
+ mRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY);
+ // If no texture created, the substitute rect 2x2 texture will be used
+ if(!mTexture)
+ {
+ mTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, 2u, 2u);
+ auto* pixelArray = new uint32_t[4]{
+ 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF};
+
+ auto pixelData = PixelData::New(reinterpret_cast<uint8_t*>(pixelArray), 16, 2, 2, Pixel::Format::RGBA8888, PixelData::DELETE_ARRAY);
+ mTexture.Upload(pixelData);
+ }
+
+ mTextureSet = TextureSet::New();
+ mTextureSet.SetTexture(0, mTexture);
+ // mTextureSet.SetSampler(0, Sampler());
+
+ mRenderer.SetTextures(mTextureSet);
+
+ // Attach renderer to the parent actor
+ mEmitter->GetActor().AddRenderer(mRenderer);
+
+ if(mBlendingMode == BlendingMode::SCREEN)
+ {
+ if(Dali::Capabilities::IsBlendEquationSupported(Dali::DevelBlendEquation::SCREEN))
+ {
+ mEmitter->GetActor().SetProperty(Dali::DevelActor::Property::BLEND_EQUATION, Dali::DevelBlendEquation::SCREEN);
+ }
+ else // Fallback to default
+ {
+ mRenderer.SetProperty(Renderer::Property::BLEND_EQUATION_RGB, BlendEquation::ADD);
+ }
+ }
+ else
+ {
+ mRenderer.SetProperty(Renderer::Property::BLEND_EQUATION_RGB, BlendEquation::ADD);
+ }
+}
+
+uint32_t ParticleRenderer::OnStreamBufferUpdate(void* streamData, size_t size)
+{
+ auto& list = GetImplementation(mEmitter->GetParticleList());
+
+ auto particleCount = list.GetActiveParticleCount(); // active particle count
+ auto particleMaxCount = list.GetParticleCount();
+ if(!particleCount)
+ {
+ return 0;
+ }
+
+ auto streamCount = list.GetStreamCount();
+
+ auto elementSize = 0u; // elements size should be cached (it's also stride of buffer) (in bytes)
+ for(auto i = 0u; i < streamCount; ++i)
+ {
+ if(!list.IsStreamLocal(i))
+ {
+ elementSize += list.GetStreamDataTypeSize(i);
+ }
+ }
+
+ // Prepare source buffer (MUST BE OPTIMIZED TO AVOID ALLOCATING AND COPYING!!)
+ auto totalSize = particleMaxCount * elementSize * 6u;
+
+ // buffer sizes must match
+ if(totalSize != size)
+ {
+ // ASSERT here ?
+ return 0;
+ }
+
+ auto* dst = reinterpret_cast<uint8_t*>(streamData);
+
+ auto& particles = list.GetParticles();
+
+ // prepare worker threads
+ auto workerCount = GetThreadPool().GetWorkerCount();
+
+ // divide particles if over the threshold
+
+ [[maybe_unused]] bool runParallel = true;
+ if(!mEmitter->IsParallelProcessingEnabled() || particleCount < workerCount * 10) // don't run parallel if only a few particles to update
+ {
+ runParallel = false;
+ }
+ else
+ {
+ // Partial to handle
+ [[maybe_unused]] auto partialSize = (particleCount / workerCount);
+
+ struct UpdateTask
+ {
+ UpdateTask(Internal::ParticleRenderer& renderer, Internal::ParticleList& list, uint32_t particleStartIndex, uint32_t particleCount, void* basePtr)
+ : owner(renderer),
+ particleList(list),
+ startIndex(particleStartIndex),
+ count(particleCount),
+ ptr(reinterpret_cast<uint8_t*>(basePtr))
+ {
+ }
+
+ void Update()
+ {
+ // execute task
+ owner.UpdateParticlesTask(particleList, startIndex, count, ptr);
+ }
+
+ Internal::ParticleRenderer& owner;
+ Internal::ParticleList& particleList;
+ uint32_t startIndex;
+ uint32_t count;
+ uint8_t* ptr;
+ };
+
+ std::vector<UpdateTask> tasks;
+ tasks.reserve(workerCount);
+ std::vector<Task> taskQueue;
+ auto count = partialSize;
+
+ for(auto i = 0u; i < workerCount; ++i)
+ {
+ auto index = i * partialSize;
+ count = partialSize;
+
+ // make sure there's no leftover particles!
+ if(i == workerCount - 1 && index + count < particleCount)
+ {
+ count = particleCount - index;
+ }
+
+ tasks.emplace_back(*this, list, index, count, streamData);
+ taskQueue.emplace_back([&t = tasks.back()](uint32_t threadId)
+ { t.Update(); });
+ }
+
+ // Execute worker tasks
+ auto future = GetThreadPool().SubmitTasks(taskQueue, 0);
+ // wait to finish
+ future->Wait();
+ }
+
+ // less particles so run on a single thread
+ if(!runParallel)
+ {
+ for(auto& p : particles)
+ {
+ // without instancing we need to duplicate data 4 times per each quad
+ auto* particleDst = dst;
+ for(auto s = 0u; s < streamCount; ++s)
+ {
+ if(!list.IsStreamLocal(s))
+ {
+ // Pointer to stream value
+ auto* valuePtr = &p.GetByIndex<uint8_t*>(s);
+
+ // Size of data
+ auto dataSize = list.GetStreamDataTypeSize(s);
+
+ memcpy(dst, valuePtr, dataSize);
+ dst += dataSize;
+ }
+ }
+ // Replicate data 5 more times for each vertex (GLES2)
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ }
+ }
+ return particleCount * 6u; // return number of elements to render
+}
+
+Renderer ParticleRenderer::GetRenderer() const
+{
+ return mRenderer;
+}
+
+void ParticleRenderer::UpdateParticlesTask(Internal::ParticleList& list,
+ uint32_t particleStartIndex,
+ uint32_t particleCount,
+ uint8_t* basePtr)
+{
+ auto& particles = list.GetParticles();
+ auto streamCount = list.GetStreamCount();
+ auto elementSize = list.GetStreamElementSize(false);
+
+ // calculate begin of buffer
+ uint8_t* dst = (basePtr + (elementSize * 6u) * particleStartIndex);
+
+ auto it = particles.begin();
+ std::advance(it, particleStartIndex);
+
+ for(; particleCount; particleCount--, it++)
+ {
+ ParticleSystem::Particle& p = *it;
+ // without instancing we need to duplicate data 4 times per each quad
+ auto* particleDst = dst;
+ for(auto s = 0u; s < streamCount; ++s)
+ {
+ if(!list.IsStreamLocal(s))
+ {
+ // Pointer to stream value
+ auto* valuePtr = &p.GetByIndex<uint8_t*>(s);
+
+ // Size of data
+ auto dataSize = list.GetStreamDataTypeSize(s);
+
+ memcpy(dst, valuePtr, dataSize);
+ dst += dataSize;
+ }
+ }
+ // Replicate data 5 more times for each vertex (GLES2)
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ memcpy(dst, particleDst, elementSize);
+ dst += elementSize;
+ }
+}
+
+bool ParticleRenderer::Initialize()
+{
+ if(!mInitialized)
+ {
+ CreateShader();
+ mInitialized = true;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_RENDERER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_RENDERER_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 <memory>
+
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+#include <dali-toolkit/public-api/particle-system/particle.h>
+
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/rendering/vertex-buffer.h>
+#include <dali/public-api/signals/render-callback.h>
+
+namespace Dali::Graphics
+{
+class Controller;
+class Shader;
+} // namespace Dali::Graphics
+
+namespace Dali::Toolkit::ParticleSystem
+{
+class ParticleEmitter;
+}
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleEmitter;
+class ParticleRenderer;
+class ParticleList;
+
+class ParticleRenderer : public Dali::BaseObject
+{
+public:
+ ParticleRenderer();
+
+ /**
+ * Create shader
+ */
+ void CreateShader();
+
+ void SetTexture(const Dali::Texture& texture);
+
+ void SetBlendingMode(BlendingMode blendingMode);
+
+ bool Initialize();
+
+ void SetEmitter(ParticleSystem::Internal::ParticleEmitter* emitter);
+
+ void UpdateParticlesTask(Internal::ParticleList& list,
+ uint32_t particleStartIndex,
+ uint32_t particleCount,
+ uint8_t* dst);
+
+ [[nodiscard]] Renderer GetRenderer() const;
+
+ uint32_t OnStreamBufferUpdate(void* data, size_t size);
+
+ bool mUsingStreamDivisor{true}; ///< If attribute divisor is supported, it's going to be used
+
+ Internal::ParticleEmitter* mEmitter{nullptr}; ///< Emitter implementation that uses the renderer
+
+ Dali::Texture mTexture;
+ Dali::TextureSet mTextureSet;
+ Dali::Renderer mRenderer;
+ Dali::Shader mShader;
+ Dali::Geometry mGeometry;
+ Dali::VertexBuffer mVertexBuffer;
+ Dali::VertexBuffer mStreamBuffer;
+ BlendingMode mBlendingMode{BlendingMode::DEFAULT};
+
+ std::unique_ptr<Dali::VertexBufferUpdateCallback> mStreamBufferUpdateCallback;
+
+ bool mInitialized{false};
+};
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+namespace Dali
+{
+inline Toolkit::ParticleSystem::Internal::ParticleRenderer& GetImplementation(Toolkit::ParticleSystem::ParticleRenderer& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleRenderer handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Toolkit::ParticleSystem::Internal::ParticleRenderer&>(handle);
+}
+
+inline const Toolkit::ParticleSystem::Internal::ParticleRenderer& GetImplementation(const Toolkit::ParticleSystem::ParticleRenderer& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleRenderer handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Toolkit::ParticleSystem::Internal::ParticleRenderer&>(handle);
+}
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_RENDERER_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-emitter-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-list-impl.h>
+#include <dali-toolkit/internal/particle-system/particle-source-impl.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+ParticleSource::ParticleSource(std::unique_ptr<ParticleSourceInterface>&& sourceUpdater)
+: mUpdater(std::move(sourceUpdater))
+{
+}
+
+void ParticleSource::Update(ParticleSystem::ParticleList& list, uint32_t count)
+{
+ // updating the source
+ mUpdater->Update(list, count);
+}
+
+ParticleSourceInterface& ParticleSource::GetUpdater() const
+{
+ return *mUpdater;
+}
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_SOURCE_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_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-source.h>
+#include <dali/public-api/object/base-object.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleList;
+class ParticleEmitter;
+class ParticleSource : public Dali::BaseObject
+{
+public:
+ ~ParticleSource() override = default;
+
+ explicit ParticleSource(std::unique_ptr<ParticleSourceInterface>&& sourceUpdater);
+
+ void Update(ParticleSystem::ParticleList& list, uint32_t count);
+
+ [[nodiscard]] ParticleSourceInterface& GetUpdater() const;
+
+private:
+ std::unique_ptr<ParticleSourceInterface> mUpdater;
+};
+
+} // namespace Dali::Toolkit::ParticleSystem::Internal
+
+namespace Dali::Toolkit::ParticleSystem
+{
+inline Internal::ParticleSource& GetImplementation(ParticleSystem::ParticleSource& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleSource handle is empty");
+
+ BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<Internal::ParticleSource&>(handle);
+}
+
+inline const Internal::ParticleSource& GetImplementation(const ParticleSystem::ParticleSource& source)
+{
+ DALI_ASSERT_ALWAYS(source && "ParticleSource handle is empty");
+
+ const BaseObject& handle = source.GetBaseObject();
+
+ return static_cast<const Internal::ParticleSource&>(handle);
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_INTERNAL_PARTICLE_SOURCE_H
\ No newline at end of file
std::string text;
CharacterIndex cursorPosition = 0u;
- Length numberOfWhiteSpaces = 0u;
if(retrieveCursor)
{
- numberOfWhiteSpaces = controller.mImpl->GetNumberOfWhiteSpaces(0u);
-
cursorPosition = controller.mImpl->GetLogicalCursorPosition();
-
- if(cursorPosition < numberOfWhiteSpaces)
- {
- cursorPosition = 0u;
- }
- else
- {
- cursorPosition -= numberOfWhiteSpaces;
- }
}
if(retrieveText)
if(!controller.mImpl->IsShowingPlaceholderText())
{
// Retrieves the normal text string.
- controller.mImpl->GetText(numberOfWhiteSpaces, text);
+ controller.mImpl->GetText(0u, text);
}
else
{
if(mEventData && mEventData->mInputMethodContext)
{
CharacterIndex cursorPosition = GetLogicalCursorPosition();
-
- const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
-
- // Update the cursor position by removing the initial white spaces.
- if(cursorPosition < numberOfWhiteSpaces)
- {
- cursorPosition = 0u;
- }
- else
- {
- cursorPosition -= numberOfWhiteSpaces;
- }
-
mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
mEventData->mInputMethodContext.NotifyCursorPosition();
}
/*
- * 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.
*
*/
-// EXTERNAL INCLUDES
+// CLASS HEADER
#include <dali-toolkit/internal/text/spannable/span-ranges-container-impl.h>
-#include <map>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
+#include <utility>
namespace Dali
{
{
struct SpanRangesContainer::Impl
{
- std::map<Dali::Toolkit::Text::BaseSpan, Dali::Toolkit::Text::Range> mSpanWithRanges; ///< The list of style-span
+ using SpanId = uint32_t;
+
+ // Key : BaseSpan, Value : <Range, Added order>
+ using SpanRangeContainer = std::map<Dali::Toolkit::Text::BaseSpan, std::pair<Dali::Toolkit::Text::Range, SpanId>>;
+
+ SpanRangeContainer mSpanWithRanges{}; ///< The list of style-span
+ SpanId mSpanId{0}; ///< The global id for each added Span. It will be used when we determine order of span.
};
SpanRangesContainer::SpanRangesContainer()
void SpanRangesContainer::AddSpan(const Dali::Toolkit::Text::BaseSpan& span, const Dali::Toolkit::Text::Range& range)
{
- mImpl->mSpanWithRanges.insert(std::make_pair(span, range));
+ mImpl->mSpanWithRanges.insert(std::make_pair(span, std::make_pair(range, mImpl->mSpanId++)));
}
void SpanRangesContainer::RemoveSpan(const Dali::Toolkit::Text::BaseSpan& span)
bool SpanRangesContainer::Contains(const Dali::Toolkit::Text::BaseSpan& span) const
{
- std::map<Dali::Toolkit::Text::BaseSpan, Dali::Toolkit::Text::Range>::iterator it = mImpl->mSpanWithRanges.find(span);
+ const auto it = mImpl->mSpanWithRanges.find(span);
return it != mImpl->mSpanWithRanges.end();
}
void SpanRangesContainer::GetSpans(std::vector<Dali::Toolkit::Text::BaseSpan>& listOfSpans) const
{
- for(std::map<Dali::Toolkit::Text::BaseSpan, Dali::Toolkit::Text::Range>::iterator it = mImpl->mSpanWithRanges.begin();
- it != mImpl->mSpanWithRanges.end();
- ++it)
+ std::map<Impl::SpanId, Dali::Toolkit::Text::BaseSpan> reorderedListOfSpans;
+
+ listOfSpans.reserve(listOfSpans.size() + mImpl->mSpanWithRanges.size());
+
+ // At first, sort the list of spans ordered by added time.
+ for(auto iter = mImpl->mSpanWithRanges.begin(), iterEnd = mImpl->mSpanWithRanges.end(); iter != iterEnd; ++iter)
{
- listOfSpans.push_back(it->first);
+ reorderedListOfSpans.insert(std::make_pair(iter->second.second, iter->first));
+ }
+
+ // Retrieve result.
+ for(auto iter = reorderedListOfSpans.begin(), iterEnd = reorderedListOfSpans.end(); iter != iterEnd; ++iter)
+ {
+ listOfSpans.push_back(iter->second);
}
}
void SpanRangesContainer::GetSpansAndRanges(std::vector<Dali::Toolkit::Text::BaseSpan>& spans, std::vector<Dali::Toolkit::Text::Range>& ranges) const
{
- for(std::map<Dali::Toolkit::Text::BaseSpan, Dali::Toolkit::Text::Range>::iterator it = mImpl->mSpanWithRanges.begin();
- it != mImpl->mSpanWithRanges.end();
- ++it)
+ std::map<Impl::SpanId, std::pair<Dali::Toolkit::Text::BaseSpan, Dali::Toolkit::Text::Range>> reorderedListOfSpansWithRanges;
+
+ spans.reserve(spans.size() + mImpl->mSpanWithRanges.size());
+ ranges.reserve(ranges.size() + mImpl->mSpanWithRanges.size());
+
+ // At first, sort the list of spans ordered by added time.
+ for(auto iter = mImpl->mSpanWithRanges.begin(), iterEnd = mImpl->mSpanWithRanges.end(); iter != iterEnd; ++iter)
+ {
+ reorderedListOfSpansWithRanges.insert(std::make_pair(iter->second.second, std::make_pair(iter->first, iter->second.first)));
+ }
+
+ // Retrieve result.
+ for(auto iter = reorderedListOfSpansWithRanges.begin(), iterEnd = reorderedListOfSpansWithRanges.end(); iter != iterEnd; ++iter)
{
- spans.push_back(it->first);
- ranges.push_back(it->second);
+ spans.push_back(iter->second.first);
+ ranges.push_back(iter->second.second);
}
}
} // namespace Internal
// TODO : Should we seperate input and output value?
preMultiplyOnLoad = externalTextureInfo.preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
}
- return externalTextureInfo.textureSet;
+
+ TextureId alphaMaskId = INVALID_TEXTURE_ID;
+ if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
+ {
+ maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, StorageType::KEEP_TEXTURE, synchronousLoading);
+ alphaMaskId = maskInfo->mAlphaMaskId;
+ textureId = RequestLoad(url, alphaMaskId, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+
+ TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
+ if(loadState == TextureManager::LoadState::UPLOADED)
+ {
+ textureSet = GetTextureSet(textureId);
+ }
+ }
+ else
+ {
+ textureSet = TextureSet::New();
+ textureSet.SetTexture(TEXTURE_INDEX, externalTextureInfo.textureSet.GetTexture(TEXTURE_INDEX));
+ }
}
}
}
textureInfo.storageType = storageType;
textureInfo.orientationCorrection = orientationCorrection;
+ // the case using external texture has already been loaded texture, so change its status to WAITING_FOR_MASK.
+ if(url.GetProtocolType() == VisualUrl::TEXTURE)
+ {
+ if(textureInfo.loadState != LoadState::UPLOADED)
+ {
+ textureInfo.loadState = TextureManager::LoadState::WAITING_FOR_MASK;
+ }
+ }
+
DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState));
// Force reloading of texture by setting loadState unless already loading or cancelled.
if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED))
{
- std::vector<Devel::PixelBuffer> pixelBuffers;
- LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers);
-
- if(pixelBuffers.empty())
+ if(url.GetProtocolType() == VisualUrl::TEXTURE)
{
- // If pixelBuffer loading is failed in synchronously, call RequestRemove() method.
- RequestRemove(textureId, nullptr);
- return INVALID_TEXTURE_ID;
- }
-
- if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
- {
- textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data
- textureInfo.loadState = LoadState::LOAD_FINISHED;
+ // Get external textureSet from cacheManager.
+ std::string location = textureInfo.url.GetLocation();
+ if(!location.empty())
+ {
+ TextureId id = std::stoi(location);
+ auto externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
+ textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
+ textureInfo.loadState = LoadState::UPLOADED;
+ }
}
- else // For the image loading.
+ else
{
- Texture maskTexture;
- if(maskTextureId != INVALID_TEXTURE_ID)
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+ LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers);
+
+ if(pixelBuffers.empty())
{
- TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
- if(maskCacheIndex != INVALID_CACHE_INDEX)
+ // If pixelBuffer loading is failed in synchronously, call RequestRemove() method.
+ RequestRemove(textureId, nullptr);
+ return INVALID_TEXTURE_ID;
+ }
+
+ if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+ {
+ textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data
+ textureInfo.loadState = LoadState::LOAD_FINISHED;
+ }
+ else // For the image loading.
+ {
+ Texture maskTexture;
+ if(maskTextureId != INVALID_TEXTURE_ID)
{
- if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_TEXTURE)
+ TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
+ if(maskCacheIndex != INVALID_CACHE_INDEX)
{
- if(!mTextureCacheManager[maskCacheIndex].textures.empty())
+ if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER)
{
- maskTexture = mTextureCacheManager[maskCacheIndex].textures[0];
+ Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
+ if(maskPixelBuffer)
+ {
+ pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+ }
+ else
+ {
+ DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
+ }
}
}
- else if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER)
+ else
{
- Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
- if(maskPixelBuffer)
- {
- pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask);
- }
- else
- {
- DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
- }
+ DALI_LOG_ERROR("Mask image is not stored in cache.\n");
}
}
- else
- {
- DALI_LOG_ERROR("Mask image is not stored in cache.\n");
- }
- }
- PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
+ PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
- // Upload texture
- UploadTextures(pixelBuffers, textureInfo);
+ // Upload texture
+ UploadTextures(pixelBuffers, textureInfo);
+ }
}
}
}
{
if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE)
{
- // Upload image texture. textureInfo.loadState will be UPLOADED.
- std::vector<Devel::PixelBuffer> pixelBuffers;
- pixelBuffers.push_back(textureInfo.pixelBuffer);
- UploadTextures(pixelBuffers, textureInfo);
+ if(textureInfo.url.GetProtocolType() == VisualUrl::TEXTURE)
+ {
+ // Get external textureSet from cacheManager.
+ std::string location = textureInfo.url.GetLocation();
+ if(!location.empty())
+ {
+ TextureId id = std::stoi(location);
+ auto externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
+ textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
+ textureInfo.loadState = LoadState::UPLOADED;
+ }
+ }
+ else
+ {
+ // Upload image texture. textureInfo.loadState will be UPLOADED.
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+ pixelBuffers.push_back(textureInfo.pixelBuffer);
+ UploadTextures(pixelBuffers, textureInfo);
+ }
// Increase reference counts for notify required textureId.
// Now we can assume that we don't remove & re-assign this textureId
mAnimationData.playRange = *array;
mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_RANGE;
}
+ else if(value.GetType() == Property::STRING)
+ {
+ std::string markerName;
+ if(value.Get(markerName))
+ {
+ Property::Array array;
+ array.Add(markerName);
+ mAnimationData.playRange = std::move(array);
+ mAnimationData.resendFlag |= VectorAnimationTask::RESEND_PLAY_RANGE;
+ }
+ }
break;
}
case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
if(value.Get(maskingType))
{
AllocateMaskData();
- mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
+ if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
+ {
+ // For external textures, only gpu masking is available.
+ // Therefore, MASKING_TYPE is set to MASKING_ON_RENDERING forcelly.
+ mMaskingData->mPreappliedMasking = false;
+ }
+ else
+ {
+ mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
+ }
}
break;
}
if(!mMaskingData)
{
mMaskingData.reset(new TextureManager::MaskingData());
+ if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
+ {
+ mMaskingData->mPreappliedMasking = false;
+ }
}
}
{
const unsigned int TOOLKIT_MAJOR_VERSION = 2;
const unsigned int TOOLKIT_MINOR_VERSION = 2;
-const unsigned int TOOLKIT_MICRO_VERSION = 24;
+const unsigned int TOOLKIT_MICRO_VERSION = 26;
const char* const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
${public_api_src_dir}/image-loader/image-url.cpp
${public_api_src_dir}/image-loader/async-image-loader.cpp
${public_api_src_dir}/image-loader/sync-image-loader.cpp
+ ${public_api_src_dir}/particle-system/particle.cpp
+ ${public_api_src_dir}/particle-system/particle-domain.cpp
+ ${public_api_src_dir}/particle-system/particle-emitter.cpp
+ ${public_api_src_dir}/particle-system/particle-list.cpp
+ ${public_api_src_dir}/particle-system/particle-modifier.cpp
+ ${public_api_src_dir}/particle-system/particle-renderer.cpp
+ ${public_api_src_dir}/particle-system/particle-source.cpp
${public_api_src_dir}/styling/style-manager.cpp
${public_api_src_dir}/transition/fade-transition.cpp
${public_api_src_dir}/transition/slide-transition.cpp
${public_api_src_dir}/visuals/text-visual-properties.h
)
+SET( public_api_particle_system_header_files
+ ${public_api_src_dir}/particle-system/particle-domain.h
+ ${public_api_src_dir}/particle-system/particle-emitter.h
+ ${public_api_src_dir}/particle-system/particle.h
+ ${public_api_src_dir}/particle-system/particle-list.h
+ ${public_api_src_dir}/particle-system/particle-modifier.h
+ ${public_api_src_dir}/particle-system/particle-renderer.h
+ ${public_api_src_dir}/particle-system/particle-source.h
+ ${public_api_src_dir}/particle-system/particle-types.h
+)
+
SET( public_api_transition_header_files
${public_api_src_dir}/transition/fade-transition.h
${public_api_src_dir}/transition/slide-transition.h
${public_api_visuals_header_files}
${public_api_camera_view_header_files}
${public_api_gl_view_header_files}
+ ${public_api_particle_system_header_files}
)
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/particle-system/particle-domain.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/particle-system/particle-domain-impl.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+ParticleDomain ParticleDomain::New()
+{
+ return {new Internal::ParticleDomain()};
+}
+
+ParticleDomain::ParticleDomain(Internal::ParticleDomain* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+ParticleDomain ParticleDomain::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleDomain*>(handle.GetObjectPtr())};
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_DOMAIN_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_DOMAIN_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleDomain;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+
+/**
+ * @class ParticleDomain
+ *
+ * @brief ParticleDomain bounds simulation area to the specified region
+ */
+class DALI_TOOLKIT_API ParticleDomain : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ ParticleDomain() = default;
+
+ /**
+ * @brief Creates new ParticleDomain object
+ *
+ * @return New ParticleDomain object
+ */
+ static ParticleDomain New();
+
+ /**
+ * @brief Downcasts a handle to ParticleDomain handle.
+ *
+ * If handle points to an ParticleDomain object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleDomain object or an uninitialized handle
+ */
+ static ParticleDomain DownCast(BaseHandle handle);
+
+private:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleDomain::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleDomain(Internal::ParticleDomain* impl);
+ /// @endcond
+};
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_DOMAIN_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/public-api/particle-system/particle-emitter.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+#include <dali-toolkit/internal/particle-system/particle-emitter-impl.h>
+
+// EXTERNAL INCLUDES
+#include <utility>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+
+ParticleEmitter ParticleEmitter::New()
+{
+ return {new Dali::Toolkit::ParticleSystem::Internal::ParticleEmitter()};
+}
+
+ParticleEmitter ParticleEmitter::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleEmitter*>(handle.GetObjectPtr())};
+}
+
+void ParticleEmitter::SetSource(const ParticleSource& particleSource)
+{
+ GetImplementation(*this).SetSource(particleSource);
+}
+
+ParticleSource ParticleEmitter::GetSource() const
+{
+ return GetImplementation(*this).GetSource();
+}
+
+void ParticleEmitter::SetDomain(const ParticleDomain& particleDomain)
+{
+ GetImplementation(*this).SetDomain(particleDomain);
+}
+
+void ParticleEmitter::SetRenderer(const ParticleRenderer& particleRenderer)
+{
+ GetImplementation(*this).SetRenderer(particleRenderer);
+}
+
+void ParticleEmitter::SetParticleCount(uint32_t maxParticleCount)
+{
+ GetImplementation(*this).SetParticleCount(maxParticleCount);
+}
+
+ParticleDomain ParticleEmitter::GetDomain() const
+{
+ return GetImplementation(*this).GetDomain();
+}
+
+ParticleRenderer ParticleEmitter::GetRenderer() const
+{
+ return GetImplementation(*this).GetRenderer();
+}
+
+uint32_t ParticleEmitter::AddModifier(const ParticleModifier& particleModifier)
+{
+ return GetImplementation(*this).AddModifier(particleModifier);
+}
+
+ParticleModifier ParticleEmitter::GetModifierAt(uint32_t index)
+{
+ return GetImplementation(*this).GetModifierAt(index);
+}
+
+void ParticleEmitter::RemoveModifierAt(uint32_t index)
+{
+ return GetImplementation(*this).RemoveModifierAt(index);
+}
+
+void ParticleEmitter::AttachTo(Actor actor)
+{
+ GetImplementation(*this).AttachTo(std::move(actor));
+}
+
+ParticleEmitter::ParticleEmitter(Internal::ParticleEmitter* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+ParticleEmitter::Status ParticleEmitter::GetStatus() const
+{
+ return GetImplementation(*this).GetStatus();
+}
+
+ParticleList& ParticleEmitter::GetParticleList()
+{
+ return GetImplementation(*this).GetParticleList();
+}
+
+void ParticleEmitter::Start()
+{
+ GetImplementation(*this).Start();
+}
+
+void ParticleEmitter::Stop()
+{
+ GetImplementation(*this).Stop();
+}
+
+void ParticleEmitter::SetEmissionRate(uint32_t ratePerSecond)
+{
+ GetImplementation(*this).SetEmissionRate(ratePerSecond);
+}
+
+uint32_t ParticleEmitter::GetEmissionRate() const
+{
+ return GetImplementation(*this).GetEmissionRate();
+}
+
+[[maybe_unused]] void ParticleEmitter::EnableParallelProcessing(bool enabled)
+{
+ GetImplementation(*this).EnableParallelProcessing(enabled);
+}
+
+bool ParticleEmitter::IsParallelProcessingEnabled() const
+{
+ return GetImplementation(*this).IsParallelProcessingEnabled();
+}
+
+void ParticleEmitter::SetInitialParticleCount(uint32_t count)
+{
+ GetImplementation(*this).SetInitialParticleCount(count);
+}
+
+uint32_t ParticleEmitter::GetInitialParticleCount() const
+{
+ return GetImplementation(*this).GetInitialParticleCount();
+}
+
+void ParticleEmitter::SetActiveParticlesLimit(uint32_t count)
+{
+ GetImplementation(*this).SetActiveParticlesLimit(count);
+}
+
+uint32_t ParticleEmitter::GetActiveParticlesLimit() const
+{
+ return GetImplementation(*this).GetActiveParticlesLimit();
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_EMITTER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_EMITTER_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/object/base-handle.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleEmitter;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+class ParticleSource;
+
+class ParticleDomain;
+
+class ParticleList;
+
+class ParticleModifier;
+
+class ParticleRenderer;
+
+/**
+ * @class ParticleEmitter
+ *
+ * ParticleEmitter manages a particle emission process. The primary function
+ * of the particle emitter class is to emit particles into a simulated environment.
+ *
+ * ParticleEmitter is responsible for:
+
+ * - Particle generation:
+ *
+ * The emitter generates particles with specific initial properties,
+ * such as position, velocity, size etc. It can create particles various ways which can be
+ * implemented by overriding ParticleSourceInterface. It may create particles in bursts, streams
+ * or in response to specific events. The ParticleSource must be set in order to generate particles
+ * in the system.
+ *
+ * - Particle simulation:
+ *
+ * The emitter updates state of each particle by invoking ParticleModifier stack. It updates particles
+ * over time to simulate desired behaviour. Particle modifiers may apply modifications to the system like
+ * applying forces (gravity, wind), integrating physics.
+ *
+ * The stack of modifiers is executed in order and output of previous modifier is an input of
+ * next one.
+ *
+ * At least one ParticleModifier must be set in order to update particle system and run
+ * the simulation.
+ *
+ * - Particle rendering
+ *
+ * ParticleRenderer must be set in order to render particles. The basic renderer renders only 2D billboard
+ * projected (always facing the camera) particles however the behaviour can be altered in order to render
+ * more complex systems.
+ *
+ * Rendering may be optimize for different graphics APIs.
+ *
+ * - Particle management
+ *
+ * The emitter manages the lifecycle of particles, including creation (via ParticleSource),
+ * update (via ParticleModifier stack) and removal (modifiers and specified lifetime of particles). It
+ * handles scenarios such as recycling particles that have reached the end of their lifespan, reusing them,
+ * or dynamically adjusting their properties based on the emitter's parameters or external factors.
+ *
+ * The particles are stored as ParticleList object which is generated internally. The emitter controls
+ * behaviour of the particle list.
+ *
+ * The basic components making the Particle System are:
+ * - ParticleEmitter - responsible for controlling the emission
+ * - ParticleSource - responsible for generating new particles in the system
+ * - ParticleModifier - responsible for altering the behaviour of particles (updates) and controlling the lifetime
+ * - ParticleRenderer - responsible for rendering the particle system
+ * - ParticleList - storage for particle data
+ * - Particle - view on a selected particle data
+ * - ParticleDomain - the domain of particle system (area/volume) that particle system is bound within
+ *
+ */
+class DALI_TOOLKIT_API ParticleEmitter : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Enum describing status of emitter
+ */
+ enum class Status
+ {
+ INCOMPLETE, ///< Status set when not all data is set in order to run simulation
+ READY, ///< Emitter ready (fully set up)
+ STARTED, ///< Emitter started
+ PAUSED, ///< Emitter paused
+ STOPPED ///< Emitter stopped
+ };
+
+ /**
+ * @brief Creates new ParticleEmitter
+ *
+ * Function creates new ParticleEmitter object with given initial specification.
+ *
+ * @return valid emitter object
+ */
+ static ParticleEmitter New();
+
+ /**
+ * @brief Downcasts a handle to ParticleEmitter handle.
+ *
+ * If handle points to an ParticleEmitter object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleEmitter object or an uninitialized handle
+ */
+ static ParticleEmitter DownCast(BaseHandle handle);
+
+ /**
+ * @brief Enables parallel processing on the CPU
+ *
+ * This flag gives a hint to attached ParticleSystem objects to use
+ * multiple threads if possible.
+ *
+ * Setting this hint may have no effect after the particle system started
+ * simulation. It should be set before calling Start() function.
+ *
+ * @param[in] enabled if True it will try to use MT
+ */
+ void EnableParallelProcessing(bool enabled);
+
+ /**
+ * @brief Tests whether parallel processing is enabled
+ *
+ * @return True if parallel processing mode is enabled.
+ */
+ [[nodiscard]] bool IsParallelProcessingEnabled() const;
+
+ /**
+ * @brief Sets emitter source
+ *
+ * ParticleSource represents objects responsible for emitting
+ * new particles as well as reusing expired particles.
+ *
+ * @param[in] particleSource valid particle emitter source object
+ *
+ */
+ void SetSource(const ParticleSource& particleSource);
+
+ /**
+ * @brief Returns currently used particle emitter source
+ *
+ * @return Handle to the ParticleSource object
+ */
+ [[nodiscard]] ParticleSource GetSource() const;
+
+ /**
+ * @brief Sets emitter domain
+ *
+ * ParticleDomain encloses the area of particle emission.
+ *
+ * @param[in] particleDomain valid particle emitter domain
+ */
+ void SetDomain(const ParticleDomain& particleDomain);
+
+ /**
+ * @brief Sets emitter domain
+ *
+ * ParticleRenderer provides implementation for rendering set of particles
+ *
+ * @param[in] particleRenderer a valid instance of ParticleRenderer
+ */
+ void SetRenderer(const ParticleRenderer& particleRenderer);
+
+ /**
+ * @brief Sets maximum particle count in the system
+ *
+ * This value is mutable but changing number of particles will
+ * force regenerating the whole system data!
+ *
+ * @param[in] maxParticleCount maximum number of particles in the system
+ */
+ void SetParticleCount(uint32_t maxParticleCount);
+
+ /**
+ * @brief Returns currently used particle emitter domain
+ *
+ * @return Handle to the ParticleDomain object
+ */
+ [[nodiscard]] ParticleDomain GetDomain() const;
+
+ /**
+ * @brief Returns currently used particle emitter renderer
+ *
+ * @return Handle to the ParticleRenderer object
+ */
+ [[nodiscard]] ParticleRenderer GetRenderer() const;
+
+
+ /**
+ * @brief Attaches particle system to an actor
+ *
+ * @param[in] actor actor to attach the particle system
+ */
+ void AttachTo(Actor actor);
+
+ /**
+ * @brief Adds new modifier
+ * @param[in] particleMmodifier valid ParticleModifier object
+ *
+ * @return Index into the modifier stack associated with added modifier
+ */
+ uint32_t AddModifier(const ParticleModifier& particleMmodifier);
+
+ /**
+ * @brief Sets particle emission rate per second
+ *
+ * @param[in] ratePerSecond maximum number of particles emitted per second
+ */
+ void SetEmissionRate(uint32_t ratePerSecond);
+
+ /**
+ * @brief Returns emission rate per second
+ *
+ * @return Value of emission rate
+ */
+ [[nodiscard]] uint32_t GetEmissionRate() const;
+
+ /**
+ * @brief number of particles to be emitted on start of the emitter
+ *
+ * @param[in] count Initial count of particles in the system
+ */
+ void SetInitialParticleCount(uint32_t count);
+
+ /**
+ * @brief returns number of particles being emitted on start of the emitter
+ *
+ * @return number of particles
+ */
+ [[nodiscard]] uint32_t GetInitialParticleCount() const;
+
+ /**
+ * @brief Sets maximum number of particles alive
+ *
+ * This function limits number of active particles in the system.
+ *
+ * If set to 0, there is no limit.
+ *
+ * If set to non-zero, if the system reaches limit, no new particles
+ * will spawn until some of them die.
+ *
+ * @param[in] count Limit of active particles
+ */
+ void SetActiveParticlesLimit(uint32_t count);
+
+ /**
+ * @brief Returns active particles limit
+ *
+ * @return Number of active particles system is limited to
+ */
+ [[nodiscard]] uint32_t GetActiveParticlesLimit() const;
+
+ /**
+ * @brief Returns modifier in stack
+ *
+ * @param[in] index Index of modifier
+ * @return Returns valid pointer to the modifier or nullptr
+ */
+ ParticleModifier GetModifierAt(uint32_t index);
+
+ /**
+ * @brief Removes modifier at specified index
+ *
+ * @param[in] index of modifier to be removed
+ */
+ void RemoveModifierAt(uint32_t index);
+
+ /**
+ * @brief Returns ParticleList object
+ *
+ * @return Valid reference to the ParticleList
+ */
+ ParticleList& GetParticleList();
+
+ /**
+ * @brief Starts emitting particles
+ */
+ void Start();
+
+ /**
+ * @brief Stops emitting particles
+ */
+ void Stop();
+
+ /**
+ * @brief Returns current emitter status
+ *
+ * @return Current emitter status
+ */
+ [[nodiscard]] Status GetStatus() const;
+
+ /**
+ * @brief Constructor
+ */
+ ParticleEmitter() = default;
+
+private:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleEmitter::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleEmitter(Dali::Toolkit::ParticleSystem::Internal::ParticleEmitter* impl);
+ /// @endcond
+};
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_EMITTER_H
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-list-impl.h>
+#include <dali-toolkit/public-api/particle-system/particle-list.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector3>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT3;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector4>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT4;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<Vector2>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT2;
+}
+
+template<>
+ParticleStream::StreamDataType StreamDataTypeWrapper<float>::GetType()
+{
+ return ParticleStream::StreamDataType::FLOAT;
+}
+
+ParticleList ParticleList::New(uint32_t capacity, const ParticleStreamTypeFlags& defaultStreams)
+{
+ return {new ParticleSystem::Internal::ParticleList(capacity, defaultStreams)};
+}
+
+ParticleList ParticleList::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleList*>(handle.GetObjectPtr())};
+}
+
+uint32_t ParticleList::AddStream(void* defaults, size_t dataTypeSize, ParticleStream::StreamDataType dataType, bool localStream)
+{
+ return GetImplementation(*this).AddStream(dataTypeSize, defaults, dataType, "", localStream);
+}
+
+void* ParticleList::GetRawStream(uint32_t streamIndex)
+{
+ return GetImplementation(*this).GetRawStream(streamIndex);
+}
+
+void* ParticleList::GetDefaultStream(ParticleStreamTypeFlagBit streamBit)
+{
+ return GetImplementation(*this).GetDefaultStream(streamBit);
+}
+
+uint32_t ParticleList::GetActiveParticleCount()
+{
+ return GetImplementation(*this).GetActiveParticleCount();
+}
+
+ParticleList::ParticleList(Dali::Toolkit::ParticleSystem::Internal::ParticleList* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+uint32_t ParticleList::GetCapacity() const
+{
+ return GetImplementation(*this).GetParticleCount();
+}
+
+Particle ParticleList::NewParticle(float lifetime)
+{
+ return GetImplementation(*this).NewParticle(lifetime);
+}
+
+uint32_t ParticleList::GetParticleDataSize(bool includeLocalStreams)
+{
+ return GetImplementation(*this).GetStreamElementSize(includeLocalStreams);
+}
+
+std::list<Particle>& ParticleList::GetActiveParticles()
+{
+ return GetImplementation(*this).GetParticles();
+}
+
+ParticleList::ParticleList() = default;
+
+
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_LIST_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_LIST_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/common/list-wrapper.h>
+#include <cinttypes>
+
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleList;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+/**
+ * The function is a wrapper needed to retrieve data type enum through template
+ */
+template<class T>
+struct StreamDataTypeWrapper
+{
+ static ParticleStream::StreamDataType GetType()
+ {
+ return {};
+ }
+};
+
+/**
+ * @class ParticleList
+ *
+ * ParticleList defines a storage or a partial view on an existing storage of particle-related data.
+ *
+ * ParticleList contains streams of data (properties) laid out independently (non-interleaved).
+ *
+ * The layout is more optimal for:
+ * - parallel processing
+ * - data-oriented processing (CPU-cache friendly)
+ * - adding custom streams of data (for example, physics properties)
+ *
+ * Some streams are being added automatically by the emitter when certain modifiers are being added.
+ * If the modifier requires particular data it the emitter will update the list with proper stream.
+ *
+ * There are several built-in streams defined:
+ * - POSITION
+ * - VELOCITY
+ * - COLOR
+ * - OPACITY
+ * - SIZE
+ *
+ * The New() function allows adding streams upon creation.
+ *
+ * Custom streams may be used by custom renderers, modifiers and sources but also may play
+ * a role of temporary storage space when it comes to computing particle parameters.
+ *
+ * Represents all or subset of group of particles
+ */
+class DALI_TOOLKIT_API ParticleList : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ ParticleList();
+
+ /**
+ * @brief Bit flags grouping built-in particle streams by type
+ */
+ struct ParticleStreamTypeFlags
+ {
+ ParticleStreamTypeFlags(const ParticleStreamTypeFlags& flags)
+ {
+ value = flags.value;
+ }
+
+ ParticleStreamTypeFlags(const ParticleStreamTypeFlagBit& bit)
+ : value(bit)
+ {
+ }
+
+ ParticleStreamTypeFlags& operator|=(ParticleStreamTypeFlagBit flagBit)
+ {
+ value |= flagBit;
+ return *this;
+ }
+
+ ParticleStreamTypeFlags operator&(ParticleStreamTypeFlagBit flagBit)
+ {
+ return (value & flagBit);
+ }
+
+ explicit operator uint32_t() const
+ {
+ return value;
+ }
+
+ explicit operator bool() const
+ {
+ return value;
+ }
+
+ uint32_t value;
+ };
+
+ /**
+ * @brief Creates new ParticleList
+ *
+ * ParticleList is a storage object that contains data for particle system
+ * layouted as an array of streams.
+ *
+ * Stream may contain data like position, velocity, etc.
+ *
+ * When new ParticleList is created the default streams can be pre-allocated.
+ *
+ * @param[in] capacity Maximum capacity (number of particles in the system)
+ * @param[in] defaultStreams Default data streams to pre-allocate
+ */
+ static ParticleList New(uint32_t capacity, const ParticleStreamTypeFlags& defaultStreams);
+
+ /**
+ * @brief Downcasts a handle to ParticleList handle.
+ *
+ * If handle points to an ParticleList object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleList object or an uninitialized handle
+ */
+ static ParticleList DownCast(BaseHandle handle);
+
+ /**
+ * @brief Registers new data-stream with given unique identifier
+ *
+ * @tparam T Type of data stored in the stream
+ * @param[in] defaults Default value of the stream uninitialized data
+ *
+ * Streams added using AddStream() function are automatically passed into
+ * the shader program as input attributes.
+ *
+ * If there is no need for the stream to be used as a shader attribute
+ * then use AddLocalStream() instead.
+ *
+ * @return Returns index of an allocated data stream
+ */
+ template<class T>
+ inline uint32_t AddStream(T defaults)
+ {
+ return AddStream(&defaults, StreamDataTypeWrapper<T>::GetType(), sizeof(T), false);
+ }
+
+ /**
+ * @brief Adds local data stream
+ *
+ * @tparam T Type of data stored in the stream
+ * @param[in] defaults Default value of the stream
+ *
+ * @return Returns index of newly allocated data stream
+ */
+ template<class T>
+ inline uint32_t AddLocalStream(T defaults)
+ {
+ return AddStream(&defaults, sizeof(T), StreamDataTypeWrapper<T>::GetType(), true);
+ }
+
+ template<class T>
+ T* GetStream(uint32_t streamIndex)
+ {
+ return reinterpret_cast<T*>(GetRawStream(streamIndex));
+ }
+
+ template<class T>
+ T* GetDefaultStream(ParticleStreamTypeFlagBit streamFlagBit)
+ {
+ return reinterpret_cast<T*>(GetDefaultStream(streamFlagBit));
+ }
+
+ /**
+ * @brief Returns size of list including only active particles
+ *
+ * @return
+ */
+ uint32_t GetActiveParticleCount();
+
+ /**
+ * @brief Returns capacity of particle list
+ */
+ [[nodiscard]] uint32_t GetCapacity() const;
+
+ /**
+ * Creates new particle in the list with specified lifetime
+ *
+ * @param[in] lifetime Expected lifetime of new particle (0.0f - lives forever)
+ * @return index into data streams
+ */
+ Particle NewParticle(float lifetime);
+
+ /**
+ * @brief Returns internal data size of streams
+ *
+ * @param[in] includeLocalStreams If true, the size will include local streams
+ * @return Size of data structure
+ */
+ uint32_t GetParticleDataSize(bool includeLocalStreams);
+
+ std::list<Particle>& GetActiveParticles();
+
+private:
+ /// @cond internal
+ /**
+ * @brief Adds new data stream to the list
+ *
+ * @param[in] defaults Default values to fill the stream with
+ * @param[in] dataTypeSize size of stored data type
+ * @param[in] dataType Stream data type
+ * @param[in] localStream Flag indicating whether stream is local (not used in shaders) or not
+ * @return Index of new stream
+ */
+ uint32_t
+ AddStream(void* defaults, size_t dataTypeSize, ParticleStream::StreamDataType dataType, bool localStream);
+ /// @endcond
+
+ /// @cond internal
+ /**
+ * @brief Returns raw data pointer to the stream at given index
+ *
+ * @param[in] streamIndex Stream index
+ *
+ * @return Valid data pointer or nullptr if index invalid
+ */
+ void* GetRawStream(uint32_t streamIndex);
+ /// @endcond
+
+ /// @cond internal
+ /**
+ * @brief Returns raw data stream of a default (built-in) stream
+ * @param[in] streamBit Bit (ParticleStreamTypeFlagBit) representing the stream
+ *
+ * @return Returns valid pointer to the data or nullptr if stream hasn't been used
+ */
+ void* GetDefaultStream(ParticleStreamTypeFlagBit streamBit);
+ /// @endcond
+
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleList::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleList(Dali::Toolkit::ParticleSystem::Internal::ParticleList* impl);
+ /// @endcond
+};
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif
--- /dev/null
+/*
+ * 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 <memory>
+
+#include <dali-toolkit/internal/particle-system/particle-modifier-impl.h>
+#include <dali-toolkit/public-api/particle-system/particle-modifier.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+ParticleModifier ParticleModifier::New(std::unique_ptr<ParticleModifierInterface>&& modifierUpdater)
+{
+ return {new Internal::ParticleModifier(std::move(modifierUpdater))};
+}
+
+ParticleModifier::ParticleModifier(Internal::ParticleModifier* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+ParticleModifier ParticleModifier::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleModifier*>(handle.GetObjectPtr())};
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_MODIFIER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/signals/callback.h>
+#include <memory>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleModifier;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+class ParticleEmitter;
+
+class ParticleList;
+
+class DALI_TOOLKIT_API ParticleModifierInterface
+{
+public:
+ /**
+ * @brief Destructor
+ */
+ virtual ~ParticleModifierInterface() = default;
+
+ /**
+ * @brief Update function to update the modifier to alter the behavior of particles.
+ * @param[in] particleList List of particles
+ * @param[in] firstParticleIndex Index of the first particle
+ * @param[in] particleCount Number of particles
+ */
+ virtual void Update(ParticleList& particleList, uint32_t firstParticleIndex, uint32_t particleCount) = 0;
+
+ /**
+ * @brief Will be called to check whether modifier supports multi-threading
+ *
+ * If modifier supports multi-threading then Update() function will be called
+ * providing partial range of particles to process.
+ *
+ * It is important to make sure, the batch of particles has no dependencies
+ * on particles from outside the batch. If this is the case, the function
+ * must return false and single threaded process will proceed.
+ *
+ * @return True if modifier supports multi-threading
+ */
+ virtual bool IsMultiThreaded()
+ {
+ return false;
+ }
+};
+
+/**
+ * @class ParticleModifier
+ *
+ * @brief ParticleModifer allows altering particle behaviour during simulation.
+ *
+ * Multiple modifiers can be used in the modifier stack.
+ *
+ * Output of previous modifier becomes an input of the next one.
+ *
+ */
+class DALI_TOOLKIT_API ParticleModifier : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ ParticleModifier() = default;
+
+ /**
+ * @brief Destructor
+ */
+ ~ParticleModifier() = default;
+
+ /**
+ * @brief Creates new modifier with given functor object.
+ * The modifier takes over the ownership over the functor object.
+ *
+ * @param[in] modifierUpdater Functor for the modifier
+ * @return New ParticleModifier object
+ */
+ static ParticleModifier New(std::unique_ptr<ParticleModifierInterface>&& modifierUpdater);
+
+ /**
+ * @brief Creates new modifier with given class and construction arguments
+ *
+ * @return New ParticleModifier object
+ */
+ template<class T, class... Args>
+ static ParticleModifier New(Args... args)
+ {
+ return New(std::move(std::make_unique<T>(args...)));
+ }
+
+ /**
+ * @brief Downcasts a handle to ParticleModifier handle.
+ *
+ * If handle points to an ParticleModifier object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleModifier object or an uninitialized handle
+ */
+ static ParticleModifier DownCast(BaseHandle handle);
+
+private:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleModifier::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleModifier(Internal::ParticleModifier* impl);
+ /// @endcond
+};
+
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-renderer-impl.h>
+#include <dali-toolkit/public-api/particle-system/particle-renderer.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+ParticleRenderer ParticleRenderer::New()
+{
+ return {new Internal::ParticleRenderer()};
+}
+
+ParticleRenderer::ParticleRenderer(Internal::ParticleRenderer* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+void ParticleRenderer::SetTexture(const Dali::Texture& texture)
+{
+ GetImplementation(*this).SetTexture(texture);
+}
+
+void ParticleRenderer::SetBlendingMode(BlendingMode blendingMode)
+{
+ GetImplementation(*this).SetBlendingMode(blendingMode);
+}
+
+ParticleRenderer ParticleRenderer::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleRenderer*>(handle.GetObjectPtr())};
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_RENDERER_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_RENDERER_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/signals/callback.h>
+#include <memory>
+
+namespace Dali
+{
+class Texture;
+}
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleRenderer;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+/**
+ * @brief Blending modes for particle renderer
+ */
+enum class DALI_TOOLKIT_API BlendingMode
+{
+ ADDITIVE, ///< Additive blending mode (default)
+ DEFAULT = ADDITIVE,
+ SCREEN ///< SCREEN mode, Advanced blending support required
+};
+
+class DALI_TOOLKIT_API ParticleRenderer : public Dali::BaseHandle
+{
+public:
+ ParticleRenderer() = default;
+
+ /**
+ * @brief Creates new instance of basic 2D renderer
+ *
+ * @return Valid instance of renderer
+ */
+ static ParticleRenderer New();
+
+ /**
+ * @brief Sets blending mode for the renderer
+ *
+ * @param[in] blendingMode Valid particle blending mode to be used
+ */
+ void SetBlendingMode(BlendingMode blendingMode);
+
+ /**
+ * @brief Sets renderable as a 2D texture (sprites)
+ *
+ * @param[in] texture Valid texture
+ */
+ void SetTexture(const Dali::Texture& texture);
+
+ /**
+ * @brief Downcasts a handle to ParticleRenderer handle.
+ *
+ * If handle points to an ParticleRenderer object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleRenderer object or an uninitialized handle
+ */
+ static ParticleRenderer DownCast(BaseHandle handle);
+
+private:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleRenderer::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleRenderer(Internal::ParticleRenderer* impl);
+ /// @endcond
+};
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_RENDERER_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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/internal/particle-system/particle-source-impl.h>
+#include <dali-toolkit/public-api/particle-system/particle-source.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+
+ParticleSource ParticleSource::New(std::unique_ptr<ParticleSourceInterface>&& sourceUpdater)
+{
+ return {new Internal::ParticleSource(std::move(sourceUpdater))};
+}
+
+ParticleSource::ParticleSource(Internal::ParticleSource* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+ParticleSource ParticleSource::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::ParticleSource*>(handle.GetObjectPtr())};
+}
+
+ParticleSourceInterface& ParticleSource::GetSourceCallback() const
+{
+ return GetImplementation(*this).GetUpdater();
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_SOURCE_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/signals/callback.h>
+#include <memory>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+namespace Dali::Toolkit::ParticleSystem::Internal
+{
+class ParticleSource;
+}
+
+namespace Dali::Toolkit::ParticleSystem
+{
+class ParticleEmitter;
+
+class ParticleList;
+
+class ParticleDomain;
+
+class ParticleModifier;
+
+class ParticleRenderer;
+
+/**
+ * @brief Interface to override as a particle source
+ *
+ * ParticleSourceInterace allows altering behaviour for
+ * generating new particles.
+ */
+class DALI_TOOLKIT_API ParticleSourceInterface
+{
+public:
+ /**
+ * @brief Adds new particles
+ * @param[in] outList List to write back
+ * @param[in] count Requested particles count
+ *
+ * @return Function should return a Number of emitted particles
+ */
+ virtual uint32_t Update(ParticleList& outList, uint32_t count) = 0;
+
+ /**
+ * @brief Called when source is added to the emitter
+ */
+ virtual void Init() = 0;
+};
+
+/**
+ * @class ParticleSource
+ *
+ * ParticleSource defines a logic associated with particles emission.
+ * The emitter will use ParticleSource in order to spawn new particles.
+ *
+ * ParticleSource manages how, where and how many particles to emit.
+ *
+ * ParticleSource uses an implementation of ParticleSourceInterface when
+ * emitting new particles.
+ */
+class DALI_TOOLKIT_API ParticleSource : public Dali::BaseHandle
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ ParticleSource() = default;
+
+ /**
+ * @brief Destructor
+ */
+ ~ParticleSource() = default;
+
+ /**
+ * @brief Creates a new instance of ParticleSource
+ *
+ * New instance is given an ownership over valid implementation
+ * of a ParticleSourceInferface.
+ *
+ * @param[in] particleSourceCallback Valid implementation of ParticleSourceInterface
+ *
+ * @return New ParticleSource object handle
+ */
+ static ParticleSource New(std::unique_ptr<ParticleSourceInterface>&& particleSourceCallback);
+
+ /**
+ * @brief Creates new ParticleSource with given source callback and arguments
+ *
+ * The function creates a ParticleSource of a given class (T) and constructor
+ * arguments.
+ *
+ * @return New ParticleSource object handle
+ */
+ template<class T, class... Args>
+ static ParticleSource New(Args... args)
+ {
+ return New(std::move(std::make_unique<T>(args...)));
+ }
+
+ /**
+ * @brief Downcasts a handle to ParticleSource handle.
+ *
+ * If handle points to an ParticleSource object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a ParticleSource object or an uninitialized handle
+ */
+ static ParticleSource DownCast(BaseHandle handle);
+
+ /**
+ * @brief Returns associated particle source callback
+ *
+ * @return Valid reference to associated callback
+ */
+ [[nodiscard]] ParticleSourceInterface& GetSourceCallback() const;
+
+private:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by ParticleSource::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ ParticleSource(Internal::ParticleSource* impl);
+ /// @endcond
+};
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_SOURCE_H
\ No newline at end of file
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_TYPES_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_TYPES_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.
+ *
+ */
+
+// EXTERNAL_INCLUDES
+#include <cinttypes>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+using ParticleStreamTypeFlagBit = uint32_t;
+
+namespace ParticleStream DALI_TOOLKIT_API
+{
+constexpr ParticleStreamTypeFlagBit POSITION_STREAM_BIT = 1 << 0; ///< 3D Position stream
+constexpr ParticleStreamTypeFlagBit ROTATION_STREAM_BIT = 1 << 1; ///< 3D Rotation stream
+constexpr ParticleStreamTypeFlagBit SCALE_STREAM_BIT = 1 << 2; ///< 3D Scale stream
+constexpr ParticleStreamTypeFlagBit SIZE_STREAM_BIT = 1 << 3;
+constexpr ParticleStreamTypeFlagBit COLOR_STREAM_BIT = 1 << 4;
+constexpr ParticleStreamTypeFlagBit OPACITY_STREAM_BIT = 1 << 5;
+constexpr ParticleStreamTypeFlagBit VELOCITY_STREAM_BIT = 1 << 6;
+constexpr ParticleStreamTypeFlagBit LIFETIME_STREAM_BIT = 1 << 7;
+constexpr ParticleStreamTypeFlagBit ALL_STREAMS = 255;
+constexpr ParticleStreamTypeFlagBit DEFAULT_STREAMS = 255; ///< Default streams
+
+/**
+ * Stream data types
+ */
+enum class StreamDataType
+{
+ FLOAT,
+ FLOAT2,
+ FLOAT3,
+ FLOAT4,
+ INT,
+ INT2,
+ INT3,
+ INT4
+};
+
+} // namespace ParticleStream DALI_TOOLKIT_API
+} // namespace Dali::Toolkit::ParticleSystem
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_TYPES_H
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/particle-system/particle-impl.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+
+Particle Particle::DownCast(BaseHandle handle)
+{
+ return {dynamic_cast<Internal::Particle*>(handle.GetObjectPtr())};
+}
+
+void* Particle::Get(ParticleStreamTypeFlagBit streamBit)
+{
+ return GetImplementation(*this).Get(streamBit);
+}
+
+void* Particle::GetByIndex(uint32_t streamIndex)
+{
+ return GetImplementation(*this).GetByIndex(streamIndex);
+}
+
+Particle::Particle(Dali::Toolkit::ParticleSystem::Internal::Particle* impl)
+: Dali::BaseHandle(impl)
+{
+}
+
+uint32_t Particle::GetIndex() const
+{
+ return GetImplementation(*this).GetIndex();
+}
+
+} // namespace Dali::Toolkit::ParticleSystem
--- /dev/null
+#ifndef DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_H
+#define DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_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.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/particle-system/particle-types.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-handle.h>
+
+namespace Dali::Toolkit::ParticleSystem
+{
+namespace Internal
+{
+class Particle;
+}
+
+/**
+ * @class Particle
+ *
+ * Particle represents a single instance of a particle in the particle system.
+ *
+ * Particle provides a simple interface that allows R/W access to selected data stream and
+ * allows ignoring a placement of the particle data within the stream (Particle serves as a
+ * view on particular data within a stream).
+ */
+class DALI_TOOLKIT_API Particle : public BaseHandle
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ Particle() = default;
+
+ /**
+ * @brief Downcasts a handle to Particle handle.
+ *
+ * If handle points to an Particle object, the downcast produces valid handle.
+ * If not, the returned handle is left uninitialized.
+ *
+ * @param[in] handle to An object
+ * @return handle to a Particle object or an uninitialized handle
+ */
+ static Particle DownCast(BaseHandle handle);
+
+ /**
+ * @brief Returns writeable reference to the data for specified stream
+ *
+ * The ParticleStreamTypeFlagBit allows accessing only pre-defined streams
+ * defined by the ParticleSystem. For custom streams GetByIndex() should be used.
+ *
+ * @tparam T type of data
+ * @param[in] streamBit Stream to access data from
+ * @return Reference to the data value
+ */
+ template<class T>
+ T& Get(ParticleStreamTypeFlagBit streamBit)
+ {
+ return *reinterpret_cast<T*>(Get(streamBit));
+ }
+
+ /**
+ * @brief Returns writeable reference to the data for a stream specified by stream index.
+ *
+ * This function allows accessing builtin streams as well as custom ones. The index of a custom
+ * stream should be stored upon creation.
+ *
+ * @tparam T type of data
+ * @param[in] streamIndex Index of stream in the emitter
+ * @return Reference to the data value
+ */
+ template<class T>
+ T& GetByIndex(uint32_t streamIndex)
+ {
+ return *reinterpret_cast<T*>(GetByIndex(streamIndex));
+ }
+
+ /**
+ * @brief Returns an index of particle within emitter data streams.
+ *
+ * @return Index of particle
+ */
+ [[nodiscard]] uint32_t GetIndex() const;
+
+public:
+ /// @cond internal
+ /**
+ * @brief This constructor is used by Particle::New() methods.
+ *
+ * @param [in] impl A pointer to a newly allocated implementation
+ */
+ Particle(Dali::Toolkit::ParticleSystem::Internal::Particle* impl);
+ /// @endcond
+
+private:
+ /// @cond internal
+ /**
+ * @brief Returns pointer to the value
+ *
+ *
+ * The ParticleStreamTypeFlagBit allows accessing only pre-defined streams
+ * defined by the ParticleSystem. For custom streams GetByIndex() should be used.
+ *
+ * @param[in] streamBit Stream to access data from
+ *
+ * @return void* to the memory within stream that stores the data
+ */
+ void* Get(ParticleStreamTypeFlagBit streamBit);
+ /// @endcond
+
+ /// @cond internal
+ /**
+ * @brief Returns writeable reference to the data for a stream specified by stream index.
+ *
+ * This function allows accessing builtin streams as well as custom ones. The index of a custom
+ * stream should be stored upon creation.
+ *
+ * @param[in] streamIndex Index of stream in the emitter
+ *
+ * @return void* to the memory within stream that stores the data
+ */
+ void* GetByIndex(uint32_t streamIndex);
+ /// @endcond
+};
+} // namespace Dali::Toolkit::ParticleSystem
+
+#endif // DALI_TOOLKIT_PARTICLE_SYSTEM_PARTICLE_H
\ No newline at end of file
Name: dali2-toolkit
Summary: Dali 3D engine Toolkit
-Version: 2.2.24
+Version: 2.2.26
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT