Merge branch '4.1' into 4.2-beta

# Conflicts:
#	spine-ts/package-lock.json
#	spine-ts/package.json
#	spine-ts/spine-canvas/package.json
#	spine-ts/spine-core/package.json
#	spine-ts/spine-player/package.json
#	spine-ts/spine-threejs/package.json
#	spine-ts/spine-webgl/package.json
This commit is contained in:
Mario Zechner 2022-08-05 08:54:14 +02:00
commit 4a5176c2c7
30 changed files with 18626 additions and 161 deletions

View File

@ -316,6 +316,8 @@
* Added example component `SkeletonRenderTexture` to render a `SkeletonRenderer` to a `RenderTexture`, mainly for proper transparency. Added an example scene named `RenderTexture FadeOut Transparency` that demonstrates usage for a fadeout transparency effect.
* Added another fadeout example component named `SkeletonRenderTextureFadeout` which takes over transparency fadeout when enabled. You can use this component as-is, attach it in disabled state and enable it to start a fadeout effect.
* Timeline clips now offer an additional `Alpha` parameter for setting a custom constant mix alpha value other than 1.0, just as `TrackEntry.Alpha`. Defaults to 1.0.
* `SkeletonGraphic` now provides additional render callback delegates `OnInstructionsPrepared`, `AssignMeshOverrideSingleRenderer` and `AssignMeshOverrideMultipleRenderers`. `OnInstructionsPrepared` is raised at the end of LateUpdate after render instructions are done, target renderers are prepared, and the mesh is ready to be generated. The two `AssignMeshOverride` delegates allow separate code to take over mesh and material assignment of a `SkeletonGraphic` component.
* Added example component `SkeletonGraphicRenderTexture` to render a `SkeletonGraphic` to a `RenderTexture` (similar as `SkeletonRenderTexture`), mainly for proper transparency. Extended example scene `RenderTexture FadeOut Transparency` accordingly.
* **Changes of default values**

View File

@ -11,8 +11,8 @@ if(MSVC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set (CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Werror -Wextra -pedantic -Wnonportable-include-path -Wshadow -std=c89")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Werror -Wextra -Wnon-virtual-dtor -pedantic -Wnonportable-include-path -Wshadow -std=c++11 -fno-exceptions -fno-rtti")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Wextra -pedantic -Wnonportable-include-path -Wshadow -std=c89")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Wextra -Wnon-virtual-dtor -pedantic -Wnonportable-include-path -Wshadow -std=c++11 -fno-exceptions -fno-rtti")
if (${SPINE_SANITIZE})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined")
@ -22,7 +22,7 @@ endif()
if((${SPINE_SFML}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sfml"))
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_OSX_ARCHITECTURES x86_64)
set(CMAKE_OSX_ARCHITECTURES x86_64;arm64)
set(ONLY_ACTIVE_ARCH NO)
endif()
add_subdirectory(spine-c)
@ -31,5 +31,15 @@ if((${SPINE_SFML}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sfml"))
add_subdirectory(spine-sfml/cpp)
endif()
if((${SPINE_SDL}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sdl"))
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_OSX_ARCHITECTURES x86_64;arm64)
set(ONLY_ACTIVE_ARCH NO)
endif()
add_subdirectory(spine-c)
add_subdirectory(spine-cpp)
add_subdirectory(spine-sdl)
endif()
# add_subdirectory(spine-c/spine-c-unit-tests)
add_subdirectory(spine-cpp/spine-cpp-unit-tests)

View File

@ -155,6 +155,12 @@ cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spinebo
cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spineboy/Raggedy\ Spineboy.atlas.txt "$ROOT/spine-godot/example-v4/assets/raggedyspineboy/Raggedy Spineboy.atlas"
cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spineboy/Raggedy\ Spineboy.png "$ROOT/spine-godot/example-v4/assets/raggedyspineboy/"
echo "spine-sdl"
rm -f "$ROOT/spine-sdl/data/"*
cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-sdl/data/"
cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-sdl/data/"
cp -f ../spineboy/export/spineboy.png "$ROOT/spine-sdl/data/"
echo "spine-sfml-c"
rm "$ROOT/spine-sfml/c/data/"*
cp -f ../coin/export/coin-pro.json "$ROOT/spine-sfml/c/data/"
@ -382,11 +388,11 @@ UNITY_SOURCE_DIR=../spine-unity
# and error out otherwise.
sed="sed"
if [[ $OSTYPE == 'darwin'* ]]; then
if [ ! -f "/usr/local/bin/gsed" ]; then
if [ ! -f "/opt/homebrew/bin/gsed" ]; then
echo "macOS sed detected. Please install GNU sed via brew install gnu-sed"
exit -1
fi
sed="/usr/local/bin/gsed"
sed="/opt/homebrew/bin/gsed"
fi
# Do not delete everything in unity dirs, especially not .meta files.

View File

@ -21,6 +21,9 @@ spotless {
'spine-cocos2dx/src/**/*.h',
'spine-cocos2dx/example/Classes/**/*.cpp',
'spine-cocos2dx/example/Classes/**/*.h',
'spine-sdl/**/*.c',
'spine-sdl/**/*.cpp',
'spine-sdl/**/*.h',
'spine-sfml/**/*.c',
'spine-sfml/**/*.cpp',
'spine-sfml/**/*.h',

View File

@ -118,6 +118,65 @@ extern "C" {
return self->items[self->size - 1]; \
}
#define _SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(name, itemType) \
name* name##_create(int initialCapacity) { \
name* array = CALLOC(name, 1); \
array->size = 0; \
array->capacity = initialCapacity; \
array->items = CALLOC(itemType, initialCapacity); \
return array; \
} \
void name##_dispose(name* self) { \
FREE(self->items); \
FREE(self); \
} \
void name##_clear(name* self) { \
self->size = 0; \
} \
name* name##_setSize(name* self, int newSize) { \
self->size = newSize; \
if (self->capacity < newSize) { \
self->capacity = MAX(8, (int)(self->size * 1.75f)); \
self->items = REALLOC(self->items, itemType, self->capacity); \
} \
return self; \
} \
void name##_ensureCapacity(name* self, int newCapacity) { \
if (self->capacity >= newCapacity) return; \
self->capacity = newCapacity; \
self->items = REALLOC(self->items, itemType, self->capacity); \
} \
void name##_add(name* self, itemType value) { \
if (self->size == self->capacity) { \
self->capacity = MAX(8, (int)(self->size * 1.75f)); \
self->items = REALLOC(self->items, itemType, self->capacity); \
} \
self->items[self->size++] = value; \
} \
void name##_addAll(name* self, name* other) { \
int i = 0; \
for (; i < other->size; i++) { \
name##_add(self, other->items[i]); \
} \
} \
void name##_addAllValues(name* self, itemType* values, int offset, int count) { \
int i = offset, n = offset + count; \
for (; i < n; i++) { \
name##_add(self, values[i]); \
} \
} \
void name##_removeAt(name* self, int index) { \
self->size--; \
memmove(self->items + index, self->items + index + 1, sizeof(itemType) * (self->size - index)); \
} \
itemType name##_pop(name* self) { \
itemType item = self->items[--self->size]; \
return item; \
} \
itemType name##_peek(name* self) { \
return self->items[self->size - 1]; \
}
_SP_ARRAY_DECLARE_TYPE(spFloatArray, float)
_SP_ARRAY_DECLARE_TYPE(spIntArray, int)

View File

