Qugurun

  • 8 вер 2024 р.
  • Приєднався 3 трав 2024 р.

    Thanks for the investigation. I'll be adding both of your suggestions to our build this week.

    @Mario

    Hello I have completed the changes needed to load spine sprites from file. The path error was only one issue there is also the problem that the Resource loader for the images doesn't load images from path only from resources. So Image::load was needed.

    This does however introduce a new concern that each load will import the image from the drive as there is no access to the Resource Cache. I overcame this in GDScript for now. It would be better for this to be handled in the loader by at least keeping a WeakRef to any images and reuse them should they already exist.

    Is there any way i could ask for the c++ changes to go into the base code. I am happy to meet your development standards just let me know what I need to improve or change. It would just be simpler for me in the long run if I don't have to make this modification each time there is a new version.

    Changed to SpinAtlasResource.cpp

    GodotSpineTextureLoader::fix_path

    This was changed to resolve the error I mentioned earlier.

    	static String fix_path(const String &path) {
    		if (path.size() > 5 && path[4] == '/' && path[5] == '/') return path;
    		const String prefix = "res:/";
    		auto i = path.find(prefix);
    		if (i < 0) return path;
    		auto sub_str_pos = i + prefix.size() - 1;
    		auto res = path.substr(sub_str_pos);
    
    		if (!EMPTY(res)) {
    			if (res[0] != '/') {
    				return prefix + "/" + res;
    			} else {
    				return prefix + res;
    			}
    		}
    		return path;
    	}

    GodotSpineTextureLoader::load

    This had to change to facilitate loading the texture from disk. I only implemented the load for version 4.

    	void load(spine::AtlasPage &page, const spine::String &path) override {
    		Error error = OK;
    		auto fixed_path = fix_path(String(path.buffer()));
    
    #if VERSION_MAJOR > 3
    		const String prefix = "res:/";
    		auto i = fixed_path.find(prefix);
    		Ref<Texture2D> texture;
    		if (i < 0) {
    			Ref<Image> image=Image::load_from_file(fixed_path);
    			texture = ImageTexture::create_from_image(image);
    		} else {
    			texture = ResourceLoader::load(fixed_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error);
    		}
    #else
    		Ref<Texture> texture = ResourceLoader::load(fixed_path, "", false, &error);
    #endif
    
    		if (error != OK || !texture.is_valid()) {
    			ERR_PRINT(vformat("Can't load texture: \"%s\"", String(path.buffer())));
    			auto renderer_object = memnew(SpineRendererObject);
    			renderer_object->texture = Ref<Texture>(nullptr);
    			renderer_object->normal_map = Ref<Texture>(nullptr);
    			page.texture = (void *) renderer_object;
    			return;
    		}
    
    		textures->append(texture);
    		auto renderer_object = memnew(SpineRendererObject);
    		renderer_object->texture = texture;
    		renderer_object->normal_map = Ref<Texture>(nullptr);
    
    		String new_path = vformat("%s/%s_%s", fixed_path.get_base_dir(), normal_map_prefix, fixed_path.get_file());
    		if (ResourceLoader::exists(new_path)) {
    			Ref<Texture> normal_map = ResourceLoader::load(new_path);
    			normal_maps->append(normal_map);
    			renderer_object->normal_map = normal_map;
    		}

    SpineSkeletonFileResource::_bind_methods

    Another gap was that load_from_file for skeleton was not bound to GDScript so it could not be called this is necessary to allow loading from disk.

    
    void SpineSkeletonFileResource::_bind_methods() {
    	ClassDB::bind_method(D_METHOD("load_from_file", "path"), &SpineSkeletonFileResource::load_from_file);
    	ADD_SIGNAL(MethodInfo("skeleton_file_changed"));
    }

    New GDScript to load from disk

    To overcome the issues of loading multiple atlases and skeletons into the system I built a helper class that will cache a WeakRef to both. This means if they are still in the scene then they will not be loaded again but they should not stay in ram any longer then that.

    extends Node
    class_name SpineSpriteFileLoader
    
    static var known_atlasses={}
    static var known_skeletons={}
    
    static func _load_spine_atlas(atlas_path:String)->SpineAtlasResource:
    	var atlas_res:SpineAtlasResource=null
    	if known_atlasses.has(atlas_path) and known_atlasses[atlas_path].get_ref() != null:
    		atlas_res=known_atlasses[atlas_path].get_ref()
    	else:
    		atlas_res=SpineAtlasResource.new()
    		var error:Error=atlas_res.load_from_atlas_file(atlas_path)
    		if error != OK:
    			printerr("Failure loading atlas@"+atlas_path,error)
    		known_atlasses[atlas_path]=weakref(atlas_res)
    	return atlas_res
    
    
    static func _load_spine_skeleton(skeleton_json_path:String)->SpineSkeletonFileResource:
    	var skeleton_file_res:SpineSkeletonFileResource=null
    	if known_skeletons.has(skeleton_json_path) and known_skeletons[skeleton_json_path].get_ref() != null:
    		skeleton_file_res=known_skeletons[skeleton_json_path].get_ref()
    	else:
    		skeleton_file_res=SpineSkeletonFileResource.new()
    		var error:Error=skeleton_file_res.load_from_file(skeleton_json_path)
    		if error != OK:
    			printerr("Failure loading json-spine! ",error)
    		known_skeletons[skeleton_json_path]=weakref(skeleton_file_res)
    	return skeleton_file_res
    
    
    static func load_spine_sprite(atlas_path:String,skeletonm_json_path:String)->SpineSprite:
    	var atlas_res:SpineAtlasResource=_load_spine_atlas(atlas_path)
    	var skeleton_file_res:SpineSkeletonFileResource=_load_spine_skeleton(skeletonm_json_path)
    
    	var skeleton_data_res:SpineSkeletonDataResource=SpineSkeletonDataResource.new()
    	skeleton_data_res.skeleton_file_res=skeleton_file_res
    	skeleton_data_res.atlas_res=atlas_res
    	
    	var sprite:SpineSprite=SpineSprite.new()
    	sprite.skeleton_data_res=skeleton_data_res
    	return sprite

    Best Regards
    Travis Bulford