Initial commit
This commit is contained in:
9
modules/morpher/SCsub
Normal file
9
modules/morpher/SCsub
Normal 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')
|
||||
BIN
modules/morpher/__pycache__/config.cpython-38.pyc
Normal file
BIN
modules/morpher/__pycache__/config.cpython-38.pyc
Normal file
Binary file not shown.
BIN
modules/morpher/__pycache__/config.cpython-39.pyc
Normal file
BIN
modules/morpher/__pycache__/config.cpython-39.pyc
Normal file
Binary file not shown.
666
modules/morpher/character_base.cpp
Normal file
666
modules/morpher/character_base.cpp
Normal 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 ¶meters) {
|
||||
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 ¶meters) {
|
||||
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 ¶meters) {
|
||||
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;
|
||||
}
|
||||
242
modules/morpher/character_base.h
Normal file
242
modules/morpher/character_base.h
Normal 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 ¶meters);
|
||||
void init_bone_group_modifier(const String &name,
|
||||
BoneGroupModifierData *bm,
|
||||
const Array ¶meters);
|
||||
Transform parse_transform(const Dictionary &xformdata);
|
||||
void create_mod(int type, const String &name, const String &gender, const Array ¶meters = 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
|
||||
BIN
modules/morpher/character_base.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/character_base.x11.opt.tools.64.o
Normal file
Binary file not shown.
10
modules/morpher/character_slot.cpp
Normal file
10
modules/morpher/character_slot.cpp
Normal 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);
|
||||
}
|
||||
37
modules/morpher/character_slot.h
Normal file
37
modules/morpher/character_slot.h
Normal 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
|
||||
BIN
modules/morpher/character_slot.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/character_slot.x11.opt.tools.64.o
Normal file
Binary file not shown.
5
modules/morpher/config.py
Normal file
5
modules/morpher/config.py
Normal file
@@ -0,0 +1,5 @@
|
||||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
63
modules/morpher/config_data.cpp
Normal file
63
modules/morpher/config_data.cpp
Normal 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;
|
||||
}
|
||||
49
modules/morpher/config_data.h
Normal file
49
modules/morpher/config_data.h
Normal 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
|
||||
BIN
modules/morpher/config_data.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/config_data.x11.opt.tools.64.o
Normal file
Binary file not shown.
126
modules/morpher/map_storage.cpp
Normal file
126
modules/morpher/map_storage.cpp
Normal 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;
|
||||
}
|
||||
63
modules/morpher/map_storage.h
Normal file
63
modules/morpher/map_storage.h
Normal 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
|
||||
BIN
modules/morpher/map_storage.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/map_storage.x11.opt.tools.64.o
Normal file
Binary file not shown.
244
modules/morpher/morpher.cpp
Normal file
244
modules/morpher/morpher.cpp
Normal 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
216
modules/morpher/morpher.h
Normal 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
|
||||
BIN
modules/morpher/morpher.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/morpher.x11.opt.tools.64.o
Normal file
Binary file not shown.
17
modules/morpher/partition.h
Normal file
17
modules/morpher/partition.h
Normal 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() {
|
||||
}
|
||||
};
|
||||
50
modules/morpher/register_types.cpp
Normal file
50
modules/morpher/register_types.cpp
Normal 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() {
|
||||
}
|
||||
32
modules/morpher/register_types.h
Normal file
32
modules/morpher/register_types.h
Normal 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();
|
||||
BIN
modules/morpher/register_types.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/register_types.x11.opt.tools.64.o
Normal file
Binary file not shown.
294
modules/morpher/triangle.cpp
Normal file
294
modules/morpher/triangle.cpp
Normal 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
257
modules/morpher/triangle.h
Normal 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
|
||||
BIN
modules/morpher/triangle.x11.opt.tools.64.o
Normal file
BIN
modules/morpher/triangle.x11.opt.tools.64.o
Normal file
Binary file not shown.
Reference in New Issue
Block a user