Skip to content

Instantly share code, notes, and snippets.

@nikki93
Created June 16, 2021 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikki93/f73dc7a2662027d98b97073274b4e8c3 to your computer and use it in GitHub Desktop.
Save nikki93/f73dc7a2662027d98b97073274b4e8c3 to your computer and use it in GitHub Desktop.
//
// Inspector
//
// int
static bool inspect(int &val, const PropAttribs &attribs) {
uiText("%d", val);
return false;
}
// double
static bool inspect(double &val, const PropAttribs &attribs) {
uiText("%.8g", val);
return false;
}
// float
static bool inspect(float &val, const PropAttribs &attribs) {
double d = val;
if (inspect(d, attribs)) {
val = float(d);
return true;
};
return false;
}
// Vec2
static bool inspect(Vec2 &val, const PropAttribs &attribs) {
uiText("%.8g, %.8g", val.x, val.y);
return false;
}
// rl::Rectangle
static bool inspect(rl::Rectangle &val, const PropAttribs &attribs) {
uiText("%.8g, %.8g, %.8g, %.8g", val.x, val.y, val.width, val.height);
return false;
}
// bool
static bool inspect(bool &val, const PropAttribs &attribs) {
uiText(val ? "true" : "false");
return false;
}
// char array
template<int N>
static bool inspect(char (&val)[N], const PropAttribs &attribs) {
uiText("\"%s\"", val);
return false;
}
// Seq<T, N>
template<typename T, unsigned N>
static bool inspect(Seq<T, N> &val, const PropAttribs &attribs) {
uiText("--");
return false;
}
// Props
static bool inspect(auto &val, const PropAttribs &attribs) {
auto result = false;
forEachProp(val, [&]<typename P>(P &prop) {
ui("div")("prop-name")([&]() {
uiText(P::name.data());
});
ui("div")("prop-value-container")([&]() {
result = result || inspect(prop(), P::attribs);
});
});
return result;
}
// Inspector
static void uiEditInspector() {
static const auto titleify = [](std::string_view title) {
static char buf[64];
auto i = 0;
for (auto c : title) {
if (i >= int(sizeof(buf) - 2)) {
break;
}
if (std::isupper(c)) {
if (i > 0) {
buf[i++] = ' ';
}
buf[i++] = char(std::tolower(c));
} else {
buf[i++] = c;
}
}
buf[i] = '\0';
return buf;
};
ui("div")("inspector")([&]() {
each([&](Entity ent, EditSelect &sel) {
static char buf[64];
void (*remover)(Entity ent) = nullptr;
GameComponents::each([&]<typename T>(T *) {
if (has<T>(ent)) {
auto title = titleify(getTypeName<T>());
auto open = !std::strcmp(edit.inspectCurrTitle(), title);
ui("details")(title)("open", open)([&]() {
ui("summary")("click", [&]() {
edit.inspectCurrTitle()[0] = '\0';
std::strncat(edit.inspectCurrTitle(), title, sizeof(edit.inspectCurrTitle()) - 1);
})([&]() {
uiText(title);
ui("button")("remove")("click", [&]() {
remover = [](Entity ent) {
remove<T>(ent);
std::snprintf(buf, sizeof(buf), "remove '%s'", titleify(getTypeName<T>()));
saveEditSnapshot(buf);
};
});
});
auto &comp = get<T>(ent);
if constexpr (requires { inspect(comp, ent); }) {
inspect(comp, ent);
} else {
inspect(comp, PropAttribs {});
}
});
}
});
ui("div")("add-bar")([&]() {
GameComponents::each([&]<typename T>(T *) {
if (!has<T>(ent)) {
auto title = titleify(getTypeName<T>());
ui("button")("add")("label", title)("click", [&]() {
add<T>(ent);
std::snprintf(buf, sizeof(buf), "add '%s'", title);
saveEditSnapshot(buf);
});
}
});
});
if (remover) {
remover(ent);
}
});
});
}
//
// Read / write
//
//
// Primitives
//
// int
inline void read(int &val, const cJSON *jsn) {
if (cJSON_IsNumber(jsn)) {
val = jsn->valueint;
}
}
inline cJSON *write(const int &val) {
return cJSON_CreateNumber(val);
}
// float
inline void read(float &val, const cJSON *jsn) {
if (cJSON_IsNumber(jsn)) {
val = float(jsn->valuedouble);
}
}
inline cJSON *write(const float &val) {
return cJSON_CreateNumber(val);
}
// double
inline void read(double &val, const cJSON *jsn) {
if (cJSON_IsNumber(jsn)) {
val = jsn->valuedouble;
}
}
inline cJSON *write(const double &val) {
return cJSON_CreateNumber(val);
}
// bool
inline void read(bool &val, const cJSON *jsn) {
if (cJSON_IsBool(jsn)) {
val = cJSON_IsTrue(jsn);
}
}
inline cJSON *write(const bool &val) {
return cJSON_CreateBool(val);
}
// char array
template<int N>
void read(char (&val)[N], const cJSON *jsn) {
if (cJSON_IsString(jsn)) {
val[0] = '\0';
std::strncat(val, cJSON_GetStringValue(jsn), sizeof(val) - 1);
}
}
inline cJSON *write(const char *val) {
return cJSON_CreateString(val);
}
// Entity
inline void read(Entity &val, const cJSON *jsn) {
if (cJSON_IsNumber(jsn)) {
val = Entity(jsn->valueint);
}
}
inline cJSON *write(const Entity &val) {
return cJSON_CreateNumber(uint64_t(val));
}
//
// Graphics
//
// PERF: Can use direct access to nodes -- array get scans
// Vec2
inline void read(Vec2 &val, const cJSON *jsn) {
if (cJSON_IsArray(jsn) && cJSON_GetArraySize(jsn) == 2) {
read(val.x, cJSON_GetArrayItem(jsn, 0));
read(val.y, cJSON_GetArrayItem(jsn, 1));
}
}
inline cJSON *write(const Vec2 &val) {
auto result = cJSON_CreateArray();
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.x));
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.y));
return result;
}
// rl::Rectangle
inline void read(rl::Rectangle &val, const cJSON *jsn) {
if (cJSON_IsArray(jsn) && cJSON_GetArraySize(jsn) == 4) {
read(val.x, cJSON_GetArrayItem(jsn, 0));
read(val.y, cJSON_GetArrayItem(jsn, 1));
read(val.width, cJSON_GetArrayItem(jsn, 2));
read(val.height, cJSON_GetArrayItem(jsn, 3));
}
}
inline cJSON *write(const rl::Rectangle &val) {
auto result = cJSON_CreateArray();
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.x));
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.y));
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.width));
cJSON_AddItemToArray(result, cJSON_CreateNumber(val.height));
return result;
}
// rl::Camera
struct ReadWriteCamera {
Prop(Vec2, offset) { 0, 0 };
Prop(Vec2, target) { 0, 0 };
Prop(float, rotation) { 0 };
Prop(float, zoom) { 1 };
};
inline void read(rl::Camera2D &val, const cJSON *jsn) {
ReadWriteCamera rw { { val.offset }, { val.target }, { val.rotation }, { val.zoom } };
read(rw, jsn);
val = { rw.offset(), rw.target(), rw.rotation(), rw.zoom() };
}
inline cJSON *write(const rl::Camera2D &val) {
return write(ReadWriteCamera { { val.offset }, { val.target }, { val.rotation }, { val.zoom } });
}
//
// Containers
//
// T[N]
template<typename T, unsigned N>
void read(T (&val)[N], const cJSON *jsn) {
if (cJSON_IsArray(jsn)) {
auto i = 0;
for (auto elemJsn = jsn->child; elemJsn; elemJsn = elemJsn->next) {
if (i >= int(N)) {
break;
}
read(val[i++], elemJsn);
}
}
}
template<typename T, unsigned N>
cJSON *write(const T (&val)[N]) {
auto result = cJSON_CreateArray();
for (auto i = 0; i < int(N); ++i) {
cJSON_AddItemToArray(result, write(val[i]));
}
return result;
}
// Seq<T, N>
template<typename T, unsigned N>
void read(Seq<T, N> &val, const cJSON *jsn) {
if (cJSON_IsArray(jsn)) {
for (auto elemJsn = jsn->child; elemJsn; elemJsn = elemJsn->next) {
val.emplace_back();
read(val.back(), elemJsn);
}
}
}
template<typename T, unsigned N>
cJSON *write(const Seq<T, N> &val) {
auto result = cJSON_CreateArray();
for (auto &elem : val) {
cJSON_AddItemToArray(result, write(elem));
}
return result;
}
// cJSON *
inline void read(cJSON *&val, const cJSON *jsn) {
val = cJSON_Duplicate(jsn, true);
}
inline cJSON *write(cJSON *const &jsn) {
return cJSON_Duplicate(jsn, true);
}
//
// Props
//
void read(auto &val, const cJSON *jsn) {
if (cJSON_IsObject(jsn)) {
for (auto elemJsn = jsn->child; elemJsn; elemJsn = elemJsn->next) {
const auto key = elemJsn->string;
const auto keyHash = hash(key);
forEachProp(val, [&]<typename P>(P &prop) {
if (keyHash == P::nameHash && key == P::name) {
read(prop(), elemJsn);
}
});
}
}
}
cJSON *write(const auto &val) {
auto result = cJSON_CreateObject();
forEachProp(val, [&]<typename P>(P &prop) {
cJSON_AddItemToObjectCS(result, P::name.data(), write(prop()));
});
return result;
}
//
// Scene
//
void readScene(const cJSON *jsn) {
for (auto entJsn = cJSON_GetObjectItemCaseSensitive(jsn, "entities")->child; entJsn;
entJsn = entJsn->next) {
Entity ent;
if (auto idJsn = cJSON_GetObjectItemCaseSensitive(entJsn, "id")) {
ent = createEntity(Entity(cJSON_GetNumberValue(idJsn)));
} else {
ent = createEntity();
}
for (auto compJsn = cJSON_GetObjectItemCaseSensitive(entJsn, "components")->child; compJsn;
compJsn = compJsn->next) {
const auto typeName
= cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(compJsn, "_type"));
const auto typeNameHash = hash(typeName);
GameComponents::each([&]<typename T>(T *) {
constexpr auto TName = getTypeName<T>();
constexpr auto TNameHash = hash(TName);
if (typeNameHash == TNameHash && typeName == TName) {
T comp {};
read(comp, compJsn);
add<T>(ent, std::move(comp));
}
});
}
}
}
void readScene(const char *assetName) {
auto root = cJSON_Parse(getAssetContents(assetName));
readScene(root);
cJSON_Delete(root);
}
cJSON *writeScene() {
auto result = cJSON_CreateObject();
Seq<Entity> entities;
each([&](Entity ent) {
entities.push_back(ent);
});
std::reverse(entities.begin(), entities.end());
auto entitiesJsn = cJSON_CreateArray();
cJSON_AddItemToObjectCS(result, "entities", entitiesJsn);
for (auto ent : entities) {
auto entityJsn = cJSON_CreateObject();
cJSON_AddItemToArray(entitiesJsn, entityJsn);
auto idJsn = cJSON_CreateNumber(uint64_t(ent));
cJSON_AddItemToObjectCS(entityJsn, "id", idJsn);
auto compsJsn = cJSON_CreateArray();
cJSON_AddItemToObjectCS(entityJsn, "components", compsJsn);
GameComponents::each([&]<typename T>(T *) {
if (has<T>(ent)) {
auto &comp = get<T>(ent);
auto compJsn = write(comp);
static char buf[96];
constexpr auto typeName = getTypeName<T>();
buf[0] = '\0';
std::strncat(buf, typeName.data(), std::min(sizeof(buf) - 1, typeName.size()));
buf[typeName.size()] = '\0';
cJSON_AddItemToObjectCS(compJsn, "_type", cJSON_CreateString(buf));
if (compJsn->child->prev != compJsn->child) {
compJsn->child->prev->next = compJsn->child;
compJsn->child = compJsn->child->prev;
compJsn->child->prev->next = NULL;
}
cJSON_AddItemToArray(compsJsn, compJsn);
}
});
}
return result;
}
const char *writeSceneToString(bool formatted) {
auto jsn = writeScene();
auto result = stringify(jsn, formatted);
cJSON_Delete(jsn);
return result;
}
void writeSceneToAsset(const char *assetName, bool formatted) {
auto contents = writeSceneToString(formatted);
if (contents[0] != '\0') {
writeAssetContents(assetName, contents);
}
}
//
// Scene
//
void readScene(const cJSON *jsn) {
for (auto entJsn = cJSON_GetObjectItemCaseSensitive(jsn, "entities")->child; entJsn;
entJsn = entJsn->next) {
Entity ent;
if (auto idJsn = cJSON_GetObjectItemCaseSensitive(entJsn, "id")) {
ent = createEntity(Entity(cJSON_GetNumberValue(idJsn)));
} else {
ent = createEntity();
}
for (auto compJsn = cJSON_GetObjectItemCaseSensitive(entJsn, "components")->child; compJsn;
compJsn = compJsn->next) {
const auto typeName
= cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(compJsn, "_type"));
const auto typeNameHash = hash(typeName);
GameComponents::each([&]<typename T>(T *) {
constexpr auto TName = getTypeName<T>();
constexpr auto TNameHash = hash(TName);
if (typeNameHash == TNameHash && typeName == TName) {
T comp {};
read(comp, compJsn);
add<T>(ent, std::move(comp));
}
});
}
}
}
void readScene(const char *assetName) {
auto root = cJSON_Parse(getAssetContents(assetName));
readScene(root);
cJSON_Delete(root);
}
cJSON *writeScene() {
auto result = cJSON_CreateObject();
Seq<Entity> entities;
each([&](Entity ent) {
entities.push_back(ent);
});
std::reverse(entities.begin(), entities.end());
auto entitiesJsn = cJSON_CreateArray();
cJSON_AddItemToObjectCS(result, "entities", entitiesJsn);
for (auto ent : entities) {
auto entityJsn = cJSON_CreateObject();
cJSON_AddItemToArray(entitiesJsn, entityJsn);
auto idJsn = cJSON_CreateNumber(uint64_t(ent));
cJSON_AddItemToObjectCS(entityJsn, "id", idJsn);
auto compsJsn = cJSON_CreateArray();
cJSON_AddItemToObjectCS(entityJsn, "components", compsJsn);
GameComponents::each([&]<typename T>(T *) {
if (has<T>(ent)) {
auto &comp = get<T>(ent);
auto compJsn = write(comp);
static char buf[96];
constexpr auto typeName = getTypeName<T>();
buf[0] = '\0';
std::strncat(buf, typeName.data(), std::min(sizeof(buf) - 1, typeName.size()));
buf[typeName.size()] = '\0';
cJSON_AddItemToObjectCS(compJsn, "_type", cJSON_CreateString(buf));
if (compJsn->child->prev != compJsn->child) {
compJsn->child->prev->next = compJsn->child;
compJsn->child = compJsn->child->prev;
compJsn->child->prev->next = NULL;
}
cJSON_AddItemToArray(compsJsn, compJsn);
}
});
}
return result;
}
const char *writeSceneToString(bool formatted) {
auto jsn = writeScene();
auto result = stringify(jsn, formatted);
cJSON_Delete(jsn);
return result;
}
void writeSceneToAsset(const char *assetName, bool formatted) {
auto contents = writeSceneToString(formatted);
if (contents[0] != '\0') {
writeAssetContents(assetName, contents);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment