Add (new) expression subfolders

This commit is contained in:
Llennpie 2023-12-17 18:58:42 -05:00
parent bd757178e9
commit 867d1cfd80
7 changed files with 96 additions and 56 deletions

View file

@ -261,8 +261,6 @@ void sdynos_imgui_init() {
model_list = GetModelList("dynos/packs");
RefreshColorCodeList();
LoadEyesFolder();
//model_details = "" + std::to_string(sDynosPacks.Count()) + " model pack";
//if (sDynosPacks.Count() != 1) model_details += "s";
}

View file

@ -45,6 +45,7 @@ void ShowExpressionContextMenu(Expression* expression, int id) {
// Refresh Textures
if (ImGui::Button(ICON_FK_REFRESH " Refresh Textures###refresh_textures")) {
expression->Textures = LoadExpressionTextures(*expression);
expression->Folders = LoadExpressionFolders(*expression);
// We attempt to keep each currently-selected texture
// If our index is out of bounds (e.g. PNG was deleted) reset it
for (int i = 0; i < expression->Textures.size(); i++) {
@ -65,6 +66,30 @@ void ShowExpressionContextMenu(Expression* expression, int id) {
}
}
void RecursiveSelector(Expression* expression, int recurse_index, std::string parent_path) {
// Folders
for (int n = recurse_index; n < expression->Folders.size(); n++) {
TexturePath folder = expression->Folders[n];
if (folder.ParentPath() != parent_path &&
folder.ParentPath() != parent_path + "/") continue;
if (ImGui::TreeNode(folder.FileName.c_str())) {
RecursiveSelector(expression, recurse_index + 1, folder.FilePath);
ImGui::TreePop();
}
}
// Textures
for (int i = 0; i < expression->Textures.size(); i++) {
TexturePath texture = expression->Textures[i];
if (texture.ParentPath() != parent_path &&
texture.ParentPath() != parent_path + "/") continue;
if (ImGui::Selectable(texture.FileName.c_str(), expression->CurrentIndex == i)) {
expression->CurrentIndex = i;
}
}
}
/* A selection menu for a single Expression; A larger, one-click child window */
void OpenEyeSelector(Expression* expression) {
if (expression->Textures.size() <= 0) {
@ -72,16 +97,8 @@ void OpenEyeSelector(Expression* expression) {
return;
};
ImGui::BeginChild(("###menu_eye_%s", expression->Name.c_str()), ImVec2(200, 75), true);
for (int n = 0; n < expression->Textures.size(); n++) {
bool is_selected = (expression->CurrentIndex == n);
std::string entry_name = expression->Textures[n].FileName;
if (ImGui::Selectable(entry_name.c_str(), is_selected)) {
//gfx_precache_textures();
expression->CurrentIndex = n;
}
}
ImGui::BeginChild(("###menu_eye_%s", expression->Name.c_str()), ImVec2(200, 125), true);
RecursiveSelector(expression, 0, expression->FolderPath);
ImGui::EndChild();
ShowExpressionContextMenu(expression, 0);
}
@ -89,16 +106,12 @@ void OpenEyeSelector(Expression* expression) {
/* Creates a nested expression selection menu for a model; Contains dropdown OR checkbox widgets */
void OpenExpressionSelector() {
if (custom_eyes_enabled) {
// Vanilla Eye Selector
if (current_model.UsingVanillaEyes())
OpenEyeSelector(&VanillaEyes);
else {
// Model Eye Selector
if (current_model.Expressions[0].Name == "eyes")
OpenEyeSelector(&current_model.Expressions[0]);
}
if (current_model.Expressions.size() > 0 && current_model.CustomEyeSupport)
// Model Eye Selector
OpenEyeSelector(&current_model.Expressions[0]);
}
if (!current_model.Active) return;
if (current_model.Expressions.size() > 1) {
// Other Expressions
if (current_model.Expressions.size() > 8) ImGui::BeginChild(("###menu_exp_model"), ImVec2(200, 190), false, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_AlwaysAutoResize);
@ -128,15 +141,7 @@ void OpenExpressionSelector() {
} else {
// Use dropdown
if (ImGui::BeginCombo(label_name.c_str(), current_model.Expressions[i].Textures[current_model.Expressions[i].CurrentIndex].FileName.c_str(), ImGuiComboFlags_None)) {
for (int n = 0; n < current_model.Expressions[i].Textures.size(); n++) {
bool is_selected = (current_model.Expressions[i].CurrentIndex == n);
std::string entry_name = current_model.Expressions[i].Textures[n].FileName;
if (ImGui::Selectable(entry_name.c_str(), is_selected)) {
//gfx_precache_textures();
current_model.Expressions[i].CurrentIndex = n;
}
}
RecursiveSelector(&current_model.Expressions[i], 0, current_model.Expressions[i].FolderPath);
ImGui::EndCombo();
}
ShowExpressionContextMenu(&current_model.Expressions[i], i);

View file

@ -6171,7 +6171,7 @@ void ImGui::TreePop()
}
window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1;
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
//IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
PopID();
}

View file

@ -129,7 +129,9 @@ Model LoadModelData(std::string folderPath) {
// Enabled by default, but authors can optionally disable the feature
// If disabled, the "Custom Eyes" checkbox will be hidden from the menu
if (root.isMember("eye_support"))
model.CustomEyeSupport = root["eye_support"].asBool();
model.CustomEyeSupport = root["eye_support"].asBool() && fs::is_directory(folderPath + "/expressions");
if (!model.CustomEyeSupport) custom_eyes_enabled = false;
// EXPERIMENTAL: Custom Blink Cycle (optional)
if (root.isMember("custom_blink_cycle"))
model.CustomBlinkCycle = root["custom_blink_cycle"].asBool();

View file

@ -33,8 +33,9 @@ class Model {
// Always enabled for non-model Mario
if (this->DynOSId == -1) return (true);
else {
if (!std::filesystem::is_directory(this->FolderPath + "/expressions/eyes"))
return (this->Active && this->CustomEyeSupport);
if (!std::filesystem::is_directory(this->FolderPath + "/expressions/eyes") &&
!std::filesystem::is_directory(this->FolderPath + "/expressions/eye"))
return (this->Active && this->CustomEyeSupport);
}
return false;
}
@ -78,6 +79,10 @@ class Model {
return this->FolderName;
}
Model() {
LoadEyesFolder();
}
};
extern int current_model_id;

View file

@ -42,39 +42,35 @@ namespace fs = std::filesystem;
bool custom_eyes_enabled;
bool show_vmario_emblem;
Expression VanillaEyes;
/* Loads textures from dynos/eyes/ into a global Expression */
void LoadEyesFolder() {
// Check if the dynos/eyes/ folder exists
if (fs::is_directory("dynos/eyes")) {
/* Loads subfolders into an expression */
std::vector<TexturePath> LoadExpressionFolders(Expression expression) {
std::vector<TexturePath> folders;
VanillaEyes.Name = "eyes";
VanillaEyes.FolderPath = "dynos/eyes";
// Load Textures
for (const auto & entry : fs::directory_iterator("dynos/eyes")) {
// Only allow PNG files
if (entry.path().extension() == ".png") {
TexturePath texture;
texture.FileName = entry.path().filename().generic_u8string();
texture.FilePath = entry.path().generic_u8string();
VanillaEyes.Textures.push_back(texture);
// Check if the expression's folder exists
if (fs::is_directory(fs::path(expression.FolderPath))) {
for (const auto & entry : fs::recursive_directory_iterator(expression.FolderPath)) {
if (fs::is_directory(entry.path())) {
// Only allow PNG files
TexturePath folder;
folder.FileName = entry.path().filename().generic_u8string();
folder.FilePath = entry.path().generic_u8string();
folders.push_back(folder);
}
}
}
return folders;
}
/* Loads textures into an expression */
std::vector<TexturePath> LoadExpressionTextures(Expression expression) {
std::vector<TexturePath> textures;
// Check if the expression's folder exists
if (fs::is_directory(fs::path(expression.FolderPath))) {
for (const auto & entry : fs::directory_iterator(expression.FolderPath)) {
for (const auto & entry : fs::recursive_directory_iterator(expression.FolderPath)) {
if (!fs::is_directory(entry.path())) {
// Only allow PNG files
if (entry.path().extension() == ".png") {
// Only allow PNG files
TexturePath texture;
texture.FileName = entry.path().filename().generic_u8string();
texture.FilePath = entry.path().generic_u8string();
@ -95,15 +91,15 @@ std::vector<Expression> LoadExpressions(std::string modelFolderPath) {
for (const auto & entry : fs::directory_iterator(modelFolderPath + "/expressions")) {
// Load individual expression folders
if (fs::is_directory(entry.path())) {
Expression expression;
expression.FolderPath = entry.path().generic_u8string();
expression.Name = entry.path().filename().generic_u8string();
if (expression.Name == "eye")
expression.Name = "eyes";
expression.FolderPath = entry.path().generic_u8string();
// Load all PNG files
expression.Textures = LoadExpressionTextures(expression);
expression.Folders = LoadExpressionFolders(expression);
// Eyes will always appear first
if (expression.Name == "eyes") {
@ -113,11 +109,28 @@ std::vector<Expression> LoadExpressions(std::string modelFolderPath) {
}
}
}
// If no eyes folder was loaded, load from vanilla eyes
if (current_model.UsingVanillaEyes())
expressions_list.insert(expressions_list.begin(), VanillaEyes);
}
return expressions_list;
}
Expression VanillaEyes;
/* Loads textures from dynos/eyes/ into a global Expression */
void LoadEyesFolder() {
// Check if the dynos/eyes/ folder exists
if (fs::is_directory("dynos/eyes")) {
VanillaEyes.Name = "eyes";
VanillaEyes.FolderPath = "dynos/eyes";
VanillaEyes.Textures = LoadExpressionTextures(VanillaEyes);
VanillaEyes.Folders = LoadExpressionFolders(VanillaEyes);
}
current_model.Expressions.insert(current_model.Expressions.begin(), VanillaEyes);
}
std::map<std::string, std::string*> heap_strs = {};
std::string* stack_to_heap(std::string str) {
if (heap_strs.find(str) == heap_strs.end()) heap_strs.insert({ str, new std::string(str) });
@ -151,14 +164,14 @@ const void* saturn_bind_texture(const void* input) {
}
// Vanilla eye textures
if (custom_eyes_enabled && current_model.UsingVanillaEyes() && VanillaEyes.Textures.size() > 0) {
if (custom_eyes_enabled && current_model.UsingVanillaEyes() && current_model.Expressions[0].Textures.size() > 0) {
if (texName.find("saturn_eye") != string::npos ||
// Unused vanilla textures
texName == "actors/mario/mario_eyes_left_unused.rgba16.png" ||
texName == "actors/mario/mario_eyes_right_unused.rgba16.png" ||
texName == "actors/mario/mario_eyes_up_unused.rgba16.png" ||
texName == "actors/mario/mario_eyes_down_unused.rgba16.png") {
outputTexture = stack_to_heap(VanillaEyes.Textures[VanillaEyes.CurrentIndex].GetRelativePath())->c_str();
outputTexture = stack_to_heap(current_model.Expressions[0].Textures[current_model.Expressions[0].CurrentIndex].GetRelativePath())->c_str();
const void* output = static_cast<const void*>(outputTexture);
return output;
}

View file

@ -19,6 +19,21 @@ public:
std::string GetRelativePath() {
return "../../" + this->FilePath;//.substr(0, this->FilePath.size() - 4);
}
/* Returns true if the entry is a subfolder */
bool IsFolder() {
return this->FileName.find(".png") == std::string::npos;
}
/* Returns a subfolder file count, or texture length */
int Size() {
if (this->IsFolder())
return std::distance(std::filesystem::directory_iterator(this->FilePath), std::filesystem::directory_iterator{}) - 1;
else
return FilePath.length();
}
std::string ParentPath() {
return FilePath.substr(0, this->FilePath.length() - this->FileName.length());
}
};
class Expression {
@ -30,6 +45,7 @@ public:
std::string Name;
std::string FolderPath;
std::vector<TexturePath> Textures;
std::vector<TexturePath> Folders;
/* The index of the current selected texture */
int CurrentIndex = 0;
@ -49,6 +65,7 @@ extern Expression VanillaEyes;
extern void LoadEyesFolder();
std::vector<TexturePath> LoadExpressionTextures(Expression);
std::vector<TexturePath> LoadExpressionFolders(Expression);
std::vector<Expression> LoadExpressions(std::string);
void saturn_copy_file(std::string from, std::string to);