Created
June 16, 2021 21:35
-
-
Save nikki93/f73dc7a2662027d98b97073274b4e8c3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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