@ -967,7 +967,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
toColor(color2, Json::getString(keyMap, "dark", 0), false);
for (frame = 0, bezier = 0;; ++frame) {
timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.g, color2.g, color2.b);
timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
nextMap = keyMap->_next;
if (!nextMap) {
// timeline.shrink(); // BOZO

View File

@ -2,7 +2,7 @@
The spine-godot runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Godot](https://godotengine.org/). spine-godot is based on [spine-cpp](../spine-cpp).
# See the [spine-godot documentation](http://esotericsoftware.com/spine-godot]) for in-depth information.
# See the [spine-godot documentation](http://esotericsoftware.com/spine-godot) for in-depth information.
## Licensing

41
spine-sdl/CMakeLists.txt Normal file
View File

@ -0,0 +1,41 @@
if(MSVC)
message("MSCV detected")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
else()
message("${CMAKE_C_FLAGS}")
message("${CMAKE_CXX_FLAGS}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -std=c99")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wnon-virtual-dtor -pedantic -Wno-unused-parameter -std=c++11 -fno-exceptions -fno-rtti")
endif()
include(FetchContent)
FetchContent_Declare(SDL GIT_REPOSITORY https://github.com/libsdl-org/SDL GIT_TAG release-2.0.22)
FetchContent_MakeAvailable(SDL)
include_directories(src)
add_library(spine-sdl-c STATIC src/spine-sdl-c.c src/spine-sdl-c.h src/stb_image.h)
target_link_libraries(spine-sdl-c LINK_PUBLIC SDL2 spine-c)
install(TARGETS spine-sdl-c DESTINATION dist/lib)
install(FILES src/spine-sdl-c.h src/stb_image.h DESTINATION dist/include)
add_library(spine-sdl-cpp STATIC src/spine-sdl-cpp.cpp src/spine-sdl-cpp.h src/stb_image.h)
target_link_libraries(spine-sdl-cpp LINK_PUBLIC SDL2 spine-cpp)
install(TARGETS spine-sdl-cpp DESTINATION dist/lib)
install(FILES src/spine-sdl-cpp.h src/stb_image.h DESTINATION dist/include)
add_executable(spine-sdl-c-example example/main.c)
target_link_libraries(spine-sdl-c-example SDL2 spine-sdl-c)
add_executable(spine-sdl-cpp-example example/main.cpp)
target_link_libraries(spine-sdl-cpp-example SDL2 spine-sdl-cpp)
# copy data to build directory
add_custom_command(TARGET spine-sdl-c-example PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_LIST_DIR}/data $<TARGET_FILE_DIR:spine-sdl-c-example>/data)
add_custom_command(TARGET spine-sdl-cpp-example PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_LIST_DIR}/data $<TARGET_FILE_DIR:spine-sdl-cpp-example>/data)

64
spine-sdl/README.md Normal file
View File

@ -0,0 +1,64 @@
# 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.
## Licensing
You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge.
You can integrate the Spine Runtimes into your software free of charge, but users of your software must have their own [Spine license](https://esotericsoftware.com/spine-purchase). Please make your users aware of this requirement! This option is often chosen by those making development tools, such as an SDK, game toolkit, or software library.
In order to distribute your software containing the Spine Runtimes to others that don't have a Spine license, you need a [Spine license](https://esotericsoftware.com/spine-purchase) at the time of integration. Then you can distribute your software containing the Spine Runtimes however you like, provided others don't modify it or use it to create new software. If others want to do that, they'll need their own Spine license.
For the official legal terms governing the Spine Runtimes, please read the [Spine Runtimes License Agreement](http://esotericsoftware.com/spine-runtimes-license) and Section 2 of the [Spine Editor License Agreement](http://esotericsoftware.com/spine-editor-license#s2).
## Spine version
spine-sdl works with data exported from Spine 4.1.xx.
spine-sdl supports all Spine features except premultiplied alpha, 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.
3. 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 `#inclue <spine/file.h>`, so the `spine` directory cannot be omitted when copying the source files.
See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimesTitle) on how to use the APIs or check out the Spine SDL example in this repository.
## Example
The Spine SFML 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).
### Windows
1. Install [Visual Studio Community](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx). 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.
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-sfml-example` project in the solution explorer and select `Set as Startup Project` from the context menu
10. Right click the `spine-sfml-example` project in the solution explorer and select `Properties` from the context menu
11. Select `Debugging` in the left-hand list, then set `Working Directory` to `$(OutputPath)`
12. Click `Local Windows Debugger` to run the example
The entire example code is contained in [main.cpp](example/main.cpp#L61)
### Linux
1. Install the [SDL build dependencies](https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md)
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.
4. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder
5. Type `mkdir build && cd build && cmake ../..` to generate Make files
6. Type `make` to compile the example
7. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++)
### Mac OS X
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`
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.
4. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder
5. Type `mkdir build && cd build && cmake ../..` to generate Make files
6. Type `make` to compile the example
7. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
spineboy.png
size: 1024, 256
filter: Linear, Linear
scale: 0.5
crosshair
bounds: 813, 160, 45, 45
eye-indifferent
bounds: 569, 2, 47, 45
eye-surprised
bounds: 643, 7, 47, 45
rotate: 90
front-bracer
bounds: 811, 51, 29, 40
front-fist-closed
bounds: 807, 93, 38, 41
front-fist-open
bounds: 815, 210, 43, 44
front-foot
bounds: 706, 64, 63, 35
rotate: 90
front-shin
bounds: 80, 11, 41, 92
front-thigh
bounds: 754, 12, 23, 56
front-upper-arm
bounds: 618, 5, 23, 49
goggles
bounds: 214, 20, 131, 83
gun
bounds: 347, 14, 105, 102
rotate: 90
head
bounds: 80, 105, 136, 149
hoverboard-board
bounds: 2, 8, 246, 76
rotate: 90
hoverboard-thruster
bounds: 478, 2, 30, 32
hoverglow-small
bounds: 218, 117, 137, 38
rotate: 90
mouth-grind
bounds: 775, 80, 47, 30
rotate: 90
mouth-oooo
bounds: 779, 31, 47, 30
rotate: 90
mouth-smile
bounds: 783, 207, 47, 30
rotate: 90
muzzle-glow
bounds: 779, 4, 25, 25
muzzle-ring
bounds: 451, 14, 25, 105
muzzle01
bounds: 664, 60, 67, 40
rotate: 90
muzzle02
bounds: 580, 56, 68, 42
rotate: 90
muzzle03
bounds: 478, 36, 83, 53
rotate: 90
muzzle04
bounds: 533, 49, 75, 45
rotate: 90
muzzle05
bounds: 624, 56, 68, 38
rotate: 90
neck
bounds: 806, 8, 18, 21
portal-bg
bounds: 258, 121, 133, 133
portal-flare1
bounds: 690, 2, 56, 30
rotate: 90
portal-flare2
bounds: 510, 3, 57, 31
portal-flare3
bounds: 722, 4, 58, 30
rotate: 90
portal-shade
bounds: 393, 121, 133, 133
portal-streaks1
bounds: 528, 126, 126, 128
portal-streaks2
bounds: 656, 129, 125, 125
rear-bracer
bounds: 826, 13, 28, 36
rear-foot
bounds: 743, 70, 57, 30
rotate: 90
rear-shin
bounds: 174, 14, 38, 89
rear-thigh
bounds: 783, 158, 28, 47
rear-upper-arm
bounds: 783, 136, 20, 44
rotate: 90
torso
bounds: 123, 13, 49, 90

BIN
spine-sdl/data/spineboy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

90
spine-sdl/example/main.c Normal file
View File

@ -0,0 +1,90 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine-sdl-c.h>
#include <SDL.h>
int main() {
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;
}
spAtlas *atlas = spAtlas_createFromFile("data/spineboy.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->skeleton->x = 400;
drawable->skeleton->y = 500;
spSkeleton_setToSetupPose(drawable->skeleton);
spSkeletonDrawable_update(drawable, 0);
spAnimationState_setAnimationByName(drawable->animationState, 0, "portal", 0);
spAnimationState_addAnimationByName(drawable->animationState, 0, "run", -1, 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;
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;
spSkeletonDrawable_update(drawable, deltaTime);
spSkeletonDrawable_draw(drawable, renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@ -0,0 +1,90 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine-sdl-cpp.h>
#include <SDL.h>
int main() {
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.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.animationState->getData()->setDefaultMix(0.2f);
drawable.skeleton->setPosition(400, 500);
drawable.skeleton->setToSetupPose();
drawable.update(0);
drawable.animationState->setAnimation(0, "portal", true);
drawable.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;
drawable.update(deltaTime);
drawable.draw(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

211
spine-sdl/src/spine-sdl-c.c Normal file
View File

@ -0,0 +1,211 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "spine-sdl-c.h"
#include <spine/spine.h>
#include <spine/extension.h>
#include <SDL.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
_SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(spSdlVertexArray, SDL_Vertex)
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->sdlIndices = spIntArray_create(12);
self->sdlVertices = spSdlVertexArray_create(12);
self->worldVertices = spFloatArray_create(12);
self->clipper = spSkeletonClipping_create();
return self;
}
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 spSkeletonDrawable_update(spSkeletonDrawable *self, float delta) {
spAnimationState_update(self->animationState, delta);
spAnimationState_apply(self->animationState, self->skeleton);
spSkeleton_updateWorldTransform(self->skeleton);
}
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) continue;
// 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;
}
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;
}
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]);
switch (slot->data->blendMode) {
case SP_BLEND_MODE_NORMAL:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
case SP_BLEND_MODE_MULTIPLY:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break;
case SP_BLEND_MODE_ADDITIVE:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
break;
case SP_BLEND_MODE_SCREEN:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
}
SDL_RenderGeometry(renderer, texture, self->sdlVertices->items, self->sdlVertices->size, self->sdlIndices->items,
indicesCount);
spSkeletonClipping_clipEnd(clipper, slot);
}
spSkeletonClipping_clipEnd2(clipper);
}
void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) {
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 (!texture) {
stbi_image_free(imageData);
return;
}
if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) {
stbi_image_free(imageData);
return;
}
stbi_image_free(imageData);
self->rendererObject = texture;
return;
}
void _spAtlasPage_disposeTexture(spAtlasPage *self) {
SDL_DestroyTexture((SDL_Texture *) self->rendererObject);
}
char *_spUtil_readFile(const char *path, int *length) {
return _spReadFile(path, length);
}

