ANIMATION: configure more values via lua

Martin Gerhardy 2019-11-22 15:22:00 +01:00
parent 99abef4d13
commit 1a688e8d87
30 changed files with 479 additions and 382 deletions

View File

@ -0,0 +1,82 @@
/**
* @file
*/
#pragma once
#include "voxelformat/MeshCache.h"
#include "AnimationSettings.h"
#include "core/Assert.h"
#include <string>
namespace animation {
/**
* @brief Cache @c voxel::Mesh instances for @c AnimationEntity
*/
template<typename T>
class AnimationCache : public voxelformat::MeshCache {
protected:
bool load(const std::string& filename, size_t meshIndex, const voxel::Mesh* (&meshes)[std::enum_value(T::Max)]) {
voxel::Mesh& mesh = cacheEntry(filename.c_str());
if (mesh.getNoOfVertices() > 0) {
meshes[meshIndex] = &mesh;
return true;
}
if (loadMesh(filename.c_str(), mesh)) {
meshes[meshIndex] = &mesh;
return true;
}
meshes[meshIndex] = nullptr;
return false;
}
bool getMeshes(const AnimationSettings<T>& settings, const voxel::Mesh* (&meshes)[std::enum_value(T::Max)],
std::function<bool()> loadAdditional = {}) {
for (size_t i = 0; i < settings.paths.size(); ++i) {
if (settings.paths[i].empty()) {
meshes[i] = nullptr;
continue;
}
const std::string& fullPath = settings.fullPath((T)i);
if (!load(fullPath, i, meshes)) {
Log::error("Failed to load %s", fullPath.c_str());
return false;
}
}
if (loadAdditional && !loadAdditional()) {
return false;
}
return true;
}
public:
bool getModel(const char *fullPath, BoneId bid, Vertices& vertices, Indices& indices) {
voxel::Mesh& mesh = cacheEntry(fullPath);
if (mesh.getNoOfVertices() <= 0) {
if (!loadMesh(fullPath, mesh)) {
return false;
}
}
vertices.clear();
indices.clear();
const uint8_t boneIdInt = std::enum_value(bid);
vertices.reserve(mesh.getNoOfVertices());
for (const voxel::VoxelVertex& v : mesh.getVertexVector()) {
vertices.emplace_back(Vertex{v.position, v.colorIndex, boneIdInt, v.ambientOcclusion});
}
//vertices.resize(mesh.getNoOfVertices());
indices.reserve(mesh.getNoOfIndices());
for (voxel::IndexType idx : mesh.getIndexVector()) {
indices.push_back((IndexType)idx);
}
//indices.resize(mesh.getNoOfIndices());
return true;
}
};
}

View File

@ -0,0 +1,75 @@
/**
* @file
*/
#pragma once
#include "core/String.h"
#include "core/Common.h"
#include "Animation.h"
#include "BoneId.h"
#include <array>
namespace animation {
static inline std::string luaFilename(const char *character) {
return core::string::format("%s.lua", character);
}
template<typename T>
struct AnimationSettings {
std::array<std::string, std::enum_value(T::Max)> paths;
std::string basePath;
BoneIds boneIdsArray[std::enum_value(T::Max)] {};
std::string fullPath(T type, const char* name) const {
const std::string& p = path(type, name);
return core::string::format("%s/%s.vox", basePath.c_str(), p.c_str());
}
std::string fullPath(T type) const {
const std::string& p = path(type);
return core::string::format("%s/%s.vox", basePath.c_str(), p.c_str());
}
/**
* @brief Get the original path the settings were loaded with
*/
const std::string& path(T type) const {
if (paths[std::enum_value(type)].empty()) {
static const std::string EMPTY;
return EMPTY;
}
return paths[std::enum_value(type)];
}
/**
* @brief Get the default path for the mesh type, but with a new name
*/
std::string path(T type, const char *name) const {
return core::string::format("%s/%s", animation::toString(type), name);
}
inline void setPath(T type, const char *str) {
paths[std::enum_value(type)] = str;
}
inline BoneIds* boneIds(const char *name) {
T id = toEnum(name);
if (id == T::Max) {
return nullptr;
}
return &boneIdsArray[(int)id];
}
inline const BoneIds& boneIds(T id) const {
core_assert(id != T::Max);
return boneIdsArray[(int)id];
}
inline const BoneIds& boneIds(size_t id) const {
return boneIdsArray[id];
}
};
}

View File

