Initial commit

This commit is contained in:
Segey Lapin
2021-07-31 03:30:12 +03:00
commit 91cf9d2d34
249 changed files with 27582 additions and 0 deletions

9
modules/morpher/SCsub Normal file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python
Import('env')
from compat import isbasestring
# Godot source files
env.add_source_files(env.modules_sources, "*.cpp")
Export('env')

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,666 @@
#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;
}

View File

@@ -0,0 +1,242 @@
#ifndef CHARACTER_BASE_H
#define CHARACTER_BASE_H
#include "character_slot.h"
#include "config_data.h"
#include <core/io/json.h>
#include <core/io/resource_loader.h>
#include <core/os/file_access.h>
#include <core/reference.h>
#include <core/resource.h>
#include <scene/resources/packed_scene.h>
#include <cassert>
/* modifier data classes */
class ModifierDataBase : public Reference {
protected:
friend class CharacterModifiers;
int type;
String gender;
public:
static const int TYPE_BLEND = 1;
static const int TYPE_BLEND_SYM = 2;
static const int TYPE_BONE = 3;
static const int TYPE_SYMMETRY = 4;
static const int TYPE_PAIR = 5;
static const int TYPE_GROUP = 6;
protected:
String mod_name;
bool empty;
ModifierDataBase() :
empty(true),
gender("common")
{
}
};
class BlendModifierData : public ModifierDataBase {
protected:
friend class BlendModifierSymData;
friend class CharacterModifiers;
float minp[3];
float maxp[3];
float cd[3];
float minn[3];
float maxn[3];
float cdn[3];
PoolVector<int> mod_indices;
PoolVector<float> mod_data;
public:
BlendModifierData() {
type = TYPE_BLEND;
}
};
class BlendModifierSymData : public ModifierDataBase {
friend class CharacterModifiers;
BlendModifierData plus;
BlendModifierData minus;
public:
BlendModifierSymData() {
type = TYPE_BLEND_SYM;
}
};
class BoneModifierData : public ModifierDataBase {
friend class CharacterModifiers;
String bone_name;
Transform xform;
int bone_id;
public:
BoneModifierData(): bone_id(-1) {
type = TYPE_BONE;
}
};
class BoneGroupModifierData : public ModifierDataBase {
friend class CharacterModifiers;
static const int MAX_BONES = 32;
int bones[MAX_BONES];
Transform xforms[MAX_BONES];
String bone_names[MAX_BONES];
int bone_count;
public:
BoneGroupModifierData(): bone_count(0) {
type = TYPE_GROUP;
}
~BoneGroupModifierData()
{
}
};
class CharacterSlotInstance;
class Skeleton;
class CharacterModifiers : public Reference {
HashMap<String, Ref<ModifierDataBase> > modifiers;
template <class T>
void create(const String &name, const String &gender) {
Ref<T> mod = memnew(T);
mod->mod_name = name;
mod->gender = gender;
modifiers[name] = mod;
}
protected:
bool mods_created;
void init_blend_modifier(const String &name,
BlendModifierData *bm);
void init_bone_modifier(const String &name,
BoneModifierData *bm,
const Array &parameters);
void init_bone_group_modifier(const String &name,
BoneGroupModifierData *bm,
const Array &parameters);
Transform parse_transform(const Dictionary &xformdata);
void create_mod(int type, const String &name, const String &gender, const Array &parameters = Array());
public:
CharacterModifiers() : mods_created(false)
{
}
PoolVector<String> get_modifier_list() const;
PoolVector<String> get_base_modifier_list() const;
void create_modifiers();
void modify_bones(CharacterInstance *ci);
void modify(CharacterSlotInstance *si, ModifierDataBase *mod, float value);
void modify(Skeleton *skel, ModifierDataBase *mod, float value);
void modify(CharacterInstance *ci, CharacterSlotInstance *si,
const HashMap<String, float> &values);
static CharacterModifiers *get_singleton();
~CharacterModifiers()
{
}
};
class CharacterGender {
friend class CharacterGenderList;
friend class CharacterInstanceList;
friend class CharacterInstance;
String name;
Ref<PackedScene> scene;
HashMap<String, CharacterSlot> slot_list;
String left_foot;
String right_foot;
String pelvis;
};
class CharacterGenderList : public Reference {
friend class CharacterInstanceList;
GDCLASS(CharacterGenderList, Reference)
CharacterGenderList();
protected:
HashMap<String, CharacterGender> genders;
static void _bind_methods();
Node *instance(const String &gender) {
const CharacterGender &g = genders[gender];
Node *ret = g.scene->instance();
return ret;
}
public:
void config();
void create_gender(const String &name, Ref<PackedScene> base);
void remove_gender(const String &name);
static CharacterGenderList *get_singleton() {
static CharacterGenderList *gl = NULL;
if (!gl)
gl = memnew(CharacterGenderList);
return gl;
}
};
class CharacterInstance : public Reference {
GDCLASS(CharacterInstance, Reference)
friend class CharacterInstanceList;
friend class CharacterModifiers;
NodePath scene_root;
HashMap<String, CharacterSlotInstance> slots;
HashMap<String, float> mod_values;
int left_foot_id;
int right_foot_id;
int pelvis_id;
bool mod_updated;
CharacterGender *gender;
CharacterInstance(): mod_updated(false), gender(NULL)
{
}
Node *get_scene_root() const;
Skeleton *get_skeleton() const;
const String &get_gender_name() const
{
assert(gender);
return gender->name;
}
};
class CharacterInstanceList : public Reference {
GDCLASS(CharacterInstanceList, Reference)
List<Ref<CharacterInstance> > instance_list;
protected:
HashMap<String, HashMap<int, Vector<int> > > same_verts;
static void _bind_methods();
protected:
void init_slot(CharacterInstance *ci,
CharacterSlotInstance *si);
void update_slot(CharacterInstance *ci,
CharacterSlotInstance *si);
public:
CharacterInstanceList() {
}
void remove(Node *scene);
Node *create(const String &gender, const Transform &xform, const Dictionary &slot_conf);
Ref<CharacterInstance> get_instance(Node *scene);
void set_mod_value(Node *scene,
const String &mod, float value);
Node *get_scene(CharacterInstance *ci);
PoolVector<String> get_modifier_list()
{
return CharacterModifiers::get_singleton()->get_modifier_list();
}
PoolVector<String> get_base_modifier_list()
{
return CharacterModifiers::get_singleton()->get_base_modifier_list();
}
Dictionary get_instance_modifier_values(Node *scene)
{
Dictionary ret;
Ref<CharacterInstance> ci = get_instance(scene);
for (const String *key = ci->mod_values.next(NULL);
key;
key = ci->mod_values.next(key))
ret[*key] = ci->mod_values[*key];
return ret;
}
float get_mod_value(Node *scene, const String &mod);
Skeleton *get_skeleton(Node *scene) const;
static CharacterInstanceList *get_singleton();
void update();
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,10 @@
#include "character_slot.h"
CharacterSlotInstance::CharacterSlotInstance() :
slot(NULL),
mesh_no(0),
dirty(false), meshdata(NULL) {
}
CharacterSlotInstance::~CharacterSlotInstance() {
if (meshdata)
memdelete_arr(meshdata);
}

View File

@@ -0,0 +1,37 @@
#ifndef CHARACTER_SLOT_H
#define CHARACTER_SLOT_H
#include <core/reference.h>
#include <scene/resources/mesh.h>
class CharacterSlot {
friend class CharacterGenderList;
friend class CharacterInstanceList;
friend class CharacterModifiers;
String name;
String category;
String match;
String helper;
bool mandatory;
bool blend_skip;
};
class CharacterInstance;
class CharacterSlotInstance {
friend class CharacterInstanceList;
friend class CharacterModifiers;
const CharacterSlot *slot;
NodePath slot_path;
int mesh_no;
bool dirty;
Ref<ArrayMesh> mesh;
float *meshdata;
int vertex_count;
int uv_index;
HashMap<int, Vector<int> > same_verts;
CharacterInstance *char_instance;
NodePath node_path;
public:
CharacterSlotInstance();
~CharacterSlotInstance();
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,5 @@
def can_build(env, platform):
return True
def configure(env):
pass

View File

@@ -0,0 +1,63 @@
#include "config_data.h"
#include <core/io/json.h>
#include <core/os/file_access.h>
#include <cassert>
ConfigData::ConfigData() {
FileAccess *fd = FileAccess::open("res://characters/config.json", FileAccess::READ);
assert(fd);
String confdata = fd->get_as_utf8_string();
fd->close();
String err;
int err_line;
Variant adata;
Error e = JSON::parse(confdata, adata, err, err_line);
if (e != OK)
printf("json parse error: %ls at line %d\n", err.c_str(), err_line);
assert(e == OK);
config = adata;
}
ConfigData *ConfigData::get_singleton() {
static ConfigData data;
return &data;
}
AccessoryData::AccessoryData() {
const String &accessory_path =
ConfigData::get_singleton()->get()["accessory_path"];
FileAccess *fd = FileAccess::open(accessory_path, FileAccess::READ);
assert(fd);
String confdata = fd->get_as_utf8_string();
fd->close();
String err;
int err_line;
Variant adata;
Error e = JSON::parse(confdata, adata, err, err_line);
if (e != OK)
printf("json parse error: %ls at line %d\n", err.c_str(), err_line);
assert(e == OK);
accessory = adata;
}
AccessoryData *AccessoryData::get_singleton() {
static AccessoryData data;
return &data;
}
Ref<ArrayMesh> AccessoryData::get_mesh(const Dictionary &entry) const {
const Array materials = entry["materials"];
const String &mesh_path = entry["path"];
int i;
Error err = OK;
Ref<ArrayMesh> mesh_orig = ResourceLoader::load(mesh_path, "", &err);
Ref<ArrayMesh> mesh = mesh_orig->duplicate();
mesh->set_meta("orig_path", mesh_orig->get_path());
if (err != OK) {
printf("Could not read resource %ls\n", mesh_path.c_str());
return NULL;
}
for (i = 0; i < mesh->get_surface_count(); i++) {
const Dictionary &mat_data = materials[i];
const String &mat_path = mat_data["path"];
Ref<Material> mat = ResourceLoader::load(mat_path, "", &err);
mesh->surface_set_material(i, mat);
}
return mesh;
}

View File

@@ -0,0 +1,49 @@
#ifndef CONFIG_DATA_H
#define CONFIG_DATA_H
#include <core/reference.h>
#include <scene/resources/mesh.h>
/* config data */
class ConfigData {
Dictionary config;
ConfigData();
public:
static ConfigData *get_singleton();
const Dictionary &get() {
return config;
}
};
class AccessoryData {
Dictionary accessory;
AccessoryData();
public:
PoolVector<Dictionary> get_matching_entries(const String &gender,
const String &category, const String &match) const {
const Dictionary &cat = accessory[gender];
const Dictionary &items = cat[category];
PoolVector<Dictionary> ret;
for (const Variant *key = items.next(NULL);
key; key = items.next(key)) {
const String k = *key;
if (k.match(match)) {
const Dictionary &item = items[*key];
ret.push_back(item);
}
}
return ret;
}
Dictionary get_entry(const String &gender,
const String &category, const String &name) const {
const Dictionary &cat = accessory[gender];
const Dictionary &items = cat[category];
return items[name];
}
Ref<ArrayMesh> get_mesh(const Dictionary &entry) const;
static AccessoryData *get_singleton();
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,126 @@
#include "map_storage.h"
#include <core/image.h>
#include <core/io/compression.h>
#include <core/io/json.h>
#include <core/os/file_access.h>
#include <core/resource.h>
#include <cassert>
MapStorage::MapStorage() :
pos(0) {
#ifdef JAVASCRIPT_ENABLED
buffer = memnew_arr(uint8_t, MAX_BUF);
#endif
printf("map_storage created\n");
FileAccess *fd = FileAccess::open("res://characters/config.json", FileAccess::READ);
assert(fd);
String confdata = fd->get_as_utf8_string();
fd->close();
String err;
int err_line;
Variant adata;
Error e = JSON::parse(confdata, adata, err, err_line);
if (e != OK)
printf("json parse error: %ls at line %d\n", err.c_str(), err_line);
assert(e == OK);
config = adata;
printf("loading\n");
load();
printf("loading done\n");
}
PoolVector<float> MapStorage::get_minmax(const String &shape_name) {
int i;
PoolVector<float> minmax;
minmax.resize(12);
struct datablock *d = data[shape_name];
for (i = 0; i < 3; i++)
minmax.write()[i] = d->minp[i];
for (i = 0; i < 3; i++)
minmax.write()[i + 3] = d->maxp[i];
for (i = 0; i < 3; i++)
minmax.write()[i + 6] = d->min_normal[i];
for (i = 0; i < 3; i++)
minmax.write()[i + 9] = d->max_normal[i];
return minmax;
}
void MapStorage::load() {
int i, j;
float minp[3], maxp[3], min_normal[3], max_normal[3];
int width, height, format;
int nwidth, nheight, nformat;
int dec_size, comp_size;
assert(config.has("map_path"));
const String &map_path = config["map_path"];
FileAccess *fd = FileAccess::open(map_path, FileAccess::READ);
if (!fd)
return;
int count = fd->get_32();
for (j = 0; j < count; j++) {
struct datablock *d =
(struct datablock *) allocate(sizeof(struct datablock));
String shape_name = fd->get_pascal_string();
printf("loading shape: %ls\n", shape_name.c_str());
for (i = 0; i < 3; i++)
d->minp[i] = fd->get_float();
for (i = 0; i < 3; i++)
d->maxp[i] = fd->get_float();
d->width = fd->get_32();
d->height = fd->get_32();
d->format = fd->get_32();
d->dec_size = fd->get_32();
comp_size = fd->get_32();
d->buf = allocate(comp_size);
d->buf_size = comp_size;
fd->get_buffer(d->buf, comp_size);
for (i = 0; i < 3; i++)
d->min_normal[i] = fd->get_float();
for (i = 0; i < 3; i++)
d->max_normal[i] = fd->get_float();
d->width_normal = fd->get_32();
d->height_normal = fd->get_32();
d->format_normal = fd->get_32();
d->dec_size_normal = fd->get_32();
comp_size = fd->get_32();
d->buf_normal = allocate(comp_size);
d->buf_normal_size = comp_size;
fd->get_buffer(d->buf_normal, comp_size);
data[shape_name] = d;
}
}
MapStorage::~MapStorage()
{
#ifdef JAVASCRIPT_ENABLED
memdelete_arr(buffer);
#endif
}
Ref<Image> MapStorage::get_image(const String &name) const {
assert(data.has(name));
const struct datablock *d = data[name];
PoolVector<uint8_t> imgdecbuf;
imgdecbuf.resize(d->dec_size);
Compression::decompress(imgdecbuf.write().ptr(), d->dec_size,
d->buf, d->buf_size, Compression::MODE_FASTLZ);
Ref<Image> img = memnew(Image);
assert(img.ptr() != NULL);
img->create(d->width, d->height,
false, (Image::Format)d->format, imgdecbuf);
return img;
}
Ref<Image> MapStorage::get_normal_image(const String &name) const {
const struct datablock *d = data[name];
PoolVector<uint8_t> imgdecbuf;
imgdecbuf.resize(d->dec_size_normal);
Compression::decompress(imgdecbuf.write().ptr(),
d->dec_size_normal, d->buf_normal,
d->buf_normal_size, Compression::MODE_FASTLZ);
Ref<Image> img = memnew(Image);
assert(img.ptr() != NULL);
img->create(d->width_normal, d->height_normal,
false, (Image::Format)d->format_normal, imgdecbuf);
return img;
}
MapStorage *MapStorage::get_singleton() {
static MapStorage ms;
return &ms;
}

View File

@@ -0,0 +1,63 @@
#ifndef MAP_STORAGE_H
#define MAP_STORAGE_H
#include <core/reference.h>
#include <core/image.h>
#include <cassert>
class MapStorage {
static const long MAX_BUF = 52 * 1024 * 1024;
struct datablock {
String name;
float minp[3], maxp[3], min_normal[3], max_normal[3];
String helper;
int width;
int height;
int format;
int dec_size;
uint8_t *buf;
int buf_size;
int width_normal;
int height_normal;
int format_normal;
int dec_size_normal;
uint8_t *buf_normal;
int buf_normal_size;
};
#ifdef JAVASCRIPT_ENABLED
uint8_t *buffer;
#else
uint8_t buffer[MAX_BUF];
#endif
unsigned long pos;
HashMap<String, struct datablock *> data;
Dictionary config;
void load();
MapStorage();
uint8_t *allocate(unsigned long size) {
assert(pos + size < MAX_BUF);
uint8_t *ret = &buffer[pos];
pos += (size + 32) & (~32UL);
return ret;
}
public:
const Dictionary &get_config() const {
return config;
}
PoolVector<String> get_list() const {
PoolVector<String> ret;
for (const String *key = data.next(NULL);
key; key = data.next(key)) {
ret.push_back(*key);
}
return ret;
}
Ref<Image> get_image(const String &name) const;
Ref<Image> get_normal_image(const String &name) const;
PoolVector<float> get_minmax(const String &shape_name);
static MapStorage *get_singleton();
~MapStorage();
};
#endif

Binary file not shown.

244
modules/morpher/morpher.cpp Normal file
View File

@@ -0,0 +1,244 @@
#include "morpher.h"
#include <core/io/compression.h>
#include <core/math/math_funcs.h>
DNA_::DNA_(String &path) {
}
DNA_::DNA_() {
}
DNA_::~DNA_() {
}
void DNA_::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_mesh", "part", "mesh", "same_verts"), &DNA_::add_mesh);
ClassDB::bind_method(D_METHOD("add_cloth_mesh", "cloth_name", "cloth_helper", "mesh"), &DNA_::add_cloth_mesh);
ClassDB::bind_method(D_METHOD("add_body_mesh", "body_mesh", "same_verts"), &DNA_::add_body_mesh);
ClassDB::bind_method(D_METHOD("_prepare_cloth", "body_mesh", "cloth_mesh"), &DNA_::_prepare_cloth);
ClassDB::bind_method(D_METHOD("find_body_points", "body_mesh", "cloth_mesh"), &DNA_::find_body_points);
ClassDB::bind_method(D_METHOD("get_modifier_list"), &DNA_::get_modifier_list);
ClassDB::bind_method(D_METHOD("triangulate_uv", "v0", "vs", "uvs"), &DNA_::triangulate_uv);
ClassDB::bind_method(D_METHOD("get_replace_point_id", "point_pool", "vc", "vb_id", "selected_points"), &DNA_::get_replace_point_id);
ClassDB::bind_method(D_METHOD("triangulate_uv_arrays", "point", "uv_index", "arrays", "points"), &DNA_::triangulate_uv_arrays);
ClassDB::bind_method(D_METHOD("ensure_uv2", "arrays"), &DNA_::ensure_uv2);
ClassDB::bind_method(D_METHOD("get_closest_points", "point", "pool_vertices", "indices", "count"), &DNA_::get_closest_points);
ClassDB::bind_method(D_METHOD("load", "path"), &DNA_::load);
ClassDB::bind_method(D_METHOD("modify_mesh", "orig_mesh", "same_verts"), &DNA_::modify_mesh);
ClassDB::bind_method(D_METHOD("modify_part", "part"), &DNA_::modify_part);
ClassDB::bind_method(D_METHOD("set_modifier_value", "mod_name", "value"), &DNA_::set_modifier_value);
}
Dictionary DNA_::find_body_points(const Ref<ArrayMesh> &body_mesh, const Ref<ArrayMesh> &cloth_mesh) {
Dictionary tmp;
Array arrays_cloth = cloth_mesh->surface_get_arrays(0);
Array arrays_body = body_mesh->surface_get_arrays(0);
const PoolVector<Vector3> &cloth_vertices = arrays_cloth[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &body_vertices = arrays_body[Mesh::ARRAY_VERTEX];
for (int vcloth = 0; vcloth < cloth_vertices.size(); vcloth++) {
for (int vbody = 0; vbody < body_vertices.size(); vbody++) {
Vector3 vc = cloth_vertices[vcloth];
Vector3 vb = body_vertices[vbody];
if (vc.distance_to(vb) < 0.02f) {
if (tmp.has(vcloth)) {
PoolVector<int> data = tmp[vcloth];
data.push_back(vbody);
tmp[vcloth] = data;
} else {
PoolVector<int> data;
data.push_back(vbody);
tmp[vcloth] = data;
}
}
}
}
return tmp;
}
Ref<ArrayMesh> DNA_::_prepare_cloth(const Ref<ArrayMesh> &body_mesh, const Ref<ArrayMesh> &cloth_mesh) {
Array arrays_cloth = cloth_mesh->surface_get_arrays(0);
ensure_uv2(arrays_cloth);
Array arrays_body = body_mesh->surface_get_arrays(0);
const PoolVector<Vector3> &cloth_vertices = arrays_cloth[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &body_vertices = arrays_body[Mesh::ARRAY_VERTEX];
const PoolVector<Vector2> &body_uvs = arrays_body[Mesh::ARRAY_TEX_UV];
Dictionary tmp = find_body_points(body_mesh, cloth_mesh);
PoolVector<Vector2> cloth_uv2 = arrays_cloth[Mesh::ARRAY_TEX_UV2];
Array tmp_keys = tmp.keys();
for (int i = 0; i < tmp_keys.size(); i++) {
int k = tmp_keys[i];
Vector3 vc = cloth_vertices[k];
PoolVector<int> data = tmp[k];
PoolVector<int> res = get_closest_points(vc, body_vertices, data, 3);
if (res.size() == 3) {
Vector2 uv = triangulate_uv_arrays(vc, Mesh::ARRAY_TEX_UV, arrays_body, res);
cloth_uv2.write()[k] = uv;
}
}
arrays_cloth[Mesh::ARRAY_TEX_UV2] = cloth_uv2;
for (int s = 0; s < cloth_uv2.size(); s++) {
PoolVector<Vector2> uv1 = arrays_cloth[Mesh::ARRAY_TEX_UV];
PoolVector<Vector2> uv2 = arrays_cloth[Mesh::ARRAY_TEX_UV2];
}
Ref<ArrayMesh> new_mesh = memnew(ArrayMesh);
new_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays_cloth);
return new_mesh;
}
Ref<ArrayMesh> DNA_::modify_mesh(const Ref<ArrayMesh> orig_mesh, const Dictionary same_verts) {
Ref<ArrayMesh> ret = memnew(ArrayMesh);
PoolVector<String> modifier_list = get_modifier_list();
lock_all_images();
Rect2 rect = collect_rects();
modify_mesh_mutex.lock();
int surface_count = orig_mesh->get_surface_count();
for (int surface = 0; surface < surface_count; surface++) {
Array arrays = orig_mesh->surface_get_arrays(surface);
int uv_index = Mesh::ARRAY_TEX_UV;
if (arrays[Mesh::ARRAY_TEX_UV2].get_type() != Variant::NIL)
uv_index = Mesh::ARRAY_TEX_UV2;
PoolVector<Vector3> vertices = arrays[Mesh::ARRAY_VERTEX];
PoolVector<Vector3> normals = arrays[Mesh::ARRAY_NORMAL];
PoolVector<Vector2> uvs = arrays[uv_index];
mod_cache.resize(vertices.size());
Vector3 n, v, diff, diffn;
Vector2 uv;
for (int index = 0; index < vertices.size(); index++) {
// printf("init index: %d v: %ls n: %ls\n", index, String(v).c_str(), String(n).c_str());
uv = uvs[index];
if (!rect.has_point(uv))
continue;
v = vertices[index];
n = normals[index];
if (mod_cache[index].get_type() == Variant::NIL) {
Dictionary data;
mod_cache[index] = data;
}
for (int ki = 0; ki < modifier_list.size(); ki++) {
String key = modifier_list[ki];
Rect2 mrect = map_rect[key];
float value = mod_value[key];
if (!mrect.has_point(uv) || value < 0.0001)
continue;
Dictionary cache = mod_cache[index];
if (cache.has(key)) {
Array data = cache[key];
diff = data[0];
diffn = data[1];
} else {
diff = Vector3();
diffn = Vector3();
Vector2 pos(uv.x * (float)image_sizes[key].x, uv.y * (float)image_sizes[key].y);
Color offset = map_vertices[key]->get_pixelv(pos);
Color offsetn = map_normals[key]->get_pixelv(pos);
Vector3 pdiff(offset.r, offset.g, offset.b);
Vector3 ndiff(offsetn.r, offsetn.g, offsetn.b);
for (int u = 0; u < 3; u++) {
diff[u] = Math::range_lerp(pdiff[u], 0.0f, 1.0f, min_point[u], max_point[u]);
diffn[u] = Math::range_lerp(ndiff[u], 0.0f, 1.0f, min_normal[u], max_normal[u]);
if (fabs(diff[u]) < 0.0001f)
diff[u] = 0.0f;
if (fabs(diffn[u]) < 0.0001f)
diffn[u] = 0.0f;
}
Array cache_data;
cache_data.push_back(diff);
cache_data.push_back(diffn);
cache[key] = cache_data;
// printf("diff: %ls diffn: %ls\n", String(diff).c_str(), String(diffn).c_str());
}
v -= diff * value;
n -= diffn * value;
}
vertices.write()[index] = v;
normals.write()[index] = n;
}
Array idx_keys = same_verts.keys();
for (int vxk = 0; vxk < idx_keys.size(); vxk++) {
int xv = idx_keys[vxk];
Array data = same_verts[xv];
if (data.size() <= 1)
continue;
Vector3 vx = vertices[data[0]];
for (int idx = 1; idx < data.size(); idx++)
vx.linear_interpolate(vertices[data[idx]], 0.5f);
for (int idx = 0; idx < data.size(); idx++)
vertices.write()[data[idx]] = vx;
}
arrays[Mesh::ARRAY_VERTEX] = vertices;
arrays[Mesh::ARRAY_NORMAL] = normals;
ret->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
Ref<Material> mat = orig_mesh->surface_get_material(surface);
if (mat != NULL)
ret->surface_set_material(surface, mat);
}
modify_mesh_mutex.unlock();
unlock_all_images();
return ret;
}
Variant DNA_::get_var(FileAccess *f) const {
uint32_t len = f->get_32();
PoolVector<uint8_t> buff;
buff.resize(len);
PoolVector<uint8_t>::Write w = buff.write();
f->get_buffer(&w[0], len);
w.release();
PoolVector<uint8_t>::Read r = buff.read();
Variant v;
decode_variant(v, &r[0], len, NULL, false);
return v;
}
bool DNA_::load(const String &path) {
Error err;
FileAccess *f = FileAccess::open(path, FileAccess::READ, &err);
if (!f || err != OK)
return false;
min_point = get_var(f);
max_point = get_var(f);
min_normal = get_var(f);
max_normal = get_var(f);
maps = get_var(f);
vert_indices = get_var(f);
printf("min: %ls max: %ls\n", String(min_point).c_str(), String(max_point).c_str());
if (f)
f->close();
Array map_keys = maps.keys();
for (int i = 0; i < map_keys.size(); i++) {
String key = map_keys[i];
Dictionary map = maps[key];
Rect2 rect = map["rect"];
map_rect[key] = rect;
PoolVector<uint8_t> compressed_data = map["image_data"];
PoolVector<uint8_t> compressed_normal_data = map["image_normal_data"];
printf("%ls: %ls\n / points: %d normals: %d\n",
key.c_str(),
String(rect).c_str(),
compressed_data.size(),
compressed_normal_data.size());
int width, height, size, normal_size, format;
size = map["image_size"];
normal_size = map["image_normal_size"];
width = map["width"];
height = map["height"];
format = map["format"];
printf("size: %d normal size: %d\n", size, normal_size);
image_sizes[key] = Vector2i(width, height);
PoolVector<uint8_t> decompressed_data, decompressed_normal_data;
decompressed_data.resize(size);
decompressed_normal_data.resize(normal_size);
Compression::decompress(decompressed_data.write().ptr(), size,
compressed_data.read().ptr(),
compressed_data.size(), Compression::MODE_FASTLZ);
Compression::decompress(decompressed_normal_data.write().ptr(),
normal_size, compressed_normal_data.read().ptr(),
compressed_normal_data.size(), Compression::MODE_FASTLZ);
Ref<Image> vertex_image = memnew(Image);
Ref<Image> normal_image = memnew(Image);
vertex_image->create(width, height, false, (enum Image::Format)format, decompressed_data);
normal_image->create(width, height, false, (enum Image::Format)format, decompressed_normal_data);
map_vertices[key] = vertex_image;
map_normals[key] = vertex_image;
}
return true;
}

216
modules/morpher/morpher.h Normal file
View File

@@ -0,0 +1,216 @@
#ifndef MORPHER_H
#define MORPHER_H
#include <core/io/marshalls.h>
#include <core/os/file_access.h>
#include <core/reference.h>
#include <core/resource.h>
#include <scene/resources/mesh.h>
class DNA_ : public Reference {
GDCLASS(DNA_, Reference)
protected:
Vector3 min_point, max_point, min_normal, max_normal;
Dictionary maps;
Dictionary vert_indices;
Dictionary meshes;
Dictionary clothes;
Mutex modify_mesh_mutex;
HashMap<String, Ref<Image> > map_vertices, map_normals;
HashMap<String, float> mod_value;
HashMap<String, Rect2> map_rect;
HashMap<String, Vector2i> image_sizes;
Array mod_cache;
static void _bind_methods();
Variant get_var(FileAccess *f) const;
void lock_all_images() {
PoolVector<String> modifier_list = get_modifier_list();
for (int i = 0; i < modifier_list.size(); i++) {
String mod_name = modifier_list[i];
Ref<Image> iv = map_vertices[mod_name];
Ref<Image> in = map_normals[mod_name];
iv->lock();
in->lock();
}
}
void unlock_all_images() {
PoolVector<String> modifier_list = get_modifier_list();
for (int i = 0; i < modifier_list.size(); i++) {
String mod_name = modifier_list[i];
Ref<Image> iv = map_vertices[mod_name];
Ref<Image> in = map_normals[mod_name];
iv->unlock();
in->unlock();
}
}
Rect2 collect_rects() {
Rect2 rect;
PoolVector<String> modifier_list = get_modifier_list();
for (int i = 0; i < modifier_list.size(); i++) {
String mod_name = modifier_list[i];
if (mod_value[mod_name] > 0.0001f) {
if (rect.size.length() == 0.0)
rect = map_rect[mod_name];
else
rect = rect.merge(map_rect[mod_name]);
}
}
return rect;
}
static int farthest_index(const PoolVector<Vector3> &points, const Vector3 &point) {
if (points.size() < 2)
return -1;
float distance = points[0].distance_squared_to(point);
int index = 0;
for (int i = 1; i < points.size(); i++) {
float d1 = points[i].distance_squared_to(point);
if (distance < d1) {
distance = d1;
index = i;
}
}
return index;
}
template <class T>
static bool pool_has(const PoolVector<T> &data, const T value) {
for (int i = 0; i < data.size(); i++)
if (data[i] == value)
return true;
return false;
}
struct mesh_data {
String name;
Ref<ArrayMesh> orig_mesh;
#if 0
HashMap<int, PoolVector<int> > indices;
#endif
Dictionary indices;
};
HashMap<String, struct mesh_data> meshes_;
public:
DNA_(String &path);
DNA_();
~DNA_();
static PoolVector<Vector3> fill_vector(const PoolVector<Vector3> &point_pool,
const PoolVector<int> &selected_points) {
PoolVector<Vector3> points;
points.resize(selected_points.size());
for (int i = 0; i < points.size(); i++) {
points[i] = point_pool[selected_points[i]];
}
return points;
}
int get_replace_point_id(const PoolVector<Vector3> &point_pool,
const Vector3 &vc, const int &vb_id,
const PoolVector<int> &selected_points) {
if (pool_has(selected_points, vb_id))
return -1;
PoolVector<Vector3> points = fill_vector(point_pool, selected_points);
int farthest = farthest_index(points, vc);
float d1 = vc.distance_squared_to(point_pool[vb_id]);
float d2 = vc.distance_squared_to(point_pool[farthest]);
if (d1 < d2)
return farthest;
return -1;
}
PoolVector<String> get_modifier_list() {
PoolVector<String> data;
Array kdata = maps.keys();
for (int i = 0; i < kdata.size(); i++)
data.push_back(kdata[i]);
return data;
}
Vector2 triangulate_uv(Vector3 v0, const PoolVector<Vector3> &vs, const PoolVector<Vector2> &uvs) {
float d1 = v0.distance_to(vs[0]);
float d2 = v0.distance_to(vs[1]);
float d3 = v0.distance_to(vs[2]);
float _ln = MAX(d1, MAX(d2, d3));
Vector3 v(d1 / _ln, d2 / _ln, d3 / _ln);
Vector2 midp = (uvs[0] + uvs[1] + uvs[2]) / 3.0f;
Vector2 uv = midp.linear_interpolate(uvs[0], v.x) +
midp.linear_interpolate(uvs[1], v.y) +
midp.linear_interpolate(uvs[2], v.z);
uv /= 3.0f;
return uv;
}
Vector2 triangulate_uv_arrays(const Vector3 &point, int uv_index, const Array &arrays, const PoolVector<int> &points) {
PoolVector<Vector3> vertices;
PoolVector<Vector2> uvs;
const PoolVector<Vector3> &pool_vertices = arrays[Mesh::ARRAY_VERTEX];
switch (uv_index) {
case Mesh::ARRAY_TEX_UV:
case Mesh::ARRAY_TEX_UV2:
break;
default:
return Vector2();
}
const PoolVector<Vector2> &pool_uvs = arrays[uv_index];
for (int i = 0; i < points.size(); i++) {
vertices.push_back(pool_vertices[points[i]]);
uvs.push_back(pool_uvs[points[i]]);
}
return triangulate_uv(point, vertices, uvs);
}
void ensure_uv2(Array arrays) {
if (arrays[Mesh::ARRAY_TEX_UV2].get_type() == Variant::NIL)
arrays[Mesh::ARRAY_TEX_UV2] = arrays[Mesh::ARRAY_TEX_UV];
}
PoolVector<int> get_closest_points(const Vector3 &point, const PoolVector<Vector3> &pool_vertices, const PoolVector<int> &indices, int count) {
PoolVector<int> ret;
for (int i = 0; i < indices.size(); i++) {
int v = indices[i];
if (ret.size() >= count) {
int id = get_replace_point_id(pool_vertices, point, v, ret);
if (id >= 0)
ret.write()[id] = v;
} else if (!pool_has(ret, v))
ret.push_back(v);
}
return ret;
}
void add_mesh(const String &part, const Ref<ArrayMesh> &mesh, const Dictionary same_verts) {
struct mesh_data data;
data.name = part;
data.orig_mesh = mesh;
data.indices = same_verts;
meshes_[part] = data;
#if 0
data["orig_mesh"] = mesh;
data["indices"] = same_verts;
meshes[part] = data;
#endif
}
Ref<ArrayMesh> _prepare_cloth(const Ref<ArrayMesh> &body_mesh, const Ref<ArrayMesh> &cloth_mesh);
Ref<ArrayMesh> get_mesh(const String &part) {
if (meshes_.has(part))
return meshes_[part].orig_mesh;
return NULL;
}
Ref<ArrayMesh> add_cloth_mesh(const String &cloth_name, String cloth_helper, const Ref<ArrayMesh> &mesh) {
Dictionary body = meshes["body"];
Ref<ArrayMesh> orig_mesh = get_mesh("body");
Ref<ArrayMesh> new_mesh = _prepare_cloth(orig_mesh, mesh);
add_mesh(cloth_name, new_mesh, Dictionary());
Dictionary cloth;
cloth["helper"] = cloth_helper;
clothes[cloth_name] = cloth;
return new_mesh;
}
void add_body_mesh(const Ref<ArrayMesh> &body_mesh, const Dictionary same_verts) {
add_mesh("body", body_mesh, same_verts);
}
Ref<ArrayMesh> modify_mesh(const Ref<ArrayMesh> orig_mesh, const Dictionary same_verts);
Dictionary find_body_points(const Ref<ArrayMesh> &body_mesh, const Ref<ArrayMesh> &cloth_mesh);
Ref<ArrayMesh> modify_part(const String &part) {
Ref<ArrayMesh> mesh = meshes_[part].orig_mesh;
Dictionary indices = meshes_[part].indices;
return modify_mesh(mesh, indices);
}
void set_modifier_value(const String &mod_name, float value) {
mod_value[mod_name] = value;
}
bool load(const String &path);
};
#endif

Binary file not shown.

View File

@@ -0,0 +1,17 @@
#ifdef TRIANGLE_H
#define TRIANGLE_H
#endif
class PartitionGrid : public Reference {
GDCLASS(PartitionGrid, Reference)
protected:
struct triangle {
int id;
int points[3];
} entry entries[5 * 5 * 5];
public:
PartitionGrid() {
}
~PartitionGrid() {
}
};

View File

@@ -0,0 +1,50 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "register_types.h"
#include "character_base.h"
#include "triangle.h"
void register_morpher_types() {
ClassDB::register_class<TriangleSet>();
ClassDB::register_class<CharacterGenderList>();
ClassDB::register_class<CharacterInstance>();
ClassDB::register_class<CharacterInstanceList>();
Engine::get_singleton()->add_singleton(
Engine::Singleton("CharacterInstanceList",
CharacterInstanceList::get_singleton()));
Engine::get_singleton()->add_singleton(
Engine::Singleton("CharacterGenderList",
CharacterGenderList::get_singleton()));
}
void unregister_morpher_types() {
}

View File

@@ -0,0 +1,32 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
void register_morpher_types();
void unregister_morpher_types();

Binary file not shown.

View File

@@ -0,0 +1,294 @@
#include "triangle.h"
void TriangleSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_from_array_shape", "arrays_base", "shape_arrays"), &TriangleSet::create_from_array_shape);
ClassDB::bind_method(D_METHOD("create_from_mesh_difference", "arrays_base", "uv_index1", "arrays_shape", "uv_index2"), &TriangleSet::create_from_array_difference);
ClassDB::bind_method(D_METHOD("draw", "vimage", "nimage", "uv_index"), &TriangleSet::draw);
ClassDB::bind_method(D_METHOD("get_data", "vimage", "nimage"), &TriangleSet::get_data);
ClassDB::bind_method(D_METHOD("get_min"), &TriangleSet::get_min);
ClassDB::bind_method(D_METHOD("get_max"), &TriangleSet::get_max);
ClassDB::bind_method(D_METHOD("get_min_normal"), &TriangleSet::get_min_normal);
ClassDB::bind_method(D_METHOD("get_max_normal"), &TriangleSet::get_max_normal);
ClassDB::bind_method(D_METHOD("save", "fd"), &TriangleSet::save);
}
void TriangleSet::create_from_array_shape(const Array &arrays_base, const Array &shape_array) {
const PoolVector<int> &data_indices = shape_array[Mesh::ARRAY_INDEX];
const PoolVector<Vector3> &base_vertices = arrays_base[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &shape_vertices = shape_array[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &base_normals = arrays_base[Mesh::ARRAY_NORMAL];
const PoolVector<Vector3> &shape_normals = shape_array[Mesh::ARRAY_NORMAL];
const PoolVector<Vector2> &uvs1 = shape_array[Mesh::ARRAY_TEX_UV];
const PoolVector<Vector2> &uvs2 = shape_array[Mesh::ARRAY_TEX_UV2];
int i;
indices = data_indices;
vertices.resize(base_vertices.size());
normals.resize(base_vertices.size());
this->uvs1.resize(base_vertices.size());
this->uvs2.resize(base_vertices.size());
PoolVector<Vector3>::Write vertices_write = this->vertices.write();
PoolVector<Vector3>::Write normals_write = this->normals.write();
PoolVector<Vector2>::Write uvs1_w = this->uvs1.write();
PoolVector<Vector2>::Write uvs2_w = this->uvs2.write();
for (i = 0; i < base_vertices.size(); i++) {
vertices_write[i] = shape_vertices[i] - base_vertices[i];
normals_write[i] = shape_normals[i] - base_normals[i];
uvs1_w[i] = uvs1.read()[i];
uvs2_w[i] = uvs2.read()[i];
}
printf("create done\n");
}
void TriangleSet::create_from_array_difference(const Array &arrays_base, int uv_index1, const Array &arrays_shape, int uv_index2) {
Vector<int> missing_vertices;
const PoolVector<Vector3> &base_vertices = arrays_base[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &shape_vertices = arrays_shape[Mesh::ARRAY_VERTEX];
const PoolVector<Vector3> &base_normals = arrays_base[Mesh::ARRAY_NORMAL];
const PoolVector<Vector3> &shape_normals = arrays_shape[Mesh::ARRAY_NORMAL];
const PoolVector<int> &shape_index = arrays_shape[Mesh::ARRAY_INDEX];
switch (uv_index1) {
case 0:
uv_index1 = Mesh::ARRAY_TEX_UV;
break;
case 1:
uv_index1 = Mesh::ARRAY_TEX_UV2;
break;
default:
uv_index1 = Mesh::ARRAY_TEX_UV;
}
switch (uv_index2) {
case 0:
uv_index2 = Mesh::ARRAY_TEX_UV;
break;
case 1:
uv_index2 = Mesh::ARRAY_TEX_UV2;
break;
default:
uv_index2 = Mesh::ARRAY_TEX_UV;
}
const PoolVector<Vector2> &base_uvs = arrays_base[uv_index1];
const PoolVector<Vector2> &shape_uvs = arrays_shape[uv_index1];
indices = arrays_base[Mesh::ARRAY_INDEX];
this->vertices.resize(base_vertices.size());
this->uvs1 = arrays_base[Mesh::ARRAY_TEX_UV];
this->uvs2 = arrays_base[Mesh::ARRAY_TEX_UV2];
normals.resize(base_vertices.size());
const float okdist = 0.001f;
int i, j;
PoolVector<Vector3>::Write vertices_write = this->vertices.write();
PoolVector<Vector3>::Write normals_write = this->normals.write();
for (i = 0; i < base_vertices.size(); i++) {
bool ok = false;
for (j = 0; j < shape_vertices.size(); j++) {
if (base_uvs[i].distance_squared_to(shape_uvs[j]) < okdist * okdist) {
vertices_write[i] = shape_vertices[j] - base_vertices[i];
normals_write[i] = shape_normals[j] - base_normals[i];
ok = true;
break;
}
}
if (!ok)
missing_vertices.push_back(i);
}
for (i = 0; i < missing_vertices.size(); i++) {
int base_index = missing_vertices[i];
Vector2 pt2 = base_uvs[base_index];
Vector3 pt = Vector3(pt2.x, pt2.y, 0.0f);
for (j = 0; j < shape_index.size(); j += 3) {
Vector3 p1 = Vector3(shape_uvs[shape_index[j + 0]].x,
shape_uvs[shape_index[j + 0]].y,
0.0f);
Vector3 p2 = Vector3(shape_uvs[shape_index[j + 1]].x,
shape_uvs[shape_index[j + 1]].y,
0.0f);
Vector3 p3 = Vector3(shape_uvs[shape_index[j + 2]].x,
shape_uvs[shape_index[j + 2]].y,
0.0f);
Vector3 b = get_baricentric(pt, p1, p2, p3);
if (b.x < 0 || b.y < 0 || b.z < 0)
continue;
Vector3 newpt = shape_vertices[shape_index[j + 0]] * b.x + shape_vertices[shape_index[j + 1]] * b.y + shape_vertices[shape_index[j + 2]] * b.z;
Vector3 newn = shape_normals[shape_index[j + 0]] * b.x + shape_normals[shape_index[j + 1]] * b.y + shape_normals[shape_index[j + 2]] * b.z;
vertices_write[base_index] = newpt - base_vertices[base_index];
normals_write[base_index] = newn - base_normals[base_index];
}
}
vertices_write.release();
normals_write.release();
}
void TriangleSet::draw(Ref<Image> vimage, Ref<Image> nimage, int uv_index) {
int i, j;
int width = vimage->get_width();
int height = vimage->get_height();
int nwidth = nimage->get_width();
int nheight = nimage->get_height();
minx = width;
miny = height;
maxx = -1;
minx = -1;
Vector2 mulv(width, height);
Vector2 muln(nwidth, nheight);
normalize_deltas();
vimage->lock();
nimage->lock();
PoolVector<Vector2>::Read uvs_r;
PoolVector<int>::Read indices_r = indices.read();
PoolVector<Vector3>::Read vertices_r = vertices.read();
PoolVector<Vector3>::Read normals_r = normals.read();
switch (uv_index) {
case 0:
uvs_r = uvs1.read();
break;
case 1:
uvs_r = uvs2.read();
break;
}
for (i = 0; i < vimage->get_height(); i++)
for (j = 0; j < vimage->get_width(); j++)
vimage->set_pixel(j, i, Color(0.5f, 0.5f, 0.5f));
for (i = 0; i < nimage->get_height(); i++)
for (j = 0; j < nimage->get_width(); j++)
nimage->set_pixel(j, i, Color(0.5f, 0.5f, 0.5f));
for (i = 0; i < indices.size(); i += 3) {
const Vector2 &vp1 = uvs_r[indices_r[i + 0]];
const Vector2 &vp2 = uvs_r[indices_r[i + 1]];
const Vector2 &vp3 = uvs_r[indices_r[i + 2]];
const float eps = 0.001f;
Vector2 center = (vp1 + vp2 + vp3) / 3.0;
Vector2 c1 = (vp1 - center).normalized() * 8.0;
Vector2 c2 = (vp2 - center).normalized() * 8.0;
Vector2 c3 = (vp3 - center).normalized() * 8.0;
const Vector3 &vc1 = vertices_r[indices_r[i + 0]];
const Vector3 &vc2 = vertices_r[indices_r[i + 1]];
const Vector3 &vc3 = vertices_r[indices_r[i + 2]];
if (vc1.length_squared() + vc2.length_squared() + vc3.length_squared() > eps * eps) {
update_bounds(vp1.x * mulv.x, vp1.y * mulv.y);
update_bounds(vp2.x * mulv.x, vp2.y * mulv.y);
update_bounds(vp3.x * mulv.x, vp3.y * mulv.y);
}
const Vector3 &nc1 = normals_r[indices_r[i + 0]];
const Vector3 &nc2 = normals_r[indices_r[i + 1]];
const Vector3 &nc3 = normals_r[indices_r[i + 2]];
float v1[] = { vp1.x * mulv.x + c1.x, vp1.y * mulv.y + c1.y, vc1.x, vc1.y, vc1.z };
float v2[] = { vp2.x * mulv.x + c2.x, vp2.y * mulv.y + c2.y, vc2.x, vc2.y, vc2.z };
float v3[] = { vp3.x * mulv.x + c3.x, vp3.y * mulv.y + c3.x, vc3.x, vc3.y, vc3.z };
float n1[] = { vp1.x * muln.x + c1.x, vp1.y * muln.y + c1.y, nc1.x, nc1.y, nc1.z };
float n2[] = { vp2.x * muln.x + c2.x, vp2.y * muln.y + c2.y, nc2.x, nc2.y, nc2.z };
float n3[] = { vp3.x * muln.x + c3.x, vp3.y * muln.y + c3.y, nc3.x, nc3.y, nc3.z };
draw_triangle(vimage.ptr(), v1, v2, v3);
draw_triangle(nimage.ptr(), n1, n2, n3);
}
for (i = 0; i < indices.size(); i += 3) {
const Vector2 &vp1 = uvs_r[indices_r[i + 0]];
const Vector2 &vp2 = uvs_r[indices_r[i + 1]];
const Vector2 &vp3 = uvs_r[indices_r[i + 2]];
const Vector3 &vc1 = vertices_r[indices_r[i + 0]];
const Vector3 &vc2 = vertices_r[indices_r[i + 1]];
const Vector3 &vc3 = vertices_r[indices_r[i + 2]];
const Vector3 &nc1 = normals_r[indices_r[i + 0]];
const Vector3 &nc2 = normals_r[indices_r[i + 1]];
const Vector3 &nc3 = normals_r[indices_r[i + 2]];
float v1[] = { vp1.x * mulv.x, vp1.y * mulv.y, vc1.x, vc1.y, vc1.z };
float v2[] = { vp2.x * mulv.x, vp2.y * mulv.y, vc2.x, vc2.y, vc2.z };
float v3[] = { vp3.x * mulv.x, vp3.y * mulv.y, vc3.x, vc3.y, vc3.z };
float n1[] = { vp1.x * muln.x, vp1.y * muln.y, nc1.x, nc1.y, nc1.z };
float n2[] = { vp2.x * muln.x, vp2.y * muln.y, nc2.x, nc2.y, nc2.z };
float n3[] = { vp3.x * muln.x, vp3.y * muln.y, nc3.x, nc3.y, nc3.z };
draw_triangle(vimage.ptr(), v1, v2, v3);
draw_triangle(nimage.ptr(), n1, n2, n3);
}
if (maxx >= width)
maxx = width - 1;
if (maxy >= height)
maxy = height - 1;
if (minx < 0)
minx = 0;
if (miny < 0)
miny = 0;
vimage->unlock();
nimage->unlock();
}
void TriangleSet::normalize_deltas() {
int i, j;
minp[0] = 100.0f;
minp[1] = 100.0f;
minp[2] = 100.0f;
maxp[0] = -100.0f;
maxp[1] = -100.0f;
maxp[2] = -100.0f;
minn[0] = 100.0f;
minn[1] = 100.0f;
minn[2] = 100.0f;
maxn[0] = -100.0f;
maxn[1] = -100.0f;
maxn[2] = -100.0f;
for (i = 0; i < vertices.size(); i++) {
for (j = 0; j < 3; j++) {
if (minp[j] > vertices[i][j])
minp[j] = vertices[i][j];
if (maxp[j] < vertices[i][j])
maxp[j] = vertices[i][j];
if (minn[j] > normals[i][j])
minn[j] = normals[i][j];
if (maxn[j] < normals[i][j])
maxn[j] = normals[i][j];
}
}
for (i = 0; i < 3; i++) {
cd[i] = maxp[i] - minp[i];
cdn[i] = maxn[i] - minn[i];
}
PoolVector<Vector3>::Write vertices_w = vertices.write();
PoolVector<Vector3>::Write normals_w = normals.write();
for (i = 0; i < vertices.size(); i++)
for (j = 0; j < 3; j++) {
if (cd[j] == 0.0f)
vertices_w[i][j] = 0.0f;
else
vertices_w[i][j] = (vertices_w[i][j] - minp[j]) / cd[j];
if (cdn[j] == 0.0f)
normals_w[i][j] = 0.0f;
else
normals_w[i][j] = (normals_w[i][j] - minn[j]) / cdn[j];
}
}
void TriangleSet::save(Ref<_File> fd, const String &shape_name, Ref<Image> vimage, Ref<Image> nimage) {
int csize;
fd->store_pascal_string(shape_name);
fd->store_float(minp[0]);
fd->store_float(minp[1]);
fd->store_float(minp[2]);
fd->store_float(maxp[0]);
fd->store_float(maxp[1]);
fd->store_float(maxp[2]);
fd->store_32(vimage->get_width());
fd->store_32(vimage->get_height());
fd->store_32(vimage->get_format());
PoolVector<uint8_t> imgbuf = vimage->get_data();
fd->store_32(imgbuf.size());
PoolVector<uint8_t> imgbuf_comp;
imgbuf_comp.resize(imgbuf.size());
csize = Compression::compress(imgbuf_comp.write().ptr(),
imgbuf.read().ptr(), imgbuf.size(),
Compression::MODE_FASTLZ);
imgbuf_comp.resize(csize);
fd->store_32(csize);
fd->store_buffer(imgbuf_comp);
fd->store_float(minn[0]);
fd->store_float(minn[1]);
fd->store_float(minn[2]);
fd->store_float(maxn[0]);
fd->store_float(maxn[1]);
fd->store_float(maxn[2]);
fd->store_32(nimage->get_width());
fd->store_32(nimage->get_height());
fd->store_32(nimage->get_format());
PoolVector<uint8_t> imgbufn = nimage->get_data();
fd->store_32(imgbufn.size());
PoolVector<uint8_t> imgbuf_compn;
imgbuf_compn.resize(imgbufn.size());
csize = Compression::compress(imgbuf_compn.write().ptr(),
imgbufn.read().ptr(), imgbufn.size(),
Compression::MODE_DEFLATE);
imgbuf_compn.resize(csize);
fd->store_32(csize);
fd->store_buffer(imgbuf_compn);
}

257
modules/morpher/triangle.h Normal file
View File

@@ -0,0 +1,257 @@
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <core/bind/core_bind.h>
#include <core/os/file_access.h>
#include <core/reference.h>
#include <core/resource.h>
#include <scene/resources/mesh.h>
#include <cassert>
class TriangleSet : public Reference {
GDCLASS(TriangleSet, Reference)
protected:
PoolVector<Vector3> vertices;
PoolVector<Vector3> normals;
PoolVector<Vector2> uvs1;
PoolVector<Vector2> uvs2;
PoolVector<int> indices;
static void _bind_methods();
float minp[3];
float maxp[3];
float cd[3];
float minn[3];
float maxn[3];
float cdn[3];
int minx, miny, maxx, maxy;
PoolVector<uint8_t> get_data(const Ref<Image> &vimage, const Ref<Image> &nimage) {
int i, j, width = vimage->get_width(), height = vimage->get_height();
const PoolVector<uint8_t> &vdata = vimage->get_data(), &ndata = nimage->get_data();
assert(vdata.size() == ndata.size());
uint32_t *h_table = memnew_arr(uint32_t, height + 1);
h_table[0] = (uint32_t)height;
uint64_t *src_buffer = memnew_arr(uint64_t, width);
uint8_t *dst_buffer = memnew_arr(uint8_t, width * 9);
PoolVector<uint8_t> ret;
if (minx == maxx || miny == maxy)
return ret;
/* map of height offsets */
ret.resize(ret.size() + height * 4 + 4);
const uint32_t *pxdata = (const uint32_t *)vdata.read().ptr();
const uint32_t *npxdata = (const uint32_t *)vdata.read().ptr();
int out_count = ret.size();
for (i = miny; i < maxy + 1; i++) {
h_table[i + 1] = out_count;
for (j = minx; j < maxx + 1; j++) {
uint64_t data_l = (pxdata[i * width + j] & 0xFFFFFFU);
uint64_t data_h = (npxdata[i * width + j] & 0xFFFFFFU);
src_buffer[j] = (data_l | (data_h << 24)) & 0xFFFFFFFFFFFFULL;
}
uint8_t count = 1;
uint64_t cur = src_buffer[0];
int dst_count = 0;
for (j = minx + 1; j < maxx + 1; j++) {
if (cur == src_buffer[j] && count < 255 && j < maxx)
count++;
else {
int k;
for (k = 0; k < 6; k++) {
dst_buffer[dst_count++] = (uint8_t)(cur & 0xff);
cur >>= 8;
}
dst_buffer[dst_count++] = count;
cur = src_buffer[j];
count = 1;
}
}
if (ret.size() < out_count + dst_count)
ret.resize(out_count + dst_count * 2);
uint8_t *dptr = ret.write().ptr();
memcpy(&dptr[out_count], dst_buffer, dst_count);
out_count += dst_count;
}
uint32_t *table_p = (uint32_t *)ret.read().ptr();
memcpy(table_p, h_table, (height + 1) * 4);
return ret;
}
static inline void draw_hline(Image *image, const float *v1, const float *v2) {
if (v1[0] < 0 && v2[0] < 0)
return;
if (v1[0] >= v2[0])
return;
if (v1[0] >= image->get_width())
return;
float l = (v2[0] - v1[0]);
Color c;
for (int i = MAX(0, (int)v1[0] - 1); i <= MIN(image->get_width() - 1, (int)v2[0] + 1); i++) {
float t = ((float)i - v1[0] + 1) / (l + 2.0f);
t = CLAMP(t, 0.0f, 1.0f);
c.r = Math::lerp(v1[2], v2[2], t);
c.g = Math::lerp(v1[3], v2[3], t);
c.b = Math::lerp(v1[4], v2[4], t);
image->set_pixel(i, v1[1], c);
}
}
static inline void flat_bottom_triangle(Image *image,
const float *v1, const float *v2, const float *v3) {
if ((v2[1] - v1[1]) < 1.0)
return;
double bdiv = (v2[1] - v1[1]);
for (int scanlineY = v1[1]; scanlineY <= v2[1]; scanlineY++) {
float t = ((double)((double)scanlineY - v1[1])) / bdiv;
t = CLAMP(t, 0.0f, 1.0f);
if (scanlineY < 0 || scanlineY >= image->get_height())
continue;
float cx1[5], cx2[5];
cx1[0] = Math::lerp(v1[0], v2[0], t);
cx1[1] = scanlineY;
cx1[2] = Math::lerp(v1[2], v2[2], t);
cx1[3] = Math::lerp(v1[3], v2[3], t);
cx1[4] = Math::lerp(v1[4], v2[4], t);
cx2[0] = Math::lerp(v1[0], v3[0], t);
cx2[1] = scanlineY;
cx2[2] = Math::lerp(v1[2], v3[2], t);
cx2[3] = Math::lerp(v1[3], v3[3], t);
cx2[4] = Math::lerp(v1[4], v3[4], t);
draw_hline(image, cx1, cx2);
}
}
static inline void flat_top_triangle(Image *image,
const float *v1, const float *v2, const float *v3) {
if ((v3[1] - v1[1]) < 1.0)
return;
double bdiv = (v3[1] - v1[1]);
for (int scanlineY = v3[1]; scanlineY > v1[1] - 1; scanlineY--) {
float t = (double)(v3[1] - (double)scanlineY) / bdiv;
t = CLAMP(t, 0.0f, 1.0f);
if (scanlineY < 0 || scanlineY >= image->get_height())
continue;
float cx1[5], cx2[5];
cx1[0] = Math::lerp(v3[0], v1[0], t);
cx1[1] = scanlineY;
cx1[2] = Math::lerp(v3[2], v1[2], t);
cx1[3] = Math::lerp(v3[3], v1[3], t);
cx1[4] = Math::lerp(v3[4], v1[4], t);
cx2[0] = Math::lerp(v3[0], v2[0], t);
cx2[1] = scanlineY;
cx2[2] = Math::lerp(v3[2], v2[2], t);
cx2[3] = Math::lerp(v3[3], v2[3], t);
cx2[4] = Math::lerp(v3[4], v2[4], t);
draw_hline(image, cx1, cx2);
}
}
inline float distance_squared(const float *v1, const float *v2) {
return Vector2(v1[0], v1[1]).distance_squared_to(Vector2(v2[0], v2[1]));
}
inline void draw_triangle(Image *image,
const float *v1,
const float *v2,
const float *v3) {
if (v1[1] == v2[1] && v1[1] == v3[1])
return;
float d12 = distance_squared(v1, v2);
float d13 = distance_squared(v1, v3);
const float *points[] = { v1, v2, v3 };
int i, j;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) {
if (i == j)
continue;
if (points[i][1] < points[j][1])
SWAP(points[i], points[j]);
}
if (points[0][1] == points[1][1]) {
if (points[2][0] - points[0][0] < points[2][0] - points[1][0])
flat_top_triangle(image, points[1], points[0], points[2]);
else
flat_top_triangle(image, points[0], points[1], points[2]);
} else if (points[1][1] == points[2][1]) {
if (points[1][0] - points[0][0] > points[2][0] - points[0][0])
flat_bottom_triangle(image, points[0], points[2], points[1]);
else
flat_bottom_triangle(image, points[0], points[1], points[2]);
} else {
float y01 = points[1][1] - points[0][1];
float y02 = points[2][1] - points[0][1];
float p4[5];
// Vector2 p4(points[0][0] + (y01 / y02) * (points[2][0] - points[0][0]), points[1][1]);
float t = y01 / y02;
assert(t <= 1.0f && t >= 0.0f);
p4[0] = Math::lerp(points[0][0], points[2][0], t);
p4[1] = points[1][1];
p4[2] = Math::lerp(points[0][2], points[2][2], t);
p4[3] = Math::lerp(points[0][3], points[2][3], t);
p4[4] = Math::lerp(points[0][4], points[2][4], t);
if (points[1][0] - points[0][0] > p4[0] - points[0][0])
flat_bottom_triangle(image, points[0], p4, points[1]);
else
flat_bottom_triangle(image, points[0], points[1], p4);
if (points[2][0] - points[1][0] < points[2][0] - p4[0])
flat_top_triangle(image, p4, points[1], points[2]);
else
flat_top_triangle(image, points[1], p4, points[2]);
}
}
public:
TriangleSet() {
}
~TriangleSet() {
}
void normalize_deltas();
static inline float get_area(Vector3 p1, Vector3 p2, Vector3 p3) {
return (p2 - p1).cross(p3 - p1).length() / 2.0;
}
static inline Vector3 get_baricentric(Vector3 pt, Vector3 p1, Vector3 p2, Vector3 p3) {
Vector3 p;
float area = get_area(p1, p2, p3);
if (area == 0.0) {
printf("bad triangle %ls %ls %ls\n", String(p1).c_str(), String(p2).c_str(), String(p3).c_str());
return Vector3(-1, -1, -1);
}
Vector3 n = (p2 - p1).cross(p3 - p1);
float d = n.dot(p1);
float denom = n.dot(n);
if (denom != 0.0) {
float t = (-n.dot(pt) + d) / denom;
p = pt + n * t;
} else
p = pt;
float c = get_area(p2, p3, p);
float u = c / area;
float e = get_area(p3, p1, p);
float v = e / area;
float w = 1.0f - u - v;
if (!(u < 0 || v < 0 || w < 0))
printf("%f %f %f %f %f %f %ls %f %f %f\n", u, v, w, d, denom, n.length(), String(p).c_str(), c, e, area);
return Vector3(u, v, w);
}
/* Same topology */
void create_from_array_shape(const Array &arrays_base, const Array &arrays_shape);
/* Close but not the same topology */
void create_from_array_difference(const Array &arrays_base, int uv_index1, const Array &arrays_shape, int uv_index2);
inline void update_bounds(float x, float y) {
if (minx > (int)x)
minx = (int)x;
if (miny > (int)y)
miny = (int)y;
if (maxx < ceil(x))
maxx = (int)(ceil(x));
if (maxy < ceil(y))
maxy = (int)(ceil(y));
}
void draw(Ref<Image> vimage, Ref<Image> nimage, int uv_index);
Vector3 get_min() {
return Vector3(minp[0], minp[1], minp[2]);
}
Vector3 get_max() {
return Vector3(maxp[0], maxp[1], maxp[2]);
}
Vector3 get_min_normal() {
return Vector3(minn[0], minn[1], minn[2]);
}
Vector3 get_max_normal() {
return Vector3(maxn[0], maxn[1], maxn[2]);
}
void save(Ref<_File> fd, const String &shape_name, Ref<Image> vimage, Ref<Image> nimage);
};
#endif

Binary file not shown.