[glfw] Add spine-cpp-lite example.

This commit is contained in:
Mario Zechner 2024-11-22 11:59:08 +01:00
parent 8d12efd9ef
commit 5e80111b87
6 changed files with 191 additions and 4 deletions

View File

@ -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<utf8 *>(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;

View File

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

View File

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

View File

@ -0,0 +1,125 @@
#include <glbinding/glbinding.h>
#include <glbinding/gl/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <iostream>
#include <spine-glfw.h>
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;
}

View File

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

View File

@ -2,6 +2,7 @@
#include <stdint.h>
#include <spine/spine.h>
#include <spine-cpp-lite.h>
/// 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);