template_cache.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #include "template_cache.h"
  2. #include "gl/humanoid/animation/animation_inputs.h"
  3. #include <algorithm>
  4. #include <cmath>
  5. namespace Render::GL {
  6. namespace {
  7. constexpr float k_anim_cycle_seconds = 1.0F;
  8. constexpr std::size_t k_combat_phase_slots = 7;
  9. constexpr std::size_t k_frame_slots = k_anim_frame_count;
  10. constexpr std::size_t k_idle_base = 0;
  11. constexpr std::size_t k_move_base = k_idle_base + 1;
  12. constexpr std::size_t k_run_base = k_move_base + k_frame_slots;
  13. constexpr std::size_t k_attack_melee_base = k_run_base + k_frame_slots;
  14. constexpr std::size_t k_attack_ranged_base =
  15. k_attack_melee_base + (k_combat_phase_slots * k_frame_slots);
  16. constexpr std::size_t k_construct_base =
  17. k_attack_ranged_base + (k_combat_phase_slots * k_frame_slots);
  18. constexpr std::size_t k_heal_base = k_construct_base + k_frame_slots;
  19. constexpr std::size_t k_hit_base = k_heal_base + k_frame_slots;
  20. constexpr std::size_t k_anim_dense_state_slot_count =
  21. k_hit_base + k_frame_slots;
  22. static_assert(k_anim_dense_state_slot_count ==
  23. TemplateCache::k_dense_anim_state_slots);
  24. inline auto clamp01(float v) -> float { return std::clamp(v, 0.0F, 1.0F); }
  25. inline auto phase_to_frame(float phase) -> std::uint8_t {
  26. float clamped = clamp01(phase);
  27. int idx = static_cast<int>(clamped * k_anim_frame_count);
  28. if (idx >= static_cast<int>(k_anim_frame_count)) {
  29. idx = static_cast<int>(k_anim_frame_count - 1);
  30. }
  31. return static_cast<std::uint8_t>(idx);
  32. }
  33. inline auto frame_to_phase(std::uint8_t frame) -> float {
  34. if (k_anim_frame_count == 0) {
  35. return 0.0F;
  36. }
  37. return static_cast<float>(frame) / static_cast<float>(k_anim_frame_count - 1);
  38. }
  39. inline auto time_phase(float t) -> float {
  40. if (k_anim_cycle_seconds <= 0.0F) {
  41. return 0.0F;
  42. }
  43. float wrapped = std::fmod(t, k_anim_cycle_seconds);
  44. if (wrapped < 0.0F) {
  45. wrapped += k_anim_cycle_seconds;
  46. }
  47. return wrapped / k_anim_cycle_seconds;
  48. }
  49. inline auto clamp_phase_index(CombatAnimPhase phase) -> std::size_t {
  50. auto idx = static_cast<std::size_t>(phase);
  51. return std::min<std::size_t>(idx, k_combat_phase_slots - 1);
  52. }
  53. inline auto clamp_frame_index(std::uint8_t frame) -> std::size_t {
  54. if (k_frame_slots == 0) {
  55. return 0;
  56. }
  57. return std::min<std::size_t>(frame, k_frame_slots - 1);
  58. }
  59. inline auto dense_anim_state_slot_index(AnimState state, CombatAnimPhase phase,
  60. std::uint8_t frame) -> std::size_t {
  61. const auto frame_idx = clamp_frame_index(frame);
  62. const auto phase_idx = clamp_phase_index(phase);
  63. switch (state) {
  64. case AnimState::Idle:
  65. return k_idle_base;
  66. case AnimState::Move:
  67. return k_move_base + frame_idx;
  68. case AnimState::Run:
  69. return k_run_base + frame_idx;
  70. case AnimState::AttackMelee:
  71. return k_attack_melee_base + (phase_idx * k_frame_slots) + frame_idx;
  72. case AnimState::AttackRanged:
  73. return k_attack_ranged_base + (phase_idx * k_frame_slots) + frame_idx;
  74. case AnimState::Construct:
  75. return k_construct_base + frame_idx;
  76. case AnimState::Heal:
  77. return k_heal_base + frame_idx;
  78. case AnimState::Hit:
  79. return k_hit_base + frame_idx;
  80. }
  81. return k_idle_base;
  82. }
  83. } // namespace
  84. std::size_t TemplateKeyHash::operator()(const TemplateKey &key) const noexcept {
  85. std::size_t h = std::hash<std::string>()(key.renderer_id);
  86. h ^=
  87. static_cast<std::size_t>(key.owner_id) + 0x9e3779b9 + (h << 6) + (h >> 2);
  88. h ^= static_cast<std::size_t>(key.lod) + 0x9e3779b9 + (h << 6) + (h >> 2);
  89. h ^= static_cast<std::size_t>(key.mount_lod) + 0x9e3779b9 + (h << 6) +
  90. (h >> 2);
  91. h ^= static_cast<std::size_t>(key.variant) + 0x9e3779b9 + (h << 6) + (h >> 2);
  92. h ^= static_cast<std::size_t>(key.attack_variant) + 0x9e3779b9 + (h << 6) +
  93. (h >> 2);
  94. h ^= static_cast<std::size_t>(key.state) + 0x9e3779b9 + (h << 6) + (h >> 2);
  95. h ^= static_cast<std::size_t>(key.combat_phase) + 0x9e3779b9 + (h << 6) +
  96. (h >> 2);
  97. h ^= static_cast<std::size_t>(key.frame) + 0x9e3779b9 + (h << 6) + (h >> 2);
  98. return h;
  99. }
  100. std::size_t TemplateCache::DenseDomainKeyHash::operator()(
  101. const DenseDomainKey &key) const noexcept {
  102. std::size_t h = std::hash<std::string>()(key.renderer_id);
  103. h ^=
  104. static_cast<std::size_t>(key.owner_id) + 0x9e3779b9 + (h << 6) + (h >> 2);
  105. h ^= static_cast<std::size_t>(key.lod) + 0x9e3779b9 + (h << 6) + (h >> 2);
  106. h ^= static_cast<std::size_t>(key.mount_lod) + 0x9e3779b9 + (h << 6) +
  107. (h >> 2);
  108. return h;
  109. }
  110. void TemplateRecorder::reset(std::size_t reserve_hint) {
  111. m_commands.clear();
  112. if (reserve_hint > m_commands.capacity()) {
  113. m_commands.reserve(reserve_hint);
  114. }
  115. }
  116. void TemplateRecorder::mesh(Mesh *mesh, const QMatrix4x4 &model,
  117. const QVector3D &color, Texture *texture,
  118. float alpha, int material_id) {
  119. if (mesh == nullptr) {
  120. return;
  121. }
  122. m_commands.emplace_back();
  123. RecordedMeshCmd &cmd = m_commands.back();
  124. cmd.mesh = mesh;
  125. cmd.texture = texture;
  126. cmd.shader = get_current_shader();
  127. cmd.local_model = model;
  128. cmd.color = color;
  129. cmd.alpha = alpha;
  130. cmd.material_id = material_id;
  131. }
  132. auto TemplateCache::get_or_build(const TemplateKey &key,
  133. const std::function<PoseTemplate()> &builder)
  134. -> const PoseTemplate * {
  135. {
  136. std::lock_guard<std::mutex> lock(m_mutex);
  137. auto it = m_cache.find(key);
  138. if (it != m_cache.end()) {
  139. m_lru.splice(m_lru.begin(), m_lru, it->second.lru_it);
  140. return &it->second.tpl;
  141. }
  142. }
  143. PoseTemplate built = builder();
  144. std::lock_guard<std::mutex> lock(m_mutex);
  145. auto it = m_cache.find(key);
  146. if (it != m_cache.end()) {
  147. m_lru.splice(m_lru.begin(), m_lru, it->second.lru_it);
  148. return &it->second.tpl;
  149. }
  150. while (m_cache.size() >= m_max_entries && !m_lru.empty()) {
  151. evict_lru();
  152. }
  153. m_lru.push_front(key);
  154. CacheEntry entry{std::move(built), m_lru.begin()};
  155. auto [ins_it, inserted] = m_cache.emplace(key, std::move(entry));
  156. return &ins_it->second.tpl;
  157. }
  158. auto TemplateCache::dense_slot_index(std::uint8_t variant,
  159. const AnimKey &anim_key) -> std::size_t {
  160. const std::size_t variant_slot =
  161. std::min<std::size_t>(variant, k_dense_variant_slots - 1);
  162. const std::size_t attack_slot = std::min<std::size_t>(
  163. anim_key.attack_variant, k_dense_attack_variant_slots - 1);
  164. const std::size_t anim_slot = dense_anim_state_slot_index(
  165. anim_key.state, anim_key.combat_phase, anim_key.frame);
  166. return ((variant_slot * k_dense_attack_variant_slots) + attack_slot) *
  167. k_dense_anim_state_slots +
  168. anim_slot;
  169. }
  170. auto TemplateCache::get_dense_domain_handle(
  171. const std::string &renderer_id, std::uint32_t owner_id, std::uint8_t lod,
  172. std::uint8_t mount_lod) -> DenseDomainHandle {
  173. std::lock_guard<std::mutex> lock(m_mutex);
  174. DenseDomainKey key{renderer_id, owner_id, lod, mount_lod};
  175. auto it = m_dense_domain_lookup.find(key);
  176. if (it != m_dense_domain_lookup.end()) {
  177. return DenseDomainHandle{it->second};
  178. }
  179. const std::size_t id = m_dense_domains.size();
  180. DenseDomainEntry entry;
  181. entry.key = key;
  182. entry.template_slots.assign(k_dense_anim_slot_count, nullptr);
  183. m_dense_domains.push_back(std::move(entry));
  184. m_dense_domain_lookup.emplace(std::move(key), id);
  185. return DenseDomainHandle{id};
  186. }
  187. auto TemplateCache::set_dense_slot(DenseDomainHandle domain,
  188. std::size_t dense_slot,
  189. const PoseTemplate *tpl) -> void {
  190. if (!domain.is_valid() || domain.value >= m_dense_domains.size() ||
  191. dense_slot >= k_dense_anim_slot_count) {
  192. return;
  193. }
  194. m_dense_domains[domain.value].template_slots[dense_slot] = tpl;
  195. }
  196. void TemplateCache::clear_dense_slot_for_key(const TemplateKey &key) {
  197. DenseDomainKey domain_key{key.renderer_id, key.owner_id, key.lod,
  198. key.mount_lod};
  199. auto domain_it = m_dense_domain_lookup.find(domain_key);
  200. if (domain_it == m_dense_domain_lookup.end()) {
  201. return;
  202. }
  203. if (domain_it->second >= m_dense_domains.size()) {
  204. return;
  205. }
  206. AnimKey anim_key{};
  207. anim_key.state = key.state;
  208. anim_key.combat_phase = key.combat_phase;
  209. anim_key.frame = key.frame;
  210. anim_key.attack_variant = key.attack_variant;
  211. const std::size_t dense_slot = dense_slot_index(key.variant, anim_key);
  212. if (dense_slot >= k_dense_anim_slot_count) {
  213. return;
  214. }
  215. m_dense_domains[domain_it->second].template_slots[dense_slot] = nullptr;
  216. }
  217. auto TemplateCache::get_or_build_dense(
  218. DenseDomainHandle domain, std::size_t dense_slot, const TemplateKey &key,
  219. const std::function<PoseTemplate()> &builder) -> const PoseTemplate * {
  220. const bool use_dense =
  221. domain.is_valid() && dense_slot < k_dense_anim_slot_count;
  222. {
  223. std::lock_guard<std::mutex> lock(m_mutex);
  224. if (use_dense && domain.value < m_dense_domains.size()) {
  225. const PoseTemplate *dense_hit =
  226. m_dense_domains[domain.value].template_slots[dense_slot];
  227. if (dense_hit != nullptr) {
  228. return dense_hit;
  229. }
  230. }
  231. auto it = m_cache.find(key);
  232. if (it != m_cache.end()) {
  233. m_lru.splice(m_lru.begin(), m_lru, it->second.lru_it);
  234. if (use_dense) {
  235. set_dense_slot(domain, dense_slot, &it->second.tpl);
  236. }
  237. return &it->second.tpl;
  238. }
  239. }
  240. PoseTemplate built = builder();
  241. std::lock_guard<std::mutex> lock(m_mutex);
  242. if (use_dense && domain.value < m_dense_domains.size()) {
  243. const PoseTemplate *dense_hit =
  244. m_dense_domains[domain.value].template_slots[dense_slot];
  245. if (dense_hit != nullptr) {
  246. return dense_hit;
  247. }
  248. }
  249. auto it = m_cache.find(key);
  250. if (it != m_cache.end()) {
  251. m_lru.splice(m_lru.begin(), m_lru, it->second.lru_it);
  252. if (use_dense) {
  253. set_dense_slot(domain, dense_slot, &it->second.tpl);
  254. }
  255. return &it->second.tpl;
  256. }
  257. while (m_cache.size() >= m_max_entries && !m_lru.empty()) {
  258. evict_lru();
  259. }
  260. m_lru.push_front(key);
  261. CacheEntry entry{std::move(built), m_lru.begin()};
  262. auto [ins_it, inserted] = m_cache.emplace(key, std::move(entry));
  263. if (!inserted) {
  264. m_lru.pop_front();
  265. m_lru.splice(m_lru.begin(), m_lru, ins_it->second.lru_it);
  266. }
  267. const PoseTemplate *result = &ins_it->second.tpl;
  268. if (use_dense) {
  269. set_dense_slot(domain, dense_slot, result);
  270. }
  271. return result;
  272. }
  273. void TemplateCache::clear() {
  274. std::lock_guard<std::mutex> lock(m_mutex);
  275. m_cache.clear();
  276. m_lru.clear();
  277. m_dense_domain_lookup.clear();
  278. m_dense_domains.clear();
  279. }
  280. void TemplateCache::evict_lru() {
  281. const TemplateKey oldest_key = m_lru.back();
  282. clear_dense_slot_for_key(oldest_key);
  283. m_cache.erase(oldest_key);
  284. m_lru.pop_back();
  285. }
  286. auto make_anim_key(const AnimationInputs &anim, float phase_offset,
  287. std::uint8_t attack_variant) -> AnimKey {
  288. AnimKey key{};
  289. if (anim.is_hit_reacting) {
  290. key.state = AnimState::Hit;
  291. key.frame = phase_to_frame(1.0F - clamp01(anim.hit_reaction_intensity));
  292. key.combat_phase = CombatAnimPhase::Idle;
  293. key.attack_variant = 0;
  294. return key;
  295. }
  296. if (anim.is_constructing) {
  297. key.state = AnimState::Construct;
  298. key.frame = phase_to_frame(anim.construction_progress);
  299. key.combat_phase = CombatAnimPhase::Idle;
  300. key.attack_variant = 0;
  301. return key;
  302. }
  303. if (anim.is_healing) {
  304. key.state = AnimState::Heal;
  305. key.frame = phase_to_frame(time_phase(anim.time + phase_offset));
  306. key.combat_phase = CombatAnimPhase::Idle;
  307. key.attack_variant = 0;
  308. return key;
  309. }
  310. if (anim.is_attacking) {
  311. key.state =
  312. anim.is_melee ? AnimState::AttackMelee : AnimState::AttackRanged;
  313. key.combat_phase = anim.combat_phase;
  314. key.attack_variant = attack_variant;
  315. float phase = anim.combat_phase_progress;
  316. if (phase <= 0.0F) {
  317. phase = time_phase(anim.time + phase_offset);
  318. } else {
  319. phase = clamp01(phase + phase_offset);
  320. }
  321. key.frame = phase_to_frame(phase);
  322. return key;
  323. }
  324. if (anim.is_running) {
  325. key.state = AnimState::Run;
  326. key.frame = phase_to_frame(time_phase(anim.time + phase_offset));
  327. key.combat_phase = CombatAnimPhase::Idle;
  328. key.attack_variant = 0;
  329. return key;
  330. }
  331. if (anim.is_moving) {
  332. key.state = AnimState::Move;
  333. key.frame = phase_to_frame(time_phase(anim.time + phase_offset));
  334. key.combat_phase = CombatAnimPhase::Idle;
  335. key.attack_variant = 0;
  336. return key;
  337. }
  338. key.state = AnimState::Idle;
  339. key.frame = 0;
  340. key.combat_phase = CombatAnimPhase::Idle;
  341. key.attack_variant = 0;
  342. return key;
  343. }
  344. auto make_animation_inputs(const AnimKey &key) -> AnimationInputs {
  345. AnimationInputs anim{};
  346. anim.time = frame_to_phase(key.frame) * k_anim_cycle_seconds;
  347. anim.is_moving = false;
  348. anim.is_running = false;
  349. anim.is_attacking = false;
  350. anim.is_melee = false;
  351. anim.is_in_hold_mode = false;
  352. anim.is_exiting_hold = false;
  353. anim.hold_exit_progress = 0.0F;
  354. anim.combat_phase = CombatAnimPhase::Idle;
  355. anim.combat_phase_progress = 0.0F;
  356. anim.attack_variant = key.attack_variant;
  357. anim.is_hit_reacting = false;
  358. anim.hit_reaction_intensity = 0.0F;
  359. anim.is_healing = false;
  360. anim.healing_target_dx = 0.0F;
  361. anim.healing_target_dz = 0.0F;
  362. anim.is_constructing = false;
  363. anim.construction_progress = 0.0F;
  364. float phase = frame_to_phase(key.frame);
  365. switch (key.state) {
  366. case AnimState::Move:
  367. anim.is_moving = true;
  368. break;
  369. case AnimState::Run:
  370. anim.is_moving = true;
  371. anim.is_running = true;
  372. break;
  373. case AnimState::AttackMelee:
  374. anim.is_attacking = true;
  375. anim.is_melee = true;
  376. anim.combat_phase = key.combat_phase;
  377. anim.combat_phase_progress = phase;
  378. break;
  379. case AnimState::AttackRanged:
  380. anim.is_attacking = true;
  381. anim.is_melee = false;
  382. anim.combat_phase = key.combat_phase;
  383. anim.combat_phase_progress = phase;
  384. break;
  385. case AnimState::Construct:
  386. anim.is_constructing = true;
  387. anim.construction_progress = phase;
  388. break;
  389. case AnimState::Heal:
  390. anim.is_healing = true;
  391. break;
  392. case AnimState::Hit:
  393. anim.is_hit_reacting = true;
  394. anim.hit_reaction_intensity = 1.0F - phase;
  395. break;
  396. case AnimState::Idle:
  397. default:
  398. break;
  399. }
  400. if (anim.is_attacking && anim.combat_phase == CombatAnimPhase::Idle) {
  401. anim.combat_phase = CombatAnimPhase::Strike;
  402. }
  403. return anim;
  404. }
  405. } // namespace Render::GL