View File

@ -0,0 +1,66 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_SDL
#define SPINE_SDL
#include <spine/spine.h>
#ifdef __cplusplus
extern "C" {
#endif
struct SDL_Renderer;
struct SDL_Texture;
struct SDL_Vertex;
_SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex)
typedef struct spSkeletonDrawable {
spSkeleton *skeleton;
spAnimationState *animationState;
spSkeletonClipping *clipper;
spFloatArray *worldVertices;
spSdlVertexArray *sdlVertices;
spIntArray *sdlIndices;
} spSkeletonDrawable;
SP_API spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData);
SP_API void spSkeletonDrawable_destroy(spSkeletonDrawable *self);
SP_API void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta);
SP_API void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,224 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "spine-sdl-cpp.h"
#include <SDL.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
using namespace spine;
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);
}
SkeletonDrawable::~SkeletonDrawable() {
if (ownsAnimationStateData) delete animationState->getData();
delete animationState;
delete skeleton;
}
void SkeletonDrawable::update(float delta) {
animationState->update(delta);
animationState->apply(*skeleton);
skeleton->updateWorldTransform();
}
void SkeletonDrawable::draw(SDL_Renderer *renderer) {
Vector<unsigned short> quadIndices;
quadIndices.add(0);
quadIndices.add(1);
quadIndices.add(2);
quadIndices.add(2);
quadIndices.add(3);
quadIndices.add(0);
SDL_Texture *texture;
SDL_Vertex sdlVertex;
for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) {
Slot &slot = *skeleton->getDrawOrder()[i];
Attachment *attachment = slot.getAttachment();
if (!attachment) continue;
// Early out if the slot color is 0 or the bone is not active
if (slot.getColor().a == 0 || !slot.getBone().isActive()) {
clipper.clipEnd(slot);
continue;
}
Vector<float> *vertices = &worldVertices;
int verticesCount = 0;
Vector<float> *uvs = NULL;
Vector<unsigned short> *indices;
int indicesCount = 0;
Color *attachmentColor;
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment *regionAttachment = (RegionAttachment *) attachment;
attachmentColor = &regionAttachment->getColor();
// Early out if the slot color is 0
if (attachmentColor->a == 0) {
clipper.clipEnd(slot);
continue;
}
worldVertices.setSize(8, 0);
regionAttachment->computeWorldVertices(slot, worldVertices, 0, 2);
verticesCount = 4;
uvs = &regionAttachment->getUVs();
indices = &quadIndices;
indicesCount = 6;
texture = (SDL_Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->getRendererObject();
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment *mesh = (MeshAttachment *) attachment;
attachmentColor = &mesh->getColor();
// Early out if the slot color is 0
if (attachmentColor->a == 0) {
clipper.clipEnd(slot);
continue;
}
worldVertices.setSize(mesh->getWorldVerticesLength(), 0);
texture = (SDL_Texture *) ((AtlasRegion *) mesh->getRendererObject())->page->getRendererObject();
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2);
verticesCount = mesh->getWorldVerticesLength() >> 1;
uvs = &mesh->getUVs();
indices = &mesh->getTriangles();
indicesCount = indices->size();
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment();
clipper.clipStart(slot, clip);
continue;
} else
continue;
Uint8 r = static_cast<Uint8>(skeleton->getColor().r * slot.getColor().r * attachmentColor->r * 255);
Uint8 g = static_cast<Uint8>(skeleton->getColor().g * slot.getColor().g * attachmentColor->g * 255);
Uint8 b = static_cast<Uint8>(skeleton->getColor().b * slot.getColor().b * attachmentColor->b * 255);
Uint8 a = static_cast<Uint8>(skeleton->getColor().a * slot.getColor().a * attachmentColor->a * 255);
sdlVertex.color.r = r;
sdlVertex.color.g = g;
sdlVertex.color.b = b;
sdlVertex.color.a = a;
if (clipper.isClipping()) {
clipper.clipTriangles(worldVertices, *indices, *uvs, 2);
vertices = &clipper.getClippedVertices();
verticesCount = clipper.getClippedVertices().size() >> 1;
uvs = &clipper.getClippedUVs();
indices = &clipper.getClippedTriangles();
indicesCount = clipper.getClippedTriangles().size();
}
sdlVertices.clear();
for (int ii = 0; ii < verticesCount << 1; ii += 2) {
sdlVertex.position.x = (*vertices)[ii];
sdlVertex.position.y = (*vertices)[ii + 1];
sdlVertex.tex_coord.x = (*uvs)[ii];
sdlVertex.tex_coord.y = (*uvs)[ii + 1];
sdlVertices.add(sdlVertex);
}
sdlIndices.clear();
for (int ii = 0; ii < (int) indices->size(); ii++)
sdlIndices.add((*indices)[ii]);
switch (slot.getData().getBlendMode()) {
case BlendMode_Normal:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
case BlendMode_Multiply:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD);
break;
case BlendMode_Additive:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD);
break;
case BlendMode_Screen:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
break;
}
SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(),
indicesCount);
clipper.clipEnd(slot);
}
clipper.clipEnd();
}
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;
}
if (SDL_UpdateTexture(texture, nullptr, imageData, width * 4)) {
stbi_image_free(imageData);
return nullptr;
}
stbi_image_free(imageData);
return texture;
}
void SDLTextureLoader::load(AtlasPage &page, const String &path) {
SDL_Texture *texture = loadTexture(renderer, path);
if (!texture) return;
page.setRendererObject(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);
}
}
void SDLTextureLoader::unload(void *texture) {
SDL_DestroyTexture((SDL_Texture *) texture);
}
SpineExtension *spine::getDefaultExtension() {
return new DefaultSpineExtension();
}

View File

@ -0,0 +1,73 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_SDL
#define SPINE_SDL
#include <spine/spine.h>
struct SDL_Renderer;
struct SDL_Vertex;
namespace spine {
class SkeletonDrawable {
public:
SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr);
~SkeletonDrawable();
void update(float delta);
void draw(SDL_Renderer *renderer);
Skeleton *skeleton;
AnimationState *animationState;
private:
bool ownsAnimationStateData;
SkeletonClipping clipper;
Vector<float> worldVertices;
Vector<SDL_Vertex> sdlVertices;
Vector<int> sdlIndices;
};
class SDLTextureLoader : public spine::TextureLoader {
SDL_Renderer *renderer;
public:
SDLTextureLoader(SDL_Renderer *renderer) : renderer(renderer) {
}
void load(AtlasPage &page, const String &path);
void unload(void *texture);
};
}// namespace spine
#endif

