Skeletons can now choose between using local or world coords for processing, fixes #26468

This commit is contained in:
Juan Linietsky 2019-03-03 12:23:03 -03:00
parent 9db96d9f81
commit 2f32a75d2e
18 changed files with 119 additions and 47 deletions

View file

@ -461,6 +461,7 @@ public:
RID skeleton_create() { return RID(); }
void skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) {}
void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {}
void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {}
int skeleton_get_bone_count(RID p_skeleton) const { return 0; }
void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) {}
Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { return Transform(); }

View file

@ -2487,6 +2487,12 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
if (skeleton) {
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_IN_WORLD_COORDS, skeleton->use_world_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TRANSFORM, skeleton->world_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TRANSFORM_INVERSE, skeleton->world_transform_inverse);
}
if (use_lightmap_capture) { //this is per instance, must be set always if present
glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false);

View file

@ -429,7 +429,6 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_
if (!image.is_null()) {
image = image->duplicate();
print_line("decompressing...");
image->decompress();
ERR_FAIL_COND_V(image->is_compressed(), image);
switch (image->get_format()) {
@ -2380,7 +2379,7 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
surface->total_data_size += surface->array_byte_size + surface->index_array_byte_size;
for (int i = 0; i < surface->skeleton_bone_used.size(); i++) {
surface->skeleton_bone_used.write[i] = surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0;
surface->skeleton_bone_used.write[i] = !(surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0);
}
for (int i = 0; i < VS::ARRAY_MAX; i++) {
@ -2688,7 +2687,7 @@ AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
mtx.basis[0].x = texture[base_ofs + 0];
mtx.basis[0].y = texture[base_ofs + 1];
mtx.origin.x = texture[base_ofs + 3];
base_ofs += 256 * 4;
base_ofs += 4;
mtx.basis[1].x = texture[base_ofs + 0];
mtx.basis[1].y = texture[base_ofs + 1];
mtx.origin.y = texture[base_ofs + 3];
@ -2716,12 +2715,12 @@ AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
mtx.basis[0].y = texture[base_ofs + 1];
mtx.basis[0].z = texture[base_ofs + 2];
mtx.origin.x = texture[base_ofs + 3];
base_ofs += 256 * 4;
base_ofs += 4;
mtx.basis[1].x = texture[base_ofs + 0];
mtx.basis[1].y = texture[base_ofs + 1];
mtx.basis[1].z = texture[base_ofs + 2];
mtx.origin.y = texture[base_ofs + 3];
base_ofs += 256 * 4;
base_ofs += 4;
mtx.basis[2].x = texture[base_ofs + 0];
mtx.basis[2].y = texture[base_ofs + 1];
mtx.basis[2].z = texture[base_ofs + 2];
@ -3599,6 +3598,23 @@ void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, cons
skeleton->base_transform_2d = p_base_transform;
}
void RasterizerStorageGLES2::skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_COND(skeleton->use_2d);
skeleton->world_transform = p_world_transform;
skeleton->use_world_transform = p_enable;
if (p_enable) {
skeleton->world_transform_inverse = skeleton->world_transform.affine_inverse();
}
if (!skeleton->update_list.in_list()) {
skeleton_update_list.add(&skeleton->update_list);
}
}
void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) {
glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);

View file

@ -864,12 +864,16 @@ public:
Set<RasterizerScene::InstanceBase *> instances;
Transform2D base_transform_2d;
Transform world_transform;
Transform world_transform_inverse;
bool use_world_transform;
Skeleton() :
use_2d(false),
size(0),
tex_id(0),
update_list(this) {
update_list(this),
use_world_transform(false) {
}
};
@ -887,6 +891,7 @@ public:
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform);
void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);

View file