@ -0,0 +1,32 @@
/**
* @file
*/
#pragma once
#include "BoneId.h"
#include "core/Array.h"
#include "core/Common.h"
namespace animation {
static const char *boneId_strings[] = { "head", "chest", "belt", "pants",
"lefthand", "righthand", "leftfoot", "rightfoot", "tool",
"leftshoulder", "rightshoulder", "glider", "torso",
"leftwing", "rightwing", "tail" };
static_assert(lengthof(boneId_strings) == std::enum_value(BoneId::Max), "Invalid bone array dimensions");
BoneId toBoneId(const char *name) {
for (int i = 0; i < lengthof(boneId_strings); ++i) {
if (!strcmp(name, boneId_strings[i])) {
return (BoneId)i;
}
}
return BoneId::Max;
}
const char* toBoneId(const BoneId id) {
return boneId_strings[std::enum_value(id)];
}
}

View File

@ -30,10 +30,13 @@ enum class BoneId : uint8_t {
Max
};
extern BoneId toBoneId(const char *name);
extern const char* toBoneId(const BoneId id);
struct BoneIds {
uint8_t bones[2];
uint8_t bones[2] { 0, 0 };
bool mirrored[2] { false, false };
uint8_t num;
uint8_t num = 0;
};
}

View File

@ -14,10 +14,11 @@ set(SRCS
chr/LUAFunctions.h
Animation.cpp Animation.h
AnimationCache.h
AnimationRenderer.cpp AnimationRenderer.h
AnimationEntity.h
Bone.cpp Bone.h
BoneId.h
BoneId.cpp BoneId.h
BoneUtil.h
Skeleton.h Skeleton.cpp
SkeletonAttribute.h
@ -87,6 +88,8 @@ set(FILES
voxel/models/items/sword-2.vox
)
set(LUA_SRCS
chr/bones.lua
chr/dwarf-male-blacksmith.lua
chr/human-male-blacksmith.lua

View File

@ -0,0 +1,81 @@
/**
* @file
*/
#pragma once
#include "BoneId.h"
#include "commonlua/LUAFunctions.h"
#include "AnimationCache.h"
template<> struct clua_meta<animation::BoneIds> { static char const *name() {return "__meta_boneids";} };
namespace animation {
static int luaanim_boneidstostring(lua_State* s) {
BoneIds** a = clua_get<BoneIds*>(s, 1);
BoneIds& boneIds = **a;
if (boneIds.num == 0) {
lua_pushstring(s, "empty");
return 1;
}
if (boneIds.num == 1) {
lua_pushfstring(s, "num bones: 1, bone[0]: %i", (int)boneIds.bones[0]);
return 1;
}
if (boneIds.num == 2) {
lua_pushfstring(s, "num bones: 2, bone[0]: %i, bone[1]: %i", (int)boneIds.bones[0], (int)boneIds.bones[1]);
return 1;
}
lua_pushfstring(s, "error: num bones: %i", (*a)->num);
return 1;
}
static int luaanim_boneidsadd(lua_State* s) {
BoneIds** boneIdsPtrPtr = clua_get<BoneIds*>(s, 1);
BoneIds* boneIds = *boneIdsPtrPtr;
const char* boneName = luaL_checkstring(s, 2);
BoneId id = toBoneId(boneName);
if (id == BoneId::Max) {
return luaL_error(s, "Failed to resolve bone: '%s'", boneName);
}
if (boneIds->num > lengthof(boneIds->bones)) {
lua_pushboolean(s, 0);
return 1;
}
boneIds->bones[boneIds->num] = (uint8_t)id;
boneIds->mirrored[boneIds->num] = clua_optboolean(s, 3, false);
boneIds->num++;
lua_pushboolean(s, 1);
return 1;
}
void luaanim_boneidsregister(lua_State* s) {
std::vector<luaL_Reg> funcs = {
{"__tostring", luaanim_boneidstostring},
{"add", luaanim_boneidsadd},
{nullptr, nullptr}
};
clua_registerfuncs(s, &funcs.front(), clua_meta<BoneIds>::name());
}
int luaanim_pushboneids(lua_State* s, BoneIds* b) {
return clua_push(s, b);
}
template<class T>
int luaanim_bonesetup(lua_State* l) {
T* settings = lua::LUA::globalData<T>(l, "Settings");
const char* meshType = luaL_checkstring(l, 1);
BoneIds* b = settings->boneIds(meshType);
if (b == nullptr) {
return luaL_error(l, "Failed to resolve mesh type: '%s'", meshType);
}
*b = BoneIds {};
if (luaanim_pushboneids(l, b) != 1) {
return luaL_error(l, "Failed to push the bonesids");
}
return 1;
}
}

View File

@ -31,7 +31,7 @@ bool Character::init(const CharacterCachePtr& cache, const std::string& luaStrin
bool Character::initSettings(const std::string& luaString) {
CharacterSettings settings;
if (loadCharacterSettings(luaString, settings)) {
_settings.copyFrom(settings);
_settings = settings;
return true;
}
Log::warn("Failed to load the character settings");

View File

@ -12,60 +12,6 @@
namespace animation {
/**
* @return The used bones for a given mesh type.
* @note If you have mirrored bones like right, left hand, make sure that you set the mirrored
* boolean properly. Otherwise the winding order is not fixed while assembling the ibo
*/
static inline BoneIds boneIds(CharacterMeshType type) {
BoneIds b;
switch (type) {
case CharacterMeshType::Head:
b.num = 1;
b.bones[0] = std::enum_value(BoneId::Head);
break;
case CharacterMeshType::Chest:
b.num = 1;
b.bones[0] = std::enum_value(BoneId::Chest);
break;
case CharacterMeshType::Belt:
b.num = 1;
b.bones[0] = std::enum_value(BoneId::Belt);
break;
case CharacterMeshType::Pants:
b.num = 1;
b.bones[0] = std::enum_value(BoneId::Pants);
break;
case CharacterMeshType::Hand:
b.num = 2;
b.bones[0] = std::enum_value(BoneId::RightHand);
b.bones[1] = std::enum_value(BoneId::LeftHand);
b.mirrored[1] = true;
break;
case CharacterMeshType::Foot:
b.num = 2;
b.bones[0] = std::enum_value(BoneId::RightFoot);
b.bones[1] = std::enum_value(BoneId::LeftFoot);
b.mirrored[1] = true;
break;
case CharacterMeshType::Shoulder:
b.num = 2;
b.bones[0] = std::enum_value(BoneId::RightShoulder);
b.bones[1] = std::enum_value(BoneId::LeftShoulder);
b.mirrored[1] = true;
break;
case CharacterMeshType::Glider:
b.num = 1;
b.bones[0] = std::enum_value(BoneId::Glider);
break;
case CharacterMeshType::Max:
core_assert(false);
break;
}
return b;
}
bool CharacterCache::loadGlider(const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]) {
const char *fullPath = "models/glider.vox";
voxel::Mesh& mesh = cacheEntry(fullPath);
@ -78,33 +24,16 @@ bool CharacterCache::loadGlider(const voxel::Mesh* (&meshes)[std::enum_value(Cha
return true;
}
meshes[std::enum_value(CharacterMeshType::Glider)] = nullptr;
Log::error("Failed to load glider");
return false;
}
bool CharacterCache::getCharacterMeshes(const CharacterSettings& settings, const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]) {
for (size_t i = 0; i < settings.paths.size(); ++i) {
if (settings.paths[i] == nullptr || settings.paths[i]->empty()) {
meshes[i] = nullptr;
continue;
}
const std::string& fullPath = settings.fullPath((CharacterMeshType)i);
if (!load(fullPath, (CharacterMeshType)i, meshes)) {
Log::error("Failed to load %s", fullPath.c_str());
return false;
}
}
if (!loadGlider(meshes)) {
Log::error("Failed to load glider");
return false;
}
return true;
}
bool CharacterCache::getCharacterModel(const CharacterSettings& settings, Vertices& vertices, Indices& indices) {
const voxel::Mesh* meshes[std::enum_value(CharacterMeshType::Max)] {};
if (!getCharacterMeshes(settings, meshes)) {
return false;
}
getMeshes(settings, meshes, [&] () {
return loadGlider(meshes);
});
vertices.clear();
indices.clear();
vertices.reserve(3000);
@ -117,13 +46,15 @@ bool CharacterCache::getCharacterModel(const CharacterSettings& settings, Vertic
if (mesh == nullptr) {
continue;
}
const CharacterMeshType meshType = (CharacterMeshType)i;
const BoneIds& bids = boneIds(meshType);
const BoneIds& bids = settings.boneIds(i);
core_assert_msg(bids.num >= 0 && bids.num <= 2,
"number of bone ids is invalid: %i (for mesh type %i)q",
(int)bids.num, i);
for (uint8_t b = 0u; b < bids.num; ++b) {
const uint8_t boneId = bids.bones[b];
const std::vector<voxel::VoxelVertex>& meshVertices = mesh->getVertexVector();
for (voxel::VoxelVertex v : meshVertices) {
if (meshType == CharacterMeshType::Foot) {
if ((CharacterMeshType)i == CharacterMeshType::Foot) {
v.position.y -= settings.skeletonAttr.hipOffset;
}
vertices.emplace_back(Vertex{v.position, v.colorIndex, boneId, v.ambientOcclusion});
@ -151,51 +82,13 @@ bool CharacterCache::getCharacterModel(const CharacterSettings& settings, Vertic
return true;
}
bool CharacterCache::load(const std::string& filename, CharacterMeshType meshType, const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]) {
voxel::Mesh& mesh = cacheEntry(filename.c_str());
if (mesh.getNoOfVertices() > 0) {
meshes[std::enum_value(meshType)] = &mesh;
return true;
}
if (loadMesh(filename.c_str(), mesh)) {
meshes[std::enum_value(meshType)] = &mesh;
return true;
}
meshes[std::enum_value(meshType)] = nullptr;
return false;
}
bool CharacterCache::getItemModel(const char *itemName, Vertices& vertices, Indices& indices) {
char fullPath[128];
if (!core::string::formatBuf(fullPath, sizeof(fullPath), "models/items/%s.vox", itemName)) {
Log::error("Failed to initialize the item path buffer. Can't load item %s.", itemName);
return false;
}
voxel::Mesh& mesh = cacheEntry(fullPath);
if (mesh.getNoOfVertices() <= 0) {
if (!loadMesh(fullPath, mesh)) {
return false;
}
}
vertices.clear();
indices.clear();
constexpr uint8_t boneId = std::enum_value(BoneId::Tool);
vertices.reserve(mesh.getNoOfVertices());
for (const voxel::VoxelVertex& v : mesh.getVertexVector()) {
vertices.emplace_back(Vertex{v.position, v.colorIndex, boneId, v.ambientOcclusion});
}
//vertices.resize(mesh.getNoOfVertices());
indices.reserve(mesh.getNoOfIndices());
for (voxel::IndexType idx : mesh.getIndexVector()) {
indices.push_back((IndexType)idx);
}
//indices.resize(mesh.getNoOfIndices());
return true;
return getModel(fullPath, BoneId::Tool, vertices, indices);
}
}

View File

@ -4,24 +4,20 @@
#pragma once
#include "voxelformat/MeshCache.h"
#include "animation/AnimationCache.h"
#include "animation/Vertex.h"
#include "CharacterSettings.h"
#include "CharacterMeshType.h"
#include "voxel/Mesh.h"
#include <memory>
namespace animation {
/**
* @brief Cache @c voxel::Mesh instances for @c Character
*/
class CharacterCache : public voxelformat::MeshCache {
class CharacterCache : public AnimationCache<CharacterMeshType> {
private:
bool load(const std::string& filename, CharacterMeshType meshType, const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]);
bool loadGlider(const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]);
public:
bool getCharacterMeshes(const CharacterSettings& settings, const voxel::Mesh* (&meshes)[std::enum_value(CharacterMeshType::Max)]);
bool getCharacterModel(const CharacterSettings& settings, Vertices& vertices, Indices& indices);
bool getItemModel(const char *itemName, Vertices& vertices, Indices& indices);
};

View File

@ -8,10 +8,21 @@
namespace animation {
static const char *_strings[] = { "head", "chest", "belt", "pants", "hand", "foot", "shoulder", "glider" };
static_assert(lengthof(_strings) == std::enum_value(CharacterMeshType::Max), "Invalid animation array dimensions");
const char *toString(CharacterMeshType type) {
static const char *_strings[] = { "head", "chest", "belt", "pants", "hand", "foot", "shoulder", "glider" };
static_assert(lengthof(_strings) == std::enum_value(CharacterMeshType::Max), "Invalid animation array dimensions");
return _strings[std::enum_value(type)];
}
CharacterMeshType toEnum(const char *type) {
for (int i = 0; i < lengthof(_strings); ++i) {
if (!strcmp(type, _strings[i])) {
return (CharacterMeshType)i;
}
}
return CharacterMeshType::Max;
}
}

View File

@ -21,5 +21,6 @@ enum class CharacterMeshType : uint8_t {
};
extern const char *toString(CharacterMeshType type);
extern CharacterMeshType toEnum(const char *type);
}

View File

@ -11,17 +11,13 @@
namespace animation {
std::string luaFilename(const char *character) {
return core::string::format("%s.lua", character);
}
bool loadCharacterSettings(const std::string& luaString, CharacterSettings& settings) {
if (luaString.empty()) {
Log::warn("empty character settings can't get loaded");
return false;
}
// also change the voxel editor lua script saving
static const luaL_Reg funcs[] = {
static const luaL_Reg chrFuncs[] = {
{ "setRace", luaChr_SetRace },
{ "setGender", luaChr_SetGender },
{ "setChest", luaChr_SetChest },
@ -59,10 +55,17 @@ bool loadCharacterSettings(const std::string& luaString, CharacterSettings& sett
{ "setIdleTimeFactor", luaChr_SetIdleTimeFactor },
{ nullptr, nullptr }
};
static_assert(lengthof(funcs) - 9 == lengthof(ChrSkeletonAttributeMetaArray), "Array sizes should match");
static_assert(lengthof(chrFuncs) - 9 == lengthof(ChrSkeletonAttributeMetaArray), "Array sizes should match");
static const luaL_Reg boneFuncs[] = {
{ "setup", luaanim_bonesetup<CharacterSettings> },
{ nullptr, nullptr }
};
lua::LUA lua;
lua.reg("chr", funcs);
lua.reg("chr", chrFuncs);
lua.reg("bone", boneFuncs);
luaanim_boneidsregister(lua.state());
if (!lua.load(luaString)) {
Log::error("%s", lua.error().c_str());
@ -86,104 +89,8 @@ bool CharacterSettings::update() {
}
const char* racePath = race.c_str();
const char* genderPath = gender.c_str();
if (!core::string::formatBuf(basePath, sizeof(basePath), "models/characters/%s/%s", racePath, genderPath)) {
Log::error("Failed to initialize the character path buffer. Can't load models.");
return false;
}
// TODO: integrate a static_assert here
paths[std::enum_value(CharacterMeshType::Head)] = &head;
paths[std::enum_value(CharacterMeshType::Chest)] = &chest;
paths[std::enum_value(CharacterMeshType::Belt)] = &belt;
paths[std::enum_value(CharacterMeshType::Pants)] = &pants;
paths[std::enum_value(CharacterMeshType::Hand)] = &hand;
paths[std::enum_value(CharacterMeshType::Foot)] = &foot;
paths[std::enum_value(CharacterMeshType::Shoulder)] = &shoulder;
paths[std::enum_value(CharacterMeshType::Glider)] = nullptr;
basePath = core::string::format("models/characters/%s/%s", racePath, genderPath);
return true;
}
CharacterSettings::CharacterSettings() {
for (size_t i = 0; i < paths.size(); ++i) {
paths[i] = nullptr;
}
}
std::string CharacterSettings::fullPath(CharacterMeshType type, const char* name) const {
const std::string& p = path(type, name);
return core::string::format("%s/%s.vox", basePath, p.c_str());
}
std::string CharacterSettings::fullPath(CharacterMeshType type) const {
return core::string::format("%s/%s.vox", basePath, path(type));
}
const char* CharacterSettings::path(CharacterMeshType type) const {
if (paths[std::enum_value(type)] == nullptr) {
return "";
}
return paths[std::enum_value(type)]->c_str();
}
std::string CharacterSettings::path(CharacterMeshType type, const char *name) const {
return core::string::format("%s/%s", animation::toString(type), name);
}
void CharacterSettings::copyFrom(const CharacterSettings& other) {
skeletonAttr = other.skeletonAttr;
race = other.race;
gender = other.gender;
chest = other.chest;
belt = other.belt;
pants = other.pants;
hand = other.hand;
foot = other.foot;
head = other.head;
shoulder = other.shoulder;
for (size_t i = 0; i < paths.size(); ++i) {
paths[i] = nullptr;
}
memcpy(basePath, other.basePath, sizeof(basePath));
update();
}
void CharacterSettings::setRace(const char *str) {
race = str;
}
void CharacterSettings::setGender(const char *str) {
gender = str;
}
void CharacterSettings::setChest(const char *str) {
chest = str;
}
void CharacterSettings::setBelt(const char *str) {
belt = str;
}
void CharacterSettings::setPants(const char *str) {
pants = str;
}
void CharacterSettings::setHand(const char *str) {
hand = str;
}
void CharacterSettings::setFoot(const char *str) {
foot = str;
}
void CharacterSettings::setHead(const char *str) {
head = str;
}
void CharacterSettings::setShoulder(const char *str) {
shoulder = str;
}
}

View File

@ -6,7 +6,7 @@
#include "CharacterSkeletonAttribute.h"
#include "CharacterMeshType.h"
#include "core/NonCopyable.h"
#include "animation/AnimationSettings.h"
#include "core/String.h"
#include "core/Common.h"
#include <string>
@ -19,51 +19,14 @@ namespace animation {
* @brief Attributes for the character meshes
* @sa SkeletonAttribute
*/
struct CharacterSettings : public core::NonCopyable {
struct CharacterSettings : public AnimationSettings<CharacterMeshType> {
CharacterSkeletonAttribute skeletonAttr;
std::string race;
std::string gender;
std::string chest;
std::string belt;
std::string pants;
std::string hand;
std::string foot;
std::string head;
std::string shoulder;
std::array<const std::string*, std::enum_value(CharacterMeshType::Max)> paths;
char basePath[64] {};
CharacterSettings();
std::string fullPath(CharacterMeshType type, const char* name) const;
std::string fullPath(CharacterMeshType type) const;
/**
* @brief Get the original path the settings were loaded with
*/
const char* path(CharacterMeshType type) const;
/**
* @brief Get the default path for the mesh type, but with a new name
*/
std::string path(CharacterMeshType type, const char *name) const;
void copyFrom(const CharacterSettings& other);
void setRace(const char *str);
void setGender(const char *str);
void setChest(const char *str);
void setBelt(const char *str);
void setPants(const char *str);
void setHand(const char *str);
void setFoot(const char *str);
void setHead(const char *str);
void setShoulder(const char *str);
bool update();
};
extern std::string luaFilename(const char *character);
extern bool loadCharacterSettings(const std::string& luaString, CharacterSettings& settings);
}

View File

@ -7,213 +7,214 @@
#include "CharacterSettings.h"
#include "commonlua/LUA.h"
#include "core/String.h"
#include "animation/LUAShared.h"
namespace animation {
static CharacterSettings* luaGetCharacterSettings(lua_State * l) {
static CharacterSettings* luaGetAnimationSettings(lua_State * l) {
return lua::LUA::globalData<CharacterSettings>(l, "Settings");
}
static int luaChr_SetRace(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setRace(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->race = luaL_checkstring(l, 1);
return 0;
}
static int luaChr_SetGender(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setGender(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->gender = luaL_checkstring(l, 1);
return 0;
}
static int luaChr_SetChest(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setChest(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Chest, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetBelt(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setBelt(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Belt, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetPants(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setPants(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Pants, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetHand(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setHand(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Hand, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetFoot(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setFoot(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Foot, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetHead(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setHead(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Head, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetShoulder(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
settings->setShoulder(luaL_checkstring(l, 1));
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->setPath(CharacterMeshType::Shoulder, luaL_checkstring(l, 1));
return 0;
}
static int luaChr_SetScaler(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.scaler = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetHeadScale(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.headScale = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetNeckHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.neckHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetNeckForward(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.neckForward = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetNeckRight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.neckRight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetHandForward(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.handForward = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetHandRight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.handRight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetShoulderForward(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.shoulderForward = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetShoulderRight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.shoulderRight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetToolForward(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.toolForward = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetToolRight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.toolRight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetToolScale(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.toolScale = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetShoulderScale(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.shoulderScale = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetHeadHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.headHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetFootRight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.footRight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetChestHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.chestHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetBeltHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.beltHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetPantsHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.pantsHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetInvisibleLegHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.invisibleLegHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetFootHeight(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.footHeight = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetOrigin(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.origin = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetHipOffset(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.hipOffset = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetJumpTimeFactor(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.jumpTimeFactor = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetIdleTimeFactor(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.idleTimeFactor = luaL_checknumber(l, 1);
return 0;
}
static int luaChr_SetRunTimeFactor(lua_State * l) {
CharacterSettings *settings = luaGetCharacterSettings(l);
CharacterSettings *settings = luaGetAnimationSettings(l);
settings->skeletonAttr.runTimeFactor = luaL_checknumber(l, 1);
return 0;
}

View File

@ -0,0 +1,31 @@
-- bone.setup("meshtype")
-- bone.add("bonename", <mirrored>)
function setupBones()
local head = bone.setup("head")
head:add("head")
local torso = bone.setup("chest")
torso:add("chest")
local foot = bone.setup("belt")
foot:add("belt")
local pants = bone.setup("pants")
pants:add("pants")
local hand = bone.setup("hand")
hand:add("righthand")
hand:add("lefthand", true)
local foot = bone.setup("foot")
foot:add("rightfoot")
foot:add("leftfoot", true)
local shoulder = bone.setup("shoulder")
shoulder:add("rightshoulder")
shoulder:add("leftshoulder", true)
local glider = bone.setup("glider")
glider:add("glider")
end

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("dwarf")
chr.setGender("male")
chr.setHead("head/blacksmith")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("human")
chr.setGender("male")
chr.setHead("head/blacksmith")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("human")
chr.setGender("male")
chr.setHead("head/knight")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("human")
chr.setGender("male")
chr.setHead("head/shepherd")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("human")
chr.setGender("male")
chr.setHead("head/worker")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("undead")
chr.setGender("male")
chr.setHead("head/skeleton")

View File

@ -1,4 +1,7 @@
require 'chr.bones'
function init()
setupBones()
chr.setRace("undead")
chr.setGender("male")
chr.setHead("head/default")

View File

@ -47,12 +47,12 @@ TEST_F(CharacterSettingsTest, testLUA) {
EXPECT_EQ("testrace", settings.race);
EXPECT_EQ("testgender", settings.gender);
EXPECT_EQ("heads/test", settings.head);
EXPECT_EQ("belts/test", settings.belt);
EXPECT_EQ("chests/test", settings.chest);
EXPECT_EQ("pants/test", settings.pants);
EXPECT_EQ("hands/test", settings.hand);
EXPECT_EQ("feet/test", settings.foot);
EXPECT_EQ("heads/test", settings.path(CharacterMeshType::Head));
EXPECT_EQ("belts/test", settings.path(CharacterMeshType::Belt));
EXPECT_EQ("chests/test", settings.path(CharacterMeshType::Chest));
EXPECT_EQ("pants/test", settings.path(CharacterMeshType::Pants));
EXPECT_EQ("hands/test", settings.path(CharacterMeshType::Hand));
EXPECT_EQ("feet/test", settings.path(CharacterMeshType::Foot));
EXPECT_FLOAT_EQ( 42.0f, settings.skeletonAttr.scaler);
EXPECT_FLOAT_EQ(1337.0f, settings.skeletonAttr.headScale);

View File

@ -51,19 +51,6 @@ bool CheckboxVar(const char* label, const char* varName) {
return CheckboxVar(label, var);
}
bool Combo(const char* label, int* current_item, const std::vector<std::string>& items, int height_in_items) {
return Combo(label, current_item,
[](void* data, int idx, const char** out_text) {
const std::vector<std::string>* vec = (const std::vector<std::string>*)data;
if (idx < 0 || idx >= (int)vec->size()) {
return false;
}
*out_text = (*vec)[idx].c_str();
return true;
},
(void*) &items, (int)items.size(), height_in_items);
}
void TooltipText(const char* text) {
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@ -72,5 +59,4 @@ void TooltipText(const char* text) {
}
}
}

View File

@ -8,6 +8,7 @@
#include "IMGUIInternal.h"
#include "core/Var.h"
#include <vector>
#include <array>
namespace ImGui {
@ -16,8 +17,22 @@ IMGUI_API bool InputVarFloat(const char* label, core::VarPtr& var, float step =
IMGUI_API bool InputVarInt(const char* label, core::VarPtr& var, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool CheckboxVar(const char* label, core::VarPtr& var);
IMGUI_API bool CheckboxVar(const char* label, const char* varName);
IMGUI_API bool Combo(const char* label, int* current_item, const std::vector<std::string>& items, int height_in_items = -1);
template<class Collection>
static bool ComboStl(const char* label, int* current_item, const Collection& items, int height_in_items = -1) {
return Combo(label, current_item,
[](void* data, int idx, const char** out_text) {
const Collection* vec = (const Collection*)data;
if (idx < 0 || idx >= (int)vec->size()) {
return false;
}
*out_text = (*vec)[idx].c_str();
return true;
},
(void*) &items, (int)items.size(), height_in_items);
}
IMGUI_API void TooltipText(const char* text);
}

View File

@ -13,9 +13,8 @@
static bool reloadCharacter = false;
static constexpr int32_t cnt = /*(int)network::EntityType::MAX_ANIMAL - ((int)network::EntityType::BEGIN_ANIMAL + 1) +*/
(int)network::EntityType::MAX_CHARACTERS - ((int)network::EntityType::BEGIN_CHARACTERS + 1);
static std::array<const char*, cnt> validCharacters;
static constexpr int32_t cnt = (int)network::EntityType::MAX_CHARACTERS - ((int)network::EntityType::BEGIN_CHARACTERS + 1);
static std::array<std::string, cnt> validCharacters {};
TestAnimation::TestAnimation(const metric::MetricPtr& metric, const stock::StockDataProviderPtr& stockDataProvider,
const io::FilesystemPtr& filesystem,
@ -25,22 +24,19 @@ TestAnimation::TestAnimation(const metric::MetricPtr& metric, const stock::Stock
characterCache), _stockDataProvider(stockDataProvider) {
init(ORGANISATION, "testanimation");
setCameraMotion(true);
//setRenderPlane(true);
setRenderAxis(true);
int index = 0;
//for (int i = ((int)network::EntityType::BEGIN_ANIMAL) + 1; i < (int)network::EntityType::MAX_ANIMAL; ++i) {
// validCharacters[index++] = network::EnumNameEntityType((network::EntityType)i);
//}
for (int i = ((int)network::EntityType::BEGIN_CHARACTERS) + 1; i < (int)network::EntityType::MAX_CHARACTERS; ++i) {
validCharacters[index++] = network::EnumNameEntityType((network::EntityType)i);
const char *entityName = network::EnumNameEntityType((network::EntityType)i);
std::string lower = core::string::toLower(core::string::format("chr/%s", entityName));
core::string::replaceAllChars(lower, '_', '-');
validCharacters[index++] = lower;
}
}
std::string TestAnimation::currentCharacter() const {
std::string name = validCharacters[_currentCharacterIndex];
core::string::replaceAllChars(name, '_', '-');
return core::string::toLower(name);
return validCharacters[_currentCharacterIndex];
}
core::AppState TestAnimation::onConstruct() {
@ -209,13 +205,13 @@ void TestAnimation::doRender() {
}
void TestAnimation::onRenderUI() {
if (ImGui::Combo("Animation", &_animationIdx, _animations)) {
if (ImGui::ComboStl("Animation", &_animationIdx, _animations)) {
_character.setAnimation((animation::Animation)_animationIdx);
}
if (ImGui::Combo("Item/Tool", &_itemIdx, _items)) {
if (ImGui::ComboStl("Item/Tool", &_itemIdx, _items)) {
addItem(_itemIdx);
}
if (ImGui::Combo("Character", &_currentCharacterIndex, validCharacters.front(), validCharacters.size())) {
if (ImGui::ComboStl("Character", &_currentCharacterIndex, validCharacters)) {
loadCharacter();
}
Super::onRenderUI();

View File

@ -715,8 +715,8 @@ void SceneManager::renderAnimation(const video::Camera& camera) {
continue;
}
const int characterMeshTypeId = core::string::toInt(value);
const std::string* path = _characterSettings.paths[characterMeshTypeId];
if (path == nullptr) {
const std::string& path = _characterSettings.paths[characterMeshTypeId];
if (path.empty()) {
Log::debug("No path found for layer %i", (int)i);
continue;
}
@ -1480,7 +1480,7 @@ bool SceneManager::loadCharacter(const std::string& luaFile) {
Log::warn("Failed to load character settings from %s", luaFile.c_str());
return false;
}
_characterSettings.copyFrom(settings);
_characterSettings = settings;
voxel::VoxelVolumes volumes;
if (!_volumeCache.getCharacterVolumes(_characterSettings, volumes)) {

View File

@ -9,6 +9,7 @@
namespace voxedit {
// TODO: use AnimationSettings
extern bool saveCharacterLua(const animation::CharacterSettings& characterSettings, const char *name, const io::FilePtr& file);
}

View File

@ -14,29 +14,7 @@
namespace voxedit {
namespace anim {
bool VolumeCache::getCharacterVolumes(const animation::CharacterSettings& settings, voxel::VoxelVolumes& volumes) {
volumes.resize(std::enum_value(animation::CharacterMeshType::Max));
for (size_t i = 0; i < settings.paths.size(); ++i) {
if (settings.paths[i] == nullptr || settings.paths[i]->empty()) {
continue;
}
const std::string& fullPath = settings.fullPath((animation::CharacterMeshType)i);
if (!load(fullPath, (animation::CharacterMeshType)i, volumes)) {
Log::error("Failed to load %s", settings.paths[i]->c_str());
return false;
}
}
for (int i = 0; i < std::enum_value(animation::CharacterMeshType::Max); ++i) {
if (volumes[i].volume == nullptr) {
continue;
}
volumes[i].name = toString((animation::CharacterMeshType)i);
}
return true;
}
bool VolumeCache::load(const std::string& fullPath, animation::CharacterMeshType meshType, voxel::VoxelVolumes& volumes) {
bool VolumeCache::load(const std::string& fullPath, size_t volumeIndex, voxel::VoxelVolumes& volumes) {
Log::info("Loading volume from %s", fullPath.c_str());
const io::FilesystemPtr& fs = io::filesystem();
const io::FilePtr& file = fs->open(fullPath);
@ -50,7 +28,7 @@ bool VolumeCache::load(const std::string& fullPath, animation::CharacterMeshType
Log::error("More than one volume/layer found in %s", file->name().c_str());
return false;
}
volumes[std::enum_value(meshType)] = localVolumes[0];
volumes[volumeIndex] = localVolumes[0];
return true;
}

View File

@ -14,13 +14,34 @@ namespace voxedit {
namespace anim {
/**
* @brief Cache volume instances for @c Character
* @brief Cache volume instances for @c AnimationEntity
*/
class VolumeCache : public voxelformat::VolumeCache {
private:
bool load(const std::string& fullPath, animation::CharacterMeshType meshType, voxel::VoxelVolumes& volumes);
bool load(const std::string& fullPath, size_t volumeIndex, voxel::VoxelVolumes& volumes);
public:
bool getCharacterVolumes(const animation::CharacterSettings& settings, voxel::VoxelVolumes& volumes);
template<class T>
bool getCharacterVolumes(const animation::AnimationSettings<T>& settings, voxel::VoxelVolumes& volumes) {
volumes.resize(std::enum_value(T::Max));
for (size_t i = 0; i < settings.paths.size(); ++i) {
if (settings.paths[i].empty()) {
continue;
}
const std::string& fullPath = settings.fullPath((T)i);
if (!load(fullPath, i, volumes)) {
Log::error("Failed to load %s", settings.paths[i].c_str());
return false;
}
}
for (int i = 0; i < std::enum_value(T::Max); ++i) {
if (volumes[i].volume == nullptr) {
continue;
}
volumes[i].name = toString((T)i);
}
return true;
}
};
using VolumeCachePtr = std::shared_ptr<VolumeCache>;