spine-glfw Runtime Documentation
Licensing
Please see the Spine Runtimes License before integrating the Spine Runtimes into your applications.
Getting Started
spine-glfw is a C++ based runtime to load, manipulate and render Spine skeletons with GLFW and OpenGL.
spine-glfw requires GLFW 3.0+ and OpenGL 3.3+ and supports all Spine features including two-color tinting.
Installation
The spine-glfw runtime is available as a C++ API based on the generic spine-cpp runtime and also supports the lightweight spine-cpp-lite API. To integrate spine-glfw into your project:
- Create a new GLFW project. See the GLFW documentation or have a look at the example in spine-runtimes repository, which uses CMake as the build system.
- Download the Spine Runtimes source using git (
git clone https://github.com/esotericsoftware/spine-runtimes
). - Add the sources from
spine-cpp/spine-cpp/src/spine
and the filesspine-glfw/src/spine-glfw.cpp
andspine-glfw/src/spine-glfw.h
to your project. - Add the folders
spine-cpp/spine-cpp/include
andspine-glfw/src
to your header search path. - Link against GLFW, OpenGL, and optionally glbinding for modern OpenGL function loading.
In your C++ code, include the following header file to get access to the spine-glfw
API:
Note: spine-glfw requires OpenGL 3.3 Core Profile or higher. The runtime uses modern OpenGL features including vertex array objects, vertex buffer objects, and GLSL shaders.
Samples
The spine-glfw example works on Windows, Linux and Mac OS X. For a spine-cpp based example, see example/main.cpp, for a spine-cpp-lite example see example/main-cpp-lite.cpp.
Windows
- Install Visual Studio Community. Make sure you install support for C++ and CMake.
- Download the Spine Runtimes repository using git (
git clone https://github.com/esotericsoftware/spine-runtimes
) or download it as a zip via the download button above. - Open Visual Studio Community, then open
spine-glfw/
via theOpen a local folder
button in the Visual Studio Community launcher - Wait for CMake to finish, then select either
spine-glfw-example.exe
orspine-glfw-example-cpp-lite.exe
as the start-up project and start debugging.
The entire example code is contained in main.cpp.
Linux
- Install the GLFW build dependencies. On Ubuntu/Debian:
sudo apt-get install libglfw3-dev libgl1-mesa-dev
. - Download the Spine Runtimes repository using git (
git clone https://github.com/esotericsoftware/spine-runtimes
) or download it as a zip via the download button above. - Open a terminal, and
cd
into thespine-runtimes/spine-glfw
folder. - Type
mkdir build && cd build && cmake ..
to generate Make files. - Type
make -j
to compile the example. - Run the example by
./spine-glfw-example
(C++) or by./spine-glfw-example-cpp-lite
(C++ lite).
Mac OS X
- Install Xcode.
- Install Homebrew.
- Open a terminal and install CMake via
brew install cmake
. - Download the Spine Runtimes repository using git (
git clone https://github.com/esotericsoftware/spine-runtimes
) or download it as a zip via the download button above. - Open a terminal, and
cd
into thespine-runtimes/spine-glfw
folder. - Type
mkdir build && cd build && cmake -G Xcode ..
to generate Xcode project files. - Type
open spine-glfw.xcodeproj
to open the Xcode project. - Run the example
spine-glfw-example
(C++) orspine-glfw-example-cpp-lite
(C++ lite).
Using spine-glfw
The spine-glfw runtime supports playback and manipulation of animations created with Spine using GLFW and OpenGL. The spine-glfw runtime is implemented in C++ and is based on the generic spine-cpp runtime. It adds loading and rendering implementations based on OpenGL APIs.
Please consult the Spine Runtimes Guide for a detailed overview of the Spine Runtime architecture, and the spine-cpp documentation for information on the core APIs used to playback and manipulate animations created with Spine with C++.
Exporting for GLFW
Please follow the instructions in the Spine User Guide on how to
An export of the skeleton data and texture atlas of your skeleton will yield the following files:
skeleton-name.json
orskeleton-name.skel
, containing your skeleton and animation data.skeleton-name.atlas
, containing information about the texture atlas.- One or more
.png
files, each representing on page of your texture atlas containing the packed images your skeleton uses.
Note: The spine-glfw runtime does not support the screen blend mode available in the Spine editor.
Loading Spine skeletons
The spine-glfw runtime uses OpenGL for rendering skeletons. Before a skeleton can be loaded from exported files, a GLFW window and OpenGL context must be created:
if (!glfwInit()) {
// Handle error
return -1;
}
// Set OpenGL version to 3.3 Core Profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create window
GLFWwindow* window = glfwCreateWindow(800, 600, "Spine GLFW", NULL, NULL);
glfwMakeContextCurrent(window);
// Initialize OpenGL function loading (e.g., with glbinding)
glbinding::initialize(glfwGetProcAddress);
Next, the texture atlas can be loaded using the GlTextureLoader
:
spine::GlTextureLoader textureLoader;
spine::Atlas *atlas = new spine::Atlas("data/spineboy-pma.atlas", &textureLoader);
With the atlas loaded, the .json
or .skel
file can be loaded:
spine::SkeletonBinary binary(atlas);
spine::SkeletonData *skeletonData = binary.readSkeletonDataFile("data/spineboy-pro.skel");
For JSON format:
spine::SkeletonJson json(atlas);
spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json");
The spine::Atlas
and spine::SkeletonData
instances can then be used to create spine::Skeleton
instances for rendering.
Note: the loaded skeleton data and atlas can and should be shared across
spine::Skeleton
instances to reduce memory consumption and enable batched rendering of skeletons that share the same atlas data.
Renderer
The main addition of spine-glfw on top of spine-cpp is the renderer system. The renderer handles the OpenGL rendering pipeline including shaders, meshes, and textures. Unlike other runtimes that provide a drawable class, spine-glfw uses a more modular approach with separate renderer and mesh components.
You can create a renderer like this:
renderer_t *renderer = renderer_create();
renderer_set_viewport_size(renderer, windowWidth, windowHeight);
The renderer automatically creates and manages OpenGL shaders optimized for Spine skeleton rendering.
Creating and animating skeletons
With the skeleton data loaded, you can create a skeleton instance:
spine::Bone::setYDown(true);
// Create a skeleton from the data
spine::Skeleton skeleton(skeletonData);
skeleton.setPosition(400, 500);
skeleton.setScaleX(0.5f);
skeleton.setScaleY(0.5f);
For animation, create an animation state:
spine::AnimationStateData animationStateData(skeletonData);
animationStateData.setDefaultMix(0.2f);
spine::AnimationState animationState(&animationStateData);
// Set animations
animationState.setAnimation(0, "portal", true);
animationState.addAnimation(0, "run", true, 0);
Please refer to the spine-cpp documentation for more information on the APIs to manipulate skeletons and animation states.
Updating and rendering
In your main loop, update the animation state and skeleton, then render:
while (!glfwWindowShouldClose(window)) {
double currTime = glfwGetTime();
float delta = currTime - lastTime;
lastTime = currTime;
// Update animation state
animationState.update(delta);
animationState.apply(skeleton);
// Update skeleton
skeleton.update(delta);
skeleton.updateWorldTransform(spine::Physics_Update);
// Clear screen
gl::glClear(gl::GL_COLOR_BUFFER_BIT);
// Render skeleton
renderer_draw(renderer, &skeleton, true); // true for premultiplied alpha
// Present
glfwSwapBuffers(window);
glfwPollEvents();
}
Using spine-cpp-lite
spine-glfw also supports the lightweight spine-cpp-lite API for applications that need a C interface or are written in programming languages that cannot interface with C++ code directly. The key differences when using spine-cpp-lite with spine-glfw are:
Key Differences from spine-cpp:
- C API instead of C++: All functions use C-style naming (e.g.,
spine_skeleton_set_position
vsskeleton.setPosition
) - Manual file loading: You must manually load atlas and skeleton files into memory
- Callback-based texture loading: Textures are loaded via user-provided callback functions
- Skeleton drawable wrapper: Uses
spine_skeleton_drawable
which wraps skeleton and animation state - Different renderer function: Uses
renderer_draw_lite()
instead ofrenderer_draw()
Texture Loading with Callbacks
First, you need to provide texture loading callbacks that bridge spine-cpp-lite to spine-glfw's texture system:
void *load_texture(const char *path) {
return (void *)(uintptr_t)texture_load(path);
}
// Callback function to unload textures
void unload_texture(void *texture) {
texture_dispose((texture_t)(uintptr_t)texture);
}
Loading Atlas and Skeleton Data
Unlike spine-cpp which can load files directly, spine-cpp-lite requires manual file reading:
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);
// Read skeleton file into memory
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);
Creating and Manipulating Skeletons
spine-cpp-lite uses a drawable wrapper and C-style function calls:
spine_skeleton_drawable drawable = spine_skeleton_drawable_create(skeleton_data);
spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
// Set skeleton properties using C functions
spine_skeleton_set_position(skeleton, width / 2, height - 100);
spine_skeleton_set_scale(skeleton, 0.3f, 0.3f);
// Get animation state from drawable
spine_animation_state animation_state = spine_skeleton_drawable_get_animation_state(drawable);
spine_animation_state_data animation_state_data = spine_animation_state_get_data(animation_state);
spine_animation_state_data_set_default_mix(animation_state_data, 0.2f);
// Set animations using C functions
spine_animation_state_set_animation_by_name(animation_state, 0, "portal", true);
spine_animation_state_add_animation_by_name(animation_state, 0, "run", true, 0);
Updating and Rendering
The update loop uses C-style function calls and a different renderer function:
spine_animation_state_update(animation_state, delta);
spine_animation_state_apply(animation_state, skeleton);
spine_skeleton_update(skeleton, delta);
spine_skeleton_update_world_transform(skeleton, SPINE_PHYSICS_UPDATE);
// Render using the lite-specific function
renderer_draw_lite(renderer, skeleton, true);
The renderer_draw_lite()
function is specifically designed to work with spine-cpp-lite's spine_skeleton
opaque type, while renderer_draw()
works with spine-cpp's spine::Skeleton
class.
Cleanup
Cleanup for spine-cpp
When using the spine-cpp API, use C++ delete operators:
renderer_dispose(renderer);
// Dispose skeleton data and atlas (C++ API)
delete skeletonData;
delete atlas;
// Cleanup GLFW
glfwTerminate();
Cleanup for spine-cpp-lite
When using the spine-cpp-lite API, use the C-style dispose functions:
renderer_dispose(renderer);
// Dispose skeleton data and atlas (C API)
spine_skeleton_data_dispose(skeleton_data);
spine_atlas_dispose(atlas);
// Free manually allocated file data
free(atlas_bytes);
free(skeleton_bytes);
// Cleanup GLFW
glfwTerminate();
Note: freeing skeleton data and atlas instances will automatically dispose of any associated OpenGL textures through the texture loader. With spine-cpp-lite, you must also free any memory you allocated for file data using
malloc()
/read_file()
.