@ -59,6 +59,10 @@ uniform ivec2 skeleton_texture_size;
#endif
uniform highp mat4 skeleton_transform;
uniform highp mat4 skeleton_transform_inverse;
uniform bool skeleton_in_world_coords;
#endif
#ifdef USE_INSTANCING
@ -404,7 +408,14 @@ void main() {
#endif
world_matrix = bone_transform * world_matrix;
if (skeleton_in_world_coords) {
bone_transform = skeleton_transform * (bone_transform * skeleton_transform_inverse);
world_matrix = bone_transform * world_matrix;
} else {
world_matrix = world_matrix * bone_transform;
}
#endif
#ifdef USE_INSTANCING

View file

@ -2044,7 +2044,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
int current_blend_mode = -1;
int prev_shading = -1;
RID prev_skeleton;
RasterizerStorageGLES3::Skeleton *prev_skeleton = NULL;
state.scene_shader.set_conditional(SceneShaderGLES3::SHADELESS, true); //by default unshaded (easier to set)
@ -2058,7 +2058,10 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
RenderList::Element *e = p_elements[i];
RasterizerStorageGLES3::Material *material = e->material;
RID skeleton = e->instance->skeleton;
RasterizerStorageGLES3::Skeleton *skeleton = NULL;
if (e->instance->skeleton.is_valid()) {
skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
}
bool rebind = first;
@ -2205,15 +2208,14 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
}
if (prev_skeleton != skeleton) {
if (prev_skeleton.is_valid() != skeleton.is_valid()) {
state.scene_shader.set_conditional(SceneShaderGLES3::USE_SKELETON, skeleton.is_valid());
if ((prev_skeleton == NULL) != (skeleton == NULL)) {
state.scene_shader.set_conditional(SceneShaderGLES3::USE_SKELETON, skeleton != NULL);
rebind = true;
}
if (skeleton.is_valid()) {
RasterizerStorageGLES3::Skeleton *sk = storage->skeleton_owner.getornull(skeleton);
if (skeleton) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1);
glBindTexture(GL_TEXTURE_2D, sk->texture);
glBindTexture(GL_TEXTURE_2D, skeleton->texture);
}
}
@ -2240,6 +2242,11 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
_set_cull(e->sort_key & RenderList::SORT_KEY_MIRROR_FLAG, e->sort_key & RenderList::SORT_KEY_CULL_DISABLED_FLAG, p_reverse_cull);
if (skeleton) {
state.scene_shader.set_uniform(SceneShaderGLES3::SKELETON_TRANSFORM, skeleton->world_transform);
state.scene_shader.set_uniform(SceneShaderGLES3::SKELETON_IN_WORLD_COORDS, skeleton->use_world_transform);
}
state.scene_shader.set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, e->instance->transform);
_render_geometry(e);

View file

@ -5135,6 +5135,20 @@ void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, cons
skeleton->base_transform_2d = p_base_transform;
}
void RasterizerStorageGLES3::skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
ERR_FAIL_COND(skeleton->use_2d);
skeleton->world_transform = p_world_transform;
skeleton->use_world_transform = p_enable;
if (!skeleton->update_list.in_list()) {
skeleton_update_list.add(&skeleton->update_list);
}
}
void RasterizerStorageGLES3::update_dirty_skeletons() {
glActiveTexture(GL_TEXTURE0);

View file

@ -890,12 +890,15 @@ public:
SelfList<Skeleton> update_list;
Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton
Transform2D base_transform_2d;
bool use_world_transform;
Transform world_transform;
Skeleton() :
use_2d(false),
size(0),
texture(0),
update_list(this) {
update_list(this),
use_world_transform(false) {
}
};
@ -913,6 +916,7 @@ public:
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform);
/* Light API */

View file

@ -302,6 +302,8 @@ out highp float dp_clip;
#ifdef USE_SKELETON
uniform highp sampler2D skeleton_texture; // texunit:-1
uniform highp mat4 skeleton_transform;
uniform bool skeleton_in_world_coords;
#endif
out highp vec4 position_interp;
@ -430,7 +432,14 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0)) *
bone_weights.w;
world_matrix = transpose(m) * world_matrix;
if (skeleton_in_world_coords) {
highp mat4 bone_matrix = skeleton_transform * (transpose(m) * inverse(skeleton_transform));
world_matrix = bone_matrix * world_matrix;
} else {
world_matrix = world_matrix * transpose(m);
}
}
#endif

View file

