Ver Fonte

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

Juan Linietsky há 6 anos atrás
pai
commit
2f32a75d2e

+ 1 - 0
drivers/dummy/rasterizer_dummy.h

@@ -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(); }

+ 6 - 0
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -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);

+ 21 - 5
drivers/gles2/rasterizer_storage_gles2.cpp

@@ -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);

+ 6 - 1
drivers/gles2/rasterizer_storage_gles2.h

@@ -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);
 

+ 12 - 1
drivers/gles2/shaders/scene.glsl

@@ -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

+ 14 - 7
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -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);

+ 14 - 0
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -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);

+ 5 - 1
drivers/gles3/rasterizer_storage_gles3.h

@@ -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 */
 

+ 10 - 1
drivers/gles3/shaders/scene.glsl

@@ -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
 

+ 1 - 1
editor/import/editor_import_collada.cpp

@@ -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);

+ 1 - 0
editor/import/editor_scene_importer_gltf.cpp

@@ -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");

+ 18 - 28
scene/3d/skeleton.cpp

@@ -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() {

+ 4 - 0
scene/3d/skeleton.h

@@ -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
 

+ 2 - 2
scene/resources/packed_scene.cpp

@@ -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) {

+ 1 - 0
servers/visual/rasterizer.h

@@ -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 */
 

+ 1 - 0
servers/visual/visual_server_raster.h

@@ -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 */
 

+ 1 - 0
servers/visual/visual_server_wrap_mt.h

@@ -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 */
 

+ 1 - 0
servers/visual_server.h

@@ -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 */