[sdl] Updated to latest spine-cpp/spine-c

This commit is contained in:
Mario Zechner 2025-08-27 12:42:27 +02:00
parent 37fef98fd2
commit 927e2e8f3f
16 changed files with 618 additions and 3767 deletions

2
.gitignore vendored
View File

@ -260,3 +260,5 @@ spine-cpp/build-release-debug
spine-flutter/test/spine_flutter.dylib
spine-c/codegen/nullable.md
spine-c/.docker-built
.cache/

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
"name": "spine-c-codegen",
"type": "module",
"scripts": {
"generate": "tsx src/index.ts",
"null-analysis": "tsx src/null-analysis.ts"
"generate": "npx tsx src/index.ts",
"null-analysis": "npx tsx src/null-analysis.ts"
},
"devDependencies": {
"@types/node": "^20.0.0",

View File

@ -27,8 +27,8 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "base.h"
#include "generated/types.h"
#include "spine/Physics.h"
#include "extensions.h"
#include <spine/spine.h>
#include <spine/Version.h>
@ -327,6 +327,18 @@ spine_skeleton_drawable spine_skeleton_drawable_create(spine_skeleton_data skele
return (spine_skeleton_drawable) drawable;
}
void spine_skeleton_drawable_update(spine_skeleton_drawable drawable, float delta) {
if (!drawable) return;
_spine_skeleton_drawable *_drawable = (_spine_skeleton_drawable *) drawable;
Skeleton *skeleton = (Skeleton *) _drawable->skeleton;
AnimationState *state = (AnimationState *) _drawable->animationState;
state->update(delta);
state->apply(*skeleton);
skeleton->update(delta);
skeleton->updateWorldTransform(spine::Physics_Update);
}
spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable drawable) {
if (!drawable) return nullptr;
_spine_skeleton_drawable *_drawable = (_spine_skeleton_drawable *) drawable;

View File

@ -80,6 +80,7 @@ SPINE_C_API void spine_skeleton_data_result_dispose(spine_skeleton_data_result r
// Skeleton drawable functionsp
SPINE_C_API spine_skeleton_drawable spine_skeleton_drawable_create(spine_skeleton_data skeletonData);
SPINE_C_API void spine_skeleton_drawable_update(spine_skeleton_drawable drawable, float delta);
SPINE_C_API spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable drawable);
SPINE_C_API void spine_skeleton_drawable_dispose(spine_skeleton_drawable drawable);
SPINE_C_API spine_skeleton spine_skeleton_drawable_get_skeleton(spine_skeleton_drawable drawable);

View File

@ -22,6 +22,9 @@ spine-ios supports all Spine features except two-color tinting.
## Building
### SpineC bindings
### SpineC and SpineSwift
Build on any platform:

View File

@ -1,11 +1,10 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 4.0.0)
project(spine-sdl)
# Default flags
include(${CMAKE_SOURCE_DIR}/../flags.cmake)
# Add spine-cpp and spine-c
add_subdirectory(${CMAKE_SOURCE_DIR}/../spine-cpp ${CMAKE_BINARY_DIR}/spine-cpp-build)
add_subdirectory(${CMAKE_SOURCE_DIR}/../spine-c ${CMAKE_BINARY_DIR}/spine-c)
# SDL

View File

@ -0,0 +1,43 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "debug",
"displayName": "Debug",
"description": "Debug build with compile commands",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "release",
"displayName": "Release",
"description": "Release build",
"inherits": "debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "release",
"configurePreset": "release"
}
]
}

View File