@ -176,7 +176,7 @@ Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) {
Skeleton *sk = memnew(Skeleton);
int bone = 0;
sk->set_use_bones_in_world_transform(true); // This improves compatibility in Collada
for (int i = 0; i < p_node->children.size(); i++) {
_populate_skeleton(sk, p_node->children[i], bone, -1);

View file

@ -2121,6 +2121,7 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_f
Vector<Skeleton *> skeletons;
for (int i = 0; i < state.skins.size(); i++) {
Skeleton *s = memnew(Skeleton);
s->set_use_bones_in_world_transform(false); //GLTF does not need this since meshes are always local
String name = state.skins[i].name;
if (name == "") {
name = _gen_unique_name(state, "Skeleton");

View file

@ -198,11 +198,7 @@ void Skeleton::_notification(int p_what) {
case NOTIFICATION_ENTER_WORLD: {
if (dirty) {
dirty = false;
_make_dirty(); // property make it dirty
}
VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
} break;
case NOTIFICATION_EXIT_WORLD: {
@ -210,21 +206,7 @@ void Skeleton::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (dirty)
break; //will be eventually updated
//if moved, just update transforms
VisualServer *vs = VisualServer::get_singleton();
const Bone *bonesptr = bones.ptr();
int len = bones.size();
Transform global_transform = get_global_transform();
Transform global_transform_inverse = global_transform.affine_inverse();
for (int i = 0; i < len; i++) {
const Bone &b = bonesptr[i];
vs->skeleton_bone_set_transform(skeleton, i, global_transform * (b.transform_final * global_transform_inverse));
}
VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
} break;
case NOTIFICATION_UPDATE_SKELETON: {
@ -257,9 +239,6 @@ void Skeleton::_notification(int p_what) {
rest_global_inverse_dirty = false;
}
Transform global_transform = get_global_transform();
Transform global_transform_inverse = global_transform.affine_inverse();
for (int i = 0; i < len; i++) {
Bone &b = bonesptr[order[i]];
@ -320,7 +299,7 @@ void Skeleton::_notification(int p_what) {
}
b.transform_final = b.pose_global * b.rest_global_inverse;
vs->skeleton_bone_set_transform(skeleton, order[i], global_transform * (b.transform_final * global_transform_inverse));
vs->skeleton_bone_set_transform(skeleton, order[i], b.transform_final);
for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) {
@ -594,10 +573,6 @@ void Skeleton::_make_dirty() {
if (dirty)
return;
if (!is_inside_tree()) {
dirty = true;
return;
}
MessageQueue::get_singleton()->push_notification(this, NOTIFICATION_UPDATE_SKELETON);
dirty = true;
}
@ -771,6 +746,16 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
#endif // _3D_DISABLED
void Skeleton::set_use_bones_in_world_transform(bool p_enable) {
use_bones_in_world_transform = p_enable;
if (is_inside_tree()) {
VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
}
}
bool Skeleton::is_using_bones_in_world_transform() const {
return use_bones_in_world_transform;
}
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@ -807,6 +792,9 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
ClassDB::bind_method(D_METHOD("set_use_bones_in_world_transform", "enable"), &Skeleton::set_use_bones_in_world_transform);
ClassDB::bind_method(D_METHOD("is_using_bones_in_world_transform"), &Skeleton::is_using_bones_in_world_transform);
#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
@ -818,6 +806,7 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones_in_world_transform"), "set_use_bones_in_world_transform", "is_using_bones_in_world_transform");
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
@ -828,6 +817,7 @@ Skeleton::Skeleton() {
process_order_dirty = true;
skeleton = VisualServer::get_singleton()->skeleton_create();
set_notify_transform(true);
use_bones_in_world_transform = false;
}
Skeleton::~Skeleton() {

View file

@ -100,6 +100,7 @@ class Skeleton : public Spatial {
void _make_dirty();
bool dirty;
bool use_bones_in_world_transform;
// bind helpers
Array _get_bound_child_nodes_to_bone(int p_bone) const {
@ -179,6 +180,9 @@ public:
void localize_rests(); // used for loaders and tools
int get_process_order(int p_idx);
void set_use_bones_in_world_transform(bool p_enable);
bool is_using_bones_in_world_transform() const;
#ifndef _3D_DISABLED
// Physical bone API

View file

@ -177,8 +177,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
node = Object::cast_to<Node>(obj);
} else {
print_line("Class is disabled for: " + itos(n.type));
print_line("name: " + String(snames[n.type]));
//print_line("Class is disabled for: " + itos(n.type));
//print_line("name: " + String(snames[n.type]));
}
if (node) {

View file

@ -355,6 +355,7 @@ public:
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) = 0;
/* Light API */

View file

@ -296,6 +296,7 @@ public:
BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &)
BIND3(skeleton_set_world_transform, RID, bool, const Transform &)
/* Light API */

View file

@ -232,6 +232,7 @@ public:
FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &)
FUNC3(skeleton_set_world_transform, RID, bool, const Transform &)
/* Light API */

View file

@ -393,6 +393,7 @@ public:
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_base_transform) = 0;
/* Light API */