Skip to content

Instantly share code, notes, and snippets.

@cloudwu
Last active December 2, 2020 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cloudwu/9479d29c45efae047318f19090b4ee78 to your computer and use it in GitHub Desktop.
Save cloudwu/9479d29c45efae047318f19090b4ee78 to your computer and use it in GitHub Desktop.
#include "psystem.h"
#include <cstddef>
#include <cstdio>
#include <algorithm>
#include <vector>
#include "psystem_manager.h"
#define REMAP_CACHE 128
namespace {
template<typename T> struct type_ { static const int object_id; static const int pointer_id; };
#define PARTICLE_OBJECT_ID(T,ID) template<> const int type_<particle_system::T>::object_id = PID_##ID;
#define PARTICLE_POINTER_ID(T,ID) template<> const int type_<particle_system::T>::pointer_id = PID_##ID;
PARTICLE_OBJECT_ID(value, VALUE)
PARTICLE_OBJECT_ID(lifetime, LIFETIME)
PARTICLE_POINTER_ID(object, OBJECT)
}
class particle_system::attribute {
public:
virtual ~attribute() {};
virtual int remap(struct particle_remap *map, int n) = 0;
virtual void pop_back() = 0;
};
namespace {
template<typename T>
struct attribute_remap : public particle_system::attribute {
int remap(struct particle_remap *map, int n) override {
T * self = static_cast<T *>(this);
for (int i=0;i<n;i++) {
if (map[i].component_id != map[0].component_id)
return i;
if (map[i].to_id != PARTICLE_INVALID) {
self->move(map[i].from_id, map[i].to_id);
} else {
self->shrink(map[i].from_id);
}
}
return n;
}
};
template<typename T>
struct attribute_object final : public attribute_remap<attribute_object<T>> {
void move(int from, int to) {
data[to] = std::move(data[from]);
}
void shrink(int n) {
data.resize(n);
}
void pop_back() override {
data.pop_back();
}
void* pointer(int index) {
return reinterpret_cast<void *>(&data[index]);
}
std::vector<T> data;
};
template<typename T>
struct attribute_pointer final : public attribute_remap<attribute_pointer<T>> {
~attribute_pointer() {
for (auto& iter : data) {
delete(iter);
}
}
void move(int from, int to) {
delete(data[to]);
data[to] = data[from];
data[from] = nullptr;
}
void shrink(int n) {
int sz = data.size();
for (int i=n;i<sz;i++) {
delete(data[i]);
}
data.resize(n);
}
void pop_back() override {
delete(data.back());
data.pop_back();
}
void* pointer(int index) {
return reinterpret_cast<void *>(data[index]);
}
std::vector<T*> data;
};
}
template<typename T>
struct particle_system::type : public type_<T> {
static const int id = type_<T>::object_id;
typedef attribute_object<T> container_type;
typedef T value_type;
};
template <typename T>
struct particle_system::type<T*> : public type_<T> {
static const int id = type_<T>::pointer_id;
typedef attribute_pointer<T> container_type;
typedef T value_type;
};
particle_system::particle_system() {
manager = particlesystem_create();
init<value>();
init<lifetime>();
init<object *>();
}
particle_system::~particle_system() {
for (int i=0;i<maxid;i++) {
delete attribs[i];
}
particlesystem_release(manager);
}
template <typename T>
void particle_system::init() {
attribs[type<T>::id] = new typename type<T>::container_type;
}
template <typename T>
int particle_system::push_back(T &&v) {
int id = type<T>::id;
static_cast<typename type<T>::container_type *>(attribs[id])->data.push_back(v);
return id;
}
template <typename T>
void particle_system::remove(int index) {
particlesystem_remove(manager, type<T>::id, index);
}
template <typename T>
const typename particle_system::type<T>::value_type * particle_system::sibling(int tag, int index) {
int sindex = particlesystem_component(manager, tag, index, type<T>::id);
if (sindex == PARTICLE_INVALID)
return nullptr;
return reinterpret_cast<const typename type<T>::value_type *>
(static_cast<typename type<T>::container_type *>(attribs[type<T>::id])->pointer(sindex));
}
void
particle_system::add(const std::initializer_list<int> &a) {
if (!particlesystem_add(manager, a.size(), a.begin())) {
for (auto id : a) {
attribs[id]->pop_back();
}
}
}
template <typename T>
struct particle_system::container : public std::vector<T>, particle_system::type<T> {
static particle_system::container<T>& convert(attribute *a) {
return *static_cast<particle_system::container<T>*>(&static_cast<typename particle_system::type<T>::container_type *>(a)->data);
}
};
template <typename T>
particle_system::container<T>& particle_system::attrib() {
return container<T>::convert(attribs[container<T>::id]);
}
void
particle_system::arrange() {
struct particle_remap remap[REMAP_CACHE];
struct particle_arrange_context ctx;
int n = 0;
int cap = sizeof(remap)/sizeof(remap[0]);
do {
n = particlesystem_arrange(manager, cap, remap, &ctx);
int i=0;
while (i<n) {
int component_id = remap[i].component_id;
if (component_id < maxid) {
i+=attribs[component_id]->remap(remap+i, n-i);
} else {
++i;
}
}
} while (n == cap);
}
int
particle_system::size(int pid) {
return particlesystem_count(manager, pid);
}
void
particle_system::update_life(float dt) {
int index = 0;
for (auto &life : attrib<lifetime>()) {
printf("lifetime: %f\n", life);
life -= dt;
if (life <= 0) {
printf("REMOVE %d\n", index);
remove<lifetime>(index);
}
++index;
}
}
void
particle_system::update_value() {
for (auto &v : attrib<value>()) {
v.value += v.delta;
}
}
void
particle_system::update_print() {
int n = size(TAG_PRINT);
for (int i = 0;i<n;i++) {
const value *v = sibling<value>(TAG_PRINT, i);
if (v) {
printf("Value = %d ", v->value);
}
const object *obj = sibling<object *>(TAG_PRINT, i);
if (obj) {
printf("Object = %d ", obj->value());
}
printf("\tParticle %d\n", i);
}
}
void
particle_system::test() {
/*
static const char *names[PARTICLE_COMPONENT] {
"value",
"lifetime",
"object",
"print",
};
*/
add({
push_back(lifetime(10)),
TAG_PRINT,
});
add({
push_back(lifetime(20)),
push_back(value { 0, 1 }),
push_back(new object(42)),
TAG_PRINT,
});
add({
push_back(lifetime(15)),
push_back(value { 0, 10 }),
TAG_PRINT,
});
add({
push_back(lifetime(17)),
push_back(value { 0, 3 }),
TAG_PRINT,
});
add({
push_back(lifetime(4)),
push_back(value { 0, 5 }),
TAG_PRINT,
});
add({
push_back(lifetime(8)),
push_back(value { 0, 20 }),
TAG_PRINT,
});
for (int i=0;i<10;i++) {
printf("== Frame %d ==\n", i);
update_life(2.0f);
update_value();
update_print();
// particlesystem_debug(manager, names);
arrange();
}
}
int
main() {
particle_system P;
P.test();
return 0;
}
#ifndef particle_system_h
#define particle_system_h
#define PID_VALUE 0
#define PID_OBJECT 1
#define PARTICLE_KEY_COMPONENT 2 // key components
#define PID_LIFETIME 2
#define PID_COUNT 3 // number components types of data in particle_system
#define TAG_PRINT 3
#define PARTICLE_COMPONENT 4 // total tags and components
#include <initializer_list>
struct particle_manager;
class particle_system {
public:
struct value {
int value;
int delta;
};
class object {
public:
object(int v) : v(v) {}
int value() const { return v; }
private:
int v;
};
typedef float lifetime;
particle_system();
~particle_system();
void test();
void update_life(float dt);
void update_value();
void update_print();
static const int maxid = PID_COUNT;
private:
template <typename T> struct type;
void arrange();
void add(const std::initializer_list<int> &a);
template <typename T> void init();
template <typename T> int push_back(T &&);
template <typename T> void pop_back();
template <typename T> void remove(int index);
template <typename T> struct container;
template <typename T> const typename type<T>::value_type * sibling(int tag, int index);
template <typename T> container<T>& attrib();
int size(int pid);
class attribute;
attribute * attribs[maxid];
struct particle_manager *manager;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment