diff options
author | Emmanuel <emmanuel@chthonicsoftware.com> | 2020-09-07 11:59:13 -0600 |
---|---|---|
committer | Elias Fleckenstein <eliasfleckenstein@web.de> | 2021-03-05 09:45:58 +0100 |
commit | 6b9057a1f2c0f3fabbf061f5bd49519920dd783b (patch) | |
tree | e44766106e3de04a962be77422ddaf5d77823891 | |
parent | ac8ac191691a13162667314358e96f07a65d0d1a (diff) | |
download | minetest-6b9057a1f2c0f3fabbf061f5bd49519920dd783b.tar.xz |
Ability to define and use LUA wield animations
-rw-r--r-- | CMakeSettings.json | 17 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/client/camera.cpp | 66 | ||||
-rw-r--r-- | src/client/camera.h | 4 | ||||
-rw-r--r-- | src/itemdef.cpp | 7 | ||||
-rw-r--r-- | src/itemdef.h | 4 | ||||
-rw-r--r-- | src/script/common/c_content.cpp | 109 | ||||
-rw-r--r-- | src/script/common/c_content.h | 3 | ||||
-rw-r--r-- | src/script/lua_api/l_item.cpp | 11 | ||||
-rw-r--r-- | src/script/lua_api/l_item.h | 1 | ||||
-rw-r--r-- | src/splinesequence.cpp | 40 | ||||
-rw-r--r-- | src/splinesequence.h | 204 | ||||
-rw-r--r-- | src/wieldanimation.cpp | 211 | ||||
-rw-r--r-- | src/wieldanimation.h | 46 |
14 files changed, 709 insertions, 16 deletions
diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 000000000..b56c79e0a --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "variables": [], + "cmakeToolchain": "D:/Code Projects/vcpkg/scripts/buildsystems/vcpkg.cmake" + } + ] +}
\ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bcf8d6c7..805c5cd2e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -423,6 +423,7 @@ set(common_SRCS serverenvironment.cpp serverlist.cpp settings.cpp + splinesequence.cpp staticobject.cpp terminal_chat_console.cpp texture_override.cpp @@ -432,6 +433,7 @@ set(common_SRCS version.cpp voxel.cpp voxelalgorithms.cpp + wieldanimation.cpp hud.cpp ${common_network_SRCS} ${JTHREAD_SRCS} diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 350b685e1..f3aa4bafe 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "fontengine.h" #include "script/scripting_client.h" +#include "wieldanimation.h" #define CAMERA_OFFSET_STEP 200 #define WIELDMESH_OFFSET_X 55.0f @@ -173,8 +174,12 @@ void Camera::step(f32 dtime) bool was_under_zero = m_wield_change_timer < 0; m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); - if (m_wield_change_timer >= 0 && was_under_zero) +if (m_wield_change_timer >= 0 && was_under_zero) { m_wieldnode->setItem(m_wield_item_next, m_client); + m_wield_animation = m_wield_item_next + .getDefinition(m_client->getItemDefManager()) + .wield_animation; +} if (m_view_bobbing_state != 0) { @@ -217,15 +222,15 @@ void Camera::step(f32 dtime) } if (m_digging_button != -1) { - f32 offset = dtime * 3.5f; float m_digging_anim_was = m_digging_anim; - m_digging_anim += offset; - if (m_digging_anim >= 1) + m_digging_anim += dtime; + const WieldAnimation &wield_anim = WieldAnimation::getNamed(m_wield_animation); + if (m_digging_anim >= wield_anim.getDuration()) { m_digging_anim = 0; m_digging_button = -1; } - float lim = 0.15; + float lim = 0.05f; if(m_digging_anim_was < lim && m_digging_anim >= lim) { if (m_digging_button == 0) { @@ -553,12 +558,16 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r if (m_arm_inertia) addArmInertia(player->getYaw()); + const WieldAnimation &wield_anim = WieldAnimation::getNamed(m_wield_animation); + // Position the wielded item //v3f wield_position = v3f(45, -35, 65); v3f wield_position = v3f(m_wieldmesh_offset.X, m_wieldmesh_offset.Y, 65); //v3f wield_rotation = v3f(-100, 120, -100); v3f wield_rotation = v3f(-100, 120, -100); + core::quaternion wield_rotation_q = core::quaternion(wield_rotation * core::DEGTORAD); wield_position.Y += fabs(m_wield_change_timer)*320 - 40; +#if 0 if(m_digging_anim < 0.05 || m_digging_anim > 0.5) { f32 frac = 1.0; @@ -575,24 +584,55 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r //wield_rotation.X -= frac * 15.0 * pow(ratiothing2, 1.4f); //wield_rotation.Z += frac * 15.0 * pow(ratiothing2, 1.0f); } +#endif if (m_digging_button != -1) { f32 digfrac = m_digging_anim; - wield_position.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI); - wield_position.Y += 24 * sin(digfrac * 1.8 * M_PI); - wield_position.Z += 25 * 0.5; + v3f anim_position = wield_anim.getTranslationAt(digfrac); + wield_position.X += anim_position.X; + wield_position.Y += anim_position.Y; + wield_position.Z += anim_position.Z; // Euler angles are PURE EVIL, so why not use quaternions? - core::quaternion quat_begin(wield_rotation * core::DEGTORAD); - core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD); - core::quaternion quat_slerp; - quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI)); - quat_slerp.toEuler(wield_rotation); + core::quaternion quat_rot = wield_anim.getRotationAt(digfrac); + // apply rotation to starting rotation + wield_rotation_q *= quat_rot; + // convert back to euler angles + wield_rotation_q.toEuler(wield_rotation); wield_rotation *= core::RADTODEG; } else { f32 bobfrac = my_modf(m_view_bobbing_anim); + // std::cout << "Third block, frac = " << bobfrac << std::endl; wield_position.X -= sin(bobfrac*M_PI*2.0) * 3.0; wield_position.Y += sin(my_modf(bobfrac*2.0)*M_PI) * 3.0; +#if 0 + // to help with finding rotations, change the '0' above + // to '1' and recompile, then fly around. + + wield_position.X += -50; + wield_position.Y += 20; + wield_position.Z += 0; + + float pitch = wrapDegrees_180(player_position.X); + float yaw = wrapDegrees_180(player_position.Y); + float roll = wrapDegrees_180(player_position.Z); + //wield_rotation_q *= core::quaternion( + // (pitch) * core::DEGTORAD, + // (roll) * core::DEGTORAD, + // (yaw) * core::DEGTORAD + //); + wield_rotation_q *= core::quaternion(pitch * core::DEGTORAD, 0, 0); + wield_rotation_q *= core::quaternion(0, yaw * core::DEGTORAD, 0); + wield_rotation_q *= core::quaternion(0, 0, roll * core::DEGTORAD); + dstream << "Wield rotation " << pitch << ", " << yaw << ", " << roll << std::endl; + // -90, 15, -60 + // convert back to euler angles + wield_rotation_q.toEuler(wield_rotation); + wield_rotation *= core::RADTODEG; +#endif + } + if (!wield_position.equals(v3f(55, -35, 65))) { + // std::cout << m_digging_anim << ": " << wield_position.X-55 << ", " << wield_position.Y+35 << ", " << wield_position.Z-65 << std::endl; } m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); diff --git a/src/client/camera.h b/src/client/camera.h index 6fd8d9aa7..df156f06d 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -247,12 +247,14 @@ private: // Fall view bobbing f32 m_view_bobbing_fall = 0.0f; - // Digging animation frame (0 <= m_digging_anim < 1) + // Digging animation frame, in seconds + // (0 <= m_digging_anim < m_wield_animation.getDuration()) f32 m_digging_anim = 0.0f; // If -1, no digging animation // If 0, left-click digging animation // If 1, right-click digging animation s32 m_digging_button = -1; + std::string m_wield_animation = ""; // Animation when changing wielded item f32 m_wield_change_timer = 0.125f; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 5fb1e4c47..9a8ef89c8 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -67,6 +67,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) inventory_overlay = def.inventory_overlay; wield_image = def.wield_image; wield_overlay = def.wield_overlay; + wield_animation = def.wield_animation; wield_scale = def.wield_scale; stack_max = def.stack_max; usable = def.usable; @@ -108,6 +109,7 @@ void ItemDefinition::reset() inventory_overlay = ""; wield_image = ""; wield_overlay = ""; + wield_animation = ""; palette_image = ""; color = video::SColor(0xFFFFFFFF); wield_scale = v3f(1.0, 1.0, 1.0); @@ -164,6 +166,7 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const writeARGB8(os, color); os << serializeString16(inventory_overlay); os << serializeString16(wield_overlay); + os << serializeString(wield_animation); os << serializeString16(short_description); } @@ -219,7 +222,9 @@ void ItemDefinition::deSerialize(std::istream &is) // block to not need to increase the version. try { short_description = deSerializeString16(is); - } catch(SerializationError &e) {}; + wield_animation = deSerializeString(is); + } catch (SerializationError &e) { + } } diff --git a/src/itemdef.h b/src/itemdef.h index ebf0d3527..10b4e9069 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" #include "texture_override.h" // TextureOverride +#include <vector> +#include "wieldanimation.h" class IGameDef; class Client; struct ToolCapabilities; @@ -63,11 +65,13 @@ struct ItemDefinition */ std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems std::string inventory_overlay; // Overlay of inventory_image. + std::string wield_animation; // Named wield animation std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used std::string wield_overlay; // Overlay of wield_image. std::string palette_image; // If specified, the item will be colorized based on this video::SColor color; // The fallback color of the node. v3f wield_scale; + WieldAnimation animation; /* Item stack and interaction properties diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6995f6b61..90787e13d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "server/player_sao.h" #include "util/pointedthing.h" +#include <utility> +#include <algorithm> #include "debug.h" // For FATAL_ERROR #include <json/json.h> @@ -61,6 +63,7 @@ void read_item_definition(lua_State* L, int index, getstringfield(L, index, "inventory_overlay", def.inventory_overlay); getstringfield(L, index, "wield_image", def.wield_image); getstringfield(L, index, "wield_overlay", def.wield_overlay); + getstringfield(L, index, "wield_animation", def.wield_animation); getstringfield(L, index, "palette", def.palette_image); // Read item color. @@ -120,7 +123,113 @@ void read_item_definition(lua_State* L, int index, getstringfield(L, index, "node_placement_prediction", def.node_placement_prediction); } +/******************************************************************************/ +void read_wield_animation(lua_State *L, int index) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + std::string name; + getstringfield(L, index, "name", name); + if (WieldAnimation::repository.size() == 0) + WieldAnimation::fillRepository(); + if (WieldAnimation::repository.find(name) != WieldAnimation::repository.end()) { + WieldAnimation::repository.erase(name); + } + WieldAnimation &anim = WieldAnimation::repository[name]; + anim.name = name; + float totalDuration; + getfloatfield(L, index, "duration", totalDuration); + lua_getfield(L, index, "translation"); + if (lua_istable(L, -1)) { + int table_translation = lua_gettop(L); + lua_pushnil(L); + lua_getfield(L, table_translation, "nodes"); + if (lua_istable(L, -1)) { + int table_nodes = lua_gettop(L); + lua_pushnil(L); + std::vector<std::pair<int, v3f>> nodes; + while (lua_next(L, table_nodes) != 0) { + int i = luaL_checkinteger(L, -2); + v3f node = check_v3f(L, -1); + std::pair<int, v3f> unorderedNode(i, node); + nodes.push_back(unorderedNode); + lua_pop(L, 1); + } + sort(nodes.begin(), nodes.end()); + for (std::size_t i = 0; i < nodes.size(); i++) { + anim.m_translationspline.addNode(nodes[i].second); + } + } + lua_pop(L, 1); + lua_getfield(L, table_translation, "indices"); + if (lua_istable(L, -1)) { + int table_indices = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table_indices) != 0) { + if (lua_istable(L, -1)) { + float duration; + u32 offset = 0; + u32 length = 0; + getfloatfield(L, -1, "duration", duration); + getintfield(L, -1, "offset", offset); + getintfield(L, -1, "length", length); + anim.m_translationspline.addIndex(duration, offset, length); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + } + lua_pop(L, 1); + + lua_getfield(L, index, "rotation"); + if (lua_istable(L, -1)) { + int table_rotation = lua_gettop(L); + lua_pushnil(L); + lua_getfield(L, table_rotation, "nodes"); + if (lua_istable(L, -1)) { + int table_nodes = lua_gettop(L); + lua_pushnil(L); + std::vector<std::pair<int, v3f>> nodes; + while (lua_next(L, table_nodes) != 0) { + int i = luaL_checkinteger(L, -2); + v3f node = check_v3f(L, -1); + std::pair<int, v3f> unorderedNode(i, node); + nodes.push_back(unorderedNode); + lua_pop(L, 1); + } + sort(nodes.begin(), nodes.end()); + for (std::size_t i = 0; i < nodes.size(); i++) { + anim.m_rotationspline.addNode( + WieldAnimation::quatFromAngles( + nodes[i].second.X, + nodes[i].second.Y, + nodes[i].second.Z)); + } + } + lua_getfield(L, table_rotation, "indices"); + if (lua_istable(L, -1)) { + int table_indices = lua_gettop(L); + lua_pushnil(L); + while (lua_next(L, table_indices) != 0) { + if (lua_istable(L, -1)) { + float duration; + u32 offset = 0; + u32 length = 0; + getfloatfield(L, -1, "duration", duration); + getintfield(L, -1, "offset", offset); + getintfield(L,-1, "length", length); + anim.m_rotationspline.addIndex(duration, offset, length); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + } + anim.setDuration(totalDuration); + lua_pop(L, 1); +} /******************************************************************************/ void push_item_definition(lua_State *L, const ItemDefinition &i) { diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 29d576355..2733be663 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -33,7 +33,6 @@ extern "C" { #include <iostream> #include <vector> - #include "irrlichttypes_bloated.h" #include "util/string.h" #include "itemgroup.h" @@ -48,6 +47,7 @@ class NodeDefManager; struct PointedThing; struct ItemStack; struct ItemDefinition; + struct ToolCapabilities; struct ObjectProperties; struct SimpleSoundSpec; @@ -109,6 +109,7 @@ void push_item_definition (lua_State *L, const ItemDefinition &i); void push_item_definition_full (lua_State *L, const ItemDefinition &i); +void read_wield_animation(lua_State *L, int index); void read_object_properties (lua_State *L, int index, ServerActiveObject *sao, diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 9e0da4034..a765c96df 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -665,8 +665,19 @@ int ModApiItemMod::l_get_name_from_content_id(lua_State *L) return 1; /* number of results */ } +int ModApiItemMod::l_register_wield_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + read_wield_animation(L, table); + return 0; /* number of results */ +} + void ModApiItemMod::Initialize(lua_State *L, int top) { + API_FCT(register_wield_animation); API_FCT(register_item_raw); API_FCT(unregister_item_raw); API_FCT(register_alias_raw); diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index 16878c101..771f1f504 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -152,6 +152,7 @@ private: static int l_register_alias_raw(lua_State *L); static int l_get_content_id(lua_State *L); static int l_get_name_from_content_id(lua_State *L); + static int l_register_wield_animation(lua_State *L); public: static void Initialize(lua_State *L, int top); }; diff --git a/src/splinesequence.cpp b/src/splinesequence.cpp new file mode 100644 index 000000000..e61c7e893 --- /dev/null +++ b/src/splinesequence.cpp @@ -0,0 +1,40 @@ +/* +Minetest SplineSequence +Copyright (C) 2016-2018 Ben Deutsch <ben@bendeutsch.de> +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "splinesequence.h" +#include "irrlichttypes_extrabloated.h" + +SplineSequence<core::quaternion> quatSplineSequence; + +static void fill_it() +{ + core::quaternion a_quat; + quatSplineSequence.addNode(a_quat); + quatSplineSequence.addIndex(0.0, 0, 0); + quatSplineSequence.normalizeDurations(); +} + +// some specializations + +template <> +core::quaternion SplineSequence<core::quaternion>::_interpolate( + core::quaternion &bottom, core::quaternion &top, float alpha) const +{ + core::quaternion result; + // slerp or lerp? We're dealing with splines anyway... + result.slerp(bottom, top, alpha); + return result; +} diff --git a/src/splinesequence.h b/src/splinesequence.h new file mode 100644 index 000000000..e1163e155 --- /dev/null +++ b/src/splinesequence.h @@ -0,0 +1,204 @@ +/* +Minetest SplineSequence +Copyright (C) 2016-2018 Ben Deutsch <ben@bendeutsch.de> +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include <vector> +#include <iostream> +#include <quaternion.h> + +struct SplineIndex +{ + float duration; + unsigned int offset; + unsigned int length; +}; + +template <typename T> class SplineSequence +{ +private: + std::vector<T> nodes; + std::vector<SplineIndex> indices; + float m_totalDuration; + +public: + SplineSequence(); + ~SplineSequence(); + + std::vector<T> getNodes(); + std::vector<SplineIndex> getIndices(); + SplineSequence<T> &addNode(T node); + SplineSequence<T> &addIndex( + float duration, unsigned int offset, unsigned int length); + // after this, all durations are non-negative, and sum to 1.0 + // or passed number + SplineSequence<T> &normalizeDurations(float total = 1.0f); + + inline float getTotalDuration() { return m_totalDuration; }; + + void interpolate(T &result, float alpha) const; + + T _interpolate(T &bottom, T &top, float alpha) const; +}; + +template <typename T> SplineSequence<T>::SplineSequence() : m_totalDuration(0.0f) +{ + // noop +} + +template <typename T> SplineSequence<T>::~SplineSequence() +{ + // noop +} + +template <typename T> inline std::vector<T> SplineSequence<T>::getNodes() +{ + return this->nodes; +} + +template <typename T> +inline std::vector<SplineIndex> SplineSequence<T>::getIndices() +{ + return this->indices; +} + +template <typename T> SplineSequence<T> &SplineSequence<T>::addNode(T node) +{ + nodes.push_back(node); + return *this; +} + +template <typename T> +SplineSequence<T> &SplineSequence<T>::addIndex( + float duration, unsigned int offset, unsigned int length) +{ + // TODO: in-place constructor for structs? + // TODO: sanity checks for durations + SplineIndex index; + index.duration = duration; + index.offset = offset; + index.length = length; + indices.push_back(index); + m_totalDuration += duration; + return *this; +} + +template <typename T> +SplineSequence<T> &SplineSequence<T>::normalizeDurations(float target) +{ + float sum = 0.0; + for (std::vector<SplineIndex>::iterator i = indices.begin(); i != indices.end(); + i++) { + float d = i->duration; + if (d < 0.0) + d = 0.0; // TODO: sanity check in addIndex + sum += d; + } + + if (sum == 0.0) { + // TODO: throw exception or similar + } + float adjust = target / sum; + + for (std::vector<SplineIndex>::iterator i = indices.begin(); i != indices.end(); + i++) { + float d = i->duration; + if (d < 0.0) + d = 0.0; + d = d * adjust; + i->duration = d; + } + m_totalDuration = target; + return *this; +} + +template <typename T> void SplineSequence<T>::interpolate(T &result, float alpha) const +{ + + // find the index + std::vector<SplineIndex>::const_iterator index = indices.begin(); + while (index != indices.end() && index->duration <= alpha) { + alpha -= index->duration; + index++; + } + if (index == indices.end()) { + // search unsuccessful? + return; + } + // std::cout << "Found index " << index->offset << ", " << index->length << + // std::endl; + /* + 0.3 0.3 0.4 , 0.0 -> 1st, 0.0 rem -> 0.0 + 0.3 0.3 0.4 , 0.5 -> 2nd, 0.2 rem -> 0.66 + */ + alpha = alpha / index->duration; + // std::cout << "Remaining alpha " << alpha << std::endl; + + typename std::vector<T>::const_iterator start = nodes.begin(); + start += index->offset; + typename std::vector<T>::const_iterator end = start; + end += index->length; + // std::cout << "Start: " << start->X << " " << start->Y << " " << start->Z << + // std::endl; std::cout << "End: " << end->X << " " << end->Y << " " << end->Z << + // std::endl; std::cout << "Ends: " << *start << " " << *end << std::endl; + // these are both inclusive, but vector's range constructor + // excludes the end -> advance by one + end++; + + std::vector<T> workspace(start, end); + for (unsigned int degree = index->length; degree > 0; degree--) { + for (unsigned int i = 0; i < degree; i++) { + // std::cout << "Interpolating alpha " << alpha << ", degree " << + // degree << ", step " << i << std::endl; std::cout << "Before " << + // workspace[i] << " onto " << workspace[i+1] << std::endl; + workspace[i] = _interpolate( + workspace[i], workspace[i + 1], alpha); + //_interpolate(workspace[i], alpha, index->length); + // workspace[i] = (1.0-alpha) * workspace[i] + alpha * + // workspace[i+1]; std::cout << "After " << workspace[i] << " onto + // " << workspace[i+1] << std::endl; + } + } + result = workspace[0]; + + // SplineIndex index; + // for( + // std::vector<SplineIndex>::const_iterator i = indices.begin(); + // i != indices.end(); i++ + //) { + // if (i->duration >= alpha) { + // index = *i; + // alpha -= i->duration; + // } + //} + // if (index->duration <= 0.0) { + // return; + //} +} + +template <typename T> +T SplineSequence<T>::_interpolate(T &bottom, T &top, float alpha) const +{ + return (1.0 - alpha) * bottom + alpha * top; +} + +// declare specializations + +// quaternions have a special interpolation +template <> +core::quaternion SplineSequence<core::quaternion>::_interpolate( + core::quaternion &bottom, core::quaternion &top, float alpha) const;
\ No newline at end of file diff --git a/src/wieldanimation.cpp b/src/wieldanimation.cpp new file mode 100644 index 000000000..022588455 --- /dev/null +++ b/src/wieldanimation.cpp @@ -0,0 +1,211 @@ +/* +Minetest WieldAnimation +Copyright (C) 2018 Ben Deutsch <ben@bendeutsch.de> +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "wieldanimation.h" + +v3f WieldAnimation::getTranslationAt(float time) const +{ + v3f translation; + m_translationspline.interpolate(translation, time); + return translation; +} + +core::quaternion WieldAnimation::getRotationAt(float time) const +{ + core::quaternion rotation; + m_rotationspline.interpolate(rotation, time); + return rotation; +} + +float WieldAnimation::getDuration() const +{ + return m_duration; +} + +void WieldAnimation::setDuration(float duration) +{ + m_duration = duration; + m_translationspline.normalizeDurations(duration); + m_rotationspline.normalizeDurations(duration); +} + +core::quaternion WieldAnimation::quatFromAngles(float pitch, float yaw, float roll) +{ + // the order of angles is important: + core::quaternion res; + res *= core::quaternion(pitch * core::DEGTORAD, 0, 0); + res *= core::quaternion(0, yaw * core::DEGTORAD, 0); + res *= core::quaternion(0, 0, roll * core::DEGTORAD); + return res; +} + +const WieldAnimation &WieldAnimation::getNamed(const std::string &name) +{ + if (repository.size() == 0) + fillRepository(); + + if (repository.find(name) == repository.end()) + return repository["punch"]; + + return repository[name]; +} + +std::unordered_map<std::string, WieldAnimation> WieldAnimation::repository; + +void WieldAnimation::fillRepository() +{ + // default: "punch" + WieldAnimation &punch = repository["punch"]; + punch.m_translationspline.addNode(v3f(0, 0, 0)) + .addNode(v3f(-70, 50, 0)) + .addNode(v3f(-70, -50, 0)) + .addNode(v3f(0, 0, 0)); + punch.m_translationspline.addIndex(1.0, 0, 3); + + punch.m_rotationspline.addNode(quatFromAngles(0.0f, 0.0f, 0.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 90.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 0.0f)); + punch.m_rotationspline.addIndex(1.0, 0, 2); + punch.setDuration(0.3f); + + WieldAnimation &dig = repository["dig"]; + dig.m_translationspline.addNode(v3f(0, 0, 0)) + .addNode(v3f(-70, -50, 0)) + .addNode(v3f(-70, 50, 0)) + .addNode(v3f(0, 0, 0)); + dig.m_translationspline.addIndex(1.0, 0, 3); + + dig.m_rotationspline.addNode(quatFromAngles(0.0f, 0.0f, 0.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 135.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 135.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 0.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, -80.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 0.0f)); + dig.m_rotationspline.addIndex(1.0, 0, 2).addIndex(1.0, 2, 3); + dig.setDuration(0.3f); + + // eat (without chewing) + WieldAnimation &eat = repository["eat"]; + eat.m_translationspline.addNode(v3f(0, 0, 0)) + .addNode(v3f(-35, 20, 0)) + .addNode(v3f(-55, 10, 0)) + .addNode(v3f(-55, 10, 0)) + .addNode(v3f(-55, 15, 0)) + .addNode(v3f(-55, 10, 0)) + .addNode(v3f(-55, 15, 0)) + .addNode(v3f(-55, 10, 0)) + .addNode(v3f(-30, 0, 0)) + .addNode(v3f(0, 0, 0)) + .addNode(v3f(0, 0, 0)); + eat.m_translationspline.addIndex(1.0, 0, 3) + .addIndex(0.5, 3, 1) + .addIndex(0.5, 4, 1) + .addIndex(0.5, 5, 1) + .addIndex(0.5, 6, 1) + .addIndex(1.0, 7, 3); + + eat.m_rotationspline.addNode(quatFromAngles(0.0f, 0.0f, 0.0f)) + .addNode(quatFromAngles(-90.0f, 20.0f, -80.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 0.0f)); + eat.m_rotationspline.addIndex(1.0, 0, 1).addIndex(2.0, 1, 0).addIndex(1.0, 1, 1); + eat.setDuration(1.0f); + + // "poke" + WieldAnimation &poke = repository["poke"]; + poke.m_translationspline.addNode(v3f(0, 0, 0)) + .addNode(v3f(0, -10, 0)) + .addNode(v3f(-50, 20, 50)) + .addNode(v3f(0, -10, 0)) + .addNode(v3f(0, 0, 0)); + poke.m_translationspline.addIndex(1.0, 0, 2).addIndex(1.0, 2, 2); + + poke.m_rotationspline.addNode(quatFromAngles(0.0f, 0.0f, 0.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 90.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 90.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 90.0f)) + .addNode(quatFromAngles(0.0f, 0.0f, 0.0f)); + poke.m_rotationspline.addIndex(1.0, 0, 2).addIndex(1.0, 2, 2); + poke.setDuration(0.5f); + +} + +void WieldAnimation::serialize(std::ostream &os, u16 protocol_version) +{ + os << serializeString(name); + writeF32(os, m_duration); + std::vector<v3f> nodes = m_translationspline.getNodes(); + writeU16(os, nodes.size()); + for (const auto &node : nodes) { + writeV3F32(os, node); + } + std::vector<SplineIndex> indices = m_translationspline.getIndices(); + writeU16(os, indices.size()); + for (const auto &index : indices) { + writeF32(os, index.duration); + writeU16(os, index.offset); + writeU16(os, index.length); + } + std::vector<core::quaternion> rnodes = m_rotationspline.getNodes(); + writeU16(os, rnodes.size()); + for (const auto &node : rnodes) { + v3f out = v3f(node.X, node.Y, node.Z); + writeV3F32(os, out); + } + std::vector<SplineIndex> rindices = m_rotationspline.getIndices(); + writeU16(os, rindices.size()); + for (const auto &index : rindices) { + writeF32(os, index.duration); + writeU16(os, index.offset); + writeU16(os, index.length); + } +} + +WieldAnimation WieldAnimation::deSerialize(std::istream &is) +{ + std::string name = deSerializeString(is); + WieldAnimation &anim = repository[name]; + anim.name = name; + m_duration = readF32(is); + u32 translationNodes_size = readU32(is); + for (u32 i = 0; i < translationNodes_size; i++) { + v3f node = readV3F32(is); + anim.m_translationspline.addNode(node); + } + + u32 translationIndex_size = readU32(is); + for (u32 i = 0; i < translationIndex_size; i++) { + float duration = readF32(is); + u32 offset = readU16(is); + u32 length = readU16(is); + anim.m_translationspline.addIndex(duration, offset, length); + } + + u32 rotationNodes_size = readU32(is); + for (u32 i = 0; i < rotationNodes_size; i++) { + v3f node = readV3F32(is); + anim.m_rotationspline.addNode(quatFromAngles(node.X,node.Y, node.Z)); + } + u32 rotationIndex_size = readU32(is); + for (u32 i = 0; i < rotationIndex_size; i++) { + float duration = readF32(is); + u32 offset = readU16(is); + u32 length = readU16(is); + anim.m_rotationspline.addIndex(duration, offset, length); + } + anim.setDuration(m_duration); + return anim; +} diff --git a/src/wieldanimation.h b/src/wieldanimation.h new file mode 100644 index 000000000..774ee9a8c --- /dev/null +++ b/src/wieldanimation.h @@ -0,0 +1,46 @@ +/* +Minetest WieldAnimation +Copyright (C) 2018 Ben Deutsch <ben@bendeutsch.de> +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include <iostream> +#include "util/serialize.h" +#include "splinesequence.h" +#include <unordered_map> + +class WieldAnimation +{ +public: + std::string name; + v3f getTranslationAt(float time) const; + core::quaternion getRotationAt(float time) const; + float getDuration() const; + // call this *after* filling the splines + void setDuration(float duration); + static core::quaternion quatFromAngles(float pitch, float yaw, float roll); + + static const WieldAnimation &getNamed(const std::string &name); + + SplineSequence<v3f> m_translationspline; + SplineSequence<core::quaternion> m_rotationspline; + float m_duration; + + static std::unordered_map<std::string, WieldAnimation> repository; + static void fillRepository(); + WieldAnimation deSerialize(std::istream &is); + void serialize(std::ostream &os, u16 protocol_version); +};
\ No newline at end of file |