mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
[sdl] Updated to latest spine-cpp/spine-c
This commit is contained in:
parent
37fef98fd2
commit
927e2e8f3f
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
@ -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",
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -22,6 +22,9 @@ spine-ios supports all Spine features except two-color tinting.
|
||||
|
||||
## Building
|
||||
|
||||
### SpineC bindings
|
||||
|
||||
|
||||
### SpineC and SpineSwift
|
||||
|
||||
Build on any platform:
|
||||
|
||||
@ -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
|
||||
|
||||
43
spine-sdl/CMakePresets.json
Normal file
43
spine-sdl/CMakePresets.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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
60
spine-sdl/build.sh
Executable 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"
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 = ®ion->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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user