pandemonium_engine/modules/creaturegodot/CreatureMetaData.cpp
2024-09-29 18:28:44 +02:00

714 lines
23 KiB
C++

#include "CreatureMetaData.h"
#include "gason.h"
#include "MeshBone.h"
#include "CreatureModule.h"
#include <memory>
#include <algorithm>
#include <iostream>
#include <cstring>
// Helper functions
class JsonDataHelper
{
public:
std::unique_ptr<char> chars_data;
JsonValue base_node;
JsonAllocator allocator;
};
namespace Base64Lib {
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(uint8_t c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::vector<uint8_t> base64_decode(const std::string& encoded_string) {
int in_len = static_cast<int>(encoded_string.size());
int i = 0;
int j = 0;
int in_ = 0;
uint8_t char_array_4[4], char_array_3[3];
std::vector<uint8_t> ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i <4; i++)
char_array_4[i] = (uint8_t)base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = (uint8_t)base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
}
return ret;
}
}
// CreatureMetaData
CreatureModule::CreatureMetaData::CreatureMetaData(const std::string& json_str)
{
is_valid = false;
// Parse and load in data
auto loadJsonFromStr = [&](const std::string& str_in, JsonDataHelper& data_helper)
{
char *endptr;
size_t source_size = str_in.size();
data_helper.chars_data = std::unique_ptr<char>(new char[source_size + 1]);
data_helper.chars_data.get()[source_size] = 0;
std::copy(str_in.c_str(), str_in.c_str() + source_size, data_helper.chars_data.get());
JsonParseStatus status = jsonParse(data_helper.chars_data.get(), &endptr, &data_helper.base_node, data_helper.allocator);
return status;
};
auto getJsonNode = [&](JsonNode& json_obj, const std::string& key)
{
JsonNode * ret_node = nullptr;
for(JsonIterator it = JsonBegin(json_obj.value); it != JsonEnd(json_obj.value); ++it)
{
if( std::string((*it)->key) == key) {
ret_node = *it;
break;
}
}
return ret_node;
};
auto getJsonNodeFromArray = [this](JsonNode& json_obj, int idx)
{
int i = 0;
for(auto it = JsonBegin(json_obj.value); it != JsonEnd(json_obj.value); ++it)
{
JsonNode * cur_node = *it;
if(i == idx)
{
return cur_node;
}
i++;
}
return (JsonNode *)nullptr;
};
auto parseMeshes = [&](JsonNode * node_in)
{
for(auto it = JsonBegin(node_in->value); it != JsonEnd(node_in->value); ++it)
{
JsonNode * cur_node = *it;
auto cur_id = static_cast<int>(getJsonNode(*cur_node, "id")->value.toNumber());
auto cur_start_index = static_cast<int>(getJsonNode(*cur_node, "startIndex")->value.toNumber());
auto cur_end_index = static_cast<int>(getJsonNode(*cur_node, "endIndex")->value.toNumber());
mesh_map[cur_id] = std::make_pair(cur_start_index, cur_end_index);
}
};
auto parseRegionOrders = [&](JsonNode * node_in)
{
for(auto it = JsonBegin(node_in->value); it != JsonEnd(node_in->value); ++it)
{
JsonNode * cur_node = *it;
std::unordered_map<int, std::vector<int>> cur_switch_order_map;
std::string cur_anim_name(cur_node->key);
for(auto a_it = JsonBegin(cur_node->value); a_it != JsonEnd(cur_node->value); ++a_it)
{
auto sub_node = *a_it;
auto switch_node = getJsonNode(*sub_node, "switch_order");
std::vector<int> cur_switch_ints;
for(auto s_it = JsonBegin(switch_node->value); s_it != JsonEnd(switch_node->value); ++s_it)
{
auto val_node = *s_it;
cur_switch_ints.push_back(static_cast<int>(val_node->value.toNumber()));
}
auto cur_switch_time = static_cast<int>(getJsonNode(*sub_node, "switch_time")->value.toNumber());
cur_switch_order_map[cur_switch_time] = cur_switch_ints;
}
anim_order_map[cur_anim_name] = cur_switch_order_map;
}
};
auto parseEventTriggers = [&](JsonNode * node_in)
{
for(auto it = JsonBegin(node_in->value); it != JsonEnd(node_in->value); ++it)
{
JsonNode * cur_node = *it;
std::string cur_anim_name(cur_node->key);
std::unordered_map<int, std::pair<std::string, bool>> cur_events_map;
for(auto a_it = JsonBegin(cur_node->value); a_it != JsonEnd(cur_node->value); ++a_it)
{
auto cur_events_obj = *a_it;
auto cur_event_name = std::string(getJsonNode(*cur_events_obj, "event_name")->value.toString());
auto cur_switch_time = static_cast<int>(getJsonNode(*cur_events_obj, "switch_time")->value.toNumber());
cur_events_map[cur_switch_time] = std::make_pair(cur_event_name, false);
}
anim_events_map[cur_anim_name] = cur_events_map;
}
};
auto parseSkinSwapList = [&](JsonNode * node_in)
{
for(auto it = JsonBegin(node_in->value); it != JsonEnd(node_in->value); ++it)
{
JsonNode * cur_node = *it;
std::unordered_set<std::string> new_swap_set;
std::string swap_name(cur_node->key);
auto swap_data = getJsonNode(*cur_node, "swap");
auto swap_items = getJsonNode(*swap_data, "swap_items");
for(auto a_it = JsonBegin(swap_items->value); a_it != JsonEnd(swap_items->value); ++a_it)
{
auto cur_item = *a_it;
auto item_name = std::string(cur_item->value.toString());
new_swap_set.insert(item_name);
}
skin_swaps[swap_name] = new_swap_set;
}
};
auto parseAnimatedRegionColors = [this](JsonNode * region_colors_node)
{
for (auto it = JsonBegin(region_colors_node->value);
it != JsonEnd(region_colors_node->value); ++it)
{
auto anim_name = it->key;
auto regions_node = *it;
std::unordered_map<std::string, std::vector<std::tuple<int, uint8_t, uint8_t, uint8_t>>> regions_anim;
for (auto r_it = JsonBegin(regions_node->value); r_it != JsonEnd(regions_node->value); ++r_it)
{
auto r_name = r_it->key;
auto b64_data = r_it->value.toString();
// Data format: A list of std::tuple<int, uint8_t, uint8_t, uint8_t>
auto bytes_data = Base64Lib::base64_decode(b64_data);
int chunk_size = sizeof(int) + (sizeof(uint8_t) * 3);
int chunk_num = (int)bytes_data.size() / chunk_size;
std::vector<std::tuple<int, uint8_t, uint8_t, uint8_t>> colors_anim;
for (int m = 0; m < chunk_num; m++)
{
uint8_t * base_ptr = &(bytes_data.data()[m * chunk_size]);
uint8_t * read_ptr = base_ptr;
int frame_val = 0;
uint8_t r_val = 0, g_val = 0, b_val = 0;
std::memcpy(&frame_val, read_ptr, sizeof(int));
read_ptr += sizeof(int);
std::memcpy(&r_val, read_ptr, sizeof(uint8_t));
read_ptr += sizeof(uint8_t);
std::memcpy(&g_val, read_ptr, sizeof(uint8_t));
read_ptr += sizeof(uint8_t);
std::memcpy(&b_val, read_ptr, sizeof(uint8_t));
read_ptr += sizeof(uint8_t);
colors_anim.push_back(std::make_tuple(frame_val, r_val, g_val, b_val));
}
regions_anim[r_name] = colors_anim;
}
anim_region_colors[anim_name] = regions_anim;
}
};
auto parseMorphTargets = [this, getJsonNode, getJsonNodeFromArray]
(
JsonNode * node_morph_targets,
JsonNode * node_morph_res,
JsonNode * node_morph_space)
{
auto morph_obj = node_morph_targets;
auto morph_center_array = getJsonNode(*morph_obj, "CenterData");
morph_data.center_clip = getJsonNodeFromArray(*morph_center_array, 1)->value.toString();
auto morph_shapes_array = getJsonNode(*morph_obj, "MorphShape");
morph_data.bounds_min = glm::vec2(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
morph_data.bounds_max = glm::vec2(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
for(auto it = JsonBegin(morph_shapes_array->value); it != JsonEnd(morph_shapes_array->value); ++it)
{
auto cur_array = *it;
auto cur_clip = getJsonNodeFromArray(*cur_array, 0)->value.toString();
auto pts_array = getJsonNodeFromArray(*cur_array, 1);
glm::vec2 cur_pt(
(float)getJsonNodeFromArray(*pts_array, 0)->value.toNumber(),
(float)getJsonNodeFromArray(*pts_array, 1)->value.toNumber());
morph_data.bounds_min.x = std::min(morph_data.bounds_min.x, cur_pt.x);
morph_data.bounds_max.x = std::max(morph_data.bounds_max.x, cur_pt.x);
morph_data.bounds_min.y = std::min(morph_data.bounds_min.y, cur_pt.y);
morph_data.bounds_max.y = std::max(morph_data.bounds_max.y, cur_pt.y);
morph_data.morph_clips.push_back(std::make_pair(cur_clip, cur_pt));
}
morph_data.morph_res = static_cast<int>(node_morph_res->value.toNumber());
{
// Transform to Image space
for (auto& cur_clip : morph_data.morph_clips)
{
auto bounds_size = morph_data.bounds_max - morph_data.bounds_min;
auto& cur_pt = cur_clip.second;
cur_pt = (cur_pt - morph_data.bounds_min) / bounds_size * (float)(morph_data.morph_res - 1);
cur_pt.x = std::min(std::max(0.0f, cur_pt.x), (float)(morph_data.morph_res - 1));
cur_pt.y = std::min(std::max(0.0f, cur_pt.y), (float)(morph_data.morph_res - 1));
}
}
std::string raw_str = node_morph_space->value.toString();
auto raw_bytes = Base64Lib::base64_decode(raw_str);
for (size_t j = 0; j < morph_data.morph_clips.size(); j++)
{
int space_size = morph_data.morph_res * morph_data.morph_res;
int byte_idx = j * space_size;
std::vector<uint8_t> space_data;
space_data.resize(space_size);
std::memcpy(space_data.data(), raw_bytes.data() + byte_idx, space_size * sizeof(uint8_t));
morph_data.morph_spaces.push_back(space_data);
}
morph_data.weights.resize( morph_data.morph_clips.size());
};
// Load in MetaData types
JsonDataHelper json_data;
auto status = loadJsonFromStr(json_str, json_data);
if(status != JSON_PARSE_OK) {
#ifdef _CREATURE_DEBUG
std::cout<<"CreatureMetaData() - Error parsing Meta Data JSON!"<<std::endl;
#endif
return;
}
JsonNode * json_base = json_data.base_node.toNode();
JsonNode * node_morph_targets = nullptr, * node_morph_res = nullptr, * node_morph_space = nullptr;
while(json_base->next != nullptr)
{
auto cur_key = std::string(json_base->key);
auto cur_node = json_base;
if(cur_key == "meshes")
{
parseMeshes(cur_node);
}
else if(cur_key == "regionOrders")
{
parseRegionOrders(cur_node);
}
else if(cur_key == "eventTriggers")
{
parseEventTriggers(cur_node);
}
else if(cur_key == "skinSwapList")
{
parseSkinSwapList(cur_node);
}
else if(cur_key == "MorphTargets")
{
node_morph_targets = cur_node;
}
else if(cur_key == "MorphRes")
{
node_morph_res = cur_node;
}
else if(cur_key == "MorphSpace")
{
node_morph_space = cur_node;
}
else if(cur_key == "AnimRegionColors")
{
parseAnimatedRegionColors(cur_node);
}
json_base = json_base->next;
}
// Morph Targets Parsing
if(node_morph_targets && node_morph_res && node_morph_space)
{
parseMorphTargets(node_morph_targets, node_morph_res, node_morph_space);
}
is_valid = true;
printInfo();
}
CreatureModule::CreatureMetaData::~CreatureMetaData()
{
}
void CreatureModule::CreatureMetaData::printInfo()
{
#ifdef _CREATURE_DEBUG
std::cout<<"\nCreatureMetaData SkinSwaps: "<<std::endl;
for(const auto& cur_data : skin_swaps)
{
std::cout<<"\t-"<<cur_data.first<<std::endl;
}
std::cout<<"\nCreatureMetaData Layer Order Animation: "<<std::endl;
for(const auto& cur_data : anim_order_map)
{
std::cout<<"\t-"<<cur_data.first<<" size: "<<cur_data.second.size()<<std::endl;
}
std::cout<<"\nCreatureMetaData Events: "<<std::endl;
for(const auto& cur_data : anim_events_map)
{
std::cout<<"\t-"<<cur_data.first<<" size: "<<cur_data.second.size()<<std::endl;
}
#endif
}
bool CreatureModule::CreatureMetaData::buildSkinSwapIndices(
const std::string& swap_name,
meshRenderBoneComposition * bone_composition,
std::function<void(int, int)> indices_callback,
int& total_size
)
{
total_size = 0;
if(skin_swaps.count(swap_name) == 0)
{
return false;
}
const auto& swap_set = skin_swaps[swap_name];
const auto& regions = bone_composition->getRegions();
int offset = 0;
for (auto cur_region : regions)
{
if (swap_set.count(cur_region->getName()) > 0)
{
for(auto idx = 0; idx < cur_region->getNumIndices(); idx++)
{
indices_callback(offset + idx, cur_region->getIndices()[idx]);
}
offset += cur_region->getNumIndices();
}
}
total_size = offset;
return true;
}
void CreatureModule::CreatureMetaData::updateIndicesAndPoints(
glm::uint32 * src_indices,
std::function<void(int, int)> dst_indices_callback,
int num_indices,
const std::string& anim_name,
int time_in
)
{
auto copyIndices = [&](glm::uint32 * ref_indices, int offset, int write_indices) {
for(int i = 0; i < write_indices; i++)
{
dst_indices_callback(i + offset, ref_indices[i]);
}
};
bool has_data = false;
auto cur_order = sampleOrder(anim_name, time_in);
if(cur_order)
{
has_data = (cur_order->size() > 0);
}
if (has_data)
{
// Copy new ordering to destination
int write_offset = 0;
int total_num_write_indices = 0;
for (auto region_id : (*cur_order))
{
if (mesh_map.count(region_id) == 0)
{
// region not found, just copy and return
copyIndices(src_indices, 0, num_indices);
return;
}
// Write indices
auto& mesh_data = mesh_map[region_id];
auto num_write_indices = mesh_data.second - mesh_data.first + 1;
auto region_src_ptr = src_indices + mesh_data.first;
total_num_write_indices += num_write_indices;
if (total_num_write_indices > num_indices)
{
// overwriting boundaries of array, regions do not match so copy and return
copyIndices(src_indices, 0, num_indices);
return;
}
copyIndices(region_src_ptr, write_offset, num_write_indices);
write_offset += num_write_indices;
}
}
else {
// Nothing changded, just copy from source
copyIndices(src_indices, 0, num_indices);
}
}
void CreatureModule::CreatureMetaData::updateRegionColors(
std::unordered_map<std::string,
std::shared_ptr<CreatureModule::CreatureAnimation>>& animations)
{
for (auto& cur_pair : animations)
{
auto& clip_name = cur_pair.first;
auto& clip_anim = cur_pair.second;
if (anim_region_colors.count(clip_name) > 0)
{
const std::unordered_map<std::string, std::vector<std::tuple<int, uint8_t, uint8_t, uint8_t>>>& clip_regions_data =
anim_region_colors.at(clip_name);
auto& opacity_cache = clip_anim->getOpacityCache();
std::vector<std::vector<meshOpacityCache> >& opacity_table = opacity_cache.getCacheTable();
for (int m = opacity_cache.getStartTime(); m <= opacity_cache.getEndime(); m++)
{
auto idx = opacity_cache.getIndexByTime(m);
auto& regions_data = opacity_table[idx];
for (auto& cur_region : regions_data)
{
if (clip_regions_data.count(cur_region.getKey()) > 0)
{
const std::vector<std::tuple<int, uint8_t, uint8_t, uint8_t>>& read_anim_data =
clip_regions_data.at(cur_region.getKey());
const auto& read_colors_data = read_anim_data.at(idx);
int frame_val = std::get<0>(read_colors_data);
uint8_t r_val = std::get<1>(read_colors_data);
uint8_t g_val = std::get<2>(read_colors_data);
uint8_t b_val = std::get<3>(read_colors_data);
if (frame_val == m)
{
// check to see the frames match, then set
auto getColorFloat = [this](uint8_t val_in)
{
return static_cast<float>(val_in) / 255.0f * 100.0f;
};
cur_region.setRed(getColorFloat(r_val));
cur_region.setGreen(getColorFloat(g_val));
cur_region.setBlue(getColorFloat(b_val));
}
}
}
}
}
}
}
void CreatureModule::CreatureMetaData::updateMorphStep(
CreatureModule::CreatureManager * manager_in,
float delta_step)
{
auto creature_in = manager_in->GetCreature();
if (morph_data.play_anims_data.empty())
{
auto& all_clips = manager_in->GetAllAnimations();
morph_data.play_anims_data.resize(morph_data.morph_clips.size());
for (size_t i = 0; i < morph_data.play_anims_data.size(); i++)
{
auto& cur_play_data = morph_data.play_anims_data[i];
cur_play_data.first = morph_data.morph_clips[i].first;
const auto& cur_clip_name = cur_play_data.first;
cur_play_data.second = all_clips[cur_clip_name]->getStartTime();
}
if (morph_data.center_clip.size() > 0)
{
morph_data.play_center_anim_data.first = morph_data.center_clip;
const auto& center_clip_name = morph_data.play_center_anim_data.first;
morph_data.play_center_anim_data.second = all_clips[center_clip_name]->getStartTime();
}
morph_data.play_pts.resize(creature_in->GetTotalNumPoints() * 3);
}
std::memset(morph_data.play_pts.data(), 0, sizeof(glm::float32) * morph_data.play_pts.size());
auto updatePoints = [this, creature_in](float ratio_in)
{
auto render_pts = morph_data.play_pts.data();
for (int j = 0; j < creature_in->GetTotalNumPoints() * 3; j += 3)
{
render_pts[j] +=
(creature_in->GetRenderPts()[j] * ratio_in);
render_pts[j + 1] +=
(creature_in->GetRenderPts()[j + 1] * ratio_in);
}
};
float center_ratio = 0;
bool has_center = (morph_data.center_clip.size() > 0);
if (has_center)
{
auto radius = (float)morph_data.morph_res * 0.5f;
auto test_pt = morph_data.play_img_pt - glm::vec2(morph_data.morph_res / 2, morph_data.morph_res / 2);
center_ratio = glm::length(test_pt / ((float)morph_data.morph_res * 0.5f));
const auto& clip_name = morph_data.play_center_anim_data.first;
manager_in->SetActiveAnimationName(clip_name);
manager_in->setRunTime(morph_data.play_center_anim_data.second);
manager_in->Update(delta_step);
float inv_center_ratio = 1.0f - center_ratio;
updatePoints(inv_center_ratio);
morph_data.play_center_anim_data.second = manager_in->getRunTime();
}
for (size_t i = 0; i < morph_data.play_anims_data.size(); i++)
{
auto& cur_data = morph_data.play_anims_data[i];
const auto& clip_name = cur_data.first;
manager_in->SetActiveAnimationName(clip_name);
manager_in->setRunTime(cur_data.second);
manager_in->Update(delta_step);
cur_data.second = manager_in->getRunTime();
updatePoints((center_ratio > 0) ? (morph_data.weights[i] * center_ratio) : morph_data.weights[i]);
}
// Copy to current render points
std::memcpy(
creature_in->GetRenderPts(),
morph_data.play_pts.data(),
sizeof(glm::float32) * morph_data.play_pts.size());
}
const std::vector<int> *
CreatureModule::CreatureMetaData::sampleOrder(const std::string& anim_name, int time_in) const
{
if (anim_order_map.count(anim_name) > 0)
{
const auto& order_table = anim_order_map.at(anim_name);
if (order_table.empty())
{
return nullptr;
}
int sample_time = 0;
for(const auto& order_data : order_table)
{
auto cur_time = order_data.first;
if (time_in >= cur_time)
{
sample_time = cur_time;
}
}
return &order_table.at(sample_time);
}
return nullptr;
}
bool
CreatureModule::CreatureMetaData::addSkinSwap(
const std::string& swap_name,
const std::unordered_set<std::string>& set_in)
{
if (skin_swaps.count(swap_name))
{
return false;
}
skin_swaps[swap_name] = set_in;
return true;
}
bool
CreatureModule::CreatureMetaData::removeSkinSwap(const std::string& swap_name)
{
if (skin_swaps.count(swap_name))
{
return false;
}
skin_swaps.erase(swap_name);
return true;
}
void CreatureModule::CreatureMetaData::resetEvents(const std::string& anim_name)
{
if(anim_events_map.count(anim_name) == 0)
{
return;
}
auto& cur_events = anim_events_map[anim_name];
for(auto& cur_data : cur_events)
{
cur_data.second.second = false;
}
}
bool CreatureModule::CreatureMetaData::hasEvents(const std::string& anim_name) const
{
return anim_events_map.count(anim_name) > 0;
}
std::string CreatureModule::CreatureMetaData::runEvents(const std::string& anim_name, int time_in)
{
std::string ret_name;
if(anim_events_map.count(anim_name) == 0)
{
return ret_name;
}
int greatest_time = 0;
auto& cur_events = anim_events_map[anim_name];
for(auto& cur_data : cur_events)
{
auto trigger_time = cur_data.first;
auto& triggers = cur_data.second;
const auto& trigger_name = triggers.first;
auto& triggered = triggers.second;
if(triggered == false)
{
triggered = true;
if(trigger_time >= greatest_time)
{
ret_name = trigger_name;
greatest_time = trigger_time;
}
}
}
return ret_name;
}