@ -1,8 +1,8 @@
# spine-sdl
The spine-sdl runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [SDL](https://www.libsdl.org/). spine-sdl is based on [spine-c](../spine-c) and [spine-cpp](../spine-cpp), depending on whether you want to use a C or C++ implementation.
The spine-sdl runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [SDL](https://www.libsdl.org/). spine-sdl is based on [spine-c](../spine-c) and [spine-cpp](../spine-cpp). Note that spine-c depends on spine-cpp, so both are required regardless of which API you choose to use.
# See the [spine-sdl documentation](http://esotericsoftware.com/spine-sdl) for in-depth information
# See the [spine-sdl documentation](http://esotericsoftware.com/spine-documentation#runtimes) for in-depth information
## Licensing
@ -16,54 +16,98 @@ For the official legal terms governing the Spine Runtimes, please read the [Spin
## Spine version
spine-sdl works with data exported from Spine 4.2.xx.
spine-sdl works with data exported from Spine 4.3.xx.
spine-sdl supports all Spine features except screen blend mode and two color tinting.
## Usage
1. Create a new SDL project. See the [SDL documentation](https://wiki.libsdl.org/FrontPage) or have a look at the example in this repository.
2. Download the Spine Runtimes source using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip via the download button above.
3. If you are using C, add the sources from `spine-c/spine-c/src/spine` and `spine-sdl/src/spine-sdl-c.c` to your project, and add the folder `spine-c/spine-c/include` and `spine-sdl/src/spine-sdl-c.h` to your header search path. Note that includes are specified as `#inclue <spine/file.h>`, so the `spine` directory cannot be omitted when copying the source files.
4. If you are using C++, add the sources from `spine-cpp/spine-cpp/src/spine` and `spine-sdl/src/spine-sdl-cpp.cpp` to your project, and add the folder `spine-cpp/spine-cpp/include` and `spine-sdl/src/spine-sdl-cpp.h` to your header search path. Note that includes are specified as `#include <spine/file.h>`, so the `spine` directory cannot be omitted when copying the source files.
### Integration with CMake (Recommended)
See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimes) on how to use the APIs or check out the Spine SDL example in this repository.
The easiest way to integrate spine-sdl into your project is via CMake FetchContent:
## Example
```cmake
include(FetchContent)
FetchContent_Declare(
spine-sdl
GIT_REPOSITORY https://github.com/esotericsoftware/spine-runtimes.git
GIT_TAG 4.3
SOURCE_SUBDIR spine-sdl
)
FetchContent_MakeAvailable(spine-sdl)
The Spine SDL example works on Windows, Linux and Mac OS X. For a spine-c based example, see [example/main.c](example/main.c), for a spine-cpp example see [example/main.cpp](example/main.cpp).
# For C API
target_link_libraries(your_target spine-sdl-c)
# For C++ API
target_link_libraries(your_target spine-sdl-cpp)
```
This will automatically fetch and build spine-sdl along with its dependencies (spine-c, spine-cpp, and SDL).
### Manual Integration
If you prefer manual integration:
1. Download the Spine Runtimes source using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip.
2. Add the required source files to your project:
- For C API: Add sources from `spine-cpp/src`, `spine-c/src`, and `spine-sdl/src/spine-sdl-c.c`
- For C++ API: Add sources from `spine-cpp/src`, `spine-c/src`, and `spine-sdl/src/spine-sdl-cpp.cpp`
3. Add the include directories: `spine-cpp/include`, `spine-c/include`, and `spine-sdl/src`
4. Link against SDL2
See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimes) for detailed API usage.
## Examples
The repository includes example code for both C and C++ APIs:
- C example: [example/main.c](example/main.c)
- C++ example: [example/main.cpp](example/main.cpp)
### Building the Examples
### Windows
1. Install [Visual Studio Community](https://visualstudio.microsoft.com/downloads/). Make sure you install support for C++ as well as th Windows SDK for XP/7/8.
2. Install CMake via the [Windows installer package](https://cmake.org/download/).
3. 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.
1. Install [Visual Studio Community](https://visualstudio.microsoft.com/downloads/) with C++ support and Windows SDK.
2. Install [CMake](https://cmake.org/download/) via the Windows installer package.
3. Clone the repository: `git clone https://github.com/esotericsoftware/spine-runtimes`
4. Run CMake GUI from the start menu
5. Click `Browse Source` and select the directory `spine-runtimes`
6. Click `Browse Build` and select the `spine-runtimes/spine-sdl/build` directory. You can create the `build` folder directly in the file dialog via `New Folder`.
7. Click `Configure`. Then click `Generate`. This will create a Visual Studio solution file called `spine.sln` in `spine-runtimes/spine-sdl/build` and also download the SDL dependencies.
8. Open the `spine.sln` file in Visual Studio
9. Right click the `spine-sdl-example-c` or `spine-sdl-example-cpp` project in the solution explorer and select `Set as Startup Project` from the context menus
10. Click `Local Windows Debugger` to run the example
The entire example code is contained in [main.cpp](example/main.cpp#L61)
5. Set source directory to `spine-runtimes/spine-sdl`
6. Set build directory to `spine-runtimes/spine-sdl/build`
7. Click `Configure`, then `Generate`
8. Open the generated solution in Visual Studio
9. Set `spine-sdl-c-example` or `spine-sdl-cpp-example` as the startup project
10. Run the project
### Linux
1. Install the [SDL build dependencies](https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md)
2. 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.
3. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder
4. Type `mkdir build && cd build && cmake ../..` to generate Make files
5. Type `make` to compile the example
6. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++)
1. Install dependencies:
```bash
sudo apt-get install cmake ninja-build # Ubuntu/Debian
# or equivalent for your distribution
```
2. Clone the repository: `git clone https://github.com/esotericsoftware/spine-runtimes`
3. Build and run:
```bash
cd spine-runtimes/spine-sdl
./build.sh
./build/debug/spine-sdl-c-example # Run C example
./build/debug/spine-sdl-cpp-example # Run C++ example
```
### Mac OS X
### macOS
1. Install [Xcode](https://developer.apple.com/xcode/)
2. Install [Homebrew](http://brew.sh/)
3. Open a terminal and install CMake via `brew install cmake`
4. 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.
5. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder
6. Type `mkdir build && cd build && cmake ../..` to generate Make files
7. Type `make` to compile the example
8. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++)
3. Install dependencies:
```bash
brew install cmake ninja
```
4. Clone the repository: `git clone https://github.com/esotericsoftware/spine-runtimes`
5. Build and run:
```bash
cd spine-runtimes/spine-sdl
./build.sh
./build/debug/spine-sdl-c-example # Run C example
./build/debug/spine-sdl-cpp-example # Run C++ example
```

60
spine-sdl/build.sh Executable file
View File

@ -0,0 +1,60 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
# Source logging utilities
source ../formatters/logging/logging.sh
# Parse arguments
BUILD_TYPE="debug"
CLEAN=""
for arg in "$@"; do
case $arg in
clean)
CLEAN="true"
;;
release)
BUILD_TYPE="release"
;;
debug)
BUILD_TYPE="debug"
;;
*)
log_fail "Unknown argument: $arg"
log_detail "Usage: $0 [clean] [release|debug]"
exit 1
;;
esac
done
log_title "spine-sdl build"
# Clean if requested
if [ "$CLEAN" = "true" ]; then
log_action "Cleaning build directory"
rm -rf build
log_ok
fi
# Configure and build
log_action "Configuring $BUILD_TYPE build"
if CMAKE_OUTPUT=$(cmake --preset=$BUILD_TYPE . 2>&1); then
log_ok
else
log_fail
log_error_output "$CMAKE_OUTPUT"
exit 1
fi
log_action "Building"
if BUILD_OUTPUT=$(cmake --build --preset=$BUILD_TYPE 2>&1); then
log_ok
else
log_fail
log_error_output "$BUILD_OUTPUT"
exit 1
fi
log_summary "✓ Build successful"

View File

@ -29,46 +29,124 @@
#include <spine-sdl-c.h>
#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#undef main
int main() {
// Global renderer for texture loading callbacks
static SDL_Renderer *g_renderer = NULL;
// Texture loading callback for atlas
void *load_texture_callback(const char *path) {
extern void *load_texture(const char *path, SDL_Renderer *renderer);
return load_texture(path, g_renderer);
}
void unload_texture_callback(void *texture) {
SDL_DestroyTexture((SDL_Texture*)texture);
}
char *read_file(const char *path, int *length) {
FILE *file = fopen(path, "rb");
if (!file) return NULL;
fseek(file, 0, SEEK_END);
*length = (int)ftell(file);
fseek(file, 0, SEEK_SET);
char *data = (char*)malloc(*length + 1);
fread(data, 1, *length, file);
data[*length] = '\0';
fclose(file);
return data;
}
int main(void) {
if (SDL_Init(SDL_INIT_VIDEO)) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0);
if (!window) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s", SDL_GetError());
printf("Error: %s\n", SDL_GetError());
return -1;
}
spAtlas *atlas = spAtlas_createFromFile("data/spineboy-pma.atlas", renderer);
spSkeletonJson *json = spSkeletonJson_create(atlas);
json->scale = 0.5f;
spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "data/spineboy-pro.json");
spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData);
animationStateData->defaultMix = 0.2f;
spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData);
drawable->usePremultipliedAlpha = -1;
drawable->skeleton->x = 400;
drawable->skeleton->y = 500;
spSkeleton_setToSetupPose(drawable->skeleton);
spSkeletonDrawable_update(drawable, 0, SP_PHYSICS_UPDATE);
spAnimationState_setAnimationByName(drawable->animationState, 0, "portal", 0);
spAnimationState_addAnimationByName(drawable->animationState, 0, "run", -1, 0);
SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0);
if (!window) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// Set global renderer for texture loading
g_renderer = renderer;
// Load atlas
int atlas_length;
char *atlas_data = read_file("data/spineboy-pma.atlas", &atlas_length);
if (!atlas_data) {
printf("Failed to load atlas\n");
return -1;
}
spine_atlas_result atlas_result = spine_atlas_load_callback(atlas_data, "data/", load_texture_callback, unload_texture_callback);
spine_atlas atlas = spine_atlas_result_get_atlas(atlas_result);
free(atlas_data);
if (!atlas) {
const char *error = spine_atlas_result_get_error(atlas_result);
printf("Failed to load atlas: %s\n", error);
spine_atlas_result_dispose(atlas_result);
return -1;
}
// Load skeleton
int skeleton_length;
char *skeleton_data = read_file("data/spineboy-pro.json", &skeleton_length);
if (!skeleton_data) {
printf("Failed to load skeleton\n");
return -1;
}
spine_skeleton_data_result skeleton_result = spine_skeleton_data_load_json(atlas, skeleton_data, "data/");
spine_skeleton_data skeleton_data_handle = spine_skeleton_data_result_get_data(skeleton_result);
free(skeleton_data);
if (!skeleton_data_handle) {
const char *error = spine_skeleton_data_result_get_error(skeleton_result);
printf("Failed to load skeleton: %s\n", error);
spine_skeleton_data_result_dispose(skeleton_result);
return -1;
}
// Create drawable
spine_skeleton_drawable drawable = spine_skeleton_drawable_create(skeleton_data_handle);
spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
spine_animation_state animation_state = spine_skeleton_drawable_get_animation_state(drawable);
spine_animation_state_data animation_state_data = spine_skeleton_drawable_get_animation_state_data(drawable);
// Setup skeleton
spine_skeleton_set_position(skeleton, 400, 500);
spine_skeleton_set_scale(skeleton, 0.5f, 0.5f);
spine_skeleton_setup_pose(skeleton);
spine_skeleton_update(skeleton, 0);
spine_skeleton_update_world_transform(skeleton, SPINE_PHYSICS_UPDATE);
// Setup animation
spine_animation_state_data_set_default_mix(animation_state_data, 0.2f);
spine_animation_state_set_animation_1(animation_state, 0, "portal", false);
spine_animation_state_add_animation_1(animation_state, 0, "run", true, 0);
int quit = 0;
uint64_t lastFrameTime = SDL_GetPerformanceCounter();
while (!quit) {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
quit = -1;
quit = 1;
break;
}
}
@ -77,16 +155,26 @@ int main() {
SDL_RenderClear(renderer);
uint64_t now = SDL_GetPerformanceCounter();
double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency();
double deltaTime = (now - lastFrameTime) / (double)SDL_GetPerformanceFrequency();
lastFrameTime = now;
spSkeletonDrawable_update(drawable, deltaTime, SP_PHYSICS_UPDATE);
spSkeletonDrawable_draw(drawable, renderer);
// Update animation
spine_skeleton_drawable_update(drawable, (float)deltaTime);
// Draw
spine_sdl_draw(drawable, renderer, true);
SDL_RenderPresent(renderer);
}
// Cleanup
spine_skeleton_drawable_dispose(drawable);
spine_skeleton_data_result_dispose(skeleton_result);
spine_atlas_result_dispose(atlas_result);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@ -29,73 +29,94 @@
#include <spine-sdl-cpp.h>
#include <SDL.h>
#include <spine/Debug.h>
#include <stdio.h>
#undef main
spine::DebugExtension dbgExtension(spine::SpineExtension::getInstance());
extern spine::SkeletonRenderer *skeletonRenderer;
using namespace spine;
int main() {
spine::SpineExtension::setInstance(&dbgExtension);
{
if (SDL_Init(SDL_INIT_VIDEO)) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0);
if (!window) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s", SDL_GetError());
return -1;
}
spine::SDLTextureLoader textureLoader(renderer);
spine::Atlas atlas("data/spineboy-pma.atlas", &textureLoader);
spine::AtlasAttachmentLoader attachmentLoader(&atlas);
spine::SkeletonJson json(&attachmentLoader);
json.setScale(0.5f);
spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json");
spine::SkeletonDrawable drawable(skeletonData);
drawable.usePremultipliedAlpha = true;
drawable.animationState->getData()->setDefaultMix(0.2f);
drawable.skeleton->setPosition(400, 500);
drawable.skeleton->setToSetupPose();
drawable.animationState->setAnimation(0, "portal", true);
drawable.animationState->addAnimation(0, "run", true, 0);
drawable.update(0, spine::Physics_Update);
bool quit = false;
uint64_t lastFrameTime = SDL_GetPerformanceCounter();
while (!quit) {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
quit = true;
break;
}
}
SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255);
SDL_RenderClear(renderer);
uint64_t now = SDL_GetPerformanceCounter();
double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency();
lastFrameTime = now;
drawable.update(deltaTime, spine::Physics_Update);
drawable.draw(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
delete skeletonData;
if (SDL_Init(SDL_INIT_VIDEO)) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
delete skeletonRenderer;
dbgExtension.reportLeaks();
SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0);
if (!window) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// Load atlas and skeleton data
SDLTextureLoader textureLoader(renderer);
Atlas atlas("data/spineboy-pma.atlas", &textureLoader);
SkeletonJson json(atlas);
json.setScale(0.5f);
SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json");
if (!skeletonData) {
printf("Failed to load skeleton data\n");
return -1;
}
// Create skeleton and animation state
Skeleton skeleton(*skeletonData);
AnimationStateData animationStateData(*skeletonData);
animationStateData.setDefaultMix(0.2f);
AnimationState animationState(animationStateData);
// Setup skeleton
skeleton.setPosition(400, 500);
skeleton.setupPose();
skeleton.update(0);
skeleton.updateWorldTransform(Physics_Update);
// Setup animation
animationState.setAnimation(0, "portal", false);
animationState.addAnimation(0, "run", true, 0);
bool quit = false;
uint64_t lastFrameTime = SDL_GetPerformanceCounter();
while (!quit) {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
quit = true;
break;
}
}
SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255);
SDL_RenderClear(renderer);
uint64_t now = SDL_GetPerformanceCounter();
double deltaTime = (now - lastFrameTime) / (double)SDL_GetPerformanceFrequency();
lastFrameTime = now;
// Update animation
animationState.update(deltaTime);
animationState.apply(skeleton);
skeleton.update(deltaTime);
skeleton.updateWorldTransform(Physics_Update);
// Draw
SDL_draw(skeleton, renderer, true);
SDL_RenderPresent(renderer);
}
// Cleanup
delete skeletonData;
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
}

View File

@ -28,210 +28,154 @@
*****************************************************************************/
#include "spine-sdl-c.h"
#include <spine/spine.h>
#include <spine/extension.h>
#include <stdlib.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
_SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(spSdlVertexArray, SDL_Vertex)
static spine_skeleton_renderer renderer = NULL;
spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData) {
spBone_setYDown(-1);
spSkeletonDrawable *self = NEW(spSkeletonDrawable);
self->skeleton = spSkeleton_create(skeletonData);
self->animationState = spAnimationState_create(animationStateData);
self->usePremultipliedAlpha = 0;
self->sdlIndices = spIntArray_create(12);
self->sdlVertices = spSdlVertexArray_create(12);
self->worldVertices = spFloatArray_create(12);
self->clipper = spSkeletonClipping_create();
return self;
static void ensure_renderer(void) {
if (!renderer) {
renderer = spine_skeleton_renderer_create();
}
}
void spSkeletonDrawable_dispose(spSkeletonDrawable *self) {
spSkeleton_dispose(self->skeleton);
spAnimationState_dispose(self->animationState);
spIntArray_dispose(self->sdlIndices);
spSdlVertexArray_dispose(self->sdlVertices);
spFloatArray_dispose(self->worldVertices);
spSkeletonClipping_dispose(self->clipper);
FREE(self);
void spine_sdl_draw(spine_skeleton_drawable drawable, struct SDL_Renderer *sdl_renderer, bool premultipliedAlpha) {
spine_skeleton skeleton = spine_skeleton_drawable_get_skeleton(drawable);
spine_sdl_draw_skeleton(skeleton, sdl_renderer, premultipliedAlpha);
}
void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta, spPhysics physics) {
spAnimationState_update(self->animationState, delta);
spAnimationState_apply(self->animationState, self->skeleton);
spSkeleton_update(self->skeleton, delta);
spSkeleton_updateWorldTransform(self->skeleton, physics);
}
void spine_sdl_draw_skeleton(spine_skeleton skeleton, struct SDL_Renderer *sdl_renderer, bool premultipliedAlpha) {
ensure_renderer();
void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer) {
static unsigned short quadIndices[] = {0, 1, 2, 2, 3, 0};
spSkeleton *skeleton = self->skeleton;
spSkeletonClipping *clipper = self->clipper;
SDL_Texture *texture;
SDL_Vertex sdlVertex;
for (int i = 0; i < skeleton->slotsCount; ++i) {
spSlot *slot = skeleton->drawOrder[i];
spAttachment *attachment = slot->attachment;
if (!attachment) {
spSkeletonClipping_clipEnd(clipper, slot);
continue;
spine_render_command command = spine_skeleton_renderer_render(renderer, skeleton);
// Pre-allocate vertex and index arrays
int max_vertices = 1024;
int max_indices = 1024 * 3;
SDL_Vertex *vertices = (SDL_Vertex*)malloc(sizeof(SDL_Vertex) * max_vertices);
int *indices = (int*)malloc(sizeof(int) * max_indices);
while (command) {
int num_vertices = spine_render_command_get_num_vertices(command);
int num_indices = spine_render_command_get_num_indices(command);
// Resize buffers if needed
if (num_vertices > max_vertices) {
max_vertices = num_vertices * 2;
vertices = (SDL_Vertex*)realloc(vertices, sizeof(SDL_Vertex) * max_vertices);
}
if (num_indices > max_indices) {
max_indices = num_indices * 2;
indices = (int*)realloc(indices, sizeof(int) * max_indices);
}
// Early out if the slot color is 0 or the bone is not active
if (slot->color.a == 0 || !slot->bone->active) {
spSkeletonClipping_clipEnd(clipper, slot);
continue;
// Get vertex data from render command
float *positions = spine_render_command_get_positions(command);
float *uvs = spine_render_command_get_uvs(command);
uint32_t *colors = spine_render_command_get_colors(command);
uint16_t *command_indices = spine_render_command_get_indices(command);
SDL_Texture *texture = (SDL_Texture*)spine_render_command_get_texture(command);
spine_blend_mode blend_mode = spine_render_command_get_blend_mode(command);
// Fill SDL vertices
for (int i = 0; i < num_vertices; i++) {
vertices[i].position.x = positions[i * 2];
vertices[i].position.y = positions[i * 2 + 1];
vertices[i].tex_coord.x = uvs[i * 2];
vertices[i].tex_coord.y = uvs[i * 2 + 1];
// Convert color from packed uint32 to SDL_Color
uint32_t color = colors[i];
vertices[i].color.r = (color >> 24) & 0xFF;
vertices[i].color.g = (color >> 16) & 0xFF;
vertices[i].color.b = (color >> 8) & 0xFF;
vertices[i].color.a = color & 0xFF;
}
spFloatArray *vertices = self->worldVertices;
int verticesCount = 0;
float *uvs = NULL;
unsigned short *indices;
int indicesCount = 0;
spColor *attachmentColor = NULL;
if (attachment->type == SP_ATTACHMENT_REGION) {
spRegionAttachment *region = (spRegionAttachment *) attachment;
attachmentColor = &region->color;
// Early out if the slot color is 0
if (attachmentColor->a == 0) {
spSkeletonClipping_clipEnd(clipper, slot);
continue;
}
spFloatArray_setSize(vertices, 8);
spRegionAttachment_computeWorldVertices(region, slot, vertices->items, 0, 2);
verticesCount = 4;
uvs = region->uvs;
indices = quadIndices;
indicesCount = 6;
texture = (SDL_Texture *) ((spAtlasRegion *) region->rendererObject)->page->rendererObject;
} else if (attachment->type == SP_ATTACHMENT_MESH) {
spMeshAttachment *mesh = (spMeshAttachment *) attachment;
attachmentColor = &mesh->color;
// Early out if the slot color is 0
if (attachmentColor->a == 0) {
spSkeletonClipping_clipEnd(clipper, slot);
continue;
}
spFloatArray_setSize(vertices, mesh->super.worldVerticesLength);
spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices->items, 0, 2);
verticesCount = mesh->super.worldVerticesLength >> 1;
uvs = mesh->uvs;
indices = mesh->triangles;
indicesCount = mesh->trianglesCount;
texture = (SDL_Texture *) ((spAtlasRegion *) mesh->rendererObject)->page->rendererObject;
} else if (attachment->type == SP_ATTACHMENT_CLIPPING) {
spClippingAttachment *clip = (spClippingAttachment *) slot->attachment;
spSkeletonClipping_clipStart(clipper, slot, clip);
continue;
} else
continue;
Uint8 r = (Uint8) (skeleton->color.r * slot->color.r * attachmentColor->r * 255);
Uint8 g = (Uint8) (skeleton->color.g * slot->color.g * attachmentColor->g * 255);
Uint8 b = (Uint8) (skeleton->color.b * slot->color.b * attachmentColor->b * 255);
Uint8 a = (Uint8) (skeleton->color.a * slot->color.a * attachmentColor->a * 255);
sdlVertex.color.r = r;
sdlVertex.color.g = g;
sdlVertex.color.b = b;
sdlVertex.color.a = a;
if (spSkeletonClipping_isClipping(clipper)) {
spSkeletonClipping_clipTriangles(clipper, vertices->items, verticesCount << 1, indices, indicesCount, uvs, 2);
vertices = clipper->clippedVertices;
verticesCount = clipper->clippedVertices->size >> 1;
uvs = clipper->clippedUVs->items;
indices = clipper->clippedTriangles->items;
indicesCount = clipper->clippedTriangles->size;
// Copy indices
for (int i = 0; i < num_indices; i++) {
indices[i] = command_indices[i];
}
spSdlVertexArray_clear(self->sdlVertices);
for (int ii = 0; ii < verticesCount << 1; ii += 2) {
sdlVertex.position.x = vertices->items[ii];
sdlVertex.position.y = vertices->items[ii + 1];
sdlVertex.tex_coord.x = uvs[ii];
sdlVertex.tex_coord.y = uvs[ii + 1];
spSdlVertexArray_add(self->sdlVertices, sdlVertex);
}
spIntArray_clear(self->sdlIndices);
for (int ii = 0; ii < (int) indicesCount; ii++) spIntArray_add(self->sdlIndices, indices[ii]);
if (!self->usePremultipliedAlpha) {
switch (slot->data->blendMode) {
case SP_BLEND_MODE_NORMAL:
// Set blend mode
if (!premultipliedAlpha) {
switch (blend_mode) {
case SPINE_BLEND_MODE_NORMAL:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
case SP_BLEND_MODE_MULTIPLY:
case SPINE_BLEND_MODE_MULTIPLY:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break;
case SP_BLEND_MODE_ADDITIVE:
case SPINE_BLEND_MODE_ADDITIVE:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
break;
case SP_BLEND_MODE_SCREEN:
case SPINE_BLEND_MODE_SCREEN:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
}
} else {
SDL_BlendMode target;
switch (slot->data->blendMode) {
case SP_BLEND_MODE_NORMAL:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
switch (blend_mode) {
case SPINE_BLEND_MODE_NORMAL:
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
case SP_BLEND_MODE_MULTIPLY:
case SPINE_BLEND_MODE_MULTIPLY:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break;
case SP_BLEND_MODE_ADDITIVE:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE,
SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
case SPINE_BLEND_MODE_ADDITIVE:
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
case SP_BLEND_MODE_SCREEN:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
case SPINE_BLEND_MODE_SCREEN:
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
}
}
SDL_RenderGeometry(renderer, texture, self->sdlVertices->items, self->sdlVertices->size, self->sdlIndices->items, indicesCount);
spSkeletonClipping_clipEnd(clipper, slot);
// Draw the geometry
SDL_RenderGeometry(sdl_renderer, texture, vertices, num_vertices, indices, num_indices);
// Move to next command
spine_render_command next = spine_render_command_get_next(command);
command = next;
}
spSkeletonClipping_clipEnd2(clipper);
free(vertices);
free(indices);
}
void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) {
// Texture loading functions for atlas
void *load_texture(const char *path, SDL_Renderer *renderer) {
int width, height, components;
stbi_uc *imageData = stbi_load(path, &width, &height, &components, 4);
if (!imageData) return;
SDL_Texture *texture = SDL_CreateTexture((SDL_Renderer *) self->atlas->rendererObject, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width,
height);
if (!imageData) return NULL;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
if (!texture) {
stbi_image_free(imageData);
return;
return NULL;
}
if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) {
SDL_DestroyTexture(texture);
stbi_image_free(imageData);
return;
return NULL;
}
stbi_image_free(imageData);
self->rendererObject = texture;
return;
return texture;
}
void _spAtlasPage_disposeTexture(spAtlasPage *self) {
SDL_DestroyTexture((SDL_Texture *) self->rendererObject);
}
char *_spUtil_readFile(const char *path, int *length) {
return _spReadFile(path, length);
void unload_texture(void *texture) {
SDL_DestroyTexture((SDL_Texture*)texture);
}

View File

@ -30,34 +30,18 @@
#ifndef SPINE_SDL
#define SPINE_SDL
#include <spine/spine.h>
#include <spine-c.h>
#include <SDL.h>
#ifdef __cplusplus
extern "C" {
#endif
_SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex)
/// Renders a spine_skeleton_drawable using SDL_Renderer
void spine_sdl_draw(spine_skeleton_drawable drawable, struct SDL_Renderer *renderer, bool premultipliedAlpha);
typedef struct spSkeletonDrawable {
spSkeleton *skeleton;
spAnimationState *animationState;
int usePremultipliedAlpha;
spSkeletonClipping *clipper;
spFloatArray *worldVertices;
spSdlVertexArray *sdlVertices;
spIntArray *sdlIndices;
} spSkeletonDrawable;
SP_API spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData);
SP_API void spSkeletonDrawable_dispose(spSkeletonDrawable *self);
SP_API void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta, spPhysics physics);
SP_API void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer);
/// Renders a spine_skeleton using SDL_Renderer
void spine_sdl_draw_skeleton(spine_skeleton skeleton, struct SDL_Renderer *renderer, bool premultipliedAlpha);
#ifdef __cplusplus
}

View File

@ -28,70 +28,75 @@
*****************************************************************************/
#include "spine-sdl-cpp.h"
#include <SDL.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <spine/Debug.h>
using namespace spine;
SkeletonRenderer *skeletonRenderer = nullptr;
static SkeletonRenderer *skeletonRenderer = nullptr;
SkeletonDrawable::SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData) {
Bone::setYDown(true);
skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData);
ownsAnimationStateData = animationStateData == 0;
if (ownsAnimationStateData) animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData);
animationState = new (__FILE__, __LINE__) AnimationState(animationStateData);
static void ensureSkeletonRenderer() {
if (!skeletonRenderer) {
skeletonRenderer = new SkeletonRenderer();
}
}
SkeletonDrawable::~SkeletonDrawable() {
if (ownsAnimationStateData) delete animationState->getData();
delete animationState;
delete skeleton;
}
void spine::SDL_draw(Skeleton &skeleton, SDL_Renderer *renderer, bool premultipliedAlpha) {
ensureSkeletonRenderer();
void SkeletonDrawable::update(float delta, Physics physics) {
animationState->update(delta);
animationState->apply(*skeleton);
skeleton->update(delta);
skeleton->updateWorldTransform(physics);
}
RenderCommand *command = skeletonRenderer->render(skeleton);
inline void toSDLColor(uint32_t color, SDL_Color *sdlColor) {
sdlColor->a = (color >> 24) & 0xFF;
sdlColor->r = (color >> 16) & 0xFF;
sdlColor->g = (color >> 8) & 0xFF;
sdlColor->b = color & 0xFF;
}
// Pre-allocate vertex and index arrays
int maxVertices = 1024;
int maxIndices = 1024 * 3;
SDL_Vertex *vertices = (SDL_Vertex*)malloc(sizeof(SDL_Vertex) * maxVertices);
int *indices = (int*)malloc(sizeof(int) * maxIndices);
void SkeletonDrawable::draw(SDL_Renderer *renderer) {
if (!skeletonRenderer) skeletonRenderer = new (__FILE__, __LINE__) SkeletonRenderer();
RenderCommand *command = skeletonRenderer->render(*skeleton);
while (command) {
int numVertices = command->numVertices;
int numIndices = command->numIndices;
// Resize buffers if needed
if (numVertices > maxVertices) {
maxVertices = numVertices * 2;
vertices = (SDL_Vertex*)realloc(vertices, sizeof(SDL_Vertex) * maxVertices);
}
if (numIndices > maxIndices) {
maxIndices = numIndices * 2;
indices = (int*)realloc(indices, sizeof(int) * maxIndices);
}
// Get vertex data from render command
float *positions = command->positions;
float *uvs = command->uvs;
uint32_t *colors = command->colors;
sdlVertices.clear();
for (int ii = 0; ii < command->numVertices << 1; ii += 2) {
SDL_Vertex sdlVertex;
sdlVertex.position.x = positions[ii];
sdlVertex.position.y = positions[ii + 1];
sdlVertex.tex_coord.x = uvs[ii];
sdlVertex.tex_coord.y = uvs[ii + 1];
toSDLColor(colors[ii >> 1], &sdlVertex.color);
sdlVertices.add(sdlVertex);
}
sdlIndices.clear();
uint16_t *indices = command->indices;
for (int ii = 0; ii < command->numIndices; ii++) sdlIndices.add(indices[ii]);
uint16_t *commandIndices = command->indices;
SDL_Texture *texture = (SDL_Texture*)command->texture;
BlendMode blendMode = command->blendMode;
SDL_Texture *texture = (SDL_Texture *) command->texture;
if (!usePremultipliedAlpha) {
// Fill SDL vertices
for (int i = 0; i < numVertices; i++) {
vertices[i].position.x = positions[i * 2];
vertices[i].position.y = positions[i * 2 + 1];
vertices[i].tex_coord.x = uvs[i * 2];
vertices[i].tex_coord.y = uvs[i * 2 + 1];
// Convert color from packed uint32 to SDL_Color
uint32_t color = colors[i];
vertices[i].color.r = (color >> 24) & 0xFF;
vertices[i].color.g = (color >> 16) & 0xFF;
vertices[i].color.b = (color >> 8) & 0xFF;
vertices[i].color.a = color & 0xFF;
}
// Copy indices
for (int i = 0; i < numIndices; i++) {
indices[i] = commandIndices[i];
}
// Set blend mode
if (!premultipliedAlpha) {
switch (blendMode) {
case BlendMode_Normal:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
@ -110,69 +115,69 @@ void SkeletonDrawable::draw(SDL_Renderer *renderer) {
SDL_BlendMode target;
switch (blendMode) {
case BlendMode_Normal:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
case BlendMode_Multiply:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break;
case BlendMode_Additive:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD, SDL_BLENDFACTOR_ONE,
SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
case BlendMode_Screen:
target = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
target = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD);
SDL_SetTextureBlendMode(texture, target);
break;
}
}
SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(), command->numIndices);
command = command->next;
}
}
// Draw the geometry
SDL_RenderGeometry(renderer, texture, vertices, numVertices, indices, numIndices);
SDL_Texture *loadTexture(SDL_Renderer *renderer, const String &path) {
int width, height, components;
stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4);
if (!imageData) return nullptr;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
if (!texture) {
stbi_image_free(imageData);
return nullptr;
// Move to next command
RenderCommand *next = command->next;
command = next;
}
if (SDL_UpdateTexture(texture, nullptr, imageData, width * 4)) {
stbi_image_free(imageData);
return nullptr;
}
stbi_image_free(imageData);
return texture;
free(vertices);
free(indices);
}
void SDLTextureLoader::load(AtlasPage &page, const String &path) {
SDL_Texture *texture = loadTexture(renderer, path);
if (!texture) return;
page.texture = texture;
SDL_QueryTexture(texture, nullptr, nullptr, &page.width, &page.height);
switch (page.magFilter) {
case TextureFilter_Nearest:
SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest);
break;
case TextureFilter_Linear:
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
break;
default:
SDL_SetTextureScaleMode(texture, SDL_ScaleModeBest);
int width, height, components;
stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4);
if (!imageData) return;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
if (!texture) {
stbi_image_free(imageData);
return;
}
if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) {
SDL_DestroyTexture(texture);
stbi_image_free(imageData);
return;
}
stbi_image_free(imageData);
page.texture = texture;
page.width = width;
page.height = height;
}
void SDLTextureLoader::unload(void *texture) {
SDL_DestroyTexture((SDL_Texture *) texture);
SDL_DestroyTexture((SDL_Texture*)texture);
}
SpineExtension *spine::getDefaultExtension() {
return new DefaultSpineExtension();
}
// Default extension implementation for spine-cpp
spine::SpineExtension *spine::getDefaultExtension() {
return new spine::DefaultSpineExtension();
}

View File

@ -27,44 +27,26 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_SDL
#define SPINE_SDL
#ifndef SPINE_SDL_CPP
#define SPINE_SDL_CPP
#include <spine/spine.h>
#include <SDL.h>
namespace spine {
class SkeletonDrawable {
public:
SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr);
~SkeletonDrawable();
void update(float delta, Physics physics);
void draw(SDL_Renderer *renderer);
Skeleton *skeleton;
AnimationState *animationState;
bool usePremultipliedAlpha;
private:
bool ownsAnimationStateData;
Vector<SDL_Vertex> sdlVertices;
Vector<int> sdlIndices;
};
class SDLTextureLoader : public spine::TextureLoader {
/// Renders a spine::Skeleton using SDL_Renderer
void SDL_draw(Skeleton &skeleton, SDL_Renderer *renderer, bool premultipliedAlpha);
/// SDL texture loader for use with Atlas
class SDLTextureLoader : public TextureLoader {
SDL_Renderer *renderer;
public:
SDLTextureLoader(SDL_Renderer *renderer) : renderer(renderer) {
}
SDLTextureLoader(SDL_Renderer *renderer) : renderer(renderer) {}
void load(AtlasPage &page, const String &path);
void unload(void *texture);
};
}// namespace spine
}
#endif
#endif