7979
spine-sdl/src/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -35,8 +35,10 @@ export class ThreeJsTexture extends Texture {
constructor (image: HTMLImageElement | ImageBitmap) {
super(image);
if (image instanceof ImageBitmap) throw new Error("ImageBitmap not supported.");
this.texture = new THREE.Texture(image);
if (image instanceof ImageBitmap)
this.texture = new THREE.CanvasTexture(image);
else
this.texture = new THREE.Texture(image);
this.texture.flipY = false;
this.texture.needsUpdate = true;
}

View File

@ -153,7 +153,7 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 592567554}
m_RootOrder: 0
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
@ -256,6 +256,8 @@ MonoBehaviour:
materialsInsideMask: []
materialsOutsideMask: []
disableRenderingOnOverride: 1
updateTiming: 1
unscaledTime: 0
_animationName: run
loop: 1
timeScale: 1
@ -316,11 +318,11 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 334034152}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -5.93, y: 0, z: 5.66}
m_LocalPosition: {x: -7.83, y: 0, z: 5.66}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &541830406
GameObject:
@ -352,7 +354,7 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 592567554}
m_RootOrder: 1
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
@ -433,9 +435,11 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 1911967440}
- {fileID: 71621967}
- {fileID: 541830407}
- {fileID: 1682675646}
- {fileID: 1735507358}
m_Father: {fileID: 1799507978}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -488,6 +492,41 @@ Canvas:
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!1 &1089682726
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1089682727}
m_Layer: 0
m_Name: CustomRenderRect
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1089682727
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1089682726}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1911967440}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 126.27, y: 7.130005}
m_SizeDelta: {x: 1893.1, y: 1654.617}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1368805070
GameObject:
m_ObjectHideFlags: 0
@ -532,6 +571,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
renderTextureFadeout: {fileID: 1786065619}
renderTextureFadeoutCanvas: {fileID: 1911967443}
normalSkeletonRenderer: {fileID: 334034153}
--- !u!1 &1369381599
GameObject:
@ -805,12 +845,12 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 592567554}
m_RootOrder: 2
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -331.4, y: -186}
m_SizeDelta: {x: 1176.1, y: -365}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0.6932292, y: 0.18750001}
m_AnchoredPosition: {x: 18.079224, y: 1.5001221}
m_SizeDelta: {x: -44.942017, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1682675647
MonoBehaviour:
@ -847,12 +887,13 @@ MonoBehaviour:
m_LineSpacing: 1
m_Text: 'This scene demonstrates the problems of using conventional alpha transparency
for a fadeout effect (left), and how this problem can be fixed by using a RenderTexture
(right).
(center and right).
Spineboy on the right uses a SkeletonRenderTexture component to
render the overlapping mesh to a RenderTexture first and then draw this texture
to the scene at once using a single quad.'
The two Spineboys on the right use SkeletonRenderTexture
and SkeletonGraphicRenderTexture components to render the overlapping mesh to
a RenderTexture first and then draw this texture to the scene at once using a
single quad.'
--- !u!222 &1682675648
CanvasRenderer:
m_ObjectHideFlags: 0
@ -861,6 +902,87 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1682675645}
m_CullTransparentMesh: 1
--- !u!1 &1735507357
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1735507358}
- component: {fileID: 1735507360}
- component: {fileID: 1735507359}
m_Layer: 5
m_Name: SkeletonGraphic Notes
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1735507358
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735507357}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 592567554}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.6932292, y: 0}
m_AnchorMax: {x: 1, y: 0.18750001}
m_AnchoredPosition: {x: 6.5009766, y: -2.5753784}
m_SizeDelta: {x: -49.462, y: 1.8482}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1735507359
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735507357}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
m_FontSize: 26
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 2
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 1
m_LineSpacing: 1
m_Text: SkeletonGraphicRenderTexture supports setting a RectTransform as 'Custom
Render Rect' to define render texture bounds different from the SkeletonGraphic
rect.
--- !u!222 &1735507360
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735507357}
m_CullTransparentMesh: 1
--- !u!1 &1786065613
GameObject:
m_ObjectHideFlags: 0
@ -895,11 +1017,11 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
color: {r: 1, g: 1, b: 1, a: 1}
quadMaterial: {fileID: 2100000, guid: 4c507f887c6274a44a603d96e0eabf2a, type: 2}
targetCamera: {fileID: 0}
maxRenderTextureSize: 1024
quad: {fileID: 0}
renderTexture: {fileID: 0}
targetCamera: {fileID: 0}
quadMaterial: {fileID: 2100000, guid: 4c507f887c6274a44a603d96e0eabf2a, type: 2}
--- !u!114 &1786065615
MonoBehaviour:
m_ObjectHideFlags: 0
@ -935,6 +1057,8 @@ MonoBehaviour:
materialsInsideMask: []
materialsOutsideMask: []
disableRenderingOnOverride: 1
updateTiming: 1
unscaledTime: 0
_animationName: run
loop: 1
timeScale: 1
@ -995,11 +1119,11 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1786065613}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 3.32, y: 0, z: 5.66}
m_LocalPosition: {x: 2.38, y: 0, z: 5.66}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1786065619
MonoBehaviour:
@ -1045,3 +1169,136 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1911967439
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1911967440}
- component: {fileID: 1911967442}
- component: {fileID: 1911967441}
- component: {fileID: 1911967444}
- component: {fileID: 1911967443}
m_Layer: 0
m_Name: SkeletonGraphic (spineboy-pro)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1911967440
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1911967439}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
m_Children:
- {fileID: 1089682727}
m_Father: {fileID: 592567554}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 84, y: -317.51}
m_SizeDelta: {x: 1185.6, y: 1302.059}
m_Pivot: {x: 0.63858336, y: 0.010301443}
--- !u!114 &1911967441
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1911967439}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d85b887af7e6c3f45a2e2d2920d641bc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
skeletonDataAsset: {fileID: 11400000, guid: af38a3de26ed9b84abc2fe7c7f3b209d, type: 2}
additiveMaterial: {fileID: 2100000, guid: 2e8245019faeb8c43b75f9ca3ac8ee34, type: 2}
multiplyMaterial: {fileID: 2100000, guid: e74a1f8978a7da348a721508d0d58834, type: 2}
screenMaterial: {fileID: 2100000, guid: bab24c479f34eec45be6ea8595891569, type: 2}
initialSkinName: default
initialFlipX: 0
initialFlipY: 0
startingAnimation: run
startingLoop: 1
timeScale: 1
freeze: 0
updateWhenInvisible: 3
allowMultipleCanvasRenderers: 0
canvasRenderers: []
separatorSlotNames: []
enableSeparatorSlots: 0
separatorParts: []
updateSeparatorPartLocation: 1
disableMeshAssignmentOnOverride: 1
meshGenerator:
settings:
useClipping: 1
zSpacing: 0
pmaVertexColors: 1
tintBlack: 0
canvasGroupTintBlack: 0
calculateTangents: 0
addNormals: 0
immutableTriangles: 0
updateTiming: 1
unscaledTime: 0
--- !u!222 &1911967442
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1911967439}
m_CullTransparentMesh: 0
--- !u!114 &1911967443
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1911967439}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5fc94f89310427643babb41e000a8462, type: 3}
m_Name:
m_EditorClassIdentifier:
fadeoutSeconds: 2
--- !u!114 &1911967444
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1911967439}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6cbe1f11426513d49ad8e21e9d6643f7, type: 3}
m_Name:
m_EditorClassIdentifier:
color: {r: 1, g: 1, b: 1, a: 1}
maxRenderTextureSize: 1024
quad: {fileID: 0}
renderTexture: {fileID: 0}
targetCamera: {fileID: 0}
customRenderRect: {fileID: 1089682727}
meshRendererMaterialForTexture:
- texture: {fileID: 2800000, guid: 4ea2c33e839afb34c98f66e892b3b2d2, type: 3}
material: {fileID: 2100000, guid: f89bbf05902e77242a3ad20f3c927353, type: 2}

View File

