diff --git a/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp b/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp index 5a38ee254..cad9802ff 100644 --- a/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp +++ b/spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp @@ -206,6 +206,44 @@ spine_atlas spine_atlas_load(const utf8 *atlasData) { return (spine_atlas) result; } +class CallbackTextureLoad : public TextureLoader { + spine_texture_loader_load_func loadCb; + spine_texture_loader_unload_func unloadCb; + +public: + CallbackTextureLoad(): loadCb(nullptr), unloadCb(nullptr) {} + + void setCallbacks(spine_texture_loader_load_func load, spine_texture_loader_unload_func unload) { + loadCb = load; + unloadCb = unload; + } + + void load(AtlasPage &page, const String &path) { + page.texture = this->loadCb(path.buffer()); + } + + void unload(void *texture) { + this->unloadCb(texture); + } +}; + +CallbackTextureLoad callbackLoader; + +spine_atlas spine_atlas_load_callback(const utf8 *atlasData, const utf8 *atlasDir, spine_texture_loader_load_func load, spine_texture_loader_unload_func unload) { + if (!atlasData) return nullptr; + int32_t length = (int32_t) strlen(atlasData); + callbackLoader.setCallbacks(load, unload); + auto atlas = new (__FILE__, __LINE__) Atlas(atlasData, length, (const char *)atlasDir, &callbackLoader, true); + _spine_atlas *result = SpineExtension::calloc<_spine_atlas>(1, __FILE__, __LINE__); + result->atlas = atlas; + result->numImagePaths = (int32_t) atlas->getPages().size(); + result->imagePaths = SpineExtension::calloc(result->numImagePaths, __FILE__, __LINE__); + for (int i = 0; i < result->numImagePaths; i++) { + result->imagePaths[i] = (utf8 *) strdup(atlas->getPages()[i]->texturePath.buffer()); + } + return (spine_atlas) result; +} + int32_t spine_atlas_get_num_image_paths(spine_atlas atlas) { if (!atlas) return 0; return ((_spine_atlas *) atlas)->numImagePaths; diff --git a/spine-cpp/spine-cpp-lite/spine-cpp-lite.h b/spine-cpp/spine-cpp-lite/spine-cpp-lite.h index 804068a9f..df0ce6251 100644 --- a/spine-cpp/spine-cpp-lite/spine-cpp-lite.h +++ b/spine-cpp/spine-cpp-lite/spine-cpp-lite.h @@ -189,6 +189,10 @@ typedef enum spine_physics { typedef int32_t spine_bool; +typedef void* (*spine_texture_loader_load_func)(const char *path); + +typedef void (*spine_texture_loader_unload_func)(void *texture); + // @start: function_declarations SPINE_CPP_LITE_EXPORT int32_t spine_major_version(); @@ -210,6 +214,8 @@ SPINE_CPP_LITE_EXPORT float spine_vector_get_x(spine_vector vector); SPINE_CPP_LITE_EXPORT float spine_vector_get_y(spine_vector vector); SPINE_CPP_LITE_EXPORT spine_atlas spine_atlas_load(const utf8 *atlasData); +// @ignore +SPINE_CPP_LITE_EXPORT spine_atlas spine_atlas_load_callback(const utf8 *atlasData, const utf8 *atlasDir, spine_texture_loader_load_func load, spine_texture_loader_unload_func unload); SPINE_CPP_LITE_EXPORT int32_t spine_atlas_get_num_image_paths(spine_atlas atlas); SPINE_CPP_LITE_EXPORT utf8 *spine_atlas_get_image_path(spine_atlas atlas, int32_t index); SPINE_CPP_LITE_EXPORT spine_bool spine_atlas_is_pma(spine_atlas atlas); diff --git a/spine-glfw/CMakeLists.txt b/spine-glfw/CMakeLists.txt index 89c3898a4..9f1a99c8e 100644 --- a/spine-glfw/CMakeLists.txt +++ b/spine-glfw/CMakeLists.txt @@ -43,7 +43,7 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/../spine-cpp ${CMAKE_BINARY_DIR}/spine-cpp- # spine-glfw library add_library(spine-glfw STATIC src/spine-glfw.cpp src/spine-glfw.h src/stb_image.h) target_link_libraries(spine-glfw PRIVATE glbinding::glbinding) -target_link_libraries(spine-glfw LINK_PUBLIC spine-cpp) +target_link_libraries(spine-glfw LINK_PUBLIC spine-cpp-lite) # Example add_executable(spine-glfw-example example/main.cpp) @@ -53,6 +53,15 @@ target_link_libraries(spine-glfw-example LINK_PUBLIC spine-glfw) target_link_libraries(spine-glfw-example PRIVATE glbinding::glbinding) set_property(TARGET spine-glfw-example PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/spine-glfw") +# spine-cpp-lite Example +add_executable(spine-glfw-example-cpp-lite example/main-cpp-lite.cpp) +target_link_libraries(spine-glfw-example-cpp-lite PRIVATE glfw) +target_link_libraries(spine-glfw-example-cpp-lite PRIVATE OpenGL::GL) +target_link_libraries(spine-glfw-example-cpp-lite LINK_PUBLIC spine-glfw) +target_link_libraries(spine-glfw-example-cpp-lite PRIVATE glbinding::glbinding) +set_property(TARGET spine-glfw-example-cpp-lite PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/spine-glfw") + + # copy data to build directory add_custom_command(TARGET spine-glfw-example PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/spine-glfw/example/main-cpp-lite.cpp b/spine-glfw/example/main-cpp-lite.cpp new file mode 100644 index 000000000..ca1226ec0 --- /dev/null +++ b/spine-glfw/example/main-cpp-lite.cpp @@ -0,0 +1,125 @@ +#include +#include +#define GLFW_INCLUDE_NONE +#include +#include +#include + +using namespace spine; + +int width = 800, height = 600; + +GLFWwindow *init_glfw() { + if (!glfwInit()) { + std::cerr << "Failed to initialize GLFW" << std::endl; + return nullptr; + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + GLFWwindow *window = glfwCreateWindow(width, height, "spine-glfw", NULL, NULL); + if (!window) { + std::cerr << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return nullptr; + } + glfwMakeContextCurrent(window); + glbinding::initialize(glfwGetProcAddress); + return window; +} + +uint8_t *read_file(const char *path, int *length) { + FILE *file = fopen(path, "rb"); + if (!file) return 0; + + fseek(file, 0, SEEK_END); + *length = (int) ftell(file); + fseek(file, 0, SEEK_SET); + + uint8_t *data = (uint8_t*)malloc(*length); + fread(data, 1, *length, file); + fclose(file); + + return data; +} + +void *load_texture(const char *path) { + return (void *) (uintptr_t)texture_load(path); +} + +void unload_texture(void *texture) { + texture_dispose((texture_t)(uintptr_t)texture); +} + +int main() { + // Initialize GLFW and glbinding + GLFWwindow *window = init_glfw(); + if (!window) return -1; + + // We use a y-down coordinate system, see renderer_set_viewport_size() + Bone::setYDown(true); + + // Load the atlas and the skeleton data + int atlas_length = 0; + uint8_t *atlas_bytes = read_file("data/spineboy-pma.atlas", &atlas_length); + spine_atlas atlas = spine_atlas_load_callback((utf8 *)atlas_bytes, "data/", load_texture, unload_texture); + int skeleton_length = 0; + uint8_t *skeleton_bytes = read_file("data/spineboy-pro.skel", &skeleton_length); + spine_skeleton_data_result result = spine_skeleton_data_load_binary(atlas, skeleton_bytes, skeleton_length); + spine_skeleton_data skeleton_data = spine_skeleton_data_result_get_data(result); + + // Create a skeleton from the data, set the skeleton's position to the bottom center of + // the screen and scale it to make it smaller. + spine_skeleton_drawable drawable = spine_skeleton_drawable_create(skeleton_data); + spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable); + spine_skeleton_set_position(skeleton, width / 2, height - 100); + spine_skeleton_set_scale(skeleton, 0.3f, 0.3f); + + // Create an AnimationState to drive animations on the skeleton. Set the "portal" animation + // on track with index 0. + spine_animation_state animation_state = spine_skeleton_drawable_get_animation_state(drawable); + spine_animation_state_set_animation_by_name(animation_state, 0, "portal", true); + + // Create the renderer and set the viewport size to match the window size. This sets up a + // pixel perfect orthogonal projection for 2D rendering. + renderer_t *renderer = renderer_create(); + renderer_set_viewport_size(renderer, width, height); + + // Rendering loop + double lastTime = glfwGetTime(); + while (!glfwWindowShouldClose(window)) { + // Calculate the delta time in seconds + double currTime = glfwGetTime(); + float delta = currTime - lastTime; + lastTime = currTime; + + // Update and apply the animation state to the skeleton + spine_animation_state_update(animation_state, delta); + spine_animation_state_apply(animation_state, skeleton); + + // Update the skeleton time (used for physics) + spine_skeleton_update(skeleton, delta); + + // Calculate the new pose + spine_skeleton_update_world_transform(skeleton, SPINE_PHYSICS_UPDATE); + + // Clear the screen + gl::glClear(gl::GL_COLOR_BUFFER_BIT); + + // Render the skeleton in its current pose + renderer_draw_lite(renderer, skeleton, true); + + // Present the rendering results and poll for events + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // Dispose everything + renderer_dispose(renderer); + // delete skeletonData; + delete atlas; + + // Kill the window and GLFW + glfwTerminate(); + return 0; +} diff --git a/spine-glfw/src/spine-glfw.cpp b/spine-glfw/src/spine-glfw.cpp index e869d1e60..e46b150ae 100644 --- a/spine-glfw/src/spine-glfw.cpp +++ b/spine-glfw/src/spine-glfw.cpp @@ -8,9 +8,9 @@ using namespace gl; using namespace spine; /// Set the default extension used for memory allocations and file I/O -SpineExtension *spine::getDefaultExtension() { - return new spine::DefaultSpineExtension(); -} +// SpineExtension *spine::getDefaultExtension() { +// return new spine::DefaultSpineExtension(); +// } /// A blend mode, see https://en.esotericsoftware.com/spine-slots#Blending /// Encodes the OpenGL source and destination blend function for both premultiplied and @@ -285,6 +285,10 @@ void renderer_set_viewport_size(renderer_t *renderer, int width, int height) { shader_set_matrix4(renderer->shader, "uMatrix", matrix); } +void renderer_draw_lite(renderer_t *renderer, spine_skeleton skeleton, bool premultipliedAlpha) { + renderer_draw(renderer, (Skeleton*)skeleton, premultipliedAlpha); +} + void renderer_draw(renderer_t *renderer, Skeleton *skeleton, bool premultipliedAlpha) { shader_use(renderer->shader); shader_set_int(renderer->shader, "uTexture", 0); diff --git a/spine-glfw/src/spine-glfw.h b/spine-glfw/src/spine-glfw.h index 94c2d0b63..900b27b80 100644 --- a/spine-glfw/src/spine-glfw.h +++ b/spine-glfw/src/spine-glfw.h @@ -2,6 +2,7 @@ #include #include +#include /// A vertex of a mesh generated from a Spine skeleton struct vertex_t { @@ -86,5 +87,9 @@ void renderer_set_viewport_size(renderer_t *renderer, int width, int height); /// was constructed. void renderer_draw(renderer_t *renderer, spine::Skeleton *skeleton, bool premultipliedAlpha); +/// Draws the given skeleton. The atlas must be the atlas from which the drawable +/// was constructed. +void renderer_draw_lite(renderer_t *renderer, spine_skeleton skeleton, bool premultipliedAlpha); + /// Disposes the renderer void renderer_dispose(renderer_t *renderer); \ No newline at end of file