Files
academy2/modules/morpher/character_base.cpp
2021-07-31 03:37:28 +03:00

667 lines
22 KiB
C++

#include <sys/time.h>
#include <core/os/os.h>
#include <scene/3d/mesh_instance.h>
#include <scene/3d/spatial.h>
#include <scene/3d/skeleton.h>
#include <scene/main/scene_tree.h>
#include <scene/main/viewport.h>
#include "character_base.h"
#include "character_slot.h"
#include "map_storage.h"
template <class T>
static inline T *find_node(Node *node, const String &name = "") {
int i;
T *ret = NULL;
assert(node);
List<Node *> queue;
queue.push_back(node);
while (!queue.empty()) {
Node *item = queue.front()->get();
queue.pop_front();
ret = Object::cast_to<T>(item);
if (ret && (name.length() == 0 || ret->get_name() == name))
break;
for (i = 0; i < item->get_child_count(); i++)
queue.push_back(item->get_child(i));
}
assert(ret);
return ret;
}
void CharacterGenderList::config() {
const Dictionary &config = ConfigData::get_singleton()->get();
const Array &gdata = config["genders"];
int i;
for (i = 0; i < gdata.size(); i++) {
const Dictionary &g = gdata[i];
const String &gname = g["name"];
const String &scene = g["scene"];
const String &left_foot = g["left_foot"];
const String &right_foot = g["right_foot"];
const String &pelvis = g["pelvis"];
Error err = OK;
Ref<PackedScene> pscene = ResourceLoader::load(scene, "", &err);
if (err != OK) {
printf("Could not read resource %ls\n", scene.c_str());
continue;
}
create_gender(gname, pscene);
genders[gname].left_foot = left_foot;
genders[gname].right_foot = right_foot;
genders[gname].pelvis = pelvis;
}
}
CharacterGenderList::CharacterGenderList() {
}
void CharacterGenderList::_bind_methods() {
ClassDB::bind_method(D_METHOD("config"),
&CharacterGenderList::config);
}
void CharacterGenderList::create_gender(const String &name, Ref<PackedScene> base) {
int i;
const Dictionary &config = ConfigData::get_singleton()->get();
CharacterGender g;
g.name = name;
g.scene = base;
const Array &slot_data = config["slot_data"];
for (i = 0; i < slot_data.size(); i++) {
CharacterSlot slot;
const Dictionary &item = slot_data[i];
slot.name = item["name"];
slot.match = item["match"];
slot.category = item["category"];
slot.helper = item["helper"];
slot.mandatory = item["mandatory"];
if (item.has("blend_skip"))
slot.blend_skip = item["blend_skip"];
else
slot.blend_skip = false;
g.slot_list[slot.name] = slot;
}
genders[name] = g;
}
void CharacterGenderList::remove_gender(const String &name) {
genders.erase(name);
}
void CharacterInstanceList::_bind_methods() {
ClassDB::bind_method(D_METHOD("create", "gender", "xform", "slot_conf"),
&CharacterInstanceList::create);
ClassDB::bind_method(D_METHOD("update"),
&CharacterInstanceList::update);
ClassDB::bind_method(D_METHOD("remove", "scene"),
&CharacterInstanceList::remove);
ClassDB::bind_method(D_METHOD("set_mod_value", "scene", "mod", "value"),
&CharacterInstanceList::set_mod_value);
ClassDB::bind_method(D_METHOD("get_mod_value", "scene", "mod"),
&CharacterInstanceList::get_mod_value);
ClassDB::bind_method(D_METHOD("get_modifier_list"),
&CharacterInstanceList::get_modifier_list);
ClassDB::bind_method(D_METHOD("get_base_modifier_list"),
&CharacterInstanceList::get_base_modifier_list);
ClassDB::bind_method(D_METHOD("get_instance_modifier_values"),
&CharacterInstanceList::get_instance_modifier_values);
}
void CharacterInstanceList::remove(Node *scene)
{
Ref<CharacterInstance> char_instance = scene->get_meta("instance_data");
instance_list.erase(char_instance);
char_instance.unref();
scene->queue_delete();
}
Node *CharacterInstanceList::create(const String &gender, const Transform &xform, const Dictionary &slot_conf) {
Node *root = SceneTree::get_singleton()->get_root();
CharacterGenderList *gl = CharacterGenderList::get_singleton();
AccessoryData *ad = AccessoryData::get_singleton();
CharacterModifiers *cm = CharacterModifiers::get_singleton();
const CharacterGender &gdata = gl->genders[gender];
Node *sc = gl->instance(gender);
root->add_child(sc);
Spatial *s = Object::cast_to<Spatial>(sc);
Skeleton *skel = find_node<Skeleton>(sc);
s->set_transform(xform);
/* TODO: custom allocator */
Ref<CharacterInstance> char_instance = memnew(CharacterInstance);
char_instance->scene_root = root->get_path_to(sc);
char_instance->gender = &gl->genders[gender];
char_instance->left_foot_id = skel->find_bone(char_instance->gender->left_foot);
char_instance->right_foot_id = skel->find_bone(char_instance->gender->right_foot);
char_instance->pelvis_id = skel->find_bone(char_instance->gender->pelvis);
assert(char_instance->left_foot_id >= 0);
assert(char_instance->right_foot_id >= 0);
assert(char_instance->pelvis_id >= 0);
cm->create_modifiers();
for (const String *key = gdata.slot_list.next(NULL);
key; key = gdata.slot_list.next(key)) {
const CharacterSlot &slot = gdata.slot_list[*key];
if (!slot.mandatory)
continue;
PoolVector<Dictionary> entries = ad->get_matching_entries(gender, slot.category, slot.match);
if (entries.size() == 0)
continue;
CharacterSlotInstance si;
si.slot = &slot;
if (slot_conf.has(*key))
si.mesh_no = slot_conf[*key];
else
si.mesh_no = 0;
si.char_instance = char_instance.ptr();
MeshInstance *mi = memnew(MeshInstance);
mi->set_name(slot.name);
skel->add_child(mi);
Ref<ArrayMesh> mesh = ad->get_mesh(entries[si.mesh_no]);
mi->hide();
mi->set_mesh(mesh);
mi->show();
si.slot_path = sc->get_path_to(mi);
mi->set_skeleton_path(mi->get_path_to(skel));
si.dirty = true;
si.mesh = mesh;
si.meshdata = NULL;
si.uv_index = Mesh::ARRAY_TEX_UV2;
si.node_path = sc->get_path_to(mi);
char_instance->slots[slot.name] = si;
}
sc->set_meta("instance_data", char_instance);
instance_list.push_back(char_instance);
return sc;
}
void CharacterInstanceList::update() {
for (List<Ref<CharacterInstance> >::Element *e = instance_list.front();
e;
e = e->next()) {
Ref<CharacterInstance> &ci = e->get();
CharacterModifiers *cm = CharacterModifiers::get_singleton();
cm->modify_bones(ci.ptr());
for (const String *key = ci->slots.next(NULL);
key;
key = ci->slots.next(key)) {
CharacterSlotInstance &si = ci->slots[*key];
if (si.dirty || ci->mod_updated) {
update_slot(ci.ptr(), &si);
si.dirty = false;
}
}
ci->mod_updated = false;
}
}
void CharacterInstanceList::init_slot(CharacterInstance *ci,
CharacterSlotInstance *si) {
int i, j;
Array surface = si->mesh->surface_get_arrays(0);
const String &orig_path = si->mesh->get_meta("orig_path");
const PoolVector<Vector2> &uvdata = surface[si->uv_index];
if (uvdata.size() == 0)
return;
const PoolVector<Vector3> &vdata = surface[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &normal = surface[Mesh::ARRAY_NORMAL];
si->meshdata = memnew_arr(float, vdata.size() * 14);
si->vertex_count = vdata.size();
assert(uvdata.size() > 0);
assert(vdata.size() == uvdata.size());
assert(si->vertex_count > 0);
const Vector2 *uvs = uvdata.read().ptr();
const Vector3 *n = normal.read().ptr();
const Vector3 *v = vdata.read().ptr();
for (i = 0; i < uvdata.size(); i++) {
si->meshdata[i * 14 + 0] = uvs[i][0];
si->meshdata[i * 14 + 1] = uvs[i][1];
assert(si->meshdata[i * 14 + 0] >= 0);
assert(si->meshdata[i * 14 + 1] >= 0);
assert(si->meshdata[i * 14 + 0] <= 1.0f);
assert(si->meshdata[i * 14 + 1] <= 1.0f);
si->meshdata[i * 14 + 2] = v[i][0];
si->meshdata[i * 14 + 3] = v[i][1];
si->meshdata[i * 14 + 4] = v[i][2];
si->meshdata[i * 14 + 5] = n[i][0];
si->meshdata[i * 14 + 6] = n[i][1];
si->meshdata[i * 14 + 7] = n[i][2];
si->meshdata[i * 14 + 8] = v[i][0];
si->meshdata[i * 14 + 9] = v[i][1];
si->meshdata[i * 14 + 10] = v[i][2];
si->meshdata[i * 14 + 11] = n[i][0];
si->meshdata[i * 14 + 12] = n[i][1];
si->meshdata[i * 14 + 13] = n[i][2];
}
if (!same_verts.has(orig_path)) {
float eps_dist = 0.0001f;
for (i = 0; i < vdata.size(); i++) {
for (j = 0; j < vdata.size(); j++) {
if (i == j)
continue;
if (v[i].distance_squared_to(v[j]) < eps_dist * eps_dist) {
if (!si->same_verts.has(i)) {
si->same_verts[i] = Vector<int>();
}
si->same_verts[i].push_back(j);
}
}
}
same_verts[orig_path] = si->same_verts;
} else
si->same_verts = same_verts[orig_path];
}
void CharacterInstanceList::update_slot(CharacterInstance *ci,
CharacterSlotInstance *si) {
if (!si->mesh.ptr())
return;
Node *sc = ci->get_scene_root();
MeshInstance *slot_node = Object::cast_to<MeshInstance>(sc->get_node(si->node_path));
assert(slot_node);
slot_node->hide();
CharacterModifiers *cm = CharacterModifiers::get_singleton();
if (si->dirty) {
init_slot(ci, si);
si->dirty = false;
}
cm->modify(ci, si, ci->mod_values);
slot_node->show();
}
CharacterInstanceList *CharacterInstanceList::get_singleton() {
static CharacterInstanceList *cil = NULL;
if (!cil)
cil = memnew(CharacterInstanceList);
return cil;
}
Ref<CharacterInstance> CharacterInstanceList::get_instance(Node *scene) {
Ref<CharacterInstance> ret = scene->get_meta("instance_data");
return ret;
}
void CharacterInstanceList::set_mod_value(Node *scene,
const String &mod, float value) {
Ref<CharacterInstance> ci = get_instance(scene);
ci->mod_values[mod] = value;
ci->mod_updated = true;
}
float CharacterInstanceList::get_mod_value(Node *scene,
const String &mod) {
Ref<CharacterInstance> ci = get_instance(scene);
return ci->mod_values[mod];
}
Node *CharacterInstanceList::get_scene(CharacterInstance *ci) {
Node *scene = SceneTree::get_singleton()->get_root()->get_node(ci->scene_root);
return scene;
}
Node *CharacterInstance::get_scene_root() const
{
Node *root = SceneTree::get_singleton()->get_root();
Node *scene = root->get_node(scene_root);
return scene;
}
Skeleton *CharacterInstance::get_skeleton() const
{
Node *root = SceneTree::get_singleton()->get_root();
Node *scene = root->get_node(scene_root);
Skeleton *skel = find_node<Skeleton>(scene);
return skel;
}
Skeleton *CharacterInstanceList::get_skeleton(Node *scene) const
{
Skeleton *skel = find_node<Skeleton>(scene);
return skel;
}
PoolVector<String> CharacterModifiers::get_modifier_list() const {
PoolVector<String> ret;
ret.resize(modifiers.size());
int count = 0;
for (const String *key = modifiers.next(NULL);
key;
key = modifiers.next(key)) {
ret[count++] = *key;
}
ret.resize(count);
return ret;
}
PoolVector<String> CharacterModifiers::get_base_modifier_list() const {
PoolVector<String> ret;
for (const String *key = modifiers.next(NULL);
key;
key = modifiers.next(key)) {
if ((*key).begins_with("base:"))
ret.push_back((*key).replace("base:", ""));
if ((*key).begins_with("bone:"))
ret.push_back((*key).replace("bone:", ""));
}
return ret;
}
void CharacterModifiers::init_blend_modifier(const String &name,
BlendModifierData *bm) {
MapStorage *ms = MapStorage::get_singleton();
PoolVector<float> minmax = ms->get_minmax(name);
if (bm->mod_name.length() == 0)
bm->mod_name = name;
int index = 0, i, j;
for (i = 0; i < 3; i++)
bm->minp[i] = minmax[index++];
for (i = 0; i < 3; i++)
bm->maxp[i] = minmax[index++];
for (i = 0; i < 3; i++)
bm->minn[i] = minmax[index++];
for (i = 0; i < 3; i++)
bm->maxn[i] = minmax[index++];
bm->empty = false;
assert(bm->mod_name.length() > 0);
assert(bm->mod_name == name);
}
Transform CharacterModifiers::parse_transform(const Dictionary &xformdata)
{
Transform xform;
if (xformdata.has("uniform-scale")) {
float scale = xformdata["uniform-scale"];
Basis basisd = xform.basis.scaled(Vector3(1, 1, 1) * scale);
xform.basis = basisd;
}
if (xformdata.has("translate")) {
Array xlate = xformdata["translate"];
xform.origin = Vector3(xlate[0], xlate[1], xlate[2]);
}
return xform;
}
void CharacterModifiers::init_bone_modifier(const String &name,
BoneModifierData *bm, const Array &parameters) {
bm->bone_name = parameters[2];
const Dictionary &xformdata = parameters[3];
bm->xform = parse_transform(xformdata);
}
void CharacterModifiers::init_bone_group_modifier(const String &name,
BoneGroupModifierData *bm, const Array &parameters) {
Array bones = parameters[2];
Array xformdata = parameters[3];
int bone_count = bones.size(), i;
bm->bone_count = bone_count;
assert(bone_count <= BoneGroupModifierData::MAX_BONES);
for (i = 0; i < bone_count; i++) {
bm->bones[i] = -1;
bm->xforms[i] = parse_transform(xformdata[i]);
bm->bone_names[i] = bones[i];
}
}
void CharacterModifiers::create_mod(int type, const String &name, const String &gender, const Array &parameters) {
switch (type) {
case ModifierDataBase::TYPE_BLEND:
if (name.ends_with("_plus") || name.ends_with("_minus")) {
String group_name = name.replace("_plus", "").replace("_minus", "");
if (!modifiers.has(group_name))
create<BlendModifierSymData>(group_name, gender);
Ref<BlendModifierSymData> mod = modifiers[group_name];
if (name.ends_with("_plus"))
init_blend_modifier(name, &mod->plus);
if (name.ends_with("_minus"))
init_blend_modifier(name, &mod->minus);
} else {
if (modifiers.has(name))
break;
create<BlendModifierData>(name, gender);
Ref<BlendModifierData> mod = modifiers[name];
init_blend_modifier(name, mod.ptr());
}
break;
case ModifierDataBase::TYPE_BONE:
create<BoneModifierData>(name, gender);
{
Ref<BoneModifierData> mod = modifiers[name];
init_bone_modifier(name, mod.ptr(), parameters);
}
break;
case ModifierDataBase::TYPE_GROUP:
create<BoneGroupModifierData>(name, gender);
{
Ref<BoneGroupModifierData> mod = modifiers[name];
init_bone_group_modifier(name, mod.ptr(), parameters);
}
break;
}
}
void CharacterModifiers::create_modifiers()
{
if (mods_created)
return;
MapStorage *ms = MapStorage::get_singleton();
PoolVector<String> map_list = ms->get_list();
int i;
for (i = 0; i < map_list.size(); i++)
create_mod(ModifierDataBase::TYPE_BLEND, map_list[i], "common");
mods_created = true;
ConfigData * cd = ConfigData::get_singleton();
Dictionary conf = cd->get();
if (conf.has("bone_modifiers")) {
Dictionary bone_modifiers = conf["bone_modifiers"];
for (const Variant *key = bone_modifiers.next(NULL);
key;
key = bone_modifiers.next(key)) {
Array modifier_list = bone_modifiers[*key];
String gender = *key;
printf("gender: %ls\n", gender.c_str());
for (i = 0; i < modifier_list.size(); i++) {
Array mod = modifier_list[i];
String mod_type = mod[0];
String mod_name = mod[1];
printf("%ls %ls\n",
mod_type.c_str(),
mod_name.c_str());
if (mod_type == "bone")
create_mod(ModifierDataBase::TYPE_BONE, "bone:" + mod_name, gender, mod);
else if (mod_type == "bone-group")
create_mod(ModifierDataBase::TYPE_GROUP, "bone:" + mod_name, gender, mod);
}
}
}
}
void CharacterModifiers::modify(CharacterSlotInstance *si,
ModifierDataBase *mod,
float value) {
MapStorage *ms = MapStorage::get_singleton();
if (mod->type == ModifierDataBase::TYPE_BLEND) {
int i, j;
PoolVector<int> mod_indices;
mod_indices.resize(si->vertex_count);
PoolVector<float> mod_data;
mod_data.resize(si->vertex_count * 6);
int index_count = 0, data_count = 0;
PoolVector<int>::Write mod_indices_w = mod_indices.write();
PoolVector<float>::Write mod_data_w = mod_data.write();
BlendModifierData *_mod = Object::cast_to<BlendModifierData>(mod);
assert(_mod->mod_name.length() > 0);
Ref<Image> img = ms->get_image(_mod->mod_name);
Ref<Image> nimg = ms->get_normal_image(_mod->mod_name);
img->lock();
nimg->lock();
int width = img->get_width();
int height = img->get_height();
for (i = 0; i < si->vertex_count; i++) {
int vx = (int)(si->meshdata[i * 14 + 0] * (float)(width - 1));
int vy = (int)(si->meshdata[i * 14 + 1] * (float)(height - 1));
assert(vx >= 0 && vy >= 0);
assert(vx < width && vy < height);
Color c = img->get_pixel(vx, vy);
Color nc = nimg->get_pixel(vx, vy);
float pdelta[3], ndelta[3];
for (j = 0; j < 3; j++) {
pdelta[j] = _mod->minp[j] + (_mod->maxp[j] - _mod->minp[j]) * c[j];
ndelta[j] = _mod->minn[j] + (_mod->maxn[j] - _mod->minn[j]) * nc[j];
}
const float eps = 0.001f;
if (pdelta[0] * pdelta[0] + pdelta[1] * pdelta[1] + pdelta[2] * pdelta[2] > eps * eps) {
mod_indices_w[index_count++] = i;
// mod_indices.push_back(i);
for (j = 0; j < 3; j++) {
mod_data_w[data_count++] = pdelta[j];
mod_data_w[data_count++] = ndelta[j];
// mod_data.push_back(pdelta[j]);
// mod_data.push_back(ndelta[j]);
}
}
}
img->unlock();
nimg->unlock();
for (i = 0; i < index_count; i++) {
int index = mod_indices[i];
float vx = mod_data[i * 6 + 0];
float vy = mod_data[i * 6 + 2];
float vz = mod_data[i * 6 + 4];
float nx = mod_data[i * 6 + 1];
float ny = mod_data[i * 6 + 3];
float nz = mod_data[i * 6 + 5];
si->meshdata[index * 14 + 2] -= vx * value;
si->meshdata[index * 14 + 3] -= vy * value;
si->meshdata[index * 14 + 4] -= vz * value;
si->meshdata[index * 14 + 5] -= nx * value;
si->meshdata[index * 14 + 6] -= ny * value;
si->meshdata[index * 14 + 7] -= nz * value;
}
} else if (mod->type == ModifierDataBase::TYPE_BLEND_SYM) {
BlendModifierSymData *_mod = Object::cast_to<BlendModifierSymData>(mod);
if (value >= 0.0f)
modify(si, &_mod->plus, value);
else
modify(si, &_mod->minus, -value);
}
}
void CharacterModifiers::modify(Skeleton *skel,
ModifierDataBase *mod,
float value) {
if (mod->type == ModifierDataBase::TYPE_BONE) {
BoneModifierData *_mod = Object::cast_to<BoneModifierData>(mod);
assert(skel);
if (_mod->bone_id < 0)
_mod->bone_id = skel->find_bone(_mod->bone_name);
assert(_mod->bone_id >= 0);
skel->set_bone_custom_pose(_mod->bone_id,
skel->get_bone_custom_pose(_mod->bone_id) *
Transform().interpolate_with(_mod->xform, value));
} else if (mod->type == ModifierDataBase::TYPE_GROUP) {
int i;
BoneGroupModifierData *_mod = Object::cast_to<BoneGroupModifierData>(mod);
assert(skel);
if (_mod->bone_count > 0 && _mod->bones[0] < 0) {
for (i = 0; i < _mod->bone_count; i++)
_mod->bones[i] = skel->find_bone(_mod->bone_names[i]);
}
for (i = 0; i < _mod->bone_count; i++) {
/* TODO: gender-specific mods */
int bone_id = skel->find_bone(_mod->bone_names[i]);
if (bone_id >= 0) {
/* Transform bone_xform = skel->get_bone_custom_pose(_mod->bones[i]); */
Transform bone_xform = skel->get_bone_custom_pose(bone_id);
bone_xform *= Transform().interpolate_with(_mod->xforms[i], value);
/* skel->set_bone_custom_pose(_mod->bones[i], bone_xform); */
skel->set_bone_custom_pose(bone_id, bone_xform);
}
}
}
}
void CharacterModifiers::modify_bones(CharacterInstance *ci)
{
int i;
Skeleton *skel = ci->get_skeleton();
for (i = 0; i < skel->get_bone_count(); i++)
skel->set_bone_custom_pose(i, Transform());
Vector3 lf_orig_pos = skel->get_bone_global_pose(ci->left_foot_id).origin;
Vector3 rf_orig_pos = skel->get_bone_global_pose(ci->right_foot_id).origin;
for (const String *key = modifiers.next(NULL);
key;
key = modifiers.next(key)) {
Vector<String> splitname = (*key).split(":");
if (modifiers[*key]->gender == "common" ||
modifiers[*key]->gender == ci->get_gender_name()) {
if (splitname[0] == "bone")
modify(skel, modifiers[*key].ptr(), ci->mod_values[splitname[1]]);
}
}
Vector3 lf_pos = skel->get_bone_global_pose(ci->left_foot_id).origin;
Transform pelvis_xform = skel->get_bone_custom_pose(ci->pelvis_id);
pelvis_xform.origin += lf_pos - lf_orig_pos;
skel->set_bone_custom_pose(ci->pelvis_id, pelvis_xform);
}
void CharacterModifiers::modify(CharacterInstance *ci, CharacterSlotInstance *si,
const HashMap<String, float> &values) {
int i, j;
if (si->slot->blend_skip)
return;
Array surface = si->mesh->surface_get_arrays(0);
assert(si->meshdata);
for (i = 0; i < si->vertex_count; i++) {
si->meshdata[i * 14 + 2] = si->meshdata[i * 14 + 8];
si->meshdata[i * 14 + 3] = si->meshdata[i * 14 + 9];
si->meshdata[i * 14 + 4] = si->meshdata[i * 14 + 10];
si->meshdata[i * 14 + 5] = si->meshdata[i * 14 + 11];
si->meshdata[i * 14 + 6] = si->meshdata[i * 14 + 12];
si->meshdata[i * 14 + 7] = si->meshdata[i * 14 + 13];
}
for (const String *key = modifiers.next(NULL);
key;
key = modifiers.next(key)) {
Vector<String> splitname = (*key).split(":");
if (values.has(splitname[1]) && fabs(values[splitname[1]]) > 0.001) {
if (si->slot->helper == splitname[0]) {
modify(si, modifiers[*key].ptr(), values[splitname[1]]);
}
}
}
for (i = 0; i < si->vertex_count; i++) {
if (si->same_verts.has(i)) {
float vx = si->meshdata[i * 14 + 2];
float vy = si->meshdata[i * 14 + 3];
float vz = si->meshdata[i * 14 + 4];
float nx = si->meshdata[i * 14 + 5];
float ny = si->meshdata[i * 14 + 6];
float nz = si->meshdata[i * 14 + 7];
for (j = 0; j < si->same_verts[i].size(); j++) {
vx = Math::lerp(vx, si->meshdata[si->same_verts[i][j] * 14 + 2], 0.5f);
vy = Math::lerp(vy, si->meshdata[si->same_verts[i][j] * 14 + 3], 0.5f);
vz = Math::lerp(vz, si->meshdata[si->same_verts[i][j] * 14 + 4], 0.5f);
nx = Math::lerp(nx, si->meshdata[si->same_verts[i][j] * 14 + 5], 0.5f);
ny = Math::lerp(ny, si->meshdata[si->same_verts[i][j] * 14 + 6], 0.5f);
nz = Math::lerp(nz, si->meshdata[si->same_verts[i][j] * 14 + 7], 0.5f);
}
si->meshdata[i * 14 + 2] = vx;
si->meshdata[i * 14 + 3] = vy;
si->meshdata[i * 14 + 4] = vz;
si->meshdata[i * 14 + 5] = nx;
si->meshdata[i * 14 + 6] = ny;
si->meshdata[i * 14 + 7] = nz;
for (j = 0; j < si->same_verts[i].size(); j++) {
si->meshdata[si->same_verts[i][j] * 14 + 2] = vx;
si->meshdata[si->same_verts[i][j] * 14 + 3] = vy;
si->meshdata[si->same_verts[i][j] * 14 + 4] = vz;
si->meshdata[si->same_verts[i][j] * 14 + 5] = nx;
si->meshdata[si->same_verts[i][j] * 14 + 6] = ny;
si->meshdata[si->same_verts[i][j] * 14 + 7] = nz;
}
}
}
PoolVector<Vector3> vertices = surface[Mesh::ARRAY_VERTEX];
PoolVector<Vector3> normals = surface[Mesh::ARRAY_NORMAL];
PoolVector<Vector3>::Write vertex_w = vertices.write();
PoolVector<Vector3>::Write normal_w = normals.write();
for (i = 0; i < si->vertex_count; i++) {
vertex_w[i].x = si->meshdata[i * 14 + 2];
vertex_w[i].y = si->meshdata[i * 14 + 3];
vertex_w[i].z = si->meshdata[i * 14 + 4];
normal_w[i].x = si->meshdata[i * 14 + 5];
normal_w[i].y = si->meshdata[i * 14 + 6];
normal_w[i].z = si->meshdata[i * 14 + 7];
}
vertex_w.release();
normal_w.release();
surface[Mesh::ARRAY_VERTEX] = vertices;
surface[Mesh::ARRAY_NORMAL] = normals;
Ref<Material> mat = si->mesh->surface_get_material(0);
si->mesh->surface_remove(0);
si->mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface);
si->mesh->surface_set_material(0, mat);
}
CharacterModifiers *CharacterModifiers::get_singleton() {
static CharacterModifiers *cm = NULL;
if (!cm)
cm = memnew(CharacterModifiers);
return cm;
}