@ -39,6 +39,7 @@ namespace Spine.Unity.Examples {
public class RenderTextureFadeoutExample : MonoBehaviour {
public SkeletonRenderTextureFadeout renderTextureFadeout;
public SkeletonRenderTextureFadeout renderTextureFadeoutCanvas;
public SkeletonRenderer normalSkeletonRenderer;
float fadeoutSeconds = 2.0f;
@ -47,8 +48,9 @@ namespace Spine.Unity.Examples {
IEnumerator Start () {
while (true) {
StartFadeoutBad();
StartFadeoutGood();
yield return new WaitForSeconds(5.0f);
StartFadeoutGood(renderTextureFadeout);
StartFadeoutGood(renderTextureFadeoutCanvas);
yield return new WaitForSeconds(fadeoutSeconds + 1.0f);
}
}
void Update () {
@ -75,12 +77,12 @@ namespace Spine.Unity.Examples {
fadeoutSecondsRemaining = fadeoutSeconds;
}
void StartFadeoutGood () {
renderTextureFadeout.gameObject.SetActive(true);
void StartFadeoutGood (SkeletonRenderTextureFadeout fadeoutComponent) {
fadeoutComponent.gameObject.SetActive(true);
// enabling the SkeletonRenderTextureFadeout component starts the fadeout.
renderTextureFadeout.enabled = true;
renderTextureFadeout.OnFadeoutComplete -= DisableGameObject;
renderTextureFadeout.OnFadeoutComplete += DisableGameObject;
fadeoutComponent.enabled = true;
fadeoutComponent.OnFadeoutComplete -= DisableGameObject;
fadeoutComponent.OnFadeoutComplete += DisableGameObject;
}
void DisableGameObject (SkeletonRenderTextureFadeout target) {

View File

@ -0,0 +1,259 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2022, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2017_2_OR_NEWER
#define HAS_VECTOR2INT
#endif
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
namespace Spine.Unity.Examples {
/// <summary>
/// When enabled, this component renders a skeleton to a RenderTexture and
/// then draws this RenderTexture at a UI RawImage quad of the same size.
/// This allows changing transparency at a single quad, which produces a more
/// natural fadeout effect.
/// Note: It is recommended to keep this component disabled as much as possible
/// because of the additional rendering overhead. Only enable it when alpha blending is required.
/// </summary>
[RequireComponent(typeof(SkeletonGraphic))]
public class SkeletonGraphicRenderTexture : SkeletonRenderTextureBase {
#if HAS_VECTOR2INT
[System.Serializable]
public struct TextureMaterialPair {
public Texture texture;
public Material material;
public TextureMaterialPair (Texture texture, Material material) {
this.texture = texture;
this.material = material;
}
}
public RectTransform customRenderRect;
protected SkeletonGraphic skeletonGraphic;
public List<TextureMaterialPair> meshRendererMaterialForTexture = new List<TextureMaterialPair>();
protected CanvasRenderer quadCanvasRenderer;
protected RawImage quadRawImage;
protected readonly Vector3[] worldCorners = new Vector3[4];
protected override void Awake () {
base.Awake();
skeletonGraphic = this.GetComponent<SkeletonGraphic>();
if (targetCamera == null) {
targetCamera = skeletonGraphic.canvas.worldCamera;
if (targetCamera == null)
targetCamera = Camera.main;
}
CreateQuadChild();
}
void CreateQuadChild () {
quad = new GameObject(this.name + " RenderTexture", typeof(CanvasRenderer), typeof(RawImage));
quad.transform.SetParent(this.transform.parent, false);
quadCanvasRenderer = quad.GetComponent<CanvasRenderer>();
quadRawImage = quad.GetComponent<RawImage>();
quadMesh = new Mesh();
quadMesh.MarkDynamic();
quadMesh.name = "RenderTexture Quad";
quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
}
void Reset () {
skeletonGraphic = this.GetComponent<SkeletonGraphic>();
AtlasAssetBase[] atlasAssets = skeletonGraphic.SkeletonDataAsset.atlasAssets;
for (int i = 0; i < atlasAssets.Length; ++i) {
foreach (var material in atlasAssets[i].Materials) {
if (material.mainTexture != null) {
meshRendererMaterialForTexture.Add(
new TextureMaterialPair(material.mainTexture, material));
}
}
}
}
void OnEnable () {
skeletonGraphic.OnInstructionsPrepared += PrepareQuad;
skeletonGraphic.AssignMeshOverrideSingleRenderer += RenderSingleMeshToRenderTexture;
skeletonGraphic.AssignMeshOverrideMultipleRenderers += RenderMultipleMeshesToRenderTexture;
skeletonGraphic.disableMeshAssignmentOnOverride = true;
skeletonGraphic.OnMeshAndMaterialsUpdated += RenderOntoQuad;
var canvasRenderers = skeletonGraphic.canvasRenderers;
for (int i = 0; i < canvasRenderers.Count; ++i)
canvasRenderers[i].cull = true;
if (quadCanvasRenderer)
quadCanvasRenderer.gameObject.SetActive(true);
}
void OnDisable () {
skeletonGraphic.OnInstructionsPrepared -= PrepareQuad;
skeletonGraphic.AssignMeshOverrideSingleRenderer -= RenderSingleMeshToRenderTexture;
skeletonGraphic.AssignMeshOverrideMultipleRenderers -= RenderMultipleMeshesToRenderTexture;
skeletonGraphic.disableMeshAssignmentOnOverride = false;
skeletonGraphic.OnMeshAndMaterialsUpdated -= RenderOntoQuad;
var canvasRenderers = skeletonGraphic.canvasRenderers;
for (int i = 0; i < canvasRenderers.Count; ++i)
canvasRenderers[i].cull = false;
if (quadCanvasRenderer)
quadCanvasRenderer.gameObject.SetActive(false);
if (renderTexture)
RenderTexture.ReleaseTemporary(renderTexture);
allocatedRenderTextureSize = Vector2Int.zero;
}
void PrepareQuad (SkeletonRendererInstruction instruction) {
PrepareForMesh();
SetupQuad();
}
void RenderOntoQuad (SkeletonGraphic skeletonRenderer) {
AssignAtQuad();
}
protected void PrepareForMesh () {
// We need to get the min/max of all four corners, rotation of the skeleton
// in combination with perspective projection otherwise might lead to incorrect
// screen space min/max.
RectTransform rectTransform = customRenderRect ? customRenderRect : skeletonGraphic.rectTransform;
rectTransform.GetWorldCorners(worldCorners);
RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode;
Vector3 screenCorner0, screenCorner1, screenCorner2, screenCorner3;
// note: world corners are ordered bottom left, top left, top right, bottom right.
// This corresponds to 0, 3, 1, 2 in our desired order.
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
screenCorner0 = worldCorners[0];
screenCorner1 = worldCorners[3];
screenCorner2 = worldCorners[1];
screenCorner3 = worldCorners[2];
} else {
screenCorner0 = targetCamera.WorldToScreenPoint(worldCorners[0]);
screenCorner1 = targetCamera.WorldToScreenPoint(worldCorners[3]);
screenCorner2 = targetCamera.WorldToScreenPoint(worldCorners[1]);
screenCorner3 = targetCamera.WorldToScreenPoint(worldCorners[2]);
}
// To avoid perspective distortion when rotated, we project all vertices
// onto a plane parallel to the view frustum near plane.
// Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders.
float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f;
screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth;
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
worldCornerNoDistortion0 = screenCorner0;
worldCornerNoDistortion1 = screenCorner1;
worldCornerNoDistortion2 = screenCorner2;
worldCornerNoDistortion3 = screenCorner3;
} else {
worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0);
worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1);
worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
}
Vector3 screenSpaceMin, screenSpaceMax;
PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
screenCorner0, screenCorner1, screenCorner2, screenCorner3);
PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
}
protected Material MeshRendererMaterialForTexture (Texture texture) {
return meshRendererMaterialForTexture.Find(x => x.texture == texture).material;
}
protected void RenderSingleMeshToRenderTexture (Mesh mesh, Material graphicMaterial, Texture texture) {
Material meshRendererMaterial = MeshRendererMaterialForTexture(texture);
commandBuffer.DrawMesh(mesh, transform.localToWorldMatrix, meshRendererMaterial, 0, -1);
Graphics.ExecuteCommandBuffer(commandBuffer);
}
protected void RenderMultipleMeshesToRenderTexture (int meshCount,
Mesh[] meshes, Material[] graphicMaterials, Texture[] textures) {
for (int i = 0; i < meshCount; ++i) {
Material meshRendererMaterial = MeshRendererMaterialForTexture(textures[i]);
commandBuffer.DrawMesh(meshes[i], transform.localToWorldMatrix, meshRendererMaterial, 0, -1);
}
Graphics.ExecuteCommandBuffer(commandBuffer);
}
protected void SetupQuad () {
quadRawImage.texture = this.renderTexture;
quadRawImage.color = color;
quadCanvasRenderer.SetColor(color);
var srcRectTransform = skeletonGraphic.rectTransform;
var dstRectTransform = quadRawImage.rectTransform;
dstRectTransform.anchorMin = srcRectTransform.anchorMin;
dstRectTransform.anchorMax = srcRectTransform.anchorMax;
dstRectTransform.anchoredPosition = srcRectTransform.anchoredPosition;
dstRectTransform.pivot = srcRectTransform.pivot;
dstRectTransform.localScale = srcRectTransform.localScale;
dstRectTransform.sizeDelta = srcRectTransform.sizeDelta;
dstRectTransform.rotation = srcRectTransform.rotation;
}
protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
commandBuffer.Clear();
commandBuffer.SetRenderTarget(renderTexture);
commandBuffer.ClearRenderTarget(true, true, Color.clear);
Rect canvasRect = skeletonGraphic.canvas.pixelRect;
Matrix4x4 projectionMatrix = Matrix4x4.Ortho(
canvasRect.x, canvasRect.x + canvasRect.width,
canvasRect.y, canvasRect.y + canvasRect.height,
float.MinValue, float.MaxValue);
RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode;
if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) {
commandBuffer.SetViewMatrix(Matrix4x4.identity);
commandBuffer.SetProjectionMatrix(projectionMatrix);
} else {
commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix);
commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix);
}
Vector2 targetCameraViewportSize = targetCamera.pixelRect.size;
commandBuffer.SetViewport(new Rect(-screenSpaceMin, targetCameraViewportSize));
}
protected override void AssignMeshAtRenderer () {
quadCanvasRenderer.SetMesh(quadMesh);
}
#endif // HAS_VECTOR2INT
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6cbe1f11426513d49ad8e21e9d6643f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -35,7 +35,6 @@
#define HAS_GET_SHARED_MATERIALS
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
@ -54,20 +53,11 @@ namespace Spine.Unity.Examples {
public class SkeletonRenderTexture : SkeletonRenderTextureBase {
#if HAS_GET_SHARED_MATERIALS
public Material quadMaterial;
public Camera targetCamera;
protected SkeletonRenderer skeletonRenderer;
protected MeshRenderer meshRenderer;
protected MeshFilter meshFilter;
protected MeshRenderer quadMeshRenderer;
protected MeshFilter quadMeshFilter;
protected Vector3 worldCornerNoDistortion0;
protected Vector3 worldCornerNoDistortion1;
protected Vector3 worldCornerNoDistortion2;
protected Vector3 worldCornerNoDistortion3;
protected Vector2 uvCorner0;
protected Vector2 uvCorner1;
protected Vector2 uvCorner2;
protected Vector2 uvCorner3;
private MaterialPropertyBlock propertyBlock;
private readonly List<Material> materials = new List<Material>();
@ -160,40 +150,13 @@ namespace Spine.Unity.Examples {
worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
Vector3 screenSpaceMin =
Vector3.Min(screenCorner0, Vector3.Min(screenCorner1,
Vector3.Min(screenCorner2, screenCorner3)));
Vector3 screenSpaceMax =
Vector3.Max(screenCorner0, Vector3.Max(screenCorner1,
Vector3.Max(screenCorner2, screenCorner3)));
// ensure we are on whole pixel borders
screenSpaceMin.x = Mathf.Floor(screenSpaceMin.x);
screenSpaceMin.y = Mathf.Floor(screenSpaceMin.y);
screenSpaceMax.x = Mathf.Ceil(screenSpaceMax.x);
screenSpaceMax.y = Mathf.Ceil(screenSpaceMax.y);
// inverse-map screenCornerN to screenSpaceMin/screenSpaceMax area to get UV coordinates
uvCorner0 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner0);
uvCorner1 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner1);
uvCorner2 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner2);
uvCorner3 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner3);
requiredRenderTextureSize = new Vector2Int(
Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.x - (int)screenSpaceMin.x)),
Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.y - (int)screenSpaceMin.y)));
PrepareRenderTexture();
Vector3 screenSpaceMin, screenSpaceMax;
PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
screenCorner0, screenCorner1, screenCorner2, screenCorner3);
PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
}
protected Vector2 InverseLerp (Vector2 a, Vector2 b, Vector2 value) {
return new Vector2(
(value.x - a.x) / (b.x - a.x),
(value.y - a.y) / (b.y - a.y));
}
protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
commandBuffer.Clear();
commandBuffer.SetRenderTarget(renderTexture);
commandBuffer.ClearRenderTarget(true, true, Color.clear);
@ -214,40 +177,7 @@ namespace Spine.Unity.Examples {
Graphics.ExecuteCommandBuffer(commandBuffer);
}
protected void AssignAtQuad () {
Transform quadTransform = quadMeshRenderer.transform;
quadTransform.position = this.transform.position;
quadTransform.rotation = this.transform.rotation;
quadTransform.localScale = this.transform.localScale;
Vector3 v0 = quadTransform.InverseTransformPoint(worldCornerNoDistortion0);
Vector3 v1 = quadTransform.InverseTransformPoint(worldCornerNoDistortion1);
Vector3 v2 = quadTransform.InverseTransformPoint(worldCornerNoDistortion2);
Vector3 v3 = quadTransform.InverseTransformPoint(worldCornerNoDistortion3);
Vector3[] vertices = new Vector3[4] { v0, v1, v2, v3 };
quadMesh.vertices = vertices;
int[] indices = new int[6] { 0, 2, 1, 2, 3, 1 };
quadMesh.triangles = indices;
Vector3[] normals = new Vector3[4] {
-Vector3.forward,
-Vector3.forward,
-Vector3.forward,
-Vector3.forward
};
quadMesh.normals = normals;
float maxU = (float)requiredRenderTextureSize.x / (float)allocatedRenderTextureSize.x;
float maxV = (float)requiredRenderTextureSize.y / (float)allocatedRenderTextureSize.y;
Vector2[] uv = new Vector2[4] {
new Vector2(uvCorner0.x * maxU, uvCorner0.y * maxV),
new Vector2(uvCorner1.x * maxU, uvCorner1.y * maxV),
new Vector2(uvCorner2.x * maxU, uvCorner2.y * maxV),
new Vector2(uvCorner3.x * maxU, uvCorner3.y * maxV),
};
quadMesh.uv = uv;
protected override void AssignMeshAtRenderer () {
quadMeshFilter.mesh = quadMesh;
quadMeshRenderer.sharedMaterial.mainTexture = this.renderTexture;
quadMeshRenderer.sharedMaterial.color = color;

View File

@ -27,31 +27,38 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2019_3_OR_NEWER
#define HAS_FORCE_RENDER_OFF
#endif
#if UNITY_2018_2_OR_NEWER
#define HAS_GET_SHARED_MATERIALS
#if UNITY_2017_2_OR_NEWER
#define HAS_VECTOR2INT
#endif
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace Spine.Unity.Examples {
public abstract class SkeletonRenderTextureBase : MonoBehaviour {
#if HAS_GET_SHARED_MATERIALS
#if HAS_VECTOR2INT
public Color color = Color.white;
public int maxRenderTextureSize = 1024;
public GameObject quad;
protected Mesh quadMesh;
public RenderTexture renderTexture;
public Camera targetCamera;
protected CommandBuffer commandBuffer;
protected Vector2Int requiredRenderTextureSize;
protected Vector2Int allocatedRenderTextureSize;
protected Vector3 worldCornerNoDistortion0;
protected Vector3 worldCornerNoDistortion1;
protected Vector3 worldCornerNoDistortion2;
protected Vector3 worldCornerNoDistortion3;
protected Vector2 uvCorner0;
protected Vector2 uvCorner1;
protected Vector2 uvCorner2;
protected Vector2 uvCorner3;
protected virtual void Awake () {
commandBuffer = new CommandBuffer();
}
@ -61,6 +68,34 @@ namespace Spine.Unity.Examples {
RenderTexture.ReleaseTemporary(renderTexture);
}
protected void PrepareTextureMapping (out Vector3 screenSpaceMin, out Vector3 screenSpaceMax,
Vector3 screenCorner0, Vector3 screenCorner1, Vector3 screenCorner2, Vector3 screenCorner3) {
screenSpaceMin =
Vector3.Min(screenCorner0, Vector3.Min(screenCorner1,
Vector3.Min(screenCorner2, screenCorner3)));
screenSpaceMax =
Vector3.Max(screenCorner0, Vector3.Max(screenCorner1,
Vector3.Max(screenCorner2, screenCorner3)));
// ensure we are on whole pixel borders
screenSpaceMin.x = Mathf.Floor(screenSpaceMin.x);
screenSpaceMin.y = Mathf.Floor(screenSpaceMin.y);
screenSpaceMax.x = Mathf.Ceil(screenSpaceMax.x);
screenSpaceMax.y = Mathf.Ceil(screenSpaceMax.y);
// inverse-map screenCornerN to screenSpaceMin/screenSpaceMax area to get UV coordinates
uvCorner0 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner0);
uvCorner1 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner1);
uvCorner2 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner2);
uvCorner3 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner3);
requiredRenderTextureSize = new Vector2Int(
Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.x - (int)screenSpaceMin.x)),
Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.y - (int)screenSpaceMin.y)));
PrepareRenderTexture();
}
protected void PrepareRenderTexture () {
Vector2Int textureSize = new Vector2Int(
Mathf.NextPowerOfTwo(requiredRenderTextureSize.x),
@ -74,6 +109,45 @@ namespace Spine.Unity.Examples {
allocatedRenderTextureSize = textureSize;
}
}
#endif
protected void AssignAtQuad () {
Transform quadTransform = quad.transform;
quadTransform.position = this.transform.position;
quadTransform.rotation = this.transform.rotation;
quadTransform.localScale = this.transform.localScale;
Vector3 v0 = quadTransform.InverseTransformPoint(worldCornerNoDistortion0);
Vector3 v1 = quadTransform.InverseTransformPoint(worldCornerNoDistortion1);
Vector3 v2 = quadTransform.InverseTransformPoint(worldCornerNoDistortion2);
Vector3 v3 = quadTransform.InverseTransformPoint(worldCornerNoDistortion3);
Vector3[] vertices = new Vector3[4] { v0, v1, v2, v3 };
quadMesh.vertices = vertices;
int[] indices = new int[6] { 0, 1, 2, 2, 1, 3 };
quadMesh.triangles = indices;
Vector3[] normals = new Vector3[4] {
-Vector3.forward,
-Vector3.forward,
-Vector3.forward,
-Vector3.forward
};
quadMesh.normals = normals;
float maxU = (float)requiredRenderTextureSize.x / (float)allocatedRenderTextureSize.x;
float maxV = (float)requiredRenderTextureSize.y / (float)allocatedRenderTextureSize.y;
Vector2[] uv = new Vector2[4] {
new Vector2(uvCorner0.x * maxU, uvCorner0.y * maxV),
new Vector2(uvCorner1.x * maxU, uvCorner1.y * maxV),
new Vector2(uvCorner2.x * maxU, uvCorner2.y * maxV),
new Vector2(uvCorner3.x * maxU, uvCorner3.y * maxV),
};
quadMesh.uv = uv;
AssignMeshAtRenderer();
}
protected abstract void AssignMeshAtRenderer ();
#endif // HAS_VECTOR2INT
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccf9b5e5034b0ea45962f9cf32168dd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -183,6 +183,59 @@ namespace Spine.Unity {
#endregion
#region Overrides
// API for taking over rendering.
/// <summary>When true, no meshes and materials are assigned at CanvasRenderers if the used override
/// AssignMeshOverrideSingleRenderer or AssignMeshOverrideMultipleRenderers is non-null.</summary>
public bool disableMeshAssignmentOnOverride = true;
/// <summary>Delegate type for overriding mesh and material assignment,
/// used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
/// <param name="mesh">Mesh normally assigned at the main CanvasRenderer.</param>
/// <param name="graphicMaterial">Material normally assigned at the main CanvasRenderer.</param>
/// <param name="texture">Texture normally assigned at the main CanvasRenderer.</param>
public delegate void MeshAssignmentDelegateSingle (Mesh mesh, Material graphicMaterial, Texture texture);
/// <param name="meshCount">Number of meshes. Don't use <c>meshes.Length</c> as this might be higher
/// due to pre-allocated entries.</param>
/// <param name="meshes">Mesh array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
/// <param name="graphicMaterials">Material array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
/// <param name="textures">Texture array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
public delegate void MeshAssignmentDelegateMultiple (int meshCount, Mesh[] meshes, Material[] graphicMaterials, Texture[] textures);
event MeshAssignmentDelegateSingle assignMeshOverrideSingle;
event MeshAssignmentDelegateMultiple assignMeshOverrideMultiple;
/// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
/// Used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
public event MeshAssignmentDelegateSingle AssignMeshOverrideSingleRenderer {
add {
assignMeshOverrideSingle += value;
if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle != null) {
Initialize(false);
}
}
remove {
assignMeshOverrideSingle -= value;
if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle == null) {
Initialize(false);
}
}
}
/// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
/// Used when <c>allowMultipleCanvasRenderers</c> is true.</summary>
public event MeshAssignmentDelegateMultiple AssignMeshOverrideMultipleRenderers {
add {
assignMeshOverrideMultiple += value;
if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple != null) {
Initialize(false);
}
}
remove {
assignMeshOverrideMultiple -= value;
if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple == null) {
Initialize(false);
}
}
}
[System.NonSerialized] readonly Dictionary<Texture, Texture> customTextureOverride = new Dictionary<Texture, Texture>();
/// <summary>Use this Dictionary to override a Texture with a different Texture.</summary>
public Dictionary<Texture, Texture> CustomTextureOverride { get { return customTextureOverride; } }
@ -334,6 +387,8 @@ namespace Spine.Unity {
if (updateMode != UpdateMode.FullUpdate) return;
PrepareInstructionsAndRenderers();
if (OnInstructionsPrepared != null)
OnInstructionsPrepared(this.currentInstructions);
SetVerticesDirty(); // triggers Rebuild and avoids potential double-update in a single frame
}
@ -390,12 +445,19 @@ namespace Spine.Unity {
public bool IsValid { get { return skeleton != null; } }
public delegate void SkeletonRendererDelegate (SkeletonGraphic skeletonGraphic);
public delegate void InstructionDelegate (SkeletonRendererInstruction instruction);
/// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
public event SkeletonRendererDelegate OnRebuild;
/// <summary>OnMeshAndMaterialsUpdated is at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
/// <summary>OnInstructionsPrepared is raised at the end of <c>LateUpdate</c> after render instructions
/// are done, target renderers are prepared, and the mesh is ready to be generated.</summary>
public event InstructionDelegate OnInstructionsPrepared;
/// <summary>OnMeshAndMaterialsUpdated is raised at the end of <c>Rebuild</c> after the Mesh and
/// all materials have been updated. Note that some Unity API calls are not permitted to be issued from
/// <c>Rebuild</c>, so you may want to subscribe to <see cref="OnInstructionsPrepared"/> instead
/// from where you can issue such preparation calls.</summary>
public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
protected Spine.AnimationState state;
@ -411,6 +473,12 @@ namespace Spine.Unity {
DoubleBuffered<Spine.Unity.MeshRendererBuffers.SmartMesh> meshBuffers;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
readonly ExposedList<Mesh> meshes = new ExposedList<Mesh>();
readonly ExposedList<Material> usedMaterials = new ExposedList<Material>();
readonly ExposedList<Texture> usedTextures = new ExposedList<Texture>();
public ExposedList<Mesh> MeshesMultipleCanvasRenderers { get { return meshes; } }
public ExposedList<Material> MaterialsMultipleCanvasRenderers { get { return usedMaterials; } }
public ExposedList<Texture> TexturesMultipleCanvasRenderers { get { return usedTextures; } }
public Mesh GetLastMesh () {
return meshBuffers.GetCurrent().mesh;
@ -513,6 +581,8 @@ namespace Spine.Unity {
for (int i = 0; i < canvasRenderers.Count; ++i)
canvasRenderers[i].Clear();
DestroyMeshes();
usedMaterials.Clear();
usedTextures.Clear();
DisposeMeshBuffers();
}
@ -601,6 +671,7 @@ namespace Spine.Unity {
int submeshCount = currentInstructions.submeshInstructions.Count;
EnsureCanvasRendererCount(submeshCount);
EnsureMeshesCount(submeshCount);
EnsureUsedTexturesAndMaterialsCount(submeshCount);
EnsureSeparatorPartCount();
PrepareRendererGameObjects(currentInstructions);
}
@ -618,6 +689,7 @@ namespace Spine.Unity {
if (!this.allowMultipleCanvasRenderers) {
UpdateMeshSingleCanvasRenderer(currentInstructions);
} else {
UpdateMaterialsMultipleCanvasRenderers(currentInstructions);
UpdateMeshMultipleCanvasRenderers(currentInstructions);
}
@ -675,19 +747,67 @@ namespace Spine.Unity {
if (updateTriangles) meshGenerator.FillTriangles(mesh);
meshGenerator.FillLateVertexData(mesh);
canvasRenderer.SetMesh(mesh);
smartMesh.instructionUsed.Set(currentInstructions);
if (assignMeshOverrideSingle != null)
assignMeshOverrideSingle(mesh, this.canvasRenderer.GetMaterial(), this.mainTexture);
bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride);
if (assignAtCanvasRenderer)
canvasRenderer.SetMesh(mesh);
else
canvasRenderer.SetMesh(null);
if (currentInstructions.submeshInstructions.Count > 0) {
var material = currentInstructions.submeshInstructions.Items[0].material;
if (material != null && baseTexture != material.mainTexture) {
baseTexture = material.mainTexture;
if (overrideTexture == null)
if (overrideTexture == null && assignAtCanvasRenderer)
canvasRenderer.SetTexture(this.mainTexture);
}
}
}
//this.UpdateMaterial(); // note: This would allocate memory.
protected void UpdateMaterialsMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) {
int submeshCount = currentInstructions.submeshInstructions.Count;
bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0);
BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials;
bool hasBlendModeMaterials = blendModeMaterials.RequiresBlendModeMaterials;
bool pmaVertexColors = meshGenerator.settings.pmaVertexColors;
Material[] usedMaterialItems = usedMaterials.Items;
Texture[] usedTextureItems = usedTextures.Items;
for (int i = 0; i < submeshCount; i++) {
var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
var submeshMaterial = submeshInstructionItem.material;
if (useOriginalTextureAndMaterial) {
usedTextureItems[i] = submeshMaterial.mainTexture;
if (!hasBlendModeMaterials) {
usedMaterialItems[i] = this.materialForRendering;
} else {
BlendMode blendMode = blendModeMaterials.BlendModeForMaterial(submeshMaterial);
Material usedMaterial = this.materialForRendering;
if (blendMode == BlendMode.Additive && !pmaVertexColors && additiveMaterial) {
usedMaterial = additiveMaterial;
} else if (blendMode == BlendMode.Multiply && multiplyMaterial)
usedMaterial = multiplyMaterial;
else if (blendMode == BlendMode.Screen && screenMaterial)
usedMaterial = screenMaterial;
usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial);
}
} else {
var originalTexture = submeshMaterial.mainTexture;
Material usedMaterial;
Texture usedTexture;
if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial))
usedMaterial = material;
if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture))
usedTexture = originalTexture;
usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial);
usedTextureItems[i] = usedTexture;
}
}
}
protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) {
@ -705,6 +825,9 @@ namespace Spine.Unity {
bool mainCullTransparentMesh = this.canvasRenderer.cullTransparentMesh;
#endif
bool pmaVertexColors = meshGenerator.settings.pmaVertexColors;
Material[] usedMaterialItems = usedMaterials.Items;
Texture[] usedTextureItems = usedTextures.Items;
bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride);
for (int i = 0; i < submeshCount; i++) {
var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
meshGenerator.Begin();
@ -717,53 +840,31 @@ namespace Spine.Unity {
meshGenerator.FillTriangles(targetMesh);
meshGenerator.FillLateVertexData(targetMesh);
var submeshMaterial = submeshInstructionItem.material;
var canvasRenderer = canvasRenderers[i];
canvasRenderer.SetMesh(targetMesh);
canvasRenderer.materialCount = 1;
if (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride)
canvasRenderer.SetMesh(targetMesh);
else
canvasRenderer.SetMesh(null);
SkeletonSubmeshGraphic submeshGraphic = submeshGraphics[i];
if (useOriginalTextureAndMaterial) {
Texture usedTexture = submeshMaterial.mainTexture;
if (!hasBlendModeMaterials)
canvasRenderer.SetMaterial(this.materialForRendering, usedTexture);
else {
bool allowCullTransparentMesh = true;
BlendMode blendMode = blendModeMaterials.BlendModeForMaterial(submeshMaterial);
Material usedMaterial = this.materialForRendering;
if (blendMode == BlendMode.Normal) {
if (submeshInstructionItem.hasPMAAdditiveSlot)
allowCullTransparentMesh = false;
} else if (blendMode == BlendMode.Additive) {
if (pmaVertexColors)
allowCullTransparentMesh = false;
else if (additiveMaterial)
usedMaterial = additiveMaterial;
} else if (blendMode == BlendMode.Multiply && multiplyMaterial)
usedMaterial = multiplyMaterial;
else if (blendMode == BlendMode.Screen && screenMaterial)
usedMaterial = screenMaterial;
usedMaterial = submeshGraphic.GetModifiedMaterial(usedMaterial);
canvasRenderer.SetMaterial(usedMaterial, usedTexture);
#if HAS_CULL_TRANSPARENT_MESH
canvasRenderer.cullTransparentMesh = allowCullTransparentMesh ?
mainCullTransparentMesh : false;
#endif
if (useOriginalTextureAndMaterial && hasBlendModeMaterials) {
bool allowCullTransparentMesh = true;
BlendMode materialBlendMode = blendModeMaterials.BlendModeForMaterial(usedMaterialItems[i]);
if ((materialBlendMode == BlendMode.Normal && submeshInstructionItem.hasPMAAdditiveSlot) ||
(materialBlendMode == BlendMode.Additive && pmaVertexColors)) {
allowCullTransparentMesh = false;
}
} else {
var originalTexture = submeshMaterial.mainTexture;
Material usedMaterial;
Texture usedTexture;
if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial))
usedMaterial = material;
if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture))
usedTexture = originalTexture;
usedMaterial = submeshGraphic.GetModifiedMaterial(usedMaterial);
canvasRenderer.SetMaterial(usedMaterial, usedTexture);
#if HAS_CULL_TRANSPARENT_MESH
canvasRenderer.cullTransparentMesh = allowCullTransparentMesh ?
mainCullTransparentMesh : false;
#endif
}
canvasRenderer.materialCount = 1;
if (assignAtCanvasRenderer)
canvasRenderer.SetMaterial(usedMaterialItems[i], usedTextureItems[i]);
}
if (assignMeshOverrideMultiple != null)
assignMeshOverrideMultiple(submeshCount, meshesItems, usedMaterialItems, usedTextureItems);
}
protected void EnsureCanvasRendererCount (int targetCount) {
@ -856,6 +957,16 @@ namespace Spine.Unity {
meshes.Add(SpineMesh.NewSkeletonMesh());
}
protected void EnsureUsedTexturesAndMaterialsCount (int targetCount) {
int oldCount = usedMaterials.Count;
usedMaterials.EnsureCapacity(targetCount);
usedTextures.EnsureCapacity(targetCount);
for (int i = oldCount; i < targetCount; i++) {
usedMaterials.Add(null);
usedTextures.Add(null);
}
}
protected void DestroyMeshes () {
foreach (var mesh in meshes) {
#if UNITY_EDITOR

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2022, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
public static class MathUtilities {
public static float InverseLerp (float a, float b, float value) {
return (value - a) / (b - a);
}
/// <summary>
/// Returns the linear interpolation ratio of <c>a</c> to <c>b</c> that <c>value</c> lies on.
/// This is the t value that fulfills <c>value = lerp(a, b, t)</c>.
/// </summary>
public static Vector2 InverseLerp (Vector2 a, Vector2 b, Vector2 value) {
return new Vector2(
(value.x - a.x) / (b.x - a.x),
(value.y - a.y) / (b.y - a.y));
}
/// <summary>
/// Returns the linear interpolation ratio of <c>a</c> to <c>b</c> that <c>value</c> lies on.
/// This is the t value that fulfills <c>value = lerp(a, b, t)</c>.
/// </summary>
public static Vector3 InverseLerp (Vector3 a, Vector3 b, Vector3 value) {
return new Vector3(
(value.x - a.x) / (b.x - a.x),
(value.y - a.y) / (b.y - a.y),
(value.z - a.z) / (b.z - a.z));
}
/// <summary>
/// Returns the linear interpolation ratio of <c>a</c> to <c>b</c> that <c>value</c> lies on.
/// This is the t value that fulfills <c>value = lerp(a, b, t)</c>.
/// </summary>
public static Vector4 InverseLerp (Vector4 a, Vector4 b, Vector4 value) {
return new Vector4(
(value.x - a.x) / (b.x - a.x),
(value.y - a.y) / (b.y - a.y),
(value.z - a.z) / (b.z - a.z),
(value.w - a.w) / (b.w - a.w));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef2b5da9383ed474d895b702a9baf79e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: