@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