Spine iOS (#2504)
* Add `spine-iOS` SPM package & example app (#1) * Basic Mesh Rendering (#2) * Spine C++ Swift Wrapper (#3) * Load `Atlas` & `SkeletonData` (#4) Load & dispose `Atlas` & `SkeletonData` from bundled files. * Generate Swift classes from `spine-cpp-lite.h` (#5) * Draw `SkeletonData` render commands (#6) - Use `SkeletonData` render commands in the renderer - Simple loop for animation support * Add `BoundsProvider` (#7) - Implement & support `BoundsProvider` classes - Introduce alignment and content mode - Update c to swift script to return optional for find prefixed methods * Support `SpineController` & `Event` callbacks (#8) - Support SpineController callbacks - Support Event callbacks - Apply tint color in renderer * Support `DressUp` sample (#9) - Add `DressUp` sample - Move SpineViewController to SpineUIView - Implement SpineUIView export to image * Remove unused file * Add `Physics` sample (#10) - Add `Physics` sample - Fix offsets in `IKFollowing` sample - Fix `SpineView` background color * Add `DebugRendering` sample (#11) - Add `DebugRendering` sample - Make `SpineUIView` transparent * Move remaining files to SPM package (#12) - Move remaining files to SPM package - Rename `SpineWrapper` to `SpineCppLite` * Load assets from different sources (#13) - Load from bundle, file, http & drawable - Apply correct blend mode & pma in renderer * Add `Obj-C` + `UIKit` sample (#14) - Add `Obj-C` + `UIKit` sample - Update `Spine` to be usable in Obj-C code base * Support CocoaPods (#15) * Metal Best Practices (#16) - Tripple Buffering - Buffer Bindings - Shared Objects * Annotate functions that should return optional (#17) * Add option to disable drawing when out of viewport (#18) - Add option to disable drawing when out of viewport - Move update clock to controller so multiple views can share it * Add docs for public Spine classes/methods (#19) * Fix various regressions (#20) - Fix retain `SpineController` retain cycle - Fix issue wehre images were not rendered
39
Package.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// swift-tools-version: 5.9
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "spine-ios",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v13)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "Spine",
|
||||||
|
targets: ["Spine"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Spine",
|
||||||
|
dependencies: [
|
||||||
|
"SpineCppLite", "SpineShadersStructs"
|
||||||
|
],
|
||||||
|
path: "spine-ios/Sources/Spine",
|
||||||
|
swiftSettings: [
|
||||||
|
.interoperabilityMode(.Cxx)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "SpineCppLite",
|
||||||
|
path: "spine-ios/Sources/SpineCppLite"
|
||||||
|
),
|
||||||
|
.systemLibrary(
|
||||||
|
name: "SpineShadersStructs",
|
||||||
|
path: "spine-ios/Sources/SpineShadersStructs"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
cxxLanguageStandard: .cxx11
|
||||||
|
)
|
||||||
27
Spine.podspec
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#
|
||||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||||
|
# Run `pod lib lint spine_flutter.podspec` to validate before publishing.
|
||||||
|
#
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'Spine'
|
||||||
|
s.version = '0.0.1'
|
||||||
|
s.summary = 'Spine runtimes for iOS.'
|
||||||
|
s.description = <<-DESC
|
||||||
|
Spine runtimes for iOS.
|
||||||
|
DESC
|
||||||
|
s.homepage = 'https://esotericsoftware.com'
|
||||||
|
s.author = { "Esoteric Software LLC " => "https://esotericsoftware.com" }
|
||||||
|
s.license = { :file => 'LICENSE' }
|
||||||
|
|
||||||
|
s.source = { :git => 'https://github.com/denrase/spine-runtimes.git', :branch => 'cocoapods' }
|
||||||
|
s.source_files = 'spine-ios/Sources/Spine/**/*.{swift,metal}'
|
||||||
|
s.platform = :ios, '13.0'
|
||||||
|
|
||||||
|
s.xcconfig = {
|
||||||
|
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/SpineCppLite/spine-cpp/spine-cpp/include" "$(PODS_ROOT)/SpineCppLite/spine-cpp/spine-cpp-lite"'
|
||||||
|
}
|
||||||
|
|
||||||
|
s.swift_version = '5.0'
|
||||||
|
s.dependency 'SpineCppLite'
|
||||||
|
s.dependency 'SpineShadersStructs'
|
||||||
|
end
|
||||||
25
SpineCppLite.podspec
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||||
|
# Run `pod lib lint spine_flutter.podspec` to validate before publishing.
|
||||||
|
#
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'SpineCppLite'
|
||||||
|
s.version = '0.0.1'
|
||||||
|
s.summary = 'Spine runtimes for iOS.'
|
||||||
|
s.description = <<-DESC
|
||||||
|
Spine runtimes for iOS.
|
||||||
|
DESC
|
||||||
|
s.homepage = 'https://esotericsoftware.com'
|
||||||
|
s.author = { "Esoteric Software LLC " => "https://esotericsoftware.com" }
|
||||||
|
s.license = { :file => 'LICENSE' }
|
||||||
|
s.platform = :ios, '13.0'
|
||||||
|
|
||||||
|
s.source = { :git => 'https://github.com/denrase/spine-runtimes.git', :branch => 'cocoapods' }
|
||||||
|
s.source_files = 'spine-cpp/spine-cpp/**/*.{h,cpp}', 'spine-cpp/spine-cpp-lite/*.{h,cpp}'
|
||||||
|
s.module_map = 'spine-cpp/spine-cpp-lite/module.modulemap'
|
||||||
|
s.pod_target_xcconfig = {
|
||||||
|
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/SpineCppLite/spine-cpp/spine-cpp/include" "$(PODS_ROOT)/SpineCppLite/spine-cpp/spine-cpp-lite"',
|
||||||
|
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
|
||||||
|
'CLANG_CXX_LIBRARY' => 'libc++'
|
||||||
|
}
|
||||||
|
end
|
||||||
24
SpineShadersStructs.podspec
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#
|
||||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||||
|
# Run `pod lib lint spine_flutter.podspec` to validate before publishing.
|
||||||
|
#
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'SpineShadersStructs'
|
||||||
|
s.version = '0.0.1'
|
||||||
|
s.summary = 'Metal shaders structs for spine'
|
||||||
|
s.description = <<-DESC
|
||||||
|
Metal shaders structs for spine.
|
||||||
|
DESC
|
||||||
|
s.homepage = 'https://esotericsoftware.com'
|
||||||
|
s.author = { "Esoteric Software LLC " => "https://esotericsoftware.com" }
|
||||||
|
s.license = { :file => 'LICENSE' }
|
||||||
|
s.platform = :ios, '13.0'
|
||||||
|
|
||||||
|
s.source = { :git => 'https://github.com/denrase/spine-runtimes.git', :branch => 'cocoapods' }
|
||||||
|
s.source_files = 'spine-ios/Sources/SpineShadersStructs/*.{h,cpp}'
|
||||||
|
|
||||||
|
s.pod_target_xcconfig = {
|
||||||
|
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
|
||||||
|
'CLANG_CXX_LIBRARY' => 'libc++'
|
||||||
|
}
|
||||||
|
end
|
||||||
4
spine-cpp/spine-cpp-lite/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module SpineCppLite {
|
||||||
|
header "spine-cpp-lite.h"
|
||||||
|
export *
|
||||||
|
}
|
||||||
556
spine-cpp/spine-cpp-lite/spine-cpp-lite-codegen.py
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
input_path = os.path.join(script_directory, 'spine-cpp-lite.h')
|
||||||
|
|
||||||
|
with open(input_path, 'r') as file:
|
||||||
|
file_contents = file.read()
|
||||||
|
|
||||||
|
supported_types_to_swift_types = {
|
||||||
|
'void *': 'UnsafeMutableRawPointer',
|
||||||
|
'const utf8 *': 'String?',
|
||||||
|
'uint64_t': 'UInt64',
|
||||||
|
'float *': 'Float?',
|
||||||
|
'float': 'Float',
|
||||||
|
'int32_t': 'Int32',
|
||||||
|
'utf8 *': 'String?',
|
||||||
|
'int32_t *': 'Int32?',
|
||||||
|
'uint16_t *': 'UInt16',
|
||||||
|
'spine_bool': 'Bool'
|
||||||
|
}
|
||||||
|
|
||||||
|
def read_spine_types(data):
|
||||||
|
types_start = data.find('// @start: opaque_types') + len('// @start: opaque_types')
|
||||||
|
types_end = data.find('// @end: paque_types')
|
||||||
|
types_section = data[types_start:types_end]
|
||||||
|
return re.findall(r'SPINE_OPAQUE_TYPE\(([^)]+)\)', types_section)
|
||||||
|
|
||||||
|
def read_spine_function_declarations(data):
|
||||||
|
declarations_start = data.find('// @start: function_declarations') + len('// @start: function_declarations')
|
||||||
|
declarations_end = data.find('// @end: function_declarations')
|
||||||
|
declarations_section = data[declarations_start:declarations_end]
|
||||||
|
lines = declarations_section.split('\n')
|
||||||
|
|
||||||
|
filtered_lines = []
|
||||||
|
ignore_next = False
|
||||||
|
next_returns_optional = False
|
||||||
|
for line in lines:
|
||||||
|
if ignore_next:
|
||||||
|
ignore_next = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
if next_returns_optional:
|
||||||
|
next_returns_optional = False
|
||||||
|
line = line + "?"
|
||||||
|
|
||||||
|
if not line.strip().startswith('//') and line.strip() != '':
|
||||||
|
filtered_lines.append(line)
|
||||||
|
|
||||||
|
if line.startswith('//') and '@ignore' in line:
|
||||||
|
ignore_next = True
|
||||||
|
elif line.startswith('//') and '@optional' in line:
|
||||||
|
next_returns_optional = True
|
||||||
|
|
||||||
|
function_declaration = [
|
||||||
|
line.replace('SPINE_CPP_LITE_EXPORT', '').strip()
|
||||||
|
for line in filtered_lines
|
||||||
|
]
|
||||||
|
|
||||||
|
return function_declaration
|
||||||
|
|
||||||
|
def read_spine_enums(data):
|
||||||
|
enums_start = data.find('// @start: enums') + len('// @start: enums')
|
||||||
|
enums_end = data.find('// @end: enums')
|
||||||
|
enums_section = data[enums_start:enums_end]
|
||||||
|
return re.findall(r"typedef enum (\w+) \{", enums_section)
|
||||||
|
|
||||||
|
class SpineObject:
|
||||||
|
def __init__(self, name, functions):
|
||||||
|
self.name = name
|
||||||
|
self.functions = functions
|
||||||
|
self.function_names = {function.name for function in functions}
|
||||||
|
self.var_name = "wrappee"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"SpineObject: name: {self.name}, functions: {self.functions}"
|
||||||
|
|
||||||
|
class SpineFunction:
|
||||||
|
def __init__(self, return_type, name, parameters, returns_optional):
|
||||||
|
self.return_type = return_type
|
||||||
|
self.name = name
|
||||||
|
self.parameters = parameters
|
||||||
|
self.returns_optional = returns_optional
|
||||||
|
|
||||||
|
def isReturningSpineClass(self):
|
||||||
|
return self.return_type.startswith("spine_") and self.return_type != "spine_bool" and self.return_type not in enums
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"SpineFunction(return_type: {self.return_type}, name: {self.name}, parameters: {self.parameters}, returns_optional: {self.returns_optional})"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.__str__()
|
||||||
|
|
||||||
|
class SpineParam:
|
||||||
|
def __init__(self, type, name):
|
||||||
|
self.type = type
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def isSpineClass(self):
|
||||||
|
return self.type.startswith("spine_") and self.type != "spine_bool" and self.type not in enums
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"SpineParam(type: {self.type}, name: {self.name})"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.__str__()
|
||||||
|
|
||||||
|
def parse_function_declaration(declaration):
|
||||||
|
returns_optional = declaration.endswith("?")
|
||||||
|
|
||||||
|
# Strip semicolon and extra whitespace
|
||||||
|
declaration = declaration.strip('?').strip(';').strip()
|
||||||
|
|
||||||
|
# Use regex to split the declaration into parts
|
||||||
|
# Regex explanation:
|
||||||
|
# ^([\w\s\*]+?)\s+ - Capture the return type, possibly including spaces and asterisks (non-greedy)
|
||||||
|
# ([\w]+) - Capture the function name (alphanumeric and underscores)
|
||||||
|
# \((.*)\) - Capture the argument list in entirety
|
||||||
|
match = re.match(r'^(\S.+?\s*\*?\s*)([\w]+)\s*\((.*)\)$', declaration)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
return "Invalid function declaration"
|
||||||
|
|
||||||
|
return_type, function_name, params = match.groups()
|
||||||
|
|
||||||
|
params = params.strip()
|
||||||
|
parameters = []
|
||||||
|
if params:
|
||||||
|
# Splitting each argument on comma
|
||||||
|
param_list = params.split(',')
|
||||||
|
for param in param_list:
|
||||||
|
|
||||||
|
param_parts = []
|
||||||
|
if '*' in param: # Split at the pointer and add it as a suffix to the type
|
||||||
|
param_parts = param.rsplit('*', 1)
|
||||||
|
param_parts[0] = param_parts[0] + '*'
|
||||||
|
else: # Assuming type and name are separated by space and taking the last space as the separator
|
||||||
|
param_parts = param.rsplit(' ', 1)
|
||||||
|
param_type, param_name = param_parts
|
||||||
|
spine_param = SpineParam(type = param_type.strip(), name = param_name.strip())
|
||||||
|
parameters.append(spine_param)
|
||||||
|
|
||||||
|
return SpineFunction(
|
||||||
|
return_type = return_type.strip(),
|
||||||
|
name = function_name.strip(),
|
||||||
|
parameters = parameters,
|
||||||
|
returns_optional = returns_optional
|
||||||
|
)
|
||||||
|
|
||||||
|
types = read_spine_types(file_contents)
|
||||||
|
function_declarations = read_spine_function_declarations(file_contents)
|
||||||
|
enums = read_spine_enums(file_contents)
|
||||||
|
|
||||||
|
sorted_types = sorted(types, key=len, reverse=True) # Sorted by legth descending so we can match longest prefix.
|
||||||
|
spine_functions = [
|
||||||
|
parse_function_declaration(function_declaration)
|
||||||
|
for function_declaration in function_declarations
|
||||||
|
]
|
||||||
|
|
||||||
|
objects = []
|
||||||
|
|
||||||
|
for type in sorted_types:
|
||||||
|
object_functions = []
|
||||||
|
|
||||||
|
hits = set() ## Keep track of hits and remove them for next object
|
||||||
|
|
||||||
|
for function_declaration in function_declarations:
|
||||||
|
spine_function = parse_function_declaration(function_declaration)
|
||||||
|
if spine_function.name.startswith(type):
|
||||||
|
hits.add(function_declaration);
|
||||||
|
object_functions.append(spine_function);
|
||||||
|
|
||||||
|
object = SpineObject(name = type, functions = object_functions);
|
||||||
|
objects.append(object)
|
||||||
|
|
||||||
|
function_declarations = [item for item in function_declarations if item not in hits]
|
||||||
|
|
||||||
|
def snake_to_camel(snake_str):
|
||||||
|
# Split the string by underscore
|
||||||
|
parts = snake_str.split('_')
|
||||||
|
# Return the first part lowercased and concatenate capitalized subsequent parts
|
||||||
|
return parts[0] + ''.join(word.capitalize() for word in parts[1:])
|
||||||
|
|
||||||
|
def snake_to_title(snake_str):
|
||||||
|
# Split the string at underscores
|
||||||
|
words = snake_str.split('_')
|
||||||
|
# Capitalize the first letter of each word
|
||||||
|
words = [word.capitalize() for word in words]
|
||||||
|
# Join the words into a single string without any separator
|
||||||
|
title_str = ''.join(words)
|
||||||
|
return title_str
|
||||||
|
|
||||||
|
inset = " "
|
||||||
|
|
||||||
|
class SwiftTypeWriter:
|
||||||
|
def __init__(self, type):
|
||||||
|
self.type = type
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
parameter_type = supported_types_to_swift_types.get(self.type)
|
||||||
|
if parameter_type is None:
|
||||||
|
parameter_type = snake_to_title(self.type.replace("spine_", ""))
|
||||||
|
|
||||||
|
if parameter_type.endswith(" *"):
|
||||||
|
parameter_type = f"{parameter_type[:-2]}"
|
||||||
|
|
||||||
|
return parameter_type
|
||||||
|
|
||||||
|
class SwiftParamWriter:
|
||||||
|
def __init__(self, param):
|
||||||
|
self.param = param
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
type = SwiftTypeWriter(type = self.param.type).write()
|
||||||
|
return f"{snake_to_camel(self.param.name)}: {type}"
|
||||||
|
|
||||||
|
class SwiftFunctionBodyWriter:
|
||||||
|
def __init__(self, spine_object, spine_function, is_setter, is_getter_optional):
|
||||||
|
self.spine_object = spine_object
|
||||||
|
self.spine_function = spine_function
|
||||||
|
self.is_setter = is_setter
|
||||||
|
self.is_getter_optional = is_getter_optional
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
body = ""
|
||||||
|
|
||||||
|
num_function_name = self.spine_function.name.replace("get_", "get_num_")
|
||||||
|
swift_return_type_is_array = "get_" in self.spine_function.name and num_function_name in self.spine_object.function_names
|
||||||
|
|
||||||
|
spine_params = self.spine_function.parameters;
|
||||||
|
|
||||||
|
body = ""
|
||||||
|
if "dispose" in self.spine_function.name:
|
||||||
|
body += self.write_dispose_call()
|
||||||
|
|
||||||
|
function_call = self.write_c_function_call(spine_params)
|
||||||
|
|
||||||
|
if swift_return_type_is_array:
|
||||||
|
body += self.write_array_call(num_function_name, function_call)
|
||||||
|
body += inset + inset
|
||||||
|
body += "}"
|
||||||
|
else:
|
||||||
|
if not self.spine_function.return_type == "void":
|
||||||
|
body += "return "
|
||||||
|
|
||||||
|
if self.spine_function.isReturningSpineClass():
|
||||||
|
|
||||||
|
function_prefix = f"{self.spine_object.name}_"
|
||||||
|
function_name = self.spine_function.name.replace(function_prefix, "", 1)
|
||||||
|
|
||||||
|
if "find_" in function_name or self.spine_function.returns_optional:
|
||||||
|
body += function_call
|
||||||
|
body += ".flatMap { .init($0"
|
||||||
|
if self.spine_function.return_type in enums:
|
||||||
|
body += ".rawValue"
|
||||||
|
body += ") }"
|
||||||
|
else:
|
||||||
|
body += ".init("
|
||||||
|
body += function_call
|
||||||
|
if self.spine_function.return_type in enums:
|
||||||
|
body += ".rawValue"
|
||||||
|
body += ")"
|
||||||
|
|
||||||
|
else:
|
||||||
|
body += function_call
|
||||||
|
|
||||||
|
if self.spine_function.return_type == "const utf8 *" or self.spine_function.return_type == "utf8 *":
|
||||||
|
body += ".flatMap { String(cString: $0) }"
|
||||||
|
if self.spine_function.return_type == "int32_t *" or self.spine_function.return_type == "float *":
|
||||||
|
body += ".flatMap { $0.pointee }"
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
def write_c_function_call(self, spine_params):
|
||||||
|
function_call = ""
|
||||||
|
function_call += f"{self.spine_function.name}"
|
||||||
|
function_call += "("
|
||||||
|
|
||||||
|
# Replace name with ivar name
|
||||||
|
spine_params_with_ivar_name = spine_params
|
||||||
|
if spine_params_with_ivar_name and spine_params_with_ivar_name[0].type == self.spine_object.name:
|
||||||
|
spine_params_with_ivar_name[0].name = self.spine_object.var_name
|
||||||
|
|
||||||
|
if self.is_setter and len(spine_params_with_ivar_name) == 2:
|
||||||
|
spine_params_with_ivar_name[1].name = "newValue"
|
||||||
|
if self.is_getter_optional:
|
||||||
|
spine_params_with_ivar_name[1].name += "?"
|
||||||
|
|
||||||
|
swift_param_names = []
|
||||||
|
for idx, spine_param in enumerate(spine_params_with_ivar_name):
|
||||||
|
if spine_param.isSpineClass() and idx > 0:
|
||||||
|
swift_param_names.append(f"{spine_param.name}.wrappee")
|
||||||
|
elif spine_param.type == "spine_bool":
|
||||||
|
swift_param_names.append(f"{spine_param.name} ? -1 : 0")
|
||||||
|
else:
|
||||||
|
swift_param_names.append(spine_param.name)
|
||||||
|
|
||||||
|
|
||||||
|
function_call += ", ".join(swift_param_names)
|
||||||
|
function_call += ")"
|
||||||
|
|
||||||
|
if self.spine_function.return_type == "spine_bool":
|
||||||
|
function_call += " != 0"
|
||||||
|
|
||||||
|
return function_call
|
||||||
|
|
||||||
|
def write_array_call(self, num_function_name, function_call):
|
||||||
|
array_call = f"let num = Int({num_function_name}({self.spine_object.var_name}))"
|
||||||
|
array_call += "\n"
|
||||||
|
array_call += inset + inset
|
||||||
|
array_call += f"let ptr = {function_call}"
|
||||||
|
array_call += "\n"
|
||||||
|
array_call += inset + inset
|
||||||
|
array_call += "return (0..<num).compactMap {"
|
||||||
|
array_call += "\n"
|
||||||
|
array_call += inset + inset + inset
|
||||||
|
|
||||||
|
if self.spine_function.isReturningSpineClass():
|
||||||
|
array_call += "ptr?[$0].flatMap { .init($0) }"
|
||||||
|
else:
|
||||||
|
array_call += "ptr?[$0]"
|
||||||
|
|
||||||
|
array_call += "\n"
|
||||||
|
return array_call
|
||||||
|
|
||||||
|
def write_dispose_call(self):
|
||||||
|
dispose_body = "if disposed { return }"
|
||||||
|
dispose_body += "\n"
|
||||||
|
dispose_body += inset + inset
|
||||||
|
dispose_body += "disposed = true"
|
||||||
|
dispose_body += "\n"
|
||||||
|
dispose_body += inset + inset
|
||||||
|
return dispose_body
|
||||||
|
|
||||||
|
class SwiftFunctionWriter:
|
||||||
|
def __init__(self, spine_object, spine_function, spine_setter_function):
|
||||||
|
self.spine_object = spine_object
|
||||||
|
self.spine_function = spine_function
|
||||||
|
self.spine_setter_function = spine_setter_function
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
function_prefix = f"{self.spine_object.name}_"
|
||||||
|
function_name = self.spine_function.name.replace(function_prefix, "", 1)
|
||||||
|
is_getter = (function_name.startswith("get_") or function_name.startswith("is_")) and len(self.spine_function.parameters) < 2
|
||||||
|
|
||||||
|
num_function_name = self.spine_function.name.replace("get_", "get_num_")
|
||||||
|
swift_return_type_is_array = "get_" in self.spine_function.name and num_function_name in self.spine_object.function_names
|
||||||
|
|
||||||
|
swift_return_type_writer = SwiftTypeWriter(type = self.spine_function.return_type)
|
||||||
|
swift_return_type = swift_return_type_writer.write()
|
||||||
|
if swift_return_type_is_array:
|
||||||
|
swift_return_type = f"[{swift_return_type}]"
|
||||||
|
|
||||||
|
function_string = inset
|
||||||
|
|
||||||
|
if is_getter:
|
||||||
|
|
||||||
|
function_string += self.write_computed_property_signature(function_name, swift_return_type)
|
||||||
|
if self.spine_setter_function:
|
||||||
|
function_string += " {\n"
|
||||||
|
function_string += inset + inset
|
||||||
|
function_string += "get"
|
||||||
|
|
||||||
|
else:
|
||||||
|
function_string += self.write_method_signature(function_name, swift_return_type)
|
||||||
|
|
||||||
|
function_string += " {"
|
||||||
|
function_string += "\n"
|
||||||
|
|
||||||
|
function_string += inset + inset
|
||||||
|
|
||||||
|
if self.spine_setter_function:
|
||||||
|
function_string += inset
|
||||||
|
|
||||||
|
function_string += SwiftFunctionBodyWriter(spine_object = self.spine_object, spine_function = self.spine_function, is_setter=False, is_getter_optional=False).write()
|
||||||
|
|
||||||
|
if self.spine_setter_function:
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset + inset + "}"
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset + inset + "set {"
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset + inset + inset
|
||||||
|
function_string += SwiftFunctionBodyWriter(spine_object = self.spine_object, spine_function = self.spine_setter_function, is_setter=True, is_getter_optional=self.spine_function.returns_optional).write()
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset + inset + "}"
|
||||||
|
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset + "}"
|
||||||
|
function_string += "\n"
|
||||||
|
|
||||||
|
return function_string
|
||||||
|
|
||||||
|
def write_computed_property_signature(self, function_name, swift_return_type):
|
||||||
|
property_name = snake_to_camel(function_name.replace("get_", ""))
|
||||||
|
property_string = f"public var {property_name}: {swift_return_type}"
|
||||||
|
if self.spine_function.returns_optional:
|
||||||
|
property_string += "?"
|
||||||
|
return property_string
|
||||||
|
|
||||||
|
def write_method_signature(self, function_name, swift_return_type):
|
||||||
|
|
||||||
|
function_string = ""
|
||||||
|
|
||||||
|
if not self.spine_function.return_type == "void":
|
||||||
|
function_string += "@discardableResult"
|
||||||
|
function_string += "\n"
|
||||||
|
function_string += inset
|
||||||
|
|
||||||
|
function_string += f"public func {snake_to_camel(function_name)}"
|
||||||
|
|
||||||
|
function_string += "("
|
||||||
|
|
||||||
|
spine_params = self.spine_function.parameters;
|
||||||
|
|
||||||
|
# Filter out ivar
|
||||||
|
if spine_params and spine_params[0].type == self.spine_object.name:
|
||||||
|
spine_params_without_ivar = spine_params[1:]
|
||||||
|
else:
|
||||||
|
spine_params_without_ivar = spine_params
|
||||||
|
|
||||||
|
swift_params = [
|
||||||
|
SwiftParamWriter(param = spine_param).write()
|
||||||
|
for spine_param in spine_params_without_ivar
|
||||||
|
]
|
||||||
|
|
||||||
|
function_string += ", ".join(swift_params)
|
||||||
|
function_string += ")"
|
||||||
|
|
||||||
|
if not self.spine_function.return_type == "void":
|
||||||
|
function_string += f" -> {swift_return_type}"
|
||||||
|
|
||||||
|
if "find_" in function_name or self.spine_function.returns_optional:
|
||||||
|
function_string += "?"
|
||||||
|
|
||||||
|
return function_string
|
||||||
|
|
||||||
|
class SwiftObjectWriter:
|
||||||
|
def __init__(self, spine_object):
|
||||||
|
self.spine_object = spine_object
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
ivar_type = self.spine_object.name
|
||||||
|
ivar_name = self.spine_object.var_name
|
||||||
|
|
||||||
|
class_name = snake_to_title(self.spine_object.name.replace("spine_", ""))
|
||||||
|
|
||||||
|
object_string = f"@objc(Spine{class_name})"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += "@objcMembers"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += f"public final class {class_name}: NSObject"
|
||||||
|
object_string += " {"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += inset
|
||||||
|
object_string += f"internal let {ivar_name}: {ivar_type}"
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
if any("dispose" in function_name for function_name in self.spine_object.function_names):
|
||||||
|
object_string += inset
|
||||||
|
object_string += f"internal var disposed = false"
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
object_string += inset
|
||||||
|
object_string += f"internal init(_ {ivar_name}: {ivar_type})"
|
||||||
|
object_string += " {"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += inset + inset
|
||||||
|
object_string += f"self.{ivar_name} = {ivar_name}"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += inset + inset
|
||||||
|
object_string += "super.init()"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += inset
|
||||||
|
object_string += "}"
|
||||||
|
object_string += "\n"
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
filtered_spine_functions = [spine_function for spine_function in self.spine_object.functions if not "_get_num_" in spine_function.name]
|
||||||
|
|
||||||
|
spine_functions_by_name = {}
|
||||||
|
getter_names = []
|
||||||
|
setter_names = []
|
||||||
|
method_names = []
|
||||||
|
|
||||||
|
for spine_function in filtered_spine_functions:
|
||||||
|
spine_functions_by_name[spine_function.name] = spine_function
|
||||||
|
|
||||||
|
if ("_get_" in spine_function.name or "_is_" in spine_function.name) and len(spine_function.parameters) == 1:
|
||||||
|
getter_names.append(spine_function.name)
|
||||||
|
elif "_set_" in spine_function.name and len(spine_function.parameters) == 2:
|
||||||
|
setter_names.append(spine_function.name)
|
||||||
|
else:
|
||||||
|
method_names.append(spine_function.name)
|
||||||
|
|
||||||
|
get_set_pairs = []
|
||||||
|
|
||||||
|
for setter_name in setter_names:
|
||||||
|
getter_name_get = setter_name.replace("_set_", "_get_")
|
||||||
|
getter_name_is = setter_name.replace("_set_", "_is_")
|
||||||
|
if getter_name_get in getter_names:
|
||||||
|
getter_names.remove(getter_name_get)
|
||||||
|
get_set_pairs.append((getter_name_get, setter_name))
|
||||||
|
elif getter_name_is in getter_names:
|
||||||
|
getter_names.remove(getter_name_is)
|
||||||
|
get_set_pairs.append((getter_name_is, setter_name))
|
||||||
|
else:
|
||||||
|
method_names.append(setter_name) # Coul not find getter by name. Move to methods
|
||||||
|
|
||||||
|
# print(get_set_pairs)
|
||||||
|
|
||||||
|
for getter_name in getter_names:
|
||||||
|
spine_function = spine_functions_by_name[getter_name]
|
||||||
|
object_string += SwiftFunctionWriter(spine_object = self.spine_object, spine_function = spine_function, spine_setter_function=None).write()
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
for get_set_pair in get_set_pairs:
|
||||||
|
getter_function = spine_functions_by_name[get_set_pair[0]]
|
||||||
|
setter_function = spine_functions_by_name[get_set_pair[1]]
|
||||||
|
object_string += SwiftFunctionWriter(spine_object = self.spine_object, spine_function = getter_function, spine_setter_function=setter_function).write()
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
for method_name in method_names:
|
||||||
|
spine_function = spine_functions_by_name[method_name]
|
||||||
|
object_string += SwiftFunctionWriter(spine_object = self.spine_object, spine_function = spine_function, spine_setter_function=None).write()
|
||||||
|
object_string += "\n"
|
||||||
|
|
||||||
|
object_string += "}"
|
||||||
|
|
||||||
|
return object_string
|
||||||
|
|
||||||
|
class SwiftEnumWriter:
|
||||||
|
def __init__(self, spine_enum):
|
||||||
|
self.spine_enum = spine_enum
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
# TODO: Consider leaving spine prefix (objc) or map whole c enum to swift/objc compatible enum
|
||||||
|
return f"public typealias {snake_to_title(self.spine_enum.replace("spine_", ""))} = {self.spine_enum}"
|
||||||
|
|
||||||
|
print("import Foundation")
|
||||||
|
print("import SpineCppLite")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
for enum in enums:
|
||||||
|
print(SwiftEnumWriter(spine_enum=enum).write())
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
for object in objects:
|
||||||
|
print(SwiftObjectWriter(spine_object = object).write())
|
||||||
|
print("")
|
||||||
@ -295,6 +295,16 @@ int32_t spine_atlas_get_num_image_paths(spine_atlas atlas) {
|
|||||||
return ((_spine_atlas *) atlas)->numImagePaths;
|
return ((_spine_atlas *) atlas)->numImagePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spine_bool spine_atlas_is_pma(spine_atlas atlas) {
|
||||||
|
if (!atlas) return 0;
|
||||||
|
Atlas *_atlas = static_cast<Atlas *>(((_spine_atlas *) atlas)->atlas);
|
||||||
|
if (_atlas->getPages().size() > 0) {
|
||||||
|
return _atlas->getPages()[0]->pma;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
utf8 *spine_atlas_get_image_path(spine_atlas atlas, int32_t index) {
|
utf8 *spine_atlas_get_image_path(spine_atlas atlas, int32_t index) {
|
||||||
if (!atlas) return nullptr;
|
if (!atlas) return nullptr;
|
||||||
return ((_spine_atlas *) atlas)->imagePaths[index];
|
return ((_spine_atlas *) atlas)->imagePaths[index];
|
||||||
@ -1919,7 +1929,7 @@ void spine_slot_data_set_dark_color(spine_slot_data slot, float r, float g, floa
|
|||||||
_slot->getDarkColor().set(r, g, b, a);
|
_slot->getDarkColor().set(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
spine_bool spine_slot_data_has_dark_color(spine_slot_data slot) {
|
spine_bool spine_slot_data_get_has_dark_color(spine_slot_data slot) {
|
||||||
if (slot == nullptr) return 0;
|
if (slot == nullptr) return 0;
|
||||||
SlotData *_slot = (SlotData *) slot;
|
SlotData *_slot = (SlotData *) slot;
|
||||||
return _slot->hasDarkColor() ? -1 : 0;
|
return _slot->hasDarkColor() ? -1 : 0;
|
||||||
@ -2173,7 +2183,7 @@ void spine_bone_data_set_inherit(spine_bone_data data, spine_inherit inherit) {
|
|||||||
_data->setInherit((Inherit) inherit);
|
_data->setInherit((Inherit) inherit);
|
||||||
}
|
}
|
||||||
|
|
||||||
spine_bool spine_bone_data_is_skin_required(spine_bone_data data) {
|
spine_bool spine_bone_data_get_is_skin_required(spine_bone_data data) {
|
||||||
if (data == nullptr) return 0;
|
if (data == nullptr) return 0;
|
||||||
BoneData *_data = (BoneData *) data;
|
BoneData *_data = (BoneData *) data;
|
||||||
return _data->isSkinRequired() ? -1 : 0;
|
return _data->isSkinRequired() ? -1 : 0;
|
||||||
@ -2503,7 +2513,7 @@ float spine_bone_get_a_shear_y(spine_bone bone) {
|
|||||||
return _bone->getAShearY();
|
return _bone->getAShearY();
|
||||||
}
|
}
|
||||||
|
|
||||||
void spine_bone_set_shear_a_y(spine_bone bone, float shearY) {
|
void spine_bone_set_a_shear_y(spine_bone bone, float shearY) {
|
||||||
if (bone == nullptr) return;
|
if (bone == nullptr) return;
|
||||||
Bone *_bone = (Bone *) bone;
|
Bone *_bone = (Bone *) bone;
|
||||||
_bone->setAShearY(shearY);
|
_bone->setAShearY(shearY);
|
||||||
@ -3389,6 +3399,12 @@ spine_bool spine_ik_constraint_data_get_uniform(spine_ik_constraint_data data) {
|
|||||||
return _data->getUniform() ? -1 : 0;
|
return _data->getUniform() ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void spine_ik_constraint_data_set_uniform(spine_ik_constraint_data data, spine_bool uniform) {
|
||||||
|
if (data == nullptr) return;
|
||||||
|
IkConstraintData *_data = (IkConstraintData *) data;
|
||||||
|
_data->setUniform(uniform);
|
||||||
|
}
|
||||||
|
|
||||||
float spine_ik_constraint_data_get_mix(spine_ik_constraint_data data) {
|
float spine_ik_constraint_data_get_mix(spine_ik_constraint_data data) {
|
||||||
if (data == nullptr) return 0;
|
if (data == nullptr) return 0;
|
||||||
IkConstraintData *_data = (IkConstraintData *) data;
|
IkConstraintData *_data = (IkConstraintData *) data;
|
||||||
@ -4674,7 +4690,7 @@ float spine_physics_constraint_get_last_time(spine_physics_constraint constraint
|
|||||||
return _constraint->getLastTime();
|
return _constraint->getLastTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void spine_physics_constraint_reset(spine_physics_constraint constraint) {
|
void spine_physics_constraint_reset_fully(spine_physics_constraint constraint) {
|
||||||
if (constraint == nullptr) return;
|
if (constraint == nullptr) return;
|
||||||
PhysicsConstraint *_constraint = (PhysicsConstraint *) constraint;
|
PhysicsConstraint *_constraint = (PhysicsConstraint *) constraint;
|
||||||
_constraint->reset();
|
_constraint->reset();
|
||||||
|
|||||||
@ -59,6 +59,8 @@
|
|||||||
} name##_wrapper; \
|
} name##_wrapper; \
|
||||||
typedef name##_wrapper *name;
|
typedef name##_wrapper *name;
|
||||||
|
|
||||||
|
// @start: opaque_types
|
||||||
|
|
||||||
SPINE_OPAQUE_TYPE(spine_skeleton)
|
SPINE_OPAQUE_TYPE(spine_skeleton)
|
||||||
SPINE_OPAQUE_TYPE(spine_skeleton_data)
|
SPINE_OPAQUE_TYPE(spine_skeleton_data)
|
||||||
SPINE_OPAQUE_TYPE(spine_bone)
|
SPINE_OPAQUE_TYPE(spine_bone)
|
||||||
@ -103,8 +105,12 @@ SPINE_OPAQUE_TYPE(spine_skeleton_drawable)
|
|||||||
SPINE_OPAQUE_TYPE(spine_skin_entry)
|
SPINE_OPAQUE_TYPE(spine_skin_entry)
|
||||||
SPINE_OPAQUE_TYPE(spine_skin_entries)
|
SPINE_OPAQUE_TYPE(spine_skin_entries)
|
||||||
|
|
||||||
|
// @end: opaque_types
|
||||||
|
|
||||||
typedef char utf8;
|
typedef char utf8;
|
||||||
|
|
||||||
|
// @start: enums
|
||||||
|
|
||||||
typedef enum spine_blend_mode {
|
typedef enum spine_blend_mode {
|
||||||
SPINE_BLEND_MODE_NORMAL = 0,
|
SPINE_BLEND_MODE_NORMAL = 0,
|
||||||
SPINE_BLEND_MODE_ADDITIVE,
|
SPINE_BLEND_MODE_ADDITIVE,
|
||||||
@ -177,8 +183,12 @@ typedef enum spine_physics {
|
|||||||
|
|
||||||
} spine_physics;
|
} spine_physics;
|
||||||
|
|
||||||
|
// @end: enums
|
||||||
|
|
||||||
typedef int32_t spine_bool;
|
typedef int32_t spine_bool;
|
||||||
|
|
||||||
|
// @start: function_declarations
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_major_version();
|
SPINE_CPP_LITE_EXPORT int32_t spine_major_version();
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_minor_version();
|
SPINE_CPP_LITE_EXPORT int32_t spine_minor_version();
|
||||||
SPINE_CPP_LITE_EXPORT void spine_enable_debug_extension(spine_bool enable);
|
SPINE_CPP_LITE_EXPORT void spine_enable_debug_extension(spine_bool enable);
|
||||||
@ -200,10 +210,13 @@ SPINE_CPP_LITE_EXPORT float spine_vector_get_y(spine_vector vector);
|
|||||||
SPINE_CPP_LITE_EXPORT spine_atlas spine_atlas_load(const utf8 *atlasData);
|
SPINE_CPP_LITE_EXPORT spine_atlas spine_atlas_load(const utf8 *atlasData);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_atlas_get_num_image_paths(spine_atlas atlas);
|
SPINE_CPP_LITE_EXPORT int32_t spine_atlas_get_num_image_paths(spine_atlas atlas);
|
||||||
SPINE_CPP_LITE_EXPORT utf8 *spine_atlas_get_image_path(spine_atlas atlas, int32_t index);
|
SPINE_CPP_LITE_EXPORT utf8 *spine_atlas_get_image_path(spine_atlas atlas, int32_t index);
|
||||||
|
SPINE_CPP_LITE_EXPORT spine_bool spine_atlas_is_pma(spine_atlas atlas);
|
||||||
SPINE_CPP_LITE_EXPORT utf8 *spine_atlas_get_error(spine_atlas atlas);
|
SPINE_CPP_LITE_EXPORT utf8 *spine_atlas_get_error(spine_atlas atlas);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_atlas_dispose(spine_atlas atlas);
|
SPINE_CPP_LITE_EXPORT void spine_atlas_dispose(spine_atlas atlas);
|
||||||
|
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas atlas, const utf8 *skeletonData);
|
SPINE_CPP_LITE_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas atlas, const utf8 *skeletonData);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas atlas, const uint8_t *skeletonData, int32_t length);
|
SPINE_CPP_LITE_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas atlas, const uint8_t *skeletonData, int32_t length);
|
||||||
SPINE_CPP_LITE_EXPORT utf8 *spine_skeleton_data_result_get_error(spine_skeleton_data_result result);
|
SPINE_CPP_LITE_EXPORT utf8 *spine_skeleton_data_result_get_error(spine_skeleton_data_result result);
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton_data spine_skeleton_data_result_get_data(spine_skeleton_data_result result);
|
SPINE_CPP_LITE_EXPORT spine_skeleton_data spine_skeleton_data_result_get_data(spine_skeleton_data_result result);
|
||||||
@ -217,6 +230,7 @@ SPINE_CPP_LITE_EXPORT spine_ik_constraint_data spine_skeleton_data_find_ik_const
|
|||||||
SPINE_CPP_LITE_EXPORT spine_transform_constraint_data spine_skeleton_data_find_transform_constraint(spine_skeleton_data data, const utf8 *name);
|
SPINE_CPP_LITE_EXPORT spine_transform_constraint_data spine_skeleton_data_find_transform_constraint(spine_skeleton_data data, const utf8 *name);
|
||||||
SPINE_CPP_LITE_EXPORT spine_path_constraint_data spine_skeleton_data_find_path_constraint(spine_skeleton_data data, const utf8 *name);
|
SPINE_CPP_LITE_EXPORT spine_path_constraint_data spine_skeleton_data_find_path_constraint(spine_skeleton_data data, const utf8 *name);
|
||||||
SPINE_CPP_LITE_EXPORT spine_physics_constraint_data spine_skeleton_data_find_physics_constraint(spine_skeleton_data data, const utf8 *name);
|
SPINE_CPP_LITE_EXPORT spine_physics_constraint_data spine_skeleton_data_find_physics_constraint(spine_skeleton_data data, const utf8 *name);
|
||||||
|
// @optiona;
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_name(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_name(spine_skeleton_data data);
|
||||||
// OMITTED setName()
|
// OMITTED setName()
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_bones(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_bones(spine_skeleton_data data);
|
||||||
@ -225,6 +239,7 @@ SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_slots(spine_skeleton_d
|
|||||||
SPINE_CPP_LITE_EXPORT spine_slot_data *spine_skeleton_data_get_slots(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT spine_slot_data *spine_skeleton_data_get_slots(spine_skeleton_data data);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_skins(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_skins(spine_skeleton_data data);
|
||||||
SPINE_CPP_LITE_EXPORT spine_skin *spine_skeleton_data_get_skins(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT spine_skin *spine_skeleton_data_get_skins(spine_skeleton_data data);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_skin spine_skeleton_data_get_default_skin(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT spine_skin spine_skeleton_data_get_default_skin(spine_skeleton_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_set_default_skin(spine_skeleton_data data, spine_skin skin);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_set_default_skin(spine_skeleton_data data, spine_skin skin);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_events(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_data_get_num_events(spine_skeleton_data data);
|
||||||
@ -249,6 +264,7 @@ SPINE_CPP_LITE_EXPORT float spine_skeleton_data_get_height(spine_skeleton_data d
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_set_height(spine_skeleton_data data, float height);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_set_height(spine_skeleton_data data, float height);
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_version(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_version(spine_skeleton_data data);
|
||||||
// OMITTED setVersion()
|
// OMITTED setVersion()
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_hash(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_hash(spine_skeleton_data data);
|
||||||
// OMITTED setHash()
|
// OMITTED setHash()
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_images_path(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_skeleton_data_get_images_path(spine_skeleton_data data);
|
||||||
@ -260,7 +276,9 @@ SPINE_CPP_LITE_EXPORT float spine_skeleton_data_get_fps(spine_skeleton_data data
|
|||||||
SPINE_CPP_LITE_EXPORT float spine_skeleton_data_get_reference_scale(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT float spine_skeleton_data_get_reference_scale(spine_skeleton_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data data);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data data);
|
||||||
|
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton_drawable spine_skeleton_drawable_create(spine_skeleton_data skeletonData);
|
SPINE_CPP_LITE_EXPORT spine_skeleton_drawable spine_skeleton_drawable_create(spine_skeleton_data skeletonData);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable drawable);
|
SPINE_CPP_LITE_EXPORT spine_render_command spine_skeleton_drawable_render(spine_skeleton_drawable drawable);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable drawable);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable drawable);
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton spine_skeleton_drawable_get_skeleton(spine_skeleton_drawable drawable);
|
SPINE_CPP_LITE_EXPORT spine_skeleton spine_skeleton_drawable_get_skeleton(spine_skeleton_drawable drawable);
|
||||||
@ -268,8 +286,11 @@ SPINE_CPP_LITE_EXPORT spine_animation_state spine_skeleton_drawable_get_animatio
|
|||||||
SPINE_CPP_LITE_EXPORT spine_animation_state_data spine_skeleton_drawable_get_animation_state_data(spine_skeleton_drawable drawable);
|
SPINE_CPP_LITE_EXPORT spine_animation_state_data spine_skeleton_drawable_get_animation_state_data(spine_skeleton_drawable drawable);
|
||||||
SPINE_CPP_LITE_EXPORT spine_animation_state_events spine_skeleton_drawable_get_animation_state_events(spine_skeleton_drawable drawable);
|
SPINE_CPP_LITE_EXPORT spine_animation_state_events spine_skeleton_drawable_get_animation_state_events(spine_skeleton_drawable drawable);
|
||||||
|
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT float *spine_render_command_get_positions(spine_render_command command);
|
SPINE_CPP_LITE_EXPORT float *spine_render_command_get_positions(spine_render_command command);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT float *spine_render_command_get_uvs(spine_render_command command);
|
SPINE_CPP_LITE_EXPORT float *spine_render_command_get_uvs(spine_render_command command);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT int32_t *spine_render_command_get_colors(spine_render_command command);
|
SPINE_CPP_LITE_EXPORT int32_t *spine_render_command_get_colors(spine_render_command command);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_render_command_get_num_vertices(spine_render_command command);
|
SPINE_CPP_LITE_EXPORT int32_t spine_render_command_get_num_vertices(spine_render_command command);
|
||||||
SPINE_CPP_LITE_EXPORT uint16_t *spine_render_command_get_indices(spine_render_command command);
|
SPINE_CPP_LITE_EXPORT uint16_t *spine_render_command_get_indices(spine_render_command command);
|
||||||
@ -305,6 +326,7 @@ SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_add_animation(spin
|
|||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_set_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_set_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration);
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_add_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration, float delay);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_add_empty_animation(spine_animation_state state, int32_t trackIndex, float mixDuration, float delay);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_animation_state_set_empty_animations(spine_animation_state state, float mixDuration);
|
SPINE_CPP_LITE_EXPORT void spine_animation_state_set_empty_animations(spine_animation_state state, float mixDuration);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_get_current(spine_animation_state state, int32_t trackIndex);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_get_current(spine_animation_state state, int32_t trackIndex);
|
||||||
SPINE_CPP_LITE_EXPORT spine_animation_state_data spine_animation_state_get_data(spine_animation_state state);
|
SPINE_CPP_LITE_EXPORT spine_animation_state_data spine_animation_state_get_data(spine_animation_state state);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_animation_state_get_time_scale(spine_animation_state state);
|
SPINE_CPP_LITE_EXPORT float spine_animation_state_get_time_scale(spine_animation_state state);
|
||||||
@ -315,11 +337,13 @@ SPINE_CPP_LITE_EXPORT void spine_animation_state_set_time_scale(spine_animation_
|
|||||||
// OMITTED enableQueue()
|
// OMITTED enableQueue()
|
||||||
// OMITTED setManualTrackEntryDisposal()
|
// OMITTED setManualTrackEntryDisposal()
|
||||||
// OMITTED getManualTrackEntryDisposal()
|
// OMITTED getManualTrackEntryDisposal()
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT void spine_animation_state_dispose_track_entry(spine_animation_state state, spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT void spine_animation_state_dispose_track_entry(spine_animation_state state, spine_track_entry entry);
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_animation_state_events_get_num_events(spine_animation_state_events events);
|
SPINE_CPP_LITE_EXPORT int32_t spine_animation_state_events_get_num_events(spine_animation_state_events events);
|
||||||
SPINE_CPP_LITE_EXPORT spine_event_type spine_animation_state_events_get_event_type(spine_animation_state_events events, int32_t index);
|
SPINE_CPP_LITE_EXPORT spine_event_type spine_animation_state_events_get_event_type(spine_animation_state_events events, int32_t index);
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_events_get_track_entry(spine_animation_state_events events, int32_t index);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_animation_state_events_get_track_entry(spine_animation_state_events events, int32_t index);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_event spine_animation_state_events_get_event(spine_animation_state_events events, int32_t index);
|
SPINE_CPP_LITE_EXPORT spine_event spine_animation_state_events_get_event(spine_animation_state_events events, int32_t index);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_animation_state_events_reset(spine_animation_state_events events);
|
SPINE_CPP_LITE_EXPORT void spine_animation_state_events_reset(spine_animation_state_events events);
|
||||||
|
|
||||||
@ -359,6 +383,7 @@ SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_attachment_threshold(spine
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_attachment_threshold(spine_track_entry entry, float attachmentThreshold);
|
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_attachment_threshold(spine_track_entry entry, float attachmentThreshold);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_draw_order_threshold(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_draw_order_threshold(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_draw_order_threshold(spine_track_entry entry, float drawOrderThreshold);
|
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_draw_order_threshold(spine_track_entry entry, float drawOrderThreshold);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_next(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_next(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bool spine_track_entry_is_complete(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT spine_bool spine_track_entry_is_complete(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_time(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_time(spine_track_entry entry);
|
||||||
@ -367,7 +392,9 @@ SPINE_CPP_LITE_EXPORT float spine_track_entry_get_mix_duration(spine_track_entry
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_duration(spine_track_entry entry, float mixDuration);
|
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_duration(spine_track_entry entry, float mixDuration);
|
||||||
SPINE_CPP_LITE_EXPORT spine_mix_blend spine_track_entry_get_mix_blend(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT spine_mix_blend spine_track_entry_get_mix_blend(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_blend(spine_track_entry entry, spine_mix_blend mixBlend);
|
SPINE_CPP_LITE_EXPORT void spine_track_entry_set_mix_blend(spine_track_entry entry, spine_mix_blend mixBlend);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_mixing_from(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_mixing_from(spine_track_entry entry);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_mixing_to(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT spine_track_entry spine_track_entry_get_mixing_to(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_track_entry_reset_rotation_directions(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT void spine_track_entry_reset_rotation_directions(spine_track_entry entry);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_track_complete(spine_track_entry entry);
|
SPINE_CPP_LITE_EXPORT float spine_track_entry_get_track_complete(spine_track_entry entry);
|
||||||
@ -387,7 +414,9 @@ SPINE_CPP_LITE_EXPORT spine_bone spine_skeleton_find_bone(spine_skeleton skeleto
|
|||||||
SPINE_CPP_LITE_EXPORT spine_slot spine_skeleton_find_slot(spine_skeleton skeleton, const utf8 *slotName);
|
SPINE_CPP_LITE_EXPORT spine_slot spine_skeleton_find_slot(spine_skeleton skeleton, const utf8 *slotName);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_skin_by_name(spine_skeleton skeleton, const utf8 *skinName);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_skin_by_name(spine_skeleton skeleton, const utf8 *skinName);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_skin(spine_skeleton skeleton, spine_skin skin);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_skin(spine_skeleton skeleton, spine_skin skin);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_skeleton_get_attachment_by_name(spine_skeleton skeleton, const utf8 *slotName, const utf8 *attachmentName);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_skeleton_get_attachment_by_name(spine_skeleton skeleton, const utf8 *slotName, const utf8 *attachmentName);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_skeleton_get_attachment(spine_skeleton skeleton, int32_t slotIndex, const utf8 *attachmentName);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_skeleton_get_attachment(spine_skeleton skeleton, int32_t slotIndex, const utf8 *attachmentName);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_attachment(spine_skeleton skeleton, const utf8 *slotName, const utf8 *attachmentName);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_attachment(spine_skeleton skeleton, const utf8 *slotName, const utf8 *attachmentName);
|
||||||
SPINE_CPP_LITE_EXPORT spine_ik_constraint spine_skeleton_find_ik_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
SPINE_CPP_LITE_EXPORT spine_ik_constraint spine_skeleton_find_ik_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
||||||
@ -395,7 +424,9 @@ SPINE_CPP_LITE_EXPORT spine_transform_constraint spine_skeleton_find_transform_c
|
|||||||
SPINE_CPP_LITE_EXPORT spine_path_constraint spine_skeleton_find_path_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
SPINE_CPP_LITE_EXPORT spine_path_constraint spine_skeleton_find_path_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
||||||
SPINE_CPP_LITE_EXPORT spine_physics_constraint spine_skeleton_find_physics_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
SPINE_CPP_LITE_EXPORT spine_physics_constraint spine_skeleton_find_physics_constraint(spine_skeleton skeleton, const utf8 *constraintName);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bounds spine_skeleton_get_bounds(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_bounds spine_skeleton_get_bounds(spine_skeleton skeleton);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone spine_skeleton_get_root_bone(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_bone spine_skeleton_get_root_bone(spine_skeleton skeleton);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton_data spine_skeleton_get_data(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_skeleton_data spine_skeleton_get_data(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_get_num_bones(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_get_num_bones(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone *spine_skeleton_get_bones(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_bone *spine_skeleton_get_bones(spine_skeleton skeleton);
|
||||||
@ -412,6 +443,7 @@ SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_get_num_path_constraints(spine_skel
|
|||||||
SPINE_CPP_LITE_EXPORT spine_path_constraint *spine_skeleton_get_path_constraints(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_path_constraint *spine_skeleton_get_path_constraints(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_get_num_physics_constraints(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skeleton_get_num_physics_constraints(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT spine_physics_constraint *spine_skeleton_get_physics_constraints(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_physics_constraint *spine_skeleton_get_physics_constraints(spine_skeleton skeleton);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_skin spine_skeleton_get_skin(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_skin spine_skeleton_get_skin(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT spine_color spine_skeleton_get_color(spine_skeleton skeleton);
|
SPINE_CPP_LITE_EXPORT spine_color spine_skeleton_get_color(spine_skeleton skeleton);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_color(spine_skeleton skeleton, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_skeleton_set_color(spine_skeleton skeleton, float r, float g, float b, float a);
|
||||||
@ -462,7 +494,7 @@ SPINE_CPP_LITE_EXPORT spine_color spine_slot_data_get_color(spine_slot_data slot
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_color(spine_slot_data slot, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_color(spine_slot_data slot, float r, float g, float b, float a);
|
||||||
SPINE_CPP_LITE_EXPORT spine_color spine_slot_data_get_dark_color(spine_slot_data slot);
|
SPINE_CPP_LITE_EXPORT spine_color spine_slot_data_get_dark_color(spine_slot_data slot);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_dark_color(spine_slot_data slot, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_dark_color(spine_slot_data slot, float r, float g, float b, float a);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bool spine_slot_data_has_dark_color(spine_slot_data slot);
|
SPINE_CPP_LITE_EXPORT spine_bool spine_slot_data_get_has_dark_color(spine_slot_data slot);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_has_dark_color(spine_slot_data slot, spine_bool hasDarkColor);
|
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_has_dark_color(spine_slot_data slot, spine_bool hasDarkColor);
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_slot_data_get_attachment_name(spine_slot_data slot);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_slot_data_get_attachment_name(spine_slot_data slot);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_attachment_name(spine_slot_data slot, const utf8 *attachmentName);
|
SPINE_CPP_LITE_EXPORT void spine_slot_data_set_attachment_name(spine_slot_data slot, const utf8 *attachmentName);
|
||||||
@ -481,6 +513,7 @@ SPINE_CPP_LITE_EXPORT void spine_slot_set_color(spine_slot slot, float r, float
|
|||||||
SPINE_CPP_LITE_EXPORT spine_color spine_slot_get_dark_color(spine_slot slot);
|
SPINE_CPP_LITE_EXPORT spine_color spine_slot_get_dark_color(spine_slot slot);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_slot_set_dark_color(spine_slot slot, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_slot_set_dark_color(spine_slot slot, float r, float g, float b, float a);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bool spine_slot_has_dark_color(spine_slot slot);
|
SPINE_CPP_LITE_EXPORT spine_bool spine_slot_has_dark_color(spine_slot slot);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_slot_get_attachment(spine_slot slot);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_slot_get_attachment(spine_slot slot);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_slot_set_attachment(spine_slot slot, spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT void spine_slot_set_attachment(spine_slot slot, spine_attachment attachment);
|
||||||
// OMITTED getDeform()
|
// OMITTED getDeform()
|
||||||
@ -489,6 +522,7 @@ SPINE_CPP_LITE_EXPORT void spine_slot_set_sequence_index(spine_slot slot, int32_
|
|||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_bone_data_get_index(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT int32_t spine_bone_data_get_index(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_bone_data_get_name(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_bone_data_get_name(spine_bone_data data);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone_data spine_bone_data_get_parent(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT spine_bone_data spine_bone_data_get_parent(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_bone_data_get_length(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT float spine_bone_data_get_length(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_length(spine_bone_data data, float length);
|
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_length(spine_bone_data data, float length);
|
||||||
@ -508,7 +542,7 @@ SPINE_CPP_LITE_EXPORT float spine_bone_data_get_shear_y(spine_bone_data data);
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_shear_y(spine_bone_data data, float shearY);
|
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_shear_y(spine_bone_data data, float shearY);
|
||||||
SPINE_CPP_LITE_EXPORT spine_inherit spine_bone_data_get_inherit(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT spine_inherit spine_bone_data_get_inherit(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_inherit(spine_bone_data data, spine_inherit inherit);
|
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_inherit(spine_bone_data data, spine_inherit inherit);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bool spine_bone_data_is_skin_required(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT spine_bool spine_bone_data_get_is_skin_required(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_is_skin_required(spine_bone_data data, spine_bool isSkinRequired);
|
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_is_skin_required(spine_bone_data data, spine_bool isSkinRequired);
|
||||||
SPINE_CPP_LITE_EXPORT spine_color spine_bone_data_get_color(spine_bone_data data);
|
SPINE_CPP_LITE_EXPORT spine_color spine_bone_data_get_color(spine_bone_data data);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_color(spine_bone_data data, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_bone_data_set_color(spine_bone_data data, float r, float g, float b, float a);
|
||||||
@ -534,6 +568,7 @@ SPINE_CPP_LITE_EXPORT float spine_bone_get_world_to_local_rotation_x(spine_bone
|
|||||||
SPINE_CPP_LITE_EXPORT float spine_bone_get_world_to_local_rotation_y(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT float spine_bone_get_world_to_local_rotation_y(spine_bone bone);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone_data spine_bone_get_data(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT spine_bone_data spine_bone_get_data(spine_bone bone);
|
||||||
SPINE_CPP_LITE_EXPORT spine_skeleton spine_bone_get_skeleton(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT spine_skeleton spine_bone_get_skeleton(spine_bone bone);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone spine_bone_get_parent(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT spine_bone spine_bone_get_parent(spine_bone bone);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_bone_get_num_children(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT int32_t spine_bone_get_num_children(spine_bone bone);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bone *spine_bone_get_children(spine_bone bone);
|
SPINE_CPP_LITE_EXPORT spine_bone *spine_bone_get_children(spine_bone bone);
|
||||||
@ -588,6 +623,7 @@ SPINE_CPP_LITE_EXPORT void spine_bone_set_inherit(spine_bone data, spine_inherit
|
|||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_attachment_get_name(spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_attachment_get_name(spine_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment_type spine_attachment_get_type(spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_attachment_type spine_attachment_get_type(spine_attachment attachment);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_attachment_copy(spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_attachment_copy(spine_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_attachment_dispose(spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT void spine_attachment_dispose(spine_attachment attachment);
|
||||||
|
|
||||||
@ -603,6 +639,7 @@ SPINE_CPP_LITE_EXPORT spine_color spine_point_attachment_get_color(spine_point_a
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_point_attachment_set_color(spine_point_attachment attachment, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_point_attachment_set_color(spine_point_attachment attachment, float r, float g, float b, float a);
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT void spine_region_attachment_update_region(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT void spine_region_attachment_update_region(spine_region_attachment attachment);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT void spine_region_attachment_compute_world_vertices(spine_region_attachment attachment, spine_slot slot, float *worldVertices);
|
SPINE_CPP_LITE_EXPORT void spine_region_attachment_compute_world_vertices(spine_region_attachment attachment, spine_slot slot, float *worldVertices);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_region_attachment_get_x(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT float spine_region_attachment_get_x(spine_region_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_region_attachment_set_x(spine_region_attachment attachment, float x);
|
SPINE_CPP_LITE_EXPORT void spine_region_attachment_set_x(spine_region_attachment attachment, float x);
|
||||||
@ -622,8 +659,10 @@ SPINE_CPP_LITE_EXPORT spine_color spine_region_attachment_get_color(spine_region
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_region_attachment_set_color(spine_region_attachment attachment, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_region_attachment_set_color(spine_region_attachment attachment, float r, float g, float b, float a);
|
||||||
SPINE_CPP_LITE_EXPORT const utf8 *spine_region_attachment_get_path(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT const utf8 *spine_region_attachment_get_path(spine_region_attachment attachment);
|
||||||
// OMITTED setPath()
|
// OMITTED setPath()
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_texture_region spine_region_attachment_get_region(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_texture_region spine_region_attachment_get_region(spine_region_attachment attachment);
|
||||||
// OMITTED setRegion()
|
// OMITTED setRegion()
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_sequence spine_region_attachment_get_sequence(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_sequence spine_region_attachment_get_sequence(spine_region_attachment attachment);
|
||||||
// OMITTED setSequence()
|
// OMITTED setSequence()
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_region_attachment_get_num_offset(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t spine_region_attachment_get_num_offset(spine_region_attachment attachment);
|
||||||
@ -632,12 +671,14 @@ SPINE_CPP_LITE_EXPORT int32_t spine_region_attachment_get_num_uvs(spine_region_a
|
|||||||
SPINE_CPP_LITE_EXPORT float *spine_region_attachment_get_uvs(spine_region_attachment attachment);
|
SPINE_CPP_LITE_EXPORT float *spine_region_attachment_get_uvs(spine_region_attachment attachment);
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_world_vertices_length(spine_vertex_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_world_vertices_length(spine_vertex_attachment attachment);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT void spine_vertex_attachment_compute_world_vertices(spine_vertex_attachment attachment, spine_slot slot, float *worldVertices);
|
SPINE_CPP_LITE_EXPORT void spine_vertex_attachment_compute_world_vertices(spine_vertex_attachment attachment, spine_slot slot, float *worldVertices);
|
||||||
// OMITTED getId()
|
// OMITTED getId()
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_num_bones(spine_vertex_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_num_bones(spine_vertex_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t *spine_vertex_attachment_get_bones(spine_vertex_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t *spine_vertex_attachment_get_bones(spine_vertex_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_num_vertices(spine_vertex_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t spine_vertex_attachment_get_num_vertices(spine_vertex_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT float *spine_vertex_attachment_get_vertices(spine_vertex_attachment attachment);
|
SPINE_CPP_LITE_EXPORT float *spine_vertex_attachment_get_vertices(spine_vertex_attachment attachment);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_vertex_attachment_get_timeline_attachment(spine_vertex_attachment timelineAttachment);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_vertex_attachment_get_timeline_attachment(spine_vertex_attachment timelineAttachment);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_vertex_attachment_set_timeline_attachment(spine_vertex_attachment attachment, spine_attachment timelineAttachment);
|
SPINE_CPP_LITE_EXPORT void spine_vertex_attachment_set_timeline_attachment(spine_vertex_attachment attachment, spine_attachment timelineAttachment);
|
||||||
// OMITTED copyTo()
|
// OMITTED copyTo()
|
||||||
@ -657,8 +698,10 @@ SPINE_CPP_LITE_EXPORT const utf8 *spine_mesh_attachment_get_path(spine_mesh_atta
|
|||||||
// OMITTED setPath()
|
// OMITTED setPath()
|
||||||
SPINE_CPP_LITE_EXPORT spine_texture_region spine_mesh_attachment_get_region(spine_mesh_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_texture_region spine_mesh_attachment_get_region(spine_mesh_attachment attachment);
|
||||||
// OMITTED setRegion()
|
// OMITTED setRegion()
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_sequence spine_mesh_attachment_get_sequence(spine_mesh_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_sequence spine_mesh_attachment_get_sequence(spine_mesh_attachment attachment);
|
||||||
// OMITTED setSequence()
|
// OMITTED setSequence()
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_mesh_attachment spine_mesh_attachment_get_parent_mesh(spine_mesh_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_mesh_attachment spine_mesh_attachment_get_parent_mesh(spine_mesh_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_mesh_attachment_set_parent_mesh(spine_mesh_attachment attachment, spine_mesh_attachment parentMesh);
|
SPINE_CPP_LITE_EXPORT void spine_mesh_attachment_set_parent_mesh(spine_mesh_attachment attachment, spine_mesh_attachment parentMesh);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_mesh_attachment_get_num_edges(spine_mesh_attachment attachment);
|
SPINE_CPP_LITE_EXPORT int32_t spine_mesh_attachment_get_num_edges(spine_mesh_attachment attachment);
|
||||||
@ -669,6 +712,7 @@ SPINE_CPP_LITE_EXPORT float spine_mesh_attachment_get_height(spine_mesh_attachme
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_mesh_attachment_set_height(spine_mesh_attachment attachment, float height);
|
SPINE_CPP_LITE_EXPORT void spine_mesh_attachment_set_height(spine_mesh_attachment attachment, float height);
|
||||||
// OMITTED newLinkedMesh()
|
// OMITTED newLinkedMesh()
|
||||||
|
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_slot_data spine_clipping_attachment_get_end_slot(spine_clipping_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_slot_data spine_clipping_attachment_get_end_slot(spine_clipping_attachment attachment);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_clipping_attachment_set_end_slot(spine_clipping_attachment attachment, spine_slot_data endSlot);
|
SPINE_CPP_LITE_EXPORT void spine_clipping_attachment_set_end_slot(spine_clipping_attachment attachment, spine_slot_data endSlot);
|
||||||
SPINE_CPP_LITE_EXPORT spine_color spine_clipping_attachment_get_color(spine_clipping_attachment attachment);
|
SPINE_CPP_LITE_EXPORT spine_color spine_clipping_attachment_get_color(spine_clipping_attachment attachment);
|
||||||
@ -687,6 +731,7 @@ SPINE_CPP_LITE_EXPORT spine_color spine_path_attachment_get_color(spine_path_att
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_path_attachment_set_color(spine_path_attachment attachment, float r, float g, float b, float a);
|
SPINE_CPP_LITE_EXPORT void spine_path_attachment_set_color(spine_path_attachment attachment, float r, float g, float b, float a);
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skin_set_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name, spine_attachment attachment);
|
SPINE_CPP_LITE_EXPORT void spine_skin_set_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name, spine_attachment attachment);
|
||||||
|
// @optional
|
||||||
SPINE_CPP_LITE_EXPORT spine_attachment spine_skin_get_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name);
|
SPINE_CPP_LITE_EXPORT spine_attachment spine_skin_get_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skin_remove_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name);
|
SPINE_CPP_LITE_EXPORT void spine_skin_remove_attachment(spine_skin skin, int32_t slotIndex, const utf8 *name);
|
||||||
// OMITTED findNamesForSlot()
|
// OMITTED findNamesForSlot()
|
||||||
@ -706,6 +751,7 @@ SPINE_CPP_LITE_EXPORT int32_t spine_skin_get_num_bones(spine_skin skin);
|
|||||||
SPINE_CPP_LITE_EXPORT spine_bone_data *spine_skin_get_bones(spine_skin skin);
|
SPINE_CPP_LITE_EXPORT spine_bone_data *spine_skin_get_bones(spine_skin skin);
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_skin_get_num_constraints(spine_skin skin);
|
SPINE_CPP_LITE_EXPORT int32_t spine_skin_get_num_constraints(spine_skin skin);
|
||||||
SPINE_CPP_LITE_EXPORT spine_constraint_data *spine_skin_get_constraints(spine_skin skin);
|
SPINE_CPP_LITE_EXPORT spine_constraint_data *spine_skin_get_constraints(spine_skin skin);
|
||||||
|
// @ignore
|
||||||
SPINE_CPP_LITE_EXPORT spine_skin spine_skin_create(const utf8 *name);
|
SPINE_CPP_LITE_EXPORT spine_skin spine_skin_create(const utf8 *name);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_skin_dispose(spine_skin skin);
|
SPINE_CPP_LITE_EXPORT void spine_skin_dispose(spine_skin skin);
|
||||||
|
|
||||||
@ -808,6 +854,7 @@ SPINE_CPP_LITE_EXPORT float spine_transform_constraint_get_mix_shear_y(spine_tra
|
|||||||
SPINE_CPP_LITE_EXPORT void spine_transform_constraint_set_mix_shear_y(spine_transform_constraint constraint, float mixShearY);
|
SPINE_CPP_LITE_EXPORT void spine_transform_constraint_set_mix_shear_y(spine_transform_constraint constraint, float mixShearY);
|
||||||
SPINE_CPP_LITE_EXPORT spine_bool spine_transform_constraint_get_is_active(spine_transform_constraint constraint);
|
SPINE_CPP_LITE_EXPORT spine_bool spine_transform_constraint_get_is_active(spine_transform_constraint constraint);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_transform_constraint_set_is_active(spine_transform_constraint constraint, spine_bool isActive);
|
SPINE_CPP_LITE_EXPORT void spine_transform_constraint_set_is_active(spine_transform_constraint constraint, spine_bool isActive);
|
||||||
|
|
||||||
// OMITTED setToSetupPose()
|
// OMITTED setToSetupPose()
|
||||||
|
|
||||||
SPINE_CPP_LITE_EXPORT int32_t spine_path_constraint_data_get_num_bones(spine_path_constraint_data data);
|
SPINE_CPP_LITE_EXPORT int32_t spine_path_constraint_data_get_num_bones(spine_path_constraint_data data);
|
||||||
@ -951,7 +998,7 @@ SPINE_CPP_LITE_EXPORT void spine_physics_constraint_set_remaining(spine_physics_
|
|||||||
SPINE_CPP_LITE_EXPORT float spine_physics_constraint_get_remaining(spine_physics_constraint constraint);
|
SPINE_CPP_LITE_EXPORT float spine_physics_constraint_get_remaining(spine_physics_constraint constraint);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_set_last_time(spine_physics_constraint constraint, float value);
|
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_set_last_time(spine_physics_constraint constraint, float value);
|
||||||
SPINE_CPP_LITE_EXPORT float spine_physics_constraint_get_last_time(spine_physics_constraint constraint);
|
SPINE_CPP_LITE_EXPORT float spine_physics_constraint_get_last_time(spine_physics_constraint constraint);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_reset(spine_physics_constraint constraint);
|
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_reset_fully(spine_physics_constraint constraint);
|
||||||
// Omitted setToSetupPose()
|
// Omitted setToSetupPose()
|
||||||
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_update(spine_physics_constraint data, spine_physics physics);
|
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_update(spine_physics_constraint data, spine_physics physics);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_translate(spine_physics_constraint data, float x, float y);
|
SPINE_CPP_LITE_EXPORT void spine_physics_constraint_translate(spine_physics_constraint data, float x, float y);
|
||||||
@ -997,4 +1044,6 @@ SPINE_CPP_LITE_EXPORT void spine_texture_region_set_original_width(spine_texture
|
|||||||
SPINE_CPP_LITE_EXPORT int32_t spine_texture_region_get_original_height(spine_texture_region textureRegion);
|
SPINE_CPP_LITE_EXPORT int32_t spine_texture_region_get_original_height(spine_texture_region textureRegion);
|
||||||
SPINE_CPP_LITE_EXPORT void spine_texture_region_set_original_height(spine_texture_region textureRegion, int32_t originalHeight);
|
SPINE_CPP_LITE_EXPORT void spine_texture_region_set_original_height(spine_texture_region textureRegion, int32_t originalHeight);
|
||||||
|
|
||||||
|
// @end: function_declarations
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -23,14 +23,14 @@ class AnimationStateEvents extends StatelessWidget {
|
|||||||
controller.animationState.setListener((type, trackEntry, event) {
|
controller.animationState.setListener((type, trackEntry, event) {
|
||||||
if (type == EventType.event) {
|
if (type == EventType.event) {
|
||||||
print(
|
print(
|
||||||
"User event: { name: ${event?.getData().getName()}, intValue: ${event?.getIntValue()}, floatValue: intValue: ${event?.getFloatValue()}, stringValue: ${event?.getStringValue()} }");
|
"User event: { name: ${event?.getData().getName()}, intValue: ${event?.getIntValue()}, floatValue: ${event?.getFloatValue()}, stringValue: ${event?.getStringValue()} }");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
print("Current: ${controller.animationState.getCurrent(0)?.getAnimation().getName()}");
|
print("Current: ${controller.animationState.getCurrent(0)?.getAnimation().getName()}");
|
||||||
});
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Spineboy')),
|
appBar: AppBar(title: const Text('Animation State Listener')),
|
||||||
body: Column(children: [
|
body: Column(children: [
|
||||||
const Text("See output in console!"),
|
const Text("See output in console!"),
|
||||||
Expanded(child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller))
|
Expanded(child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller))
|
||||||
|
|||||||
@ -41,7 +41,6 @@ class PhysicsState extends State<PhysicsTest> {
|
|||||||
late SpineWidgetController controller;
|
late SpineWidgetController controller;
|
||||||
Offset? mousePosition;
|
Offset? mousePosition;
|
||||||
Offset? lastMousePosition;
|
Offset? lastMousePosition;
|
||||||
Offset? delta;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|||||||
@ -659,7 +659,7 @@ class _SpineRenderObject extends RenderBox {
|
|||||||
..translate(offsetX, offsetY)
|
..translate(offsetX, offsetY)
|
||||||
..scale(scaleX, scaleY)
|
..scale(scaleX, scaleY)
|
||||||
..translate(x, y);
|
..translate(x, y);
|
||||||
_controller._setCoordinateTransform(x + offsetX / scaleY, y + offsetY / scaleY, scaleX, scaleY);
|
_controller._setCoordinateTransform(x + offsetX / scaleX, y + offsetY / scaleY, scaleX, scaleY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -45,4 +45,4 @@ flutter:
|
|||||||
- lib/assets/libspine_flutter.js
|
- lib/assets/libspine_flutter.js
|
||||||
- lib/assets/libspine_flutter.wasm
|
- lib/assets/libspine_flutter.wasm
|
||||||
- src/spine-cpp-lite/spine-cpp-lite.cpp
|
- src/spine-cpp-lite/spine-cpp-lite.cpp
|
||||||
- src/spine-cpp-lite/spine-cpp-lite.h
|
- ../spine-cpp/spine-cpp-lite/spine-cpp-lite.h
|
||||||
|
|||||||
8
spine-ios/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
24
spine-ios/CHANGELOG.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
# Unreleased
|
||||||
|
|
||||||
|
- Add `spine-iOS` SPM package & example app (#1)
|
||||||
|
- Basic Mesh Rendering (#2)
|
||||||
|
- Spine C++ Swift Wrapper (#3)
|
||||||
|
- Load `Atlas` & `SkeletonData` (#4)
|
||||||
|
- Generate Swift classes from `spine-cpp-lite.h` (#5)
|
||||||
|
- Draw `SkeletonData` render commands (#6)
|
||||||
|
- Add `BoundsProvider` (#7)
|
||||||
|
- Support `SpineController` & `Event` callbacks (#8)
|
||||||
|
- Support `DressUp` sample (#9)
|
||||||
|
- Add `Physics` sample (#10)
|
||||||
|
- Add `DebugRendering` sample (#11)
|
||||||
|
- Move remaining files to SPM package (#12)
|
||||||
|
- Load assets from different sources (#13)
|
||||||
|
- Add `Obj-C` + `UIKit` sample (#14)
|
||||||
|
- Support CocoaPods (#15)
|
||||||
|
- Metal Best Practices (#16)
|
||||||
|
- Annotate functions that should return optional (#17)
|
||||||
|
- Add option to disable drawing when out of viewport (#18)
|
||||||
|
- Add docs for public Spine classes/methods (#19)
|
||||||
|
- Fix various regressions (#20)
|
||||||
92
spine-ios/Example - Cocoapods/.gitignore
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## User settings
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.xccheckout
|
||||||
|
|
||||||
|
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
|
||||||
|
## App packaging
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## Playgrounds
|
||||||
|
timeline.xctimeline
|
||||||
|
playground.xcworkspace
|
||||||
|
|
||||||
|
# Swift Package Manager
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
|
# Packages/
|
||||||
|
# Package.pins
|
||||||
|
# Package.resolved
|
||||||
|
# *.xcodeproj
|
||||||
|
#
|
||||||
|
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||||
|
# hence it is not needed unless you have added a package configuration file to your project
|
||||||
|
# .swiftpm
|
||||||
|
|
||||||
|
.build/
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
#
|
||||||
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
|
#
|
||||||
|
Pods/
|
||||||
|
Podfile.lock
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||||
|
*.xcworkspace
|
||||||
|
|
||||||
|
# Carthage
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
|
# Carthage/Checkouts
|
||||||
|
|
||||||
|
Carthage/Build/
|
||||||
|
|
||||||
|
# Accio dependency management
|
||||||
|
Dependencies/
|
||||||
|
.accio/
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo.
|
||||||
|
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots/**/*.png
|
||||||
|
fastlane/test_output
|
||||||
|
|
||||||
|
# Code Injection
|
||||||
|
#
|
||||||
|
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||||
|
# https://github.com/johnno1962/injectionforxcode
|
||||||
|
|
||||||
|
iOSInjectionProject/
|
||||||
|
.DS_store
|
||||||
11
spine-ios/Example - Cocoapods/Podfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Uncomment the next line to define a global platform for your project
|
||||||
|
platform :ios, '13.0'
|
||||||
|
|
||||||
|
target 'Spine iOS Example' do
|
||||||
|
# Comment the next line if you don't want to use dynamic frameworks
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
pod 'Spine', :podspec => 'https://raw.githubusercontent.com/denrase/spine-runtimes/cocoapods/Spine.podspec'
|
||||||
|
pod 'SpineCppLite', :podspec => 'https://raw.githubusercontent.com/denrase/spine-runtimes/cocoapods/SpineCppLite.podspec'
|
||||||
|
pod 'SpineShadersStructs', :podspec => 'https://raw.githubusercontent.com/denrase/spine-runtimes/cocoapods/SpineShadersStructs.podspec'
|
||||||
|
end
|
||||||
@ -0,0 +1,455 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 56;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
029E40AACC629A2B19C75855 /* Pods_Spine_iOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63A23DBBF6B9A1877F1E82A7 /* Pods_Spine_iOS_Example.framework */; };
|
||||||
|
9281EF152C07885C00BE19F5 /* Spine_iOS_ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9281EF142C07885C00BE19F5 /* Spine_iOS_ExampleApp.swift */; };
|
||||||
|
9281EF192C07885D00BE19F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9281EF182C07885D00BE19F5 /* Assets.xcassets */; };
|
||||||
|
9281EF1C2C07885D00BE19F5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9281EF1B2C07885D00BE19F5 /* Preview Assets.xcassets */; };
|
||||||
|
92BFC4B62C11BCA10051F2EA /* spineboy-pro.skel in Resources */ = {isa = PBXBuildFile; fileRef = 92BFC4B22C11BCA10051F2EA /* spineboy-pro.skel */; };
|
||||||
|
92BFC4B72C11BCA10051F2EA /* spineboy-pro.json in Resources */ = {isa = PBXBuildFile; fileRef = 92BFC4B32C11BCA10051F2EA /* spineboy-pro.json */; };
|
||||||
|
92BFC4B82C11BCA10051F2EA /* spineboy.png in Resources */ = {isa = PBXBuildFile; fileRef = 92BFC4B42C11BCA10051F2EA /* spineboy.png */; };
|
||||||
|
92BFC4B92C11BCA10051F2EA /* spineboy.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 92BFC4B52C11BCA10051F2EA /* spineboy.atlas */; };
|
||||||
|
92BFC4BB2C11BCC50051F2EA /* SimpleAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92BFC4BA2C11BCC50051F2EA /* SimpleAnimation.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
63A23DBBF6B9A1877F1E82A7 /* Pods_Spine_iOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Spine_iOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
7C440864BA6BF0A3BA8297E4 /* Pods-Spine iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spine iOS Example.debug.xcconfig"; path = "Target Support Files/Pods-Spine iOS Example/Pods-Spine iOS Example.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
9206EF0B2C119BC500320243 /* SpineCppLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SpineCppLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
9281EF112C07885C00BE19F5 /* Spine iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Spine iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
9281EF142C07885C00BE19F5 /* Spine_iOS_ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spine_iOS_ExampleApp.swift; sourceTree = "<group>"; };
|
||||||
|
9281EF182C07885D00BE19F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
9281EF1B2C07885D00BE19F5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
|
92BFC4B22C11BCA10051F2EA /* spineboy-pro.skel */ = {isa = PBXFileReference; lastKnownFileType = file; path = "spineboy-pro.skel"; sourceTree = "<group>"; };
|
||||||
|
92BFC4B32C11BCA10051F2EA /* spineboy-pro.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "spineboy-pro.json"; sourceTree = "<group>"; };
|
||||||
|
92BFC4B42C11BCA10051F2EA /* spineboy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = spineboy.png; sourceTree = "<group>"; };
|
||||||
|
92BFC4B52C11BCA10051F2EA /* spineboy.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = spineboy.atlas; sourceTree = "<group>"; };
|
||||||
|
92BFC4BA2C11BCC50051F2EA /* SimpleAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SimpleAnimation.swift; path = "Spine iOS Example/SimpleAnimation.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
|
F571992938153AF59959A005 /* Pods-Spine iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Spine iOS Example.release.xcconfig"; path = "Target Support Files/Pods-Spine iOS Example/Pods-Spine iOS Example.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
9281EF0E2C07885C00BE19F5 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
029E40AACC629A2B19C75855 /* Pods_Spine_iOS_Example.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
646ABFDC442BA39836A04383 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7C440864BA6BF0A3BA8297E4 /* Pods-Spine iOS Example.debug.xcconfig */,
|
||||||
|
F571992938153AF59959A005 /* Pods-Spine iOS Example.release.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9281EF082C07885C00BE19F5 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9281EF132C07885C00BE19F5 /* Spine iOS Example */,
|
||||||
|
9281EF122C07885C00BE19F5 /* Products */,
|
||||||
|
646ABFDC442BA39836A04383 /* Pods */,
|
||||||
|
F18879239F06E7487AA7C894 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9281EF122C07885C00BE19F5 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9281EF112C07885C00BE19F5 /* Spine iOS Example.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9281EF132C07885C00BE19F5 /* Spine iOS Example */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
92BFC4B12C11BC840051F2EA /* spineboy */,
|
||||||
|
9281EF142C07885C00BE19F5 /* Spine_iOS_ExampleApp.swift */,
|
||||||
|
92BFC4BA2C11BCC50051F2EA /* SimpleAnimation.swift */,
|
||||||
|
9281EF182C07885D00BE19F5 /* Assets.xcassets */,
|
||||||
|
9281EF1A2C07885D00BE19F5 /* Preview Content */,
|
||||||
|
);
|
||||||
|
path = "Spine iOS Example";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9281EF1A2C07885D00BE19F5 /* Preview Content */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9281EF1B2C07885D00BE19F5 /* Preview Assets.xcassets */,
|
||||||
|
);
|
||||||
|
path = "Preview Content";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
92BFC4B12C11BC840051F2EA /* spineboy */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
92BFC4B32C11BCA10051F2EA /* spineboy-pro.json */,
|
||||||
|
92BFC4B22C11BCA10051F2EA /* spineboy-pro.skel */,
|
||||||
|
92BFC4B52C11BCA10051F2EA /* spineboy.atlas */,
|
||||||
|
92BFC4B42C11BCA10051F2EA /* spineboy.png */,
|
||||||
|
);
|
||||||
|
path = spineboy;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
F18879239F06E7487AA7C894 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9206EF0B2C119BC500320243 /* SpineCppLite.framework */,
|
||||||
|
63A23DBBF6B9A1877F1E82A7 /* Pods_Spine_iOS_Example.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
9281EF102C07885C00BE19F5 /* Spine iOS Example */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 9281EF1F2C07885D00BE19F5 /* Build configuration list for PBXNativeTarget "Spine iOS Example" */;
|
||||||
|
buildPhases = (
|
||||||
|
4CE732AF2F7C247F187833EC /* [CP] Check Pods Manifest.lock */,
|
||||||
|
9281EF0D2C07885C00BE19F5 /* Sources */,
|
||||||
|
9281EF0E2C07885C00BE19F5 /* Frameworks */,
|
||||||
|
9281EF0F2C07885C00BE19F5 /* Resources */,
|
||||||
|
A39FE6D793C3DED23C563AA5 /* [CP] Embed Pods Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Spine iOS Example";
|
||||||
|
productName = "Spine iOS Example";
|
||||||
|
productReference = 9281EF112C07885C00BE19F5 /* Spine iOS Example.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
9281EF092C07885C00BE19F5 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1520;
|
||||||
|
LastUpgradeCheck = 1520;
|
||||||
|
TargetAttributes = {
|
||||||
|
9281EF102C07885C00BE19F5 = {
|
||||||
|
CreatedOnToolsVersion = 15.2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 9281EF0C2C07885C00BE19F5 /* Build configuration list for PBXProject "Spine iOS Example" */;
|
||||||
|
compatibilityVersion = "Xcode 14.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 9281EF082C07885C00BE19F5;
|
||||||
|
productRefGroup = 9281EF122C07885C00BE19F5 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
9281EF102C07885C00BE19F5 /* Spine iOS Example */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
9281EF0F2C07885C00BE19F5 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
92BFC4B72C11BCA10051F2EA /* spineboy-pro.json in Resources */,
|
||||||
|
9281EF1C2C07885D00BE19F5 /* Preview Assets.xcassets in Resources */,
|
||||||
|
92BFC4B62C11BCA10051F2EA /* spineboy-pro.skel in Resources */,
|
||||||
|
9281EF192C07885D00BE19F5 /* Assets.xcassets in Resources */,
|
||||||
|
92BFC4B92C11BCA10051F2EA /* spineboy.atlas in Resources */,
|
||||||
|
92BFC4B82C11BCA10051F2EA /* spineboy.png in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
4CE732AF2F7C247F187833EC /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Spine iOS Example-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
A39FE6D793C3DED23C563AA5 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Spine iOS Example/Pods-Spine iOS Example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Spine iOS Example/Pods-Spine iOS Example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Spine iOS Example/Pods-Spine iOS Example-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
9281EF0D2C07885C00BE19F5 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
92BFC4BB2C11BCC50051F2EA /* SimpleAnimation.swift in Sources */,
|
||||||
|
9281EF152C07885C00BE19F5 /* Spine_iOS_ExampleApp.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
9281EF1D2C07885D00BE19F5 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
9281EF1E2C07885D00BE19F5 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
9281EF202C07885D00BE19F5 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7C440864BA6BF0A3BA8297E4 /* Pods-Spine iOS Example.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMPRESS_PNG_FILES = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "\"Spine iOS Example/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = 9ZFD4KCY8F;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.esotericsoftware.spine-ios-example.Spine-iOS-Example";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRIP_PNG_TEXT = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_INTEROP_MODE = objcxx;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
9281EF212C07885D00BE19F5 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = F571992938153AF59959A005 /* Pods-Spine iOS Example.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMPRESS_PNG_FILES = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "\"Spine iOS Example/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = 9ZFD4KCY8F;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.esotericsoftware.spine-ios-example.Spine-iOS-Example";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRIP_PNG_TEXT = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_INTEROP_MODE = objcxx;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
9281EF0C2C07885C00BE19F5 /* Build configuration list for PBXProject "Spine iOS Example" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
9281EF1D2C07885D00BE19F5 /* Debug */,
|
||||||
|
9281EF1E2C07885D00BE19F5 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
9281EF1F2C07885D00BE19F5 /* Build configuration list for PBXNativeTarget "Spine iOS Example" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
9281EF202C07885D00BE19F5 /* Debug */,
|
||||||
|
9281EF212C07885D00BE19F5 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 9281EF092C07885C00BE19F5 /* Project object */;
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct SimpleAnimation: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "walk",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
// from: .http(
|
||||||
|
// atlasURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
|
||||||
|
// skeletonURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
|
||||||
|
// ),
|
||||||
|
controller: controller,
|
||||||
|
mode: .fit,
|
||||||
|
alignment: .center
|
||||||
|
)
|
||||||
|
.navigationTitle("Simple Animation")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SimpleAnimation()
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// Spine_iOS_ExampleApp.swift
|
||||||
|
// Spine iOS Example
|
||||||
|
//
|
||||||
|
// Created by Denis Andrašec on 29.05.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct Spine_iOS_ExampleApp: App {
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
SimpleAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
spineboy.png
|
||||||
|
size: 1024, 256
|
||||||
|
filter: Linear, Linear
|
||||||
|
scale: 0.5
|
||||||
|
crosshair
|
||||||
|
bounds: 352, 7, 45, 45
|
||||||
|
eye-indifferent
|
||||||
|
bounds: 862, 105, 47, 45
|
||||||
|
eye-surprised
|
||||||
|
bounds: 505, 79, 47, 45
|
||||||
|
front-bracer
|
||||||
|
bounds: 826, 66, 29, 40
|
||||||
|
front-fist-closed
|
||||||
|
bounds: 786, 65, 38, 41
|
||||||
|
front-fist-open
|
||||||
|
bounds: 710, 51, 43, 44
|
||||||
|
rotate: 90
|
||||||
|
front-foot
|
||||||
|
bounds: 210, 6, 63, 35
|
||||||
|
front-shin
|
||||||
|
bounds: 665, 128, 41, 92
|
||||||
|
rotate: 90
|
||||||
|
front-thigh
|
||||||
|
bounds: 2, 2, 23, 56
|
||||||
|
rotate: 90
|
||||||
|
front-upper-arm
|
||||||
|
bounds: 250, 205, 23, 49
|
||||||
|
goggles
|
||||||
|
bounds: 665, 171, 131, 83
|
||||||
|
gun
|
||||||
|
bounds: 798, 152, 105, 102
|
||||||
|
head
|
||||||
|
bounds: 2, 27, 136, 149
|
||||||
|
hoverboard-board
|
||||||
|
bounds: 2, 178, 246, 76
|
||||||
|
hoverboard-thruster
|
||||||
|
bounds: 722, 96, 30, 32
|
||||||
|
rotate: 90
|
||||||
|
hoverglow-small
|
||||||
|
bounds: 275, 81, 137, 38
|
||||||
|
mouth-grind
|
||||||
|
bounds: 614, 97, 47, 30
|
||||||
|
mouth-oooo
|
||||||
|
bounds: 612, 65, 47, 30
|
||||||
|
mouth-smile
|
||||||
|
bounds: 661, 64, 47, 30
|
||||||
|
muzzle-glow
|
||||||
|
bounds: 382, 54, 25, 25
|
||||||
|
muzzle-ring
|
||||||
|
bounds: 275, 54, 25, 105
|
||||||
|
rotate: 90
|
||||||
|
muzzle01
|
||||||
|
bounds: 911, 95, 67, 40
|
||||||
|
rotate: 90
|
||||||
|
muzzle02
|
||||||
|
bounds: 792, 108, 68, 42
|
||||||
|
muzzle03
|
||||||
|
bounds: 956, 171, 83, 53
|
||||||
|
rotate: 90
|
||||||
|
muzzle04
|
||||||
|
bounds: 275, 7, 75, 45
|
||||||
|
muzzle05
|
||||||
|
bounds: 140, 3, 68, 38
|
||||||
|
neck
|
||||||
|
bounds: 250, 182, 18, 21
|
||||||
|
portal-bg
|
||||||
|
bounds: 140, 43, 133, 133
|
||||||
|
portal-flare1
|
||||||
|
bounds: 554, 65, 56, 30
|
||||||
|
portal-flare2
|
||||||
|
bounds: 759, 112, 57, 31
|
||||||
|
rotate: 90
|
||||||
|
portal-flare3
|
||||||
|
bounds: 554, 97, 58, 30
|
||||||
|
portal-shade
|
||||||
|
bounds: 275, 121, 133, 133
|
||||||
|
portal-streaks1
|
||||||
|
bounds: 410, 126, 126, 128
|
||||||
|
portal-streaks2
|
||||||
|
bounds: 538, 129, 125, 125
|
||||||
|
rear-bracer
|
||||||
|
bounds: 857, 67, 28, 36
|
||||||
|
rear-foot
|
||||||
|
bounds: 663, 96, 57, 30
|
||||||
|
rear-shin
|
||||||
|
bounds: 414, 86, 38, 89
|
||||||
|
rotate: 90
|
||||||
|
rear-thigh
|
||||||
|
bounds: 756, 63, 28, 47
|
||||||
|
rear-upper-arm
|
||||||
|
bounds: 60, 5, 20, 44
|
||||||
|
rotate: 90
|
||||||
|
torso
|
||||||
|
bounds: 905, 164, 49, 90
|
||||||
|
After Width: | Height: | Size: 240 KiB |
91
spine-ios/Example/.gitignore
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## User settings
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.xccheckout
|
||||||
|
|
||||||
|
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
|
||||||
|
## App packaging
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## Playgrounds
|
||||||
|
timeline.xctimeline
|
||||||
|
playground.xcworkspace
|
||||||
|
|
||||||
|
# Swift Package Manager
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
|
# Packages/
|
||||||
|
# Package.pins
|
||||||
|
# Package.resolved
|
||||||
|
# *.xcodeproj
|
||||||
|
#
|
||||||
|
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||||
|
# hence it is not needed unless you have added a package configuration file to your project
|
||||||
|
# .swiftpm
|
||||||
|
|
||||||
|
.build/
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
#
|
||||||
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
|
#
|
||||||
|
# Pods/
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||||
|
# *.xcworkspace
|
||||||
|
|
||||||
|
# Carthage
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
|
# Carthage/Checkouts
|
||||||
|
|
||||||
|
Carthage/Build/
|
||||||
|
|
||||||
|
# Accio dependency management
|
||||||
|
Dependencies/
|
||||||
|
.accio/
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo.
|
||||||
|
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots/**/*.png
|
||||||
|
fastlane/test_output
|
||||||
|
|
||||||
|
# Code Injection
|
||||||
|
#
|
||||||
|
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||||
|
# https://github.com/johnno1962/injectionforxcode
|
||||||
|
|
||||||
|
iOSInjectionProject/
|
||||||
|
.DS_store
|
||||||
557
spine-ios/Example/Spine iOS Example.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 60;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
9205FCD42C0760B1006EE07E /* SimpleAnimationViewControllerRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205FCD32C0760B1006EE07E /* SimpleAnimationViewControllerRepresentable.swift */; };
|
||||||
|
920BD1162BEBC52D0050A5A9 /* spineboy-pro.skel in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1122BEBC52D0050A5A9 /* spineboy-pro.skel */; };
|
||||||
|
920BD1182BEBC52D0050A5A9 /* spineboy-pro.json in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1142BEBC52D0050A5A9 /* spineboy-pro.json */; };
|
||||||
|
920BD1192BEBC52D0050A5A9 /* spineboy.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1152BEBC52D0050A5A9 /* spineboy.atlas */; };
|
||||||
|
920BD11B2BEBDBA60050A5A9 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920BD11A2BEBDBA60050A5A9 /* MainView.swift */; };
|
||||||
|
920BD11D2BEBDC380050A5A9 /* PlayPauseAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920BD11C2BEBDC380050A5A9 /* PlayPauseAnimation.swift */; };
|
||||||
|
920BD1262BEBDCD80050A5A9 /* dragon_4.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BD11F2BEBDCD80050A5A9 /* dragon_4.png */; };
|
||||||
|
920BD1272BEBDCD80050A5A9 /* dragon_5.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1202BEBDCD80050A5A9 /* dragon_5.png */; };
|
||||||
|
920BD1282BEBDCD80050A5A9 /* dragon_2.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1212BEBDCD80050A5A9 /* dragon_2.png */; };
|
||||||
|
920BD1292BEBDCD80050A5A9 /* dragon-ess.skel in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1222BEBDCD80050A5A9 /* dragon-ess.skel */; };
|
||||||
|
920BD12A2BEBDCD80050A5A9 /* dragon_3.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1232BEBDCD80050A5A9 /* dragon_3.png */; };
|
||||||
|
920BD12B2BEBDCD80050A5A9 /* dragon.png in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1242BEBDCD80050A5A9 /* dragon.png */; };
|
||||||
|
920BD12C2BEBDCD80050A5A9 /* dragon.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 920BD1252BEBDCD80050A5A9 /* dragon.atlas */; };
|
||||||
|
921C1EAF2BF609F60001A0BA /* DressUp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 921C1EAE2BF609F60001A0BA /* DressUp.swift */; };
|
||||||
|
921C1EBA2BF60AE90001A0BA /* mix-and-match-pro.skel in Resources */ = {isa = PBXBuildFile; fileRef = 921C1EB72BF60AE90001A0BA /* mix-and-match-pro.skel */; };
|
||||||
|
921C1EBB2BF60AE90001A0BA /* mix-and-match.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 921C1EB82BF60AE90001A0BA /* mix-and-match.atlas */; };
|
||||||
|
921C1EBC2BF60AE90001A0BA /* mix-and-match.png in Resources */ = {isa = PBXBuildFile; fileRef = 921C1EB92BF60AE90001A0BA /* mix-and-match.png */; };
|
||||||
|
922839DF2BF4F7D8006DA9F6 /* AnimationStateEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 922839DE2BF4F7D8006DA9F6 /* AnimationStateEvents.swift */; };
|
||||||
|
9240C26E2C0754DD003EE243 /* SimpleAnimationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9240C26D2C0754DD003EE243 /* SimpleAnimationViewController.m */; };
|
||||||
|
924C0C162BCFCF21004E63F7 /* SpineExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 924C0C152BCFCF21004E63F7 /* SpineExampleApp.swift */; };
|
||||||
|
924C0C182BCFCF21004E63F7 /* SimpleAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 924C0C172BCFCF21004E63F7 /* SimpleAnimation.swift */; };
|
||||||
|
924C0C1A2BCFCF22004E63F7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 924C0C192BCFCF22004E63F7 /* Assets.xcassets */; };
|
||||||
|
925315522BFF994400C96F75 /* spineboy.png in Resources */ = {isa = PBXBuildFile; fileRef = 925315512BFF994400C96F75 /* spineboy.png */; };
|
||||||
|
92579E772C1B0E9500FDC7D5 /* DisableRendering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92579E762C1B0E9500FDC7D5 /* DisableRendering.swift */; };
|
||||||
|
925CB7E92C19BC5A00C8F47F /* Spine in Frameworks */ = {isa = PBXBuildFile; productRef = 925CB7E82C19BC5A00C8F47F /* Spine */; };
|
||||||
|
9270C16E2BFE356000BD25BC /* Physics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9270C16D2BFE356000BD25BC /* Physics.swift */; };
|
||||||
|
9270C1732BFE389600BD25BC /* celestial-circus.png in Resources */ = {isa = PBXBuildFile; fileRef = 9270C1702BFE389600BD25BC /* celestial-circus.png */; };
|
||||||
|
9270C1742BFE389600BD25BC /* celestial-circus-pro.skel in Resources */ = {isa = PBXBuildFile; fileRef = 9270C1712BFE389600BD25BC /* celestial-circus-pro.skel */; };
|
||||||
|
9270C1752BFE389600BD25BC /* celestial-circus.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 9270C1722BFE389600BD25BC /* celestial-circus.atlas */; };
|
||||||
|
928A8CC22BCFE7DF00D9D35B /* Spine in Frameworks */ = {isa = PBXBuildFile; productRef = 928A8CC12BCFE7DF00D9D35B /* Spine */; };
|
||||||
|
92D7DDA82BFF3C8800EB9E3A /* DebugRendering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92D7DDA72BFF3C8800EB9E3A /* DebugRendering.swift */; };
|
||||||
|
92FE93292BF4AB9600CCDF48 /* IKFollowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FE93282BF4AB9600CCDF48 /* IKFollowing.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
9205FCD32C0760B1006EE07E /* SimpleAnimationViewControllerRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleAnimationViewControllerRepresentable.swift; sourceTree = "<group>"; };
|
||||||
|
920BD1122BEBC52D0050A5A9 /* spineboy-pro.skel */ = {isa = PBXFileReference; lastKnownFileType = file; path = "spineboy-pro.skel"; sourceTree = "<group>"; };
|
||||||
|
920BD1142BEBC52D0050A5A9 /* spineboy-pro.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "spineboy-pro.json"; sourceTree = "<group>"; };
|
||||||
|
920BD1152BEBC52D0050A5A9 /* spineboy.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = spineboy.atlas; sourceTree = "<group>"; };
|
||||||
|
920BD11A2BEBDBA60050A5A9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
|
||||||
|
920BD11C2BEBDC380050A5A9 /* PlayPauseAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayPauseAnimation.swift; sourceTree = "<group>"; };
|
||||||
|
920BD11F2BEBDCD80050A5A9 /* dragon_4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dragon_4.png; sourceTree = "<group>"; };
|
||||||
|
920BD1202BEBDCD80050A5A9 /* dragon_5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dragon_5.png; sourceTree = "<group>"; };
|
||||||
|
920BD1212BEBDCD80050A5A9 /* dragon_2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dragon_2.png; sourceTree = "<group>"; };
|
||||||
|
920BD1222BEBDCD80050A5A9 /* dragon-ess.skel */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dragon-ess.skel"; sourceTree = "<group>"; };
|
||||||
|
920BD1232BEBDCD80050A5A9 /* dragon_3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dragon_3.png; sourceTree = "<group>"; };
|
||||||
|
920BD1242BEBDCD80050A5A9 /* dragon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dragon.png; sourceTree = "<group>"; };
|
||||||
|
920BD1252BEBDCD80050A5A9 /* dragon.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dragon.atlas; sourceTree = "<group>"; };
|
||||||
|
921C1EAE2BF609F60001A0BA /* DressUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DressUp.swift; sourceTree = "<group>"; };
|
||||||
|
921C1EB72BF60AE90001A0BA /* mix-and-match-pro.skel */ = {isa = PBXFileReference; lastKnownFileType = file; path = "mix-and-match-pro.skel"; sourceTree = "<group>"; };
|
||||||
|
921C1EB82BF60AE90001A0BA /* mix-and-match.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "mix-and-match.atlas"; sourceTree = "<group>"; };
|
||||||
|
921C1EB92BF60AE90001A0BA /* mix-and-match.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mix-and-match.png"; sourceTree = "<group>"; };
|
||||||
|
922839DE2BF4F7D8006DA9F6 /* AnimationStateEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationStateEvents.swift; sourceTree = "<group>"; };
|
||||||
|
9240C26B2C0754DC003EE243 /* Spine iOS Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Spine iOS Example-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
9240C26C2C0754DD003EE243 /* SimpleAnimationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAnimationViewController.h; sourceTree = "<group>"; };
|
||||||
|
9240C26D2C0754DD003EE243 /* SimpleAnimationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SimpleAnimationViewController.m; sourceTree = "<group>"; };
|
||||||
|
924C0C122BCFCF21004E63F7 /* Spine iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Spine iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
924C0C152BCFCF21004E63F7 /* SpineExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpineExampleApp.swift; sourceTree = "<group>"; };
|
||||||
|
924C0C172BCFCF21004E63F7 /* SimpleAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleAnimation.swift; sourceTree = "<group>"; };
|
||||||
|
924C0C192BCFCF22004E63F7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
924C0C1B2BCFCF22004E63F7 /* SpineiOSExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SpineiOSExample.entitlements; sourceTree = "<group>"; };
|
||||||
|
925315512BFF994400C96F75 /* spineboy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = spineboy.png; sourceTree = "<group>"; };
|
||||||
|
92579E762C1B0E9500FDC7D5 /* DisableRendering.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisableRendering.swift; sourceTree = "<group>"; };
|
||||||
|
9270C16D2BFE356000BD25BC /* Physics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Physics.swift; sourceTree = "<group>"; };
|
||||||
|
9270C1702BFE389600BD25BC /* celestial-circus.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "celestial-circus.png"; sourceTree = "<group>"; };
|
||||||
|
9270C1712BFE389600BD25BC /* celestial-circus-pro.skel */ = {isa = PBXFileReference; lastKnownFileType = file; path = "celestial-circus-pro.skel"; sourceTree = "<group>"; };
|
||||||
|
9270C1722BFE389600BD25BC /* celestial-circus.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "celestial-circus.atlas"; sourceTree = "<group>"; };
|
||||||
|
92D7DDA72BFF3C8800EB9E3A /* DebugRendering.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugRendering.swift; sourceTree = "<group>"; };
|
||||||
|
92FE93282BF4AB9600CCDF48 /* IKFollowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IKFollowing.swift; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
924C0C0F2BCFCF21004E63F7 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
925CB7E92C19BC5A00C8F47F /* Spine in Frameworks */,
|
||||||
|
928A8CC22BCFE7DF00D9D35B /* Spine in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
920BD1112BEBC52D0050A5A9 /* spineboy */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
925315512BFF994400C96F75 /* spineboy.png */,
|
||||||
|
920BD1122BEBC52D0050A5A9 /* spineboy-pro.skel */,
|
||||||
|
920BD1142BEBC52D0050A5A9 /* spineboy-pro.json */,
|
||||||
|
920BD1152BEBC52D0050A5A9 /* spineboy.atlas */,
|
||||||
|
);
|
||||||
|
path = spineboy;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
920BD11E2BEBDCD80050A5A9 /* dragon */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
920BD11F2BEBDCD80050A5A9 /* dragon_4.png */,
|
||||||
|
920BD1202BEBDCD80050A5A9 /* dragon_5.png */,
|
||||||
|
920BD1212BEBDCD80050A5A9 /* dragon_2.png */,
|
||||||
|
920BD1222BEBDCD80050A5A9 /* dragon-ess.skel */,
|
||||||
|
920BD1232BEBDCD80050A5A9 /* dragon_3.png */,
|
||||||
|
920BD1242BEBDCD80050A5A9 /* dragon.png */,
|
||||||
|
920BD1252BEBDCD80050A5A9 /* dragon.atlas */,
|
||||||
|
);
|
||||||
|
path = dragon;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
921C1EB02BF60AA40001A0BA /* mixandmatch */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
921C1EB72BF60AE90001A0BA /* mix-and-match-pro.skel */,
|
||||||
|
921C1EB82BF60AE90001A0BA /* mix-and-match.atlas */,
|
||||||
|
921C1EB92BF60AE90001A0BA /* mix-and-match.png */,
|
||||||
|
);
|
||||||
|
path = mixandmatch;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
924C0C092BCFCF21004E63F7 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
924C0C142BCFCF21004E63F7 /* Spine iOS Example */,
|
||||||
|
924C0C132BCFCF21004E63F7 /* Products */,
|
||||||
|
92CE2BE32BCFE57600E9B376 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
924C0C132BCFCF21004E63F7 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
924C0C122BCFCF21004E63F7 /* Spine iOS Example.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
924C0C142BCFCF21004E63F7 /* Spine iOS Example */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
92921E502BCFEEA1002E2FC4 /* Assets */,
|
||||||
|
924C0C152BCFCF21004E63F7 /* SpineExampleApp.swift */,
|
||||||
|
920BD11A2BEBDBA60050A5A9 /* MainView.swift */,
|
||||||
|
924C0C172BCFCF21004E63F7 /* SimpleAnimation.swift */,
|
||||||
|
9205FCD32C0760B1006EE07E /* SimpleAnimationViewControllerRepresentable.swift */,
|
||||||
|
9240C26C2C0754DD003EE243 /* SimpleAnimationViewController.h */,
|
||||||
|
9240C26D2C0754DD003EE243 /* SimpleAnimationViewController.m */,
|
||||||
|
920BD11C2BEBDC380050A5A9 /* PlayPauseAnimation.swift */,
|
||||||
|
922839DE2BF4F7D8006DA9F6 /* AnimationStateEvents.swift */,
|
||||||
|
92D7DDA72BFF3C8800EB9E3A /* DebugRendering.swift */,
|
||||||
|
921C1EAE2BF609F60001A0BA /* DressUp.swift */,
|
||||||
|
92FE93282BF4AB9600CCDF48 /* IKFollowing.swift */,
|
||||||
|
9270C16D2BFE356000BD25BC /* Physics.swift */,
|
||||||
|
92579E762C1B0E9500FDC7D5 /* DisableRendering.swift */,
|
||||||
|
924C0C192BCFCF22004E63F7 /* Assets.xcassets */,
|
||||||
|
924C0C1B2BCFCF22004E63F7 /* SpineiOSExample.entitlements */,
|
||||||
|
9240C26B2C0754DC003EE243 /* Spine iOS Example-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = "Spine iOS Example";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9270C16F2BFE387600BD25BC /* celestial */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9270C1712BFE389600BD25BC /* celestial-circus-pro.skel */,
|
||||||
|
9270C1722BFE389600BD25BC /* celestial-circus.atlas */,
|
||||||
|
9270C1702BFE389600BD25BC /* celestial-circus.png */,
|
||||||
|
);
|
||||||
|
path = celestial;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
92921E502BCFEEA1002E2FC4 /* Assets */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9270C16F2BFE387600BD25BC /* celestial */,
|
||||||
|
921C1EB02BF60AA40001A0BA /* mixandmatch */,
|
||||||
|
920BD11E2BEBDCD80050A5A9 /* dragon */,
|
||||||
|
920BD1112BEBC52D0050A5A9 /* spineboy */,
|
||||||
|
);
|
||||||
|
path = Assets;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
92CE2BE32BCFE57600E9B376 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
924C0C112BCFCF21004E63F7 /* Spine iOS Example */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 924C0C212BCFCF22004E63F7 /* Build configuration list for PBXNativeTarget "Spine iOS Example" */;
|
||||||
|
buildPhases = (
|
||||||
|
924C0C0E2BCFCF21004E63F7 /* Sources */,
|
||||||
|
924C0C0F2BCFCF21004E63F7 /* Frameworks */,
|
||||||
|
924C0C102BCFCF21004E63F7 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Spine iOS Example";
|
||||||
|
packageProductDependencies = (
|
||||||
|
928A8CC12BCFE7DF00D9D35B /* Spine */,
|
||||||
|
925CB7E82C19BC5A00C8F47F /* Spine */,
|
||||||
|
);
|
||||||
|
productName = "Spine iOS Example";
|
||||||
|
productReference = 924C0C122BCFCF21004E63F7 /* Spine iOS Example.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
924C0C0A2BCFCF21004E63F7 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1520;
|
||||||
|
LastUpgradeCheck = 1520;
|
||||||
|
TargetAttributes = {
|
||||||
|
924C0C112BCFCF21004E63F7 = {
|
||||||
|
CreatedOnToolsVersion = 15.2;
|
||||||
|
LastSwiftMigration = 1520;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 924C0C0D2BCFCF21004E63F7 /* Build configuration list for PBXProject "Spine iOS Example" */;
|
||||||
|
compatibilityVersion = "Xcode 14.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 924C0C092BCFCF21004E63F7;
|
||||||
|
packageReferences = (
|
||||||
|
925CB7E72C19BC5A00C8F47F /* XCLocalSwiftPackageReference "../.." */,
|
||||||
|
);
|
||||||
|
productRefGroup = 924C0C132BCFCF21004E63F7 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
924C0C112BCFCF21004E63F7 /* Spine iOS Example */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
924C0C102BCFCF21004E63F7 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
920BD12A2BEBDCD80050A5A9 /* dragon_3.png in Resources */,
|
||||||
|
924C0C1A2BCFCF22004E63F7 /* Assets.xcassets in Resources */,
|
||||||
|
920BD1282BEBDCD80050A5A9 /* dragon_2.png in Resources */,
|
||||||
|
920BD1182BEBC52D0050A5A9 /* spineboy-pro.json in Resources */,
|
||||||
|
9270C1752BFE389600BD25BC /* celestial-circus.atlas in Resources */,
|
||||||
|
9270C1742BFE389600BD25BC /* celestial-circus-pro.skel in Resources */,
|
||||||
|
921C1EBB2BF60AE90001A0BA /* mix-and-match.atlas in Resources */,
|
||||||
|
9270C1732BFE389600BD25BC /* celestial-circus.png in Resources */,
|
||||||
|
925315522BFF994400C96F75 /* spineboy.png in Resources */,
|
||||||
|
920BD1192BEBC52D0050A5A9 /* spineboy.atlas in Resources */,
|
||||||
|
920BD1292BEBDCD80050A5A9 /* dragon-ess.skel in Resources */,
|
||||||
|
920BD1162BEBC52D0050A5A9 /* spineboy-pro.skel in Resources */,
|
||||||
|
920BD12B2BEBDCD80050A5A9 /* dragon.png in Resources */,
|
||||||
|
920BD1272BEBDCD80050A5A9 /* dragon_5.png in Resources */,
|
||||||
|
921C1EBC2BF60AE90001A0BA /* mix-and-match.png in Resources */,
|
||||||
|
920BD1262BEBDCD80050A5A9 /* dragon_4.png in Resources */,
|
||||||
|
920BD12C2BEBDCD80050A5A9 /* dragon.atlas in Resources */,
|
||||||
|
921C1EBA2BF60AE90001A0BA /* mix-and-match-pro.skel in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
924C0C0E2BCFCF21004E63F7 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
9205FCD42C0760B1006EE07E /* SimpleAnimationViewControllerRepresentable.swift in Sources */,
|
||||||
|
9270C16E2BFE356000BD25BC /* Physics.swift in Sources */,
|
||||||
|
920BD11B2BEBDBA60050A5A9 /* MainView.swift in Sources */,
|
||||||
|
92D7DDA82BFF3C8800EB9E3A /* DebugRendering.swift in Sources */,
|
||||||
|
92FE93292BF4AB9600CCDF48 /* IKFollowing.swift in Sources */,
|
||||||
|
920BD11D2BEBDC380050A5A9 /* PlayPauseAnimation.swift in Sources */,
|
||||||
|
9240C26E2C0754DD003EE243 /* SimpleAnimationViewController.m in Sources */,
|
||||||
|
921C1EAF2BF609F60001A0BA /* DressUp.swift in Sources */,
|
||||||
|
922839DF2BF4F7D8006DA9F6 /* AnimationStateEvents.swift in Sources */,
|
||||||
|
924C0C182BCFCF21004E63F7 /* SimpleAnimation.swift in Sources */,
|
||||||
|
924C0C162BCFCF21004E63F7 /* SpineExampleApp.swift in Sources */,
|
||||||
|
92579E772C1B0E9500FDC7D5 /* DisableRendering.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
924C0C1F2BCFCF22004E63F7 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COMPRESS_PNG_FILES = NO;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
STRIP_PNG_TEXT = NO;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
924C0C202BCFCF22004E63F7 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COMPRESS_PNG_FILES = NO;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
STRIP_PNG_TEXT = NO;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
924C0C222BCFCF22004E63F7 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Spine iOS Example/SpineiOSExample.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
DEVELOPMENT_TEAM = 9ZFD4KCY8F;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "";
|
||||||
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
||||||
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.2;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.esotericsoftware.spine-ios-example";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Spine iOS Example/Spine iOS Example-Bridging-Header.h";
|
||||||
|
SWIFT_OBJC_INTEROP_MODE = objcxx;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
924C0C232BCFCF22004E63F7 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Spine iOS Example/SpineiOSExample.entitlements";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
|
DEVELOPMENT_TEAM = 9ZFD4KCY8F;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "";
|
||||||
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
|
||||||
|
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.2;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.esotericsoftware.spine-ios-example";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Spine iOS Example/Spine iOS Example-Bridging-Header.h";
|
||||||
|
SWIFT_OBJC_INTEROP_MODE = objcxx;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
924C0C0D2BCFCF21004E63F7 /* Build configuration list for PBXProject "Spine iOS Example" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
924C0C1F2BCFCF22004E63F7 /* Debug */,
|
||||||
|
924C0C202BCFCF22004E63F7 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
924C0C212BCFCF22004E63F7 /* Build configuration list for PBXNativeTarget "Spine iOS Example" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
924C0C222BCFCF22004E63F7 /* Debug */,
|
||||||
|
924C0C232BCFCF22004E63F7 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCLocalSwiftPackageReference section */
|
||||||
|
925CB7E72C19BC5A00C8F47F /* XCLocalSwiftPackageReference "../.." */ = {
|
||||||
|
isa = XCLocalSwiftPackageReference;
|
||||||
|
relativePath = ../..;
|
||||||
|
};
|
||||||
|
/* End XCLocalSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
925CB7E82C19BC5A00C8F47F /* Spine */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Spine;
|
||||||
|
};
|
||||||
|
928A8CC12BCFE7DF00D9D35B /* Spine */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Spine;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = 924C0C0A2BCFCF21004E63F7 /* Project object */;
|
||||||
|
}
|
||||||
7
spine-ios/Example/Spine iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
struct AnimationStateEvents: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.skeleton.scaleX = 0.5
|
||||||
|
controller.skeleton.scaleY = 0.5
|
||||||
|
controller.skeleton.findSlot(slotName: "gun")?.setColor(r: 1, g: 0, b: 0, a: 1)
|
||||||
|
controller.animationStateData.defaultMix = 0.2
|
||||||
|
let walk = controller.animationState.setAnimationByName(trackIndex: 0, animationName: "walk", loop: true)
|
||||||
|
controller.animationStateWrapper.setTrackEntryListener(entry: walk) { type, entry, event in
|
||||||
|
print("Walk animation event \(type)");
|
||||||
|
}
|
||||||
|
controller.animationState.addAnimationByName(trackIndex: 0, animationName: "jump", loop: false, delay: 2)
|
||||||
|
let run = controller.animationState.addAnimationByName(trackIndex: 0, animationName: "run", loop: true, delay: 0)
|
||||||
|
controller.animationStateWrapper.setTrackEntryListener(entry: run) { type, entry, event in
|
||||||
|
print("Run animation event \(type)");
|
||||||
|
}
|
||||||
|
controller.animationStateWrapper.setStateListener { type, entry, event in
|
||||||
|
if type == SPINE_EVENT_TYPE_EVENT, let event {
|
||||||
|
print("User event: { name: \(event.data.name ?? "--"), intValue: \(event.intValue), floatValue: \(event.floatValue), stringValue: \(event.stringValue ?? "--") }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let current = controller.animationState.getCurrent(trackIndex: 0)?.animation.name ?? "--"
|
||||||
|
print("Current: \(current)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Text("See output in console!")
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
controller: controller
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.navigationTitle("Animation State Listener")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
AnimationStateEvents()
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "16x16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "16x16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "32x32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "32x32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "128x128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "128x128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "256x256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "256x256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
celestial-circus.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
scale: 0.4
|
||||||
|
arm-back-down
|
||||||
|
bounds: 324, 401, 38, 82
|
||||||
|
rotate: 90
|
||||||
|
arm-back-up
|
||||||
|
bounds: 290, 44, 83, 116
|
||||||
|
rotate: 90
|
||||||
|
arm-front-down
|
||||||
|
bounds: 706, 2, 36, 78
|
||||||
|
rotate: 90
|
||||||
|
arm-front-up
|
||||||
|
bounds: 860, 138, 77, 116
|
||||||
|
bench
|
||||||
|
bounds: 725, 256, 189, 48
|
||||||
|
body-bottom
|
||||||
|
bounds: 879, 868, 154, 124
|
||||||
|
rotate: 90
|
||||||
|
body-top
|
||||||
|
bounds: 725, 128, 126, 133
|
||||||
|
rotate: 90
|
||||||
|
chest
|
||||||
|
bounds: 408, 26, 104, 93
|
||||||
|
cloud-back
|
||||||
|
bounds: 752, 378, 202, 165
|
||||||
|
cloud-front
|
||||||
|
bounds: 2, 2, 325, 196
|
||||||
|
rotate: 90
|
||||||
|
collar
|
||||||
|
bounds: 786, 13, 47, 26
|
||||||
|
ear
|
||||||
|
bounds: 1002, 643, 20, 28
|
||||||
|
eye-back-shadow
|
||||||
|
bounds: 428, 395, 14, 10
|
||||||
|
eye-front-shadow
|
||||||
|
bounds: 704, 529, 24, 14
|
||||||
|
eye-reflex-back
|
||||||
|
bounds: 860, 128, 8, 7
|
||||||
|
rotate: 90
|
||||||
|
eye-reflex-front
|
||||||
|
bounds: 726, 386, 10, 7
|
||||||
|
eye-white-back
|
||||||
|
bounds: 835, 23, 13, 16
|
||||||
|
eye-white-front
|
||||||
|
bounds: 1005, 1000, 22, 17
|
||||||
|
rotate: 90
|
||||||
|
eyelashes-down-back
|
||||||
|
bounds: 232, 329, 11, 6
|
||||||
|
rotate: 90
|
||||||
|
eyelashes-down-front
|
||||||
|
bounds: 913, 851, 15, 6
|
||||||
|
rotate: 90
|
||||||
|
eyelashes-top-back
|
||||||
|
bounds: 408, 395, 18, 10
|
||||||
|
eyelashes-top-front
|
||||||
|
bounds: 702, 179, 30, 16
|
||||||
|
rotate: 90
|
||||||
|
face
|
||||||
|
bounds: 514, 26, 93, 102
|
||||||
|
rotate: 90
|
||||||
|
feathers-back
|
||||||
|
bounds: 954, 625, 46, 46
|
||||||
|
feathers-front
|
||||||
|
bounds: 706, 40, 72, 86
|
||||||
|
fringe-middle-back
|
||||||
|
bounds: 200, 6, 33, 52
|
||||||
|
rotate: 90
|
||||||
|
fringe-middle-front
|
||||||
|
bounds: 878, 76, 60, 50
|
||||||
|
rotate: 90
|
||||||
|
fringe-side-back
|
||||||
|
bounds: 780, 41, 27, 94
|
||||||
|
rotate: 90
|
||||||
|
fringe-side-front
|
||||||
|
bounds: 939, 161, 26, 93
|
||||||
|
glove-bottom-back
|
||||||
|
bounds: 954, 572, 51, 41
|
||||||
|
rotate: 90
|
||||||
|
glove-bottom-front
|
||||||
|
bounds: 916, 256, 47, 48
|
||||||
|
hair-back-1
|
||||||
|
bounds: 444, 395, 132, 306
|
||||||
|
rotate: 90
|
||||||
|
hair-back-2
|
||||||
|
bounds: 438, 211, 80, 285
|
||||||
|
rotate: 90
|
||||||
|
hair-back-3
|
||||||
|
bounds: 719, 306, 70, 268
|
||||||
|
rotate: 90
|
||||||
|
hair-back-4
|
||||||
|
bounds: 438, 121, 88, 262
|
||||||
|
rotate: 90
|
||||||
|
hair-back-5
|
||||||
|
bounds: 438, 293, 88, 279
|
||||||
|
rotate: 90
|
||||||
|
hair-back-6
|
||||||
|
bounds: 200, 41, 88, 286
|
||||||
|
hair-hat-shadow
|
||||||
|
bounds: 232, 398, 90, 41
|
||||||
|
hand-back
|
||||||
|
bounds: 954, 673, 60, 47
|
||||||
|
rotate: 90
|
||||||
|
hand-front
|
||||||
|
bounds: 967, 172, 53, 60
|
||||||
|
hat-back
|
||||||
|
bounds: 954, 802, 64, 45
|
||||||
|
rotate: 90
|
||||||
|
hat-front
|
||||||
|
bounds: 780, 70, 96, 56
|
||||||
|
head-back
|
||||||
|
bounds: 618, 17, 102, 86
|
||||||
|
rotate: 90
|
||||||
|
jabot
|
||||||
|
bounds: 967, 234, 70, 55
|
||||||
|
rotate: 90
|
||||||
|
leg-back
|
||||||
|
bounds: 232, 441, 210, 333
|
||||||
|
leg-front
|
||||||
|
bounds: 444, 529, 258, 320
|
||||||
|
logo-brooch
|
||||||
|
bounds: 954, 545, 16, 25
|
||||||
|
mouth
|
||||||
|
bounds: 408, 121, 22, 6
|
||||||
|
neck
|
||||||
|
bounds: 232, 342, 39, 56
|
||||||
|
rotate: 90
|
||||||
|
nose
|
||||||
|
bounds: 742, 529, 6, 7
|
||||||
|
rotate: 90
|
||||||
|
nose-highlight
|
||||||
|
bounds: 719, 300, 4, 4
|
||||||
|
nose-shadow
|
||||||
|
bounds: 869, 128, 7, 8
|
||||||
|
pupil-back
|
||||||
|
bounds: 730, 529, 10, 14
|
||||||
|
pupil-front
|
||||||
|
bounds: 254, 21, 12, 18
|
||||||
|
rope-back
|
||||||
|
bounds: 232, 383, 10, 492
|
||||||
|
rotate: 90
|
||||||
|
rope-front
|
||||||
|
bounds: 232, 383, 10, 492
|
||||||
|
rotate: 90
|
||||||
|
rope-front-bottom
|
||||||
|
bounds: 954, 735, 42, 65
|
||||||
|
skirt
|
||||||
|
bounds: 2, 776, 440, 246
|
||||||
|
sock-bow
|
||||||
|
bounds: 408, 407, 33, 32
|
||||||
|
spine-logo-body
|
||||||
|
bounds: 879, 853, 13, 32
|
||||||
|
rotate: 90
|
||||||
|
star-big
|
||||||
|
bounds: 939, 141, 18, 24
|
||||||
|
rotate: 90
|
||||||
|
star-medium
|
||||||
|
bounds: 742, 537, 6, 8
|
||||||
|
rotate: 90
|
||||||
|
star-small
|
||||||
|
bounds: 719, 378, 3, 4
|
||||||
|
rotate: 90
|
||||||
|
underskirt
|
||||||
|
bounds: 2, 329, 445, 228
|
||||||
|
rotate: 90
|
||||||
|
underskirt-back
|
||||||
|
bounds: 444, 851, 433, 171
|
||||||
|
wing-back
|
||||||
|
bounds: 290, 129, 146, 252
|
||||||
|
wing-front
|
||||||
|
bounds: 704, 545, 304, 248
|
||||||
|
rotate: 90
|
||||||
|
After Width: | Height: | Size: 790 KiB |
112
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon.atlas
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
dragon.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
front-toe-a
|
||||||
|
bounds: 797, 381, 29, 50
|
||||||
|
front-toe-b
|
||||||
|
bounds: 942, 118, 56, 57
|
||||||
|
head
|
||||||
|
bounds: 647, 81, 296, 260
|
||||||
|
rotate: 90
|
||||||
|
left-front-leg
|
||||||
|
bounds: 942, 250, 84, 57
|
||||||
|
rotate: 90
|
||||||
|
left-front-thigh
|
||||||
|
bounds: 852, 7, 84, 72
|
||||||
|
left-wing01
|
||||||
|
bounds: 736, 433, 264, 589
|
||||||
|
right-rear-toe
|
||||||
|
bounds: 647, 2, 109, 77
|
||||||
|
right-wing01
|
||||||
|
bounds: 2, 379, 365, 643
|
||||||
|
right-wing02
|
||||||
|
bounds: 369, 379, 365, 643
|
||||||
|
right-wing03
|
||||||
|
bounds: 2, 12, 365, 643
|
||||||
|
rotate: 90
|
||||||
|
tail03
|
||||||
|
bounds: 758, 6, 73, 92
|
||||||
|
rotate: 90
|
||||||
|
tail04
|
||||||
|
bounds: 942, 177, 56, 71
|
||||||
|
tail05
|
||||||
|
bounds: 736, 379, 52, 59
|
||||||
|
rotate: 90
|
||||||
|
tail06
|
||||||
|
bounds: 942, 336, 95, 68
|
||||||
|
rotate: 90
|
||||||
|
thiagobrayner
|
||||||
|
bounds: 909, 81, 350, 31
|
||||||
|
rotate: 90
|
||||||
|
|
||||||
|
dragon_2.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
back
|
||||||
|
bounds: 795, 32, 190, 185
|
||||||
|
chin
|
||||||
|
bounds: 647, 157, 214, 146
|
||||||
|
rotate: 90
|
||||||
|
left-rear-leg
|
||||||
|
bounds: 795, 219, 206, 177
|
||||||
|
rotate: 90
|
||||||
|
left-wing02
|
||||||
|
bounds: 736, 427, 264, 589
|
||||||
|
right-wing04
|
||||||
|
bounds: 2, 373, 365, 643
|
||||||
|
right-wing05
|
||||||
|
bounds: 369, 373, 365, 643
|
||||||
|
right-wing06
|
||||||
|
bounds: 2, 6, 365, 643
|
||||||
|
rotate: 90
|
||||||
|
tail01
|
||||||
|
bounds: 647, 2, 120, 153
|
||||||
|
|
||||||
|
dragon_3.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
chest
|
||||||
|
bounds: 740, 299, 136, 122
|
||||||
|
left-rear-thigh
|
||||||
|
bounds: 647, 218, 91, 149
|
||||||
|
left-wing03
|
||||||
|
bounds: 736, 423, 264, 589
|
||||||
|
right-front-leg
|
||||||
|
bounds: 850, 196, 101, 89
|
||||||
|
rotate: 90
|
||||||
|
right-front-thigh
|
||||||
|
bounds: 740, 189, 108, 108
|
||||||
|
right-rear-leg
|
||||||
|
bounds: 878, 321, 116, 100
|
||||||
|
right-rear-thigh
|
||||||
|
bounds: 647, 67, 91, 149
|
||||||
|
right-wing07
|
||||||
|
bounds: 2, 369, 365, 643
|
||||||
|
right-wing08
|
||||||
|
bounds: 369, 369, 365, 643
|
||||||
|
right-wing09
|
||||||
|
bounds: 2, 2, 365, 643
|
||||||
|
rotate: 90
|
||||||
|
tail02
|
||||||
|
bounds: 740, 67, 95, 120
|
||||||
|
|
||||||
|
dragon_4.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
left-wing04
|
||||||
|
bounds: 2, 268, 264, 589
|
||||||
|
left-wing05
|
||||||
|
bounds: 268, 268, 264, 589
|
||||||
|
left-wing06
|
||||||
|
bounds: 534, 268, 264, 589
|
||||||
|
left-wing07
|
||||||
|
bounds: 2, 2, 264, 589
|
||||||
|
rotate: 90
|
||||||
|
|
||||||
|
dragon_5.png
|
||||||
|
size: 1024, 1024
|
||||||
|
filter: Linear, Linear
|
||||||
|
left-wing08
|
||||||
|
bounds: 2, 2, 264, 589
|
||||||
|
left-wing09
|
||||||
|
bounds: 268, 2, 264, 589
|
||||||
BIN
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon.png
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon_2.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon_3.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon_4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
spine-ios/Example/Spine iOS Example/Assets/dragon/dragon_5.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,358 @@
|
|||||||
|
mix-and-match.png
|
||||||
|
size: 1024, 512
|
||||||
|
filter: Linear, Linear
|
||||||
|
scale: 0.5
|
||||||
|
base-head
|
||||||
|
bounds: 118, 70, 95, 73
|
||||||
|
boy/arm-front
|
||||||
|
bounds: 831, 311, 36, 115
|
||||||
|
rotate: 90
|
||||||
|
boy/backpack
|
||||||
|
bounds: 249, 357, 119, 153
|
||||||
|
boy/backpack-pocket
|
||||||
|
bounds: 628, 193, 34, 62
|
||||||
|
rotate: 90
|
||||||
|
boy/backpack-strap-front
|
||||||
|
bounds: 330, 263, 38, 88
|
||||||
|
rotate: 90
|
||||||
|
boy/backpack-up
|
||||||
|
bounds: 482, 171, 21, 70
|
||||||
|
boy/body
|
||||||
|
bounds: 845, 413, 97, 132
|
||||||
|
rotate: 90
|
||||||
|
boy/boot-ribbon-front
|
||||||
|
bounds: 234, 304, 9, 11
|
||||||
|
boy/collar
|
||||||
|
bounds: 471, 243, 73, 29
|
||||||
|
rotate: 90
|
||||||
|
boy/ear
|
||||||
|
bounds: 991, 352, 19, 23
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-back-low-eyelid
|
||||||
|
bounds: 66, 72, 17, 6
|
||||||
|
boy/eye-back-pupil
|
||||||
|
bounds: 694, 279, 8, 9
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-back-up-eyelid
|
||||||
|
bounds: 460, 101, 23, 5
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-back-up-eyelid-back
|
||||||
|
bounds: 979, 414, 19, 10
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-front-low-eyelid
|
||||||
|
bounds: 1015, 203, 22, 7
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-front-pupil
|
||||||
|
bounds: 309, 50, 9, 9
|
||||||
|
boy/eye-front-up-eyelid
|
||||||
|
bounds: 991, 373, 31, 6
|
||||||
|
boy/eye-front-up-eyelid-back
|
||||||
|
bounds: 107, 76, 26, 9
|
||||||
|
rotate: 90
|
||||||
|
boy/eye-iris-back
|
||||||
|
bounds: 810, 260, 17, 17
|
||||||
|
boy/eye-iris-front
|
||||||
|
bounds: 902, 230, 18, 18
|
||||||
|
boy/eye-white-back
|
||||||
|
bounds: 599, 179, 20, 12
|
||||||
|
boy/eye-white-front
|
||||||
|
bounds: 544, 183, 27, 13
|
||||||
|
boy/eyebrow-back
|
||||||
|
bounds: 1002, 225, 20, 11
|
||||||
|
rotate: 90
|
||||||
|
boy/eyebrow-front
|
||||||
|
bounds: 975, 234, 25, 11
|
||||||
|
boy/hair-back
|
||||||
|
bounds: 629, 289, 122, 81
|
||||||
|
rotate: 90
|
||||||
|
boy/hair-bangs
|
||||||
|
bounds: 505, 180, 70, 37
|
||||||
|
rotate: 90
|
||||||
|
boy/hair-side
|
||||||
|
bounds: 979, 435, 25, 43
|
||||||
|
rotate: 90
|
||||||
|
boy/hand-backfingers
|
||||||
|
bounds: 858, 183, 19, 21
|
||||||
|
boy/hand-front-fingers
|
||||||
|
bounds: 879, 183, 19, 21
|
||||||
|
boy/hat
|
||||||
|
bounds: 218, 121, 93, 56
|
||||||
|
boy/leg-front
|
||||||
|
bounds: 85, 104, 31, 158
|
||||||
|
boy/mouth-close
|
||||||
|
bounds: 467, 100, 21, 5
|
||||||
|
girl-blue-cape/mouth-close
|
||||||
|
bounds: 467, 100, 21, 5
|
||||||
|
girl-spring-dress/mouth-close
|
||||||
|
bounds: 467, 100, 21, 5
|
||||||
|
girl/mouth-close
|
||||||
|
bounds: 467, 100, 21, 5
|
||||||
|
boy/mouth-smile
|
||||||
|
bounds: 1015, 258, 29, 7
|
||||||
|
rotate: 90
|
||||||
|
boy/nose
|
||||||
|
bounds: 323, 79, 17, 10
|
||||||
|
boy/pompom
|
||||||
|
bounds: 979, 462, 48, 43
|
||||||
|
rotate: 90
|
||||||
|
boy/zip
|
||||||
|
bounds: 922, 231, 14, 23
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/back-eyebrow
|
||||||
|
bounds: 527, 106, 18, 12
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/body-dress
|
||||||
|
bounds: 2, 264, 109, 246
|
||||||
|
girl-blue-cape/body-ribbon
|
||||||
|
bounds: 576, 193, 50, 38
|
||||||
|
girl-blue-cape/cape-back
|
||||||
|
bounds: 113, 317, 134, 193
|
||||||
|
girl-blue-cape/cape-back-up
|
||||||
|
bounds: 504, 305, 123, 106
|
||||||
|
girl-blue-cape/cape-ribbon
|
||||||
|
bounds: 396, 118, 50, 18
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/cape-shoulder-back
|
||||||
|
bounds: 420, 243, 49, 59
|
||||||
|
girl-blue-cape/cape-shoulder-front
|
||||||
|
bounds: 2, 2, 62, 76
|
||||||
|
girl-blue-cape/cape-up-front
|
||||||
|
bounds: 118, 145, 98, 117
|
||||||
|
girl-blue-cape/ear
|
||||||
|
bounds: 837, 181, 19, 23
|
||||||
|
girl-spring-dress/ear
|
||||||
|
bounds: 837, 181, 19, 23
|
||||||
|
girl/ear
|
||||||
|
bounds: 837, 181, 19, 23
|
||||||
|
girl-blue-cape/eye-back-low-eyelid
|
||||||
|
bounds: 810, 252, 17, 6
|
||||||
|
girl-spring-dress/eye-back-low-eyelid
|
||||||
|
bounds: 810, 252, 17, 6
|
||||||
|
girl/eye-back-low-eyelid
|
||||||
|
bounds: 810, 252, 17, 6
|
||||||
|
girl-blue-cape/eye-back-pupil
|
||||||
|
bounds: 309, 40, 8, 9
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/eye-back-pupil
|
||||||
|
bounds: 309, 40, 8, 9
|
||||||
|
rotate: 90
|
||||||
|
girl/eye-back-pupil
|
||||||
|
bounds: 309, 40, 8, 9
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/eye-back-up-eyelid
|
||||||
|
bounds: 573, 179, 24, 12
|
||||||
|
girl-spring-dress/eye-back-up-eyelid
|
||||||
|
bounds: 573, 179, 24, 12
|
||||||
|
girl/eye-back-up-eyelid
|
||||||
|
bounds: 573, 179, 24, 12
|
||||||
|
girl-blue-cape/eye-back-up-eyelid-back
|
||||||
|
bounds: 380, 105, 17, 11
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/eye-back-up-eyelid-back
|
||||||
|
bounds: 380, 105, 17, 11
|
||||||
|
rotate: 90
|
||||||
|
girl/eye-back-up-eyelid-back
|
||||||
|
bounds: 380, 105, 17, 11
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/eye-front-low-eyelid
|
||||||
|
bounds: 1016, 353, 18, 6
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/eye-front-low-eyelid
|
||||||
|
bounds: 1016, 353, 18, 6
|
||||||
|
rotate: 90
|
||||||
|
girl/eye-front-low-eyelid
|
||||||
|
bounds: 1016, 353, 18, 6
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/eye-front-pupil
|
||||||
|
bounds: 363, 94, 9, 9
|
||||||
|
girl-spring-dress/eye-front-pupil
|
||||||
|
bounds: 363, 94, 9, 9
|
||||||
|
girl/eye-front-pupil
|
||||||
|
bounds: 363, 94, 9, 9
|
||||||
|
girl-blue-cape/eye-front-up-eyelid
|
||||||
|
bounds: 679, 413, 30, 14
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/eye-front-up-eyelid
|
||||||
|
bounds: 679, 413, 30, 14
|
||||||
|
rotate: 90
|
||||||
|
girl/eye-front-up-eyelid
|
||||||
|
bounds: 679, 413, 30, 14
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/eye-front-up-eyelid-back
|
||||||
|
bounds: 947, 234, 26, 11
|
||||||
|
girl-spring-dress/eye-front-up-eyelid-back
|
||||||
|
bounds: 947, 234, 26, 11
|
||||||
|
girl/eye-front-up-eyelid-back
|
||||||
|
bounds: 947, 234, 26, 11
|
||||||
|
girl-blue-cape/eye-iris-back
|
||||||
|
bounds: 323, 105, 17, 17
|
||||||
|
girl-blue-cape/eye-iris-front
|
||||||
|
bounds: 467, 107, 18, 18
|
||||||
|
girl-blue-cape/eye-white-back
|
||||||
|
bounds: 621, 175, 20, 16
|
||||||
|
girl-spring-dress/eye-white-back
|
||||||
|
bounds: 621, 175, 20, 16
|
||||||
|
girl-blue-cape/eye-white-front
|
||||||
|
bounds: 643, 175, 20, 16
|
||||||
|
girl-spring-dress/eye-white-front
|
||||||
|
bounds: 643, 175, 20, 16
|
||||||
|
girl/eye-white-front
|
||||||
|
bounds: 643, 175, 20, 16
|
||||||
|
girl-blue-cape/front-eyebrow
|
||||||
|
bounds: 309, 101, 18, 12
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/hair-back
|
||||||
|
bounds: 712, 317, 117, 98
|
||||||
|
girl-blue-cape/hair-bangs
|
||||||
|
bounds: 313, 170, 91, 40
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/hair-head-side-back
|
||||||
|
bounds: 544, 198, 30, 52
|
||||||
|
girl-blue-cape/hair-head-side-front
|
||||||
|
bounds: 466, 127, 41, 42
|
||||||
|
girl-blue-cape/hair-side
|
||||||
|
bounds: 175, 2, 36, 71
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/hand-front-fingers
|
||||||
|
bounds: 902, 207, 19, 21
|
||||||
|
girl-spring-dress/hand-front-fingers
|
||||||
|
bounds: 902, 207, 19, 21
|
||||||
|
girl-blue-cape/leg-front
|
||||||
|
bounds: 519, 413, 30, 158
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/mouth-smile
|
||||||
|
bounds: 1015, 227, 29, 7
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/mouth-smile
|
||||||
|
bounds: 1015, 227, 29, 7
|
||||||
|
rotate: 90
|
||||||
|
girl/mouth-smile
|
||||||
|
bounds: 1015, 227, 29, 7
|
||||||
|
rotate: 90
|
||||||
|
girl-blue-cape/nose
|
||||||
|
bounds: 342, 82, 11, 7
|
||||||
|
girl-spring-dress/nose
|
||||||
|
bounds: 342, 82, 11, 7
|
||||||
|
girl/nose
|
||||||
|
bounds: 342, 82, 11, 7
|
||||||
|
girl-blue-cape/sleeve-back
|
||||||
|
bounds: 416, 95, 42, 29
|
||||||
|
girl-blue-cape/sleeve-front
|
||||||
|
bounds: 249, 303, 52, 119
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/arm-front
|
||||||
|
bounds: 829, 292, 17, 111
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/back-eyebrow
|
||||||
|
bounds: 309, 81, 18, 12
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/body-up
|
||||||
|
bounds: 66, 2, 64, 66
|
||||||
|
girl-spring-dress/cloak-down
|
||||||
|
bounds: 758, 227, 50, 50
|
||||||
|
girl-spring-dress/cloak-up
|
||||||
|
bounds: 628, 229, 64, 58
|
||||||
|
girl-spring-dress/eye-iris-back
|
||||||
|
bounds: 342, 105, 17, 17
|
||||||
|
girl-spring-dress/eye-iris-front
|
||||||
|
bounds: 487, 107, 18, 18
|
||||||
|
girl-spring-dress/front-eyebrow
|
||||||
|
bounds: 323, 91, 18, 12
|
||||||
|
girl-spring-dress/hair-back
|
||||||
|
bounds: 370, 417, 147, 93
|
||||||
|
girl-spring-dress/hair-bangs
|
||||||
|
bounds: 829, 250, 91, 40
|
||||||
|
girl-spring-dress/hair-head-side-back
|
||||||
|
bounds: 509, 126, 30, 52
|
||||||
|
girl-spring-dress/hair-head-side-front
|
||||||
|
bounds: 816, 206, 41, 42
|
||||||
|
girl-spring-dress/hair-side
|
||||||
|
bounds: 248, 2, 36, 71
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/leg-front
|
||||||
|
bounds: 831, 381, 30, 158
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/neck
|
||||||
|
bounds: 85, 70, 20, 32
|
||||||
|
girl-spring-dress/shoulder-ribbon
|
||||||
|
bounds: 175, 44, 36, 24
|
||||||
|
girl-spring-dress/skirt
|
||||||
|
bounds: 2, 80, 182, 81
|
||||||
|
rotate: 90
|
||||||
|
girl-spring-dress/underskirt
|
||||||
|
bounds: 519, 445, 175, 65
|
||||||
|
girl/arm-front
|
||||||
|
bounds: 712, 279, 36, 115
|
||||||
|
rotate: 90
|
||||||
|
girl/back-eyebrow
|
||||||
|
bounds: 309, 61, 18, 12
|
||||||
|
rotate: 90
|
||||||
|
girl/bag-base
|
||||||
|
bounds: 694, 219, 62, 58
|
||||||
|
girl/bag-strap-front
|
||||||
|
bounds: 370, 304, 12, 96
|
||||||
|
rotate: 90
|
||||||
|
girl/bag-top
|
||||||
|
bounds: 765, 175, 49, 50
|
||||||
|
girl/body
|
||||||
|
bounds: 370, 318, 97, 132
|
||||||
|
rotate: 90
|
||||||
|
girl/boot-ribbon-front
|
||||||
|
bounds: 323, 64, 13, 13
|
||||||
|
girl/eye-iris-back
|
||||||
|
bounds: 361, 105, 17, 17
|
||||||
|
girl/eye-iris-front
|
||||||
|
bounds: 507, 106, 18, 18
|
||||||
|
girl/eye-white-back
|
||||||
|
bounds: 665, 175, 20, 16
|
||||||
|
girl/front-eyebrow
|
||||||
|
bounds: 343, 91, 18, 12
|
||||||
|
girl/hair-back
|
||||||
|
bounds: 696, 417, 147, 93
|
||||||
|
girl/hair-bangs
|
||||||
|
bounds: 922, 247, 91, 40
|
||||||
|
girl/hair-flap-down-front
|
||||||
|
bounds: 415, 171, 70, 65
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-head-side-back
|
||||||
|
bounds: 991, 381, 30, 52
|
||||||
|
girl/hair-head-side-front
|
||||||
|
bounds: 859, 206, 41, 42
|
||||||
|
girl/hair-patch
|
||||||
|
bounds: 132, 2, 66, 41
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-side
|
||||||
|
bounds: 692, 181, 36, 71
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-strand-back-1
|
||||||
|
bounds: 948, 289, 58, 74
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-strand-back-2
|
||||||
|
bounds: 355, 170, 91, 58
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-strand-back-3
|
||||||
|
bounds: 215, 40, 92, 79
|
||||||
|
girl/hair-strand-front-1
|
||||||
|
bounds: 234, 263, 38, 94
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-strand-front-2
|
||||||
|
bounds: 576, 233, 70, 50
|
||||||
|
rotate: 90
|
||||||
|
girl/hair-strand-front-3
|
||||||
|
bounds: 313, 124, 44, 81
|
||||||
|
rotate: 90
|
||||||
|
girl/hand-front-fingers
|
||||||
|
bounds: 923, 208, 19, 21
|
||||||
|
girl/hat
|
||||||
|
bounds: 218, 179, 93, 82
|
||||||
|
girl/leg-front
|
||||||
|
bounds: 831, 349, 30, 158
|
||||||
|
rotate: 90
|
||||||
|
girl/pompom
|
||||||
|
bounds: 416, 126, 48, 43
|
||||||
|
girl/scarf
|
||||||
|
bounds: 113, 264, 119, 51
|
||||||
|
girl/scarf-back
|
||||||
|
bounds: 502, 252, 72, 51
|
||||||
|
girl/zip
|
||||||
|
bounds: 816, 179, 19, 25
|
||||||
|
After Width: | Height: | Size: 351 KiB |
@ -0,0 +1,94 @@
|
|||||||
|
spineboy.png
|
||||||
|
size: 1024, 256
|
||||||
|
filter: Linear, Linear
|
||||||
|
scale: 0.5
|
||||||
|
crosshair
|
||||||
|
bounds: 352, 7, 45, 45
|
||||||
|
eye-indifferent
|
||||||
|
bounds: 862, 105, 47, 45
|
||||||
|
eye-surprised
|
||||||
|
bounds: 505, 79, 47, 45
|
||||||
|
front-bracer
|
||||||
|
bounds: 826, 66, 29, 40
|
||||||
|
front-fist-closed
|
||||||
|
bounds: 786, 65, 38, 41
|
||||||
|
front-fist-open
|
||||||
|
bounds: 710, 51, 43, 44
|
||||||
|
rotate: 90
|
||||||
|
front-foot
|
||||||
|
bounds: 210, 6, 63, 35
|
||||||
|
front-shin
|
||||||
|
bounds: 665, 128, 41, 92
|
||||||
|
rotate: 90
|
||||||
|
front-thigh
|
||||||
|
bounds: 2, 2, 23, 56
|
||||||
|
rotate: 90
|
||||||
|
front-upper-arm
|
||||||
|
bounds: 250, 205, 23, 49
|
||||||
|
goggles
|
||||||
|
bounds: 665, 171, 131, 83
|
||||||
|
gun
|
||||||
|
bounds: 798, 152, 105, 102
|
||||||
|
head
|
||||||
|
bounds: 2, 27, 136, 149
|
||||||
|
hoverboard-board
|
||||||
|
bounds: 2, 178, 246, 76
|
||||||
|
hoverboard-thruster
|
||||||
|
bounds: 722, 96, 30, 32
|
||||||
|
rotate: 90
|
||||||
|
hoverglow-small
|
||||||
|
bounds: 275, 81, 137, 38
|
||||||
|
mouth-grind
|
||||||
|
bounds: 614, 97, 47, 30
|
||||||
|
mouth-oooo
|
||||||
|
bounds: 612, 65, 47, 30
|
||||||
|
mouth-smile
|
||||||
|
bounds: 661, 64, 47, 30
|
||||||
|
muzzle-glow
|
||||||
|
bounds: 382, 54, 25, 25
|
||||||
|
muzzle-ring
|
||||||
|
bounds: 275, 54, 25, 105
|
||||||
|
rotate: 90
|
||||||
|
muzzle01
|
||||||
|
bounds: 911, 95, 67, 40
|
||||||
|
rotate: 90
|
||||||
|
muzzle02
|
||||||
|
bounds: 792, 108, 68, 42
|
||||||
|
muzzle03
|
||||||
|
bounds: 956, 171, 83, 53
|
||||||
|
rotate: 90
|
||||||
|
muzzle04
|
||||||
|
bounds: 275, 7, 75, 45
|
||||||
|
muzzle05
|
||||||
|
bounds: 140, 3, 68, 38
|
||||||
|
neck
|
||||||
|
bounds: 250, 182, 18, 21
|
||||||
|
portal-bg
|
||||||
|
bounds: 140, 43, 133, 133
|
||||||
|
portal-flare1
|
||||||
|
bounds: 554, 65, 56, 30
|
||||||
|
portal-flare2
|
||||||
|
bounds: 759, 112, 57, 31
|
||||||
|
rotate: 90
|
||||||
|
portal-flare3
|
||||||
|
bounds: 554, 97, 58, 30
|
||||||
|
portal-shade
|
||||||
|
bounds: 275, 121, 133, 133
|
||||||
|
portal-streaks1
|
||||||
|
bounds: 410, 126, 126, 128
|
||||||
|
portal-streaks2
|
||||||
|
bounds: 538, 129, 125, 125
|
||||||
|
rear-bracer
|
||||||
|
bounds: 857, 67, 28, 36
|
||||||
|
rear-foot
|
||||||
|
bounds: 663, 96, 57, 30
|
||||||
|
rear-shin
|
||||||
|
bounds: 414, 86, 38, 89
|
||||||
|
rotate: 90
|
||||||
|
rear-thigh
|
||||||
|
bounds: 756, 63, 28, 47
|
||||||
|
rear-upper-arm
|
||||||
|
bounds: 60, 5, 20, 44
|
||||||
|
rotate: 90
|
||||||
|
torso
|
||||||
|
bounds: 905, 164, 49, 90
|
||||||
BIN
spine-ios/Example/Spine iOS Example/Assets/spineboy/spineboy.png
Normal file
|
After Width: | Height: | Size: 240 KiB |
76
spine-ios/Example/Spine iOS Example/DebugRendering.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct DebugRendering: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var model = DebugRenderingModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Color.red.ignoresSafeArea()
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
controller: model.controller,
|
||||||
|
mode: .fit,
|
||||||
|
alignment: .center
|
||||||
|
)
|
||||||
|
ForEach(model.boneRects, id: \.id) { boneLocation in
|
||||||
|
Rectangle()
|
||||||
|
.fill(.blue)
|
||||||
|
.offset(x: boneLocation.x, y: boneLocation.y)
|
||||||
|
.frame(width: boneLocation.width, height: boneLocation.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Debug Rendering")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
DebugRendering()
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DebugRenderingModel: ObservableObject {
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var controller: SpineController!
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var boneRects = [BoneRect]()
|
||||||
|
|
||||||
|
init() {
|
||||||
|
controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "walk",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAfterPaint: {
|
||||||
|
[weak self] controller in guard let self else { return }
|
||||||
|
boneRects = controller.drawable.skeleton.bones.map { bone in
|
||||||
|
let position = controller.fromSkeletonCoordinates(
|
||||||
|
position: CGPointMake(CGFloat(bone.worldX), CGFloat(bone.worldY))
|
||||||
|
)
|
||||||
|
return BoneRect(
|
||||||
|
id: UUID(),
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
width: 5,
|
||||||
|
height: 5
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BoneRect: Hashable {
|
||||||
|
let id: UUID
|
||||||
|
let x: CGFloat
|
||||||
|
let y: CGFloat
|
||||||
|
let width: CGFloat
|
||||||
|
let height: CGFloat
|
||||||
|
}
|
||||||
61
spine-ios/Example/Spine iOS Example/DisableRendering.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct DisableRendering: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "walk",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@State
|
||||||
|
var isRendering: Bool?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
List {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Scroll spine boy out of the viewport")
|
||||||
|
Text("Rendering is disabled when the spine view moves out of the viewport, preserving CPU/GPU resources.")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
controller: controller,
|
||||||
|
isRendering: $isRendering
|
||||||
|
)
|
||||||
|
.frame(minHeight: 200)
|
||||||
|
.onAppear {
|
||||||
|
isRendering = true
|
||||||
|
print("rendering enabled")
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
isRendering = false
|
||||||
|
print("rendering disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
Text("Foo")
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
|
||||||
|
Text("Bar")
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
|
||||||
|
Text("Baz")
|
||||||
|
.frame(minHeight: 400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Disable Rendering")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
DisableRendering()
|
||||||
|
}
|
||||||
129
spine-ios/Example/Spine iOS Example/DressUp.swift
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
struct DressUp: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var model = DressUpModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
List {
|
||||||
|
ForEach(model.skinImages.keys.sorted(), id: \.self) { skinName in
|
||||||
|
let rawImageData = model.skinImages[skinName]!
|
||||||
|
Button(action: { model.toggleSkin(skinName: skinName) }) {
|
||||||
|
Image(uiImage: UIImage(cgImage: rawImageData))
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: model.thumbnailSize.width, height: model.thumbnailSize.height)
|
||||||
|
.grayscale(model.selectedSkins[skinName] == true ? 0.0 : 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listStyle(.plain)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
if let drawable = model.drawable {
|
||||||
|
SpineView(
|
||||||
|
from: .drawable(drawable),
|
||||||
|
controller: model.controller,
|
||||||
|
boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"])
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Dress Up")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
DressUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DressUpModel: ObservableObject {
|
||||||
|
|
||||||
|
let thumbnailSize = CGSize(width: 200, height: 200)
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var controller: SpineController
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var drawable: SkeletonDrawableWrapper?
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var skinImages = [String: CGImage]()
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var selectedSkins = [String: Bool]()
|
||||||
|
|
||||||
|
private var customSkin: Skin?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "dance",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
disposeDrawableOnDeInit: false
|
||||||
|
)
|
||||||
|
Task.detached(priority: .high) {
|
||||||
|
let drawable = try await SkeletonDrawableWrapper.fromBundle(
|
||||||
|
atlasFileName: "mix-and-match.atlas",
|
||||||
|
skeletonFileName: "mix-and-match-pro.skel"
|
||||||
|
)
|
||||||
|
try await MainActor.run {
|
||||||
|
for skin in drawable.skeletonData.skins {
|
||||||
|
if skin.name == "default" { continue }
|
||||||
|
let skeleton = drawable.skeleton
|
||||||
|
skeleton.skin = skin
|
||||||
|
skeleton.setToSetupPose()
|
||||||
|
skeleton.update(delta: 0)
|
||||||
|
skeleton.updateWorldTransform(physics: SPINE_PHYSICS_UPDATE)
|
||||||
|
try skin.name.flatMap { skinName in
|
||||||
|
self.skinImages[skinName] = try drawable.renderToImage(
|
||||||
|
size: self.thumbnailSize,
|
||||||
|
backgroundColor: .white,
|
||||||
|
scaleFactor: UIScreen.main.scale
|
||||||
|
)
|
||||||
|
self.selectedSkins[skinName] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.toggleSkin(skinName: "full-skins/girl", drawable: drawable)
|
||||||
|
self.drawable = drawable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
drawable?.dispose()
|
||||||
|
customSkin?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleSkin(skinName: String) {
|
||||||
|
if let drawable {
|
||||||
|
toggleSkin(skinName: skinName, drawable: drawable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleSkin(skinName: String, drawable: SkeletonDrawableWrapper) {
|
||||||
|
selectedSkins[skinName] = !(selectedSkins[skinName] ?? false)
|
||||||
|
customSkin?.dispose()
|
||||||
|
customSkin = Skin.create(name: "custom-skin")
|
||||||
|
for skinName in selectedSkins.keys {
|
||||||
|
if selectedSkins[skinName] == true {
|
||||||
|
if let skin = drawable.skeletonData.findSkin(name: skinName) {
|
||||||
|
customSkin?.addSkin(other: skin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawable.skeleton.skin = customSkin
|
||||||
|
drawable.skeleton.setToSetupPose()
|
||||||
|
}
|
||||||
|
}
|
||||||
73
spine-ios/Example/Spine iOS Example/IKFollowing.swift
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct IKFollowing: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var model = IKFollowingModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
controller: model.controller,
|
||||||
|
alignment: .centerLeft
|
||||||
|
)
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { gesture in
|
||||||
|
model.crossHairPosition = model.controller.toSkeletonCoordinates(
|
||||||
|
position: gesture.location
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.navigationTitle("IK Following")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
IKFollowing()
|
||||||
|
.previewInterfaceOrientation(.landscapeLeft)
|
||||||
|
} else {
|
||||||
|
IKFollowing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class IKFollowingModel: ObservableObject {
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var controller: SpineController!
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var crossHairPosition: CGPoint?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "walk",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 1,
|
||||||
|
animationName: "aim",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAfterUpdateWorldTransforms: {
|
||||||
|
[weak self] controller in guard let self else { return }
|
||||||
|
guard let worldPosition = self.crossHairPosition else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let bone = controller.skeleton.findBone(boneName: "crosshair")!
|
||||||
|
if let parent = bone.parent {
|
||||||
|
let position = parent.worldToLocal(worldX: Float(worldPosition.x), worldY: Float(worldPosition.y))
|
||||||
|
bone.x = position.x
|
||||||
|
bone.y = position.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
61
spine-ios/Example/Spine iOS Example/MainView.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct MainView: View {
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
Section {
|
||||||
|
NavigationLink("Simple Animation") {
|
||||||
|
SimpleAnimation()
|
||||||
|
}
|
||||||
|
NavigationLink("Play/Pause") {
|
||||||
|
PlayPauseAnimation()
|
||||||
|
}
|
||||||
|
NavigationLink("Animation State Listener") {
|
||||||
|
AnimationStateEvents()
|
||||||
|
}
|
||||||
|
NavigationLink("Debug Rendering") {
|
||||||
|
DebugRendering()
|
||||||
|
}
|
||||||
|
NavigationLink("Dress Up") {
|
||||||
|
DressUp()
|
||||||
|
}
|
||||||
|
NavigationLink("IK Following") {
|
||||||
|
IKFollowing()
|
||||||
|
}
|
||||||
|
NavigationLink("Physics") {
|
||||||
|
Physics()
|
||||||
|
}
|
||||||
|
NavigationLink("Disable Rendering") {
|
||||||
|
DisableRendering()
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Swift + SwiftUI")
|
||||||
|
}
|
||||||
|
Section {
|
||||||
|
NavigationLink("Simple Animation") {
|
||||||
|
SimpleAnimationViewControllerRepresentable()
|
||||||
|
.navigationTitle("Simple Animation")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("ObjC + UIKit")
|
||||||
|
} footer: {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Text("Spine \(Spine.version)")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Spine Examples")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
NavigationView {
|
||||||
|
MainView()
|
||||||
|
}
|
||||||
|
}
|
||||||
77
spine-ios/Example/Spine iOS Example/Physics.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct Physics: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var model = PhysicsModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "celestial-circus.atlas", skeletonFileName: "celestial-circus-pro.skel"),
|
||||||
|
controller: model.controller
|
||||||
|
)
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { gesture in
|
||||||
|
model.updateBonePosition(position: gesture.location)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.navigationTitle("Physics (drag anywhere)")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
Physics()
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PhysicsModel: ObservableObject {
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var controller: SpineController!
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var mousePosition: CGPoint?
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var lastMousePosition: CGPoint?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "eyeblink-long",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "wings-and-feet",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onAfterUpdateWorldTransforms: {
|
||||||
|
[weak self] controller in guard let self else { return }
|
||||||
|
|
||||||
|
guard let lastMousePosition else {
|
||||||
|
self.lastMousePosition = mousePosition
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let mousePosition else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let dx = mousePosition.x - lastMousePosition.x
|
||||||
|
let dy = mousePosition.y - lastMousePosition.y
|
||||||
|
let positionX = controller.skeleton.x + Float(dx)
|
||||||
|
let positionY = controller.skeleton.y + Float(dy)
|
||||||
|
controller.skeleton.setPosition(x: positionX, y: positionY)
|
||||||
|
self.lastMousePosition = mousePosition
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBonePosition(position: CGPoint) {
|
||||||
|
mousePosition = controller.toSkeletonCoordinates(position: position)
|
||||||
|
}
|
||||||
|
}
|
||||||
45
spine-ios/Example/Spine iOS Example/PlayPauseAnimation.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct PlayPauseAnimation: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "flying",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "dragon.atlas", skeletonFileName: "dragon-ess.skel"),
|
||||||
|
// from: .http(
|
||||||
|
// atlasURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon.atlas")!,
|
||||||
|
// skeletonURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/dragon/dragon-ess.skel")!
|
||||||
|
// ),
|
||||||
|
controller: controller,
|
||||||
|
boundsProvider: SkinAndAnimationBounds(animation: "flying")
|
||||||
|
)
|
||||||
|
.navigationTitle("Play/Pause")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
Button(action: {
|
||||||
|
if controller.isPlaying {
|
||||||
|
controller.pause()
|
||||||
|
} else {
|
||||||
|
controller.resume()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Image(systemName: controller.isPlaying ? "pause.fill" : "play.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
PlayPauseAnimation()
|
||||||
|
}
|
||||||
35
spine-ios/Example/Spine iOS Example/SimpleAnimation.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
struct SimpleAnimation: View {
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
var controller = SpineController(
|
||||||
|
onInitialized: { controller in
|
||||||
|
controller.animationState.setAnimationByName(
|
||||||
|
trackIndex: 0,
|
||||||
|
animationName: "walk",
|
||||||
|
loop: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SpineView(
|
||||||
|
from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
|
||||||
|
// from: .http(
|
||||||
|
// atlasURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy.atlas")!,
|
||||||
|
// skeletonURL: URL(string: "https://github.com/denrase/spine-runtimes/raw/spine-ios/spine-ios/Example/Spine%20iOS%20Example/Assets/spineboy/spineboy-pro.skel")!
|
||||||
|
// ),
|
||||||
|
controller: controller,
|
||||||
|
mode: .fit,
|
||||||
|
alignment: .center
|
||||||
|
)
|
||||||
|
.navigationTitle("Simple Animation")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SimpleAnimation()
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface SimpleAnimationViewController : UIViewController
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
#import "SimpleAnimationViewController.h"
|
||||||
|
@import Spine;
|
||||||
|
|
||||||
|
@interface SimpleAnimationViewController ()
|
||||||
|
|
||||||
|
@property (nonatomic, strong) SpineController *spineController;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SimpleAnimationViewController
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.spineController = [[SpineController alloc] initOnInitialized:^(SpineController *controller) {
|
||||||
|
[controller.animationState setAnimationByNameWithTrackIndex:0 animationName:@"walk" loop:YES];
|
||||||
|
}
|
||||||
|
onBeforeUpdateWorldTransforms:nil
|
||||||
|
onAfterUpdateWorldTransforms:nil
|
||||||
|
onBeforePaint:nil
|
||||||
|
onAfterPaint:nil
|
||||||
|
disposeDrawableOnDeInit:YES];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
SpineUIView *spineView = [[SpineUIView alloc] initWithAtlasFileName:@"spineboy.atlas"
|
||||||
|
skeletonFileName:@"spineboy-pro.skel"
|
||||||
|
bundle:[NSBundle mainBundle]
|
||||||
|
controller:self.spineController
|
||||||
|
mode:ContentModeFit
|
||||||
|
alignment:AlignmentCenter
|
||||||
|
boundsProvider:[[SpineSetupPoseBounds alloc] init]
|
||||||
|
backgroundColor:[UIColor clearColor]];
|
||||||
|
spineView.frame = self.view.bounds;
|
||||||
|
spineView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||||
|
|
||||||
|
[self.view addSubview:spineView];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SimpleAnimationViewControllerRepresentable: UIViewControllerRepresentable {
|
||||||
|
typealias UIViewControllerType = SimpleAnimationViewController
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> SimpleAnimationViewController {
|
||||||
|
return SimpleAnimationViewController()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: SimpleAnimationViewController, context: Context) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "SimpleAnimationViewController.h"
|
||||||
14
spine-ios/Example/Spine iOS Example/SpineExampleApp.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import Spine
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct SpineExampleApp: App {
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
NavigationView {
|
||||||
|
MainView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
6
spine-ios/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# spine-ios
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
- Go to "Build Settings"
|
||||||
|
- Set "C++ and Objective-C Interoperability" to "C++ / Objective-C++"
|
||||||
70
spine-ios/Sources/Spine/AnimationStateWrapper.swift
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import Foundation
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
public typealias AnimationStateListener = (_ type: EventType, _ entry: TrackEntry, _ event: Event?) -> Void
|
||||||
|
|
||||||
|
/// Wrapper class around ``AnimationState``. Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies
|
||||||
|
/// multiple animations on top of each other (layering).
|
||||||
|
///
|
||||||
|
/// See [Applying Animations](http://esotericsoftware.com/spine-applying-animations/) in the Spine Runtimes Guide.
|
||||||
|
@objc(SpineAnimationStateWrapper)
|
||||||
|
@objcMembers
|
||||||
|
public final class AnimationStateWrapper: NSObject {
|
||||||
|
|
||||||
|
public let animationState: AnimationState
|
||||||
|
public let aninationStateEvents: AnimationStateEvents
|
||||||
|
|
||||||
|
private var trackEntryListeners = [spine_track_entry: AnimationStateListener]()
|
||||||
|
|
||||||
|
private var stateListener: AnimationStateListener?
|
||||||
|
|
||||||
|
public init(animationState: AnimationState, aninationStateEvents: AnimationStateEvents) {
|
||||||
|
self.animationState = animationState
|
||||||
|
self.aninationStateEvents = aninationStateEvents
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The listener for events generated by the provided ``TrackEntry``, or nil.
|
||||||
|
///
|
||||||
|
/// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation
|
||||||
|
/// for the track, so the track entry listener will not be called for ``EventType`` `SPINE_EVENT_TYPE_START`.
|
||||||
|
public func setTrackEntryListener(entry: TrackEntry, listener: AnimationStateListener?) {
|
||||||
|
if let listener {
|
||||||
|
trackEntryListeners[entry.wrappee] = listener
|
||||||
|
} else {
|
||||||
|
trackEntryListeners.removeValue(forKey: entry.wrappee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increments each track entry ``TrackEntry/trackTime``, setting queued animations as current if needed.
|
||||||
|
public func update(delta: Float) {
|
||||||
|
animationState.update(delta: delta)
|
||||||
|
|
||||||
|
let numEvents = spine_animation_state_events_get_num_events(aninationStateEvents.wrappee)
|
||||||
|
for i in 0..<numEvents {
|
||||||
|
let type = aninationStateEvents.getEventType(index: i)
|
||||||
|
|
||||||
|
let entry = aninationStateEvents.getTrackEntry(index: i)
|
||||||
|
let event = aninationStateEvents.getEvent(index: i)
|
||||||
|
|
||||||
|
if let trackEntryListener = trackEntryListeners[entry.wrappee] {
|
||||||
|
trackEntryListener(type, entry, event)
|
||||||
|
}
|
||||||
|
if let stateListener {
|
||||||
|
stateListener(type, entry, event)
|
||||||
|
}
|
||||||
|
if type == SPINE_EVENT_TYPE_DISPOSE {
|
||||||
|
spine_animation_state_dispose_track_entry(animationState.wrappee, entry.wrappee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aninationStateEvents.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The listener for events generated for all tracks managed by the ``AnimationState``, or nil.
|
||||||
|
///
|
||||||
|
/// A track entry returned from ``AnimationState/setAnimation(trackIndex:animation:loop:)`` is already the current animation
|
||||||
|
/// for the track, so the track entry listener will not be called for ``EventType`` `SPINE_EVENT_TYPE_START`.
|
||||||
|
public func setStateListener(_ stateListener: AnimationStateListener?) {
|
||||||
|
self.stateListener = stateListener
|
||||||
|
}
|
||||||
|
}
|
||||||
157
spine-ios/Sources/Spine/BoundsProvider.swift
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import Foundation
|
||||||
|
import CoreGraphics
|
||||||
|
|
||||||
|
/// Base class for bounds providers. A bounds provider calculates the axis aligned bounding box
|
||||||
|
/// used to scale and fit a skeleton inside the bounds of a ``SpineUIView``.
|
||||||
|
@objc(SpineBoundsProvider)
|
||||||
|
public protocol BoundsProvider {
|
||||||
|
func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A ``BoundsProvider`` that calculates the bounding box of the skeleton based on the visible
|
||||||
|
/// attachments in the setup pose.
|
||||||
|
@objc(SpineSetupPoseBounds)
|
||||||
|
@objcMembers
|
||||||
|
public final class SetupPoseBounds: NSObject, BoundsProvider {
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect {
|
||||||
|
return CGRect(bounds: drawable.skeleton.bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A ``BoundsProvider`` that returns fixed bounds.
|
||||||
|
@objc(SpineRawBounds)
|
||||||
|
@objcMembers
|
||||||
|
public final class RawBounds: NSObject, BoundsProvider {
|
||||||
|
public let x: Double
|
||||||
|
public let y: Double
|
||||||
|
public let width: Double
|
||||||
|
public let height: Double
|
||||||
|
|
||||||
|
public init(x: Double, y: Double, width: Double, height: Double) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect {
|
||||||
|
return CGRectMake(CGFloat(x), CGFloat(y), CGFloat(width), CGFloat(height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A ``BoundsProvider`` that calculates the bounding box needed for a combination of skins
|
||||||
|
/// and an animation.
|
||||||
|
@objc(SpineSkinAndAnimationBounds)
|
||||||
|
@objcMembers
|
||||||
|
public final class SkinAndAnimationBounds: NSObject, BoundsProvider {
|
||||||
|
|
||||||
|
private let animation: String?
|
||||||
|
private let skins: [String]
|
||||||
|
private let stepTime: TimeInterval;
|
||||||
|
|
||||||
|
/// Constructs a new provider that will use the given `skins` and `animation` to calculate
|
||||||
|
/// the bounding box of the skeleton. If no skins are given, the default skin is used.
|
||||||
|
/// The `stepTime`, given in seconds, defines at what interval the bounds should be sampled
|
||||||
|
/// across the entire animation.
|
||||||
|
public init(animation: String? = nil, skins: [String]? = nil, let stepTime: TimeInterval = 0.1) {
|
||||||
|
self.animation = animation
|
||||||
|
if let skins, !skins.isEmpty {
|
||||||
|
self.skins = skins
|
||||||
|
} else {
|
||||||
|
self.skins = ["default"]
|
||||||
|
}
|
||||||
|
self.stepTime = stepTime
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func computeBounds(for drawable: SkeletonDrawableWrapper) -> CGRect {
|
||||||
|
let data = drawable.skeletonData
|
||||||
|
let oldSkin: Skin? = drawable.skeleton.skin
|
||||||
|
let customSkin = Skin.create(name: "custom-skin")
|
||||||
|
for skinName in skins {
|
||||||
|
let skin = data.findSkin(name: skinName)
|
||||||
|
if let skin = data.findSkin(name: skinName) {
|
||||||
|
customSkin.addSkin(other: skin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawable.skeleton.skin = customSkin
|
||||||
|
drawable.skeleton.setToSetupPose();
|
||||||
|
|
||||||
|
let animation = animation.flatMap { data.findAnimation(name: $0) }
|
||||||
|
var minX = Float.Magnitude.greatestFiniteMagnitude
|
||||||
|
var minY = Float.Magnitude.greatestFiniteMagnitude
|
||||||
|
var maxX = -Float.Magnitude.greatestFiniteMagnitude
|
||||||
|
var maxY = -Float.Magnitude.greatestFiniteMagnitude
|
||||||
|
if let animation {
|
||||||
|
drawable.animationState.setAnimation(trackIndex: 0, animation: animation, loop: false)
|
||||||
|
let steps = Int(max(Double(animation.duration) / stepTime, 1.0))
|
||||||
|
for i in 0..<steps {
|
||||||
|
drawable.update(delta: i > 0 ? Float(stepTime) : 0.0)
|
||||||
|
let bounds = drawable.skeleton.bounds;
|
||||||
|
minX = min(minX, bounds.x)
|
||||||
|
minY = min(minY, bounds.y)
|
||||||
|
maxX = max(maxX, minX + bounds.width)
|
||||||
|
maxY = max(maxY, minY + bounds.height)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let bounds = drawable.skeleton.bounds;
|
||||||
|
minX = bounds.x
|
||||||
|
minY = bounds.y
|
||||||
|
maxX = minX + bounds.width
|
||||||
|
maxY = minY + bounds.height
|
||||||
|
}
|
||||||
|
drawable.skeleton.setSkinByName(skinName: "default")
|
||||||
|
drawable.animationState.clearTracks()
|
||||||
|
|
||||||
|
if let oldSkin {
|
||||||
|
drawable.skeleton.skin = oldSkin
|
||||||
|
}
|
||||||
|
drawable.skeleton.setToSetupPose()
|
||||||
|
drawable.update(delta: 0)
|
||||||
|
customSkin.dispose()
|
||||||
|
return CGRectMake(CGFloat(minX), CGFloat(minY), CGFloat(maxX - minX), CGFloat(maxY - minY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How a view should be inscribed into another view.
|
||||||
|
@objc
|
||||||
|
public enum ContentMode: Int {
|
||||||
|
case fit /// As large as possible while still containing the source view entirely within the target view.
|
||||||
|
case fill /// Fill the target view by distorting the source's aspect ratio.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How a view should aligned withing another view.
|
||||||
|
@objc
|
||||||
|
public enum Alignment: Int {
|
||||||
|
case topLeft
|
||||||
|
case topCenter
|
||||||
|
case topRight
|
||||||
|
case centerLeft
|
||||||
|
case center
|
||||||
|
case centerRight
|
||||||
|
case bottomLeft
|
||||||
|
case bottomCenter
|
||||||
|
case bottomRight
|
||||||
|
|
||||||
|
internal var x: CGFloat {
|
||||||
|
switch self {
|
||||||
|
case .topLeft, .centerLeft, .bottomLeft: return -1.0
|
||||||
|
case .topCenter, .center, .bottomCenter: return 0.0
|
||||||
|
case .topRight, .centerRight, .bottomRight: return 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var y: CGFloat {
|
||||||
|
switch self {
|
||||||
|
case .topLeft, .topCenter, .topRight: return -1.0
|
||||||
|
case .centerLeft, .center, .centerRight: return 0.0
|
||||||
|
case .bottomLeft, .bottomCenter, .bottomRight: return -1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import UIKit
|
||||||
|
import MetalKit
|
||||||
|
|
||||||
|
extension MTLClearColor {
|
||||||
|
init(_ color: UIColor) {
|
||||||
|
var red: CGFloat = 0
|
||||||
|
var green: CGFloat = 0
|
||||||
|
var blue: CGFloat = 0
|
||||||
|
var alpha: CGFloat = 0
|
||||||
|
|
||||||
|
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
||||||
|
|
||||||
|
self.init(red: Double(red), green: Double(green), blue: Double(blue), alpha: Double(alpha))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import SpineShadersStructs
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension RenderCommand {
|
||||||
|
func getVertices() -> [SpineVertex] {
|
||||||
|
var vertices = [SpineVertex]()
|
||||||
|
|
||||||
|
let indices = indices
|
||||||
|
let numVertices = numVertices
|
||||||
|
let positions = positions(numVertices: numVertices)
|
||||||
|
let uvs = uvs(numVertices: numVertices)
|
||||||
|
let colors = colors(numVertices: numVertices)
|
||||||
|
|
||||||
|
for i in 0..<indices.count {
|
||||||
|
let index = Int(indices[i])
|
||||||
|
|
||||||
|
let xIndex = 2 * index
|
||||||
|
let yIndex = xIndex + 1
|
||||||
|
|
||||||
|
let positionX = positions[xIndex]
|
||||||
|
let positionY = positions[yIndex]
|
||||||
|
let uvX = uvs[xIndex]
|
||||||
|
let uvY = uvs[yIndex]
|
||||||
|
let color = extractRGBA(from: colors[index])
|
||||||
|
|
||||||
|
let vertex = SpineVertex(
|
||||||
|
position: vector_float2(positionX, positionY),
|
||||||
|
color: color,
|
||||||
|
uv: vector_float2(uvX, uvY)
|
||||||
|
)
|
||||||
|
vertices.append(vertex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertices
|
||||||
|
}
|
||||||
|
|
||||||
|
private func extractRGBA(from color: Int32) -> vector_float4 {
|
||||||
|
guard color != -1 else {
|
||||||
|
return vector_float4(1.0, 1.0, 1.0, 1.0)
|
||||||
|
}
|
||||||
|
let alpha = (color >> 24) & 0xFF
|
||||||
|
let red = (color >> 16) & 0xFF
|
||||||
|
let green = (color >> 8) & 0xFF
|
||||||
|
let blue = color & 0xFF
|
||||||
|
|
||||||
|
return vector_float4(Float(red)/255, Float(green)/255, Float(blue)/255, (Float(alpha)/255))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import CoreGraphics
|
||||||
|
|
||||||
|
public extension SkeletonDrawableWrapper {
|
||||||
|
|
||||||
|
/// Render the ``Skeleton`` to a `CGImage`
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - size: The size of the `CGImage` that should be rendered.
|
||||||
|
/// - backgroundColor: the background color of the image
|
||||||
|
/// - scaleFactor: The scale factor. Set this to `UIScreen.main.scale` if you want to show the image in a view
|
||||||
|
func renderToImage(size: CGSize, backgroundColor: UIColor, scaleFactor: CGFloat = 1) throws -> CGImage? {
|
||||||
|
let spineView = SpineUIView(
|
||||||
|
controller: SpineController(disposeDrawableOnDeInit: false), // Doesn't own the drawable
|
||||||
|
backgroundColor: backgroundColor
|
||||||
|
)
|
||||||
|
spineView.frame = CGRect(origin: .zero, size: size)
|
||||||
|
spineView.isPaused = false
|
||||||
|
spineView.enableSetNeedsDisplay = false
|
||||||
|
spineView.framebufferOnly = false
|
||||||
|
spineView.contentScaleFactor = scaleFactor
|
||||||
|
|
||||||
|
try spineView.load(drawable: self)
|
||||||
|
spineView.renderer?.waitUntilCompleted = true
|
||||||
|
|
||||||
|
spineView.delegate?.draw(in: spineView)
|
||||||
|
|
||||||
|
guard let texture = spineView.currentDrawable?.texture else {
|
||||||
|
throw "Could not read texture."
|
||||||
|
}
|
||||||
|
let width = texture.width
|
||||||
|
let height = texture.height
|
||||||
|
let rowBytes = width * 4
|
||||||
|
let data = UnsafeMutableRawPointer.allocate(byteCount: rowBytes * height, alignment: MemoryLayout<UInt8>.alignment)
|
||||||
|
defer {
|
||||||
|
data.deallocate()
|
||||||
|
}
|
||||||
|
|
||||||
|
let region = MTLRegionMake2D(0, 0, width, height)
|
||||||
|
texture.getBytes(data, bytesPerRow: rowBytes, from: region, mipmapLevel: 0)
|
||||||
|
|
||||||
|
let bitmapInfo = CGBitmapInfo(
|
||||||
|
rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue
|
||||||
|
).union(.byteOrder32Little)
|
||||||
|
|
||||||
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
guard let context = CGContext(data: data, width: width, height: height, bitsPerComponent: 8, bytesPerRow: rowBytes, space: colorSpace, bitmapInfo: bitmapInfo.rawValue),
|
||||||
|
let cgImage = context.makeImage() else {
|
||||||
|
throw "Could not create image."
|
||||||
|
}
|
||||||
|
return cgImage
|
||||||
|
}
|
||||||
|
}
|
||||||
19
spine-ios/Sources/Spine/Metal/SpineObjects.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
import MetalKit
|
||||||
|
|
||||||
|
/// Shared objects that live throughout applications lifecycle
|
||||||
|
///
|
||||||
|
/// Persistent Objects
|
||||||
|
/// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/PersistentObjects.html#//apple_ref/doc/uid/TP40016642-CH3-SW1
|
||||||
|
internal final class SpineObjects {
|
||||||
|
|
||||||
|
static let shared = SpineObjects()
|
||||||
|
|
||||||
|
internal lazy var device: MTLDevice = {
|
||||||
|
MTLCreateSystemDefaultDevice()!
|
||||||
|
}()
|
||||||
|
|
||||||
|
internal lazy var commandQueue: MTLCommandQueue = {
|
||||||
|
device.makeCommandQueue()!
|
||||||
|
}()
|
||||||
|
}
|
||||||
322
spine-ios/Sources/Spine/Metal/SpineRenderer.swift
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
import Foundation
|
||||||
|
import MetalKit
|
||||||
|
import SpineShadersStructs
|
||||||
|
import Spine
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
protocol SpineRendererDelegate: AnyObject {
|
||||||
|
func spineRendererWillUpdate(_ spineRenderer: SpineRenderer)
|
||||||
|
func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval)
|
||||||
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer)
|
||||||
|
|
||||||
|
func spineRendererWillDraw(_ spineRenderer: SpineRenderer)
|
||||||
|
func spineRendererDidDraw(_ spineRenderer: SpineRenderer)
|
||||||
|
|
||||||
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol SpineRendererDataSource: AnyObject {
|
||||||
|
func isPlaying(_ spineRenderer: SpineRenderer) -> Bool
|
||||||
|
func renderCommands(_ spineRenderer: SpineRenderer) -> [RenderCommand]
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class SpineRenderer: NSObject, MTKViewDelegate {
|
||||||
|
|
||||||
|
private let device: MTLDevice
|
||||||
|
private let textures: [MTLTexture]
|
||||||
|
private let commandQueue: MTLCommandQueue
|
||||||
|
|
||||||
|
private var sizeInPoints: CGSize = .zero
|
||||||
|
private var viewPortSize = vector_uint2(0, 0)
|
||||||
|
private var transform = SpineTransform(
|
||||||
|
translation: vector_float2(0, 0),
|
||||||
|
scale: vector_float2(1, 1),
|
||||||
|
offset: vector_float2(0, 0)
|
||||||
|
)
|
||||||
|
internal var lastDraw: CFTimeInterval = 0
|
||||||
|
internal var waitUntilCompleted = false
|
||||||
|
private var pipelineStatesByBlendMode = [Int: MTLRenderPipelineState]()
|
||||||
|
|
||||||
|
private static let numberOfBuffers = 3
|
||||||
|
private static let defaultBufferSize = 32 * 1024 // 32KB
|
||||||
|
|
||||||
|
private var buffers = [MTLBuffer]()
|
||||||
|
private let bufferingSemaphore = DispatchSemaphore(value: SpineRenderer.numberOfBuffers)
|
||||||
|
private var currentBufferIndex: Int = 0
|
||||||
|
|
||||||
|
weak var dataSource: SpineRendererDataSource?
|
||||||
|
weak var delegate: SpineRendererDelegate?
|
||||||
|
|
||||||
|
internal init(
|
||||||
|
device: MTLDevice,
|
||||||
|
commandQueue: MTLCommandQueue,
|
||||||
|
pixelFormat: MTLPixelFormat,
|
||||||
|
atlasPages: [UIImage],
|
||||||
|
pma: Bool
|
||||||
|
) throws {
|
||||||
|
self.device = device
|
||||||
|
self.commandQueue = commandQueue
|
||||||
|
|
||||||
|
let bundle: Bundle
|
||||||
|
#if SWIFT_PACKAGE // SPM
|
||||||
|
bundle = .module
|
||||||
|
#else // CocoaPods
|
||||||
|
bundle = Bundle(for: SpineRenderer.self)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
let defaultLibrary = try device.makeDefaultLibrary(bundle: bundle)
|
||||||
|
let textureLoader = MTKTextureLoader(device: device)
|
||||||
|
textures = try atlasPages
|
||||||
|
.compactMap { $0.cgImage }
|
||||||
|
.map {
|
||||||
|
try textureLoader.newTexture(
|
||||||
|
cgImage: $0,
|
||||||
|
options: [
|
||||||
|
.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
|
||||||
|
.SRGB: false,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let blendModes = [
|
||||||
|
SPINE_BLEND_MODE_NORMAL,
|
||||||
|
SPINE_BLEND_MODE_ADDITIVE,
|
||||||
|
SPINE_BLEND_MODE_MULTIPLY,
|
||||||
|
SPINE_BLEND_MODE_SCREEN
|
||||||
|
]
|
||||||
|
for blendMode in blendModes {
|
||||||
|
let descriptor = MTLRenderPipelineDescriptor()
|
||||||
|
descriptor.vertexFunction = defaultLibrary.makeFunction(name: "vertexShader")
|
||||||
|
descriptor.fragmentFunction = defaultLibrary.makeFunction(name: "fragmentShader")
|
||||||
|
descriptor.colorAttachments[0].pixelFormat = pixelFormat
|
||||||
|
descriptor.colorAttachments[0].apply(
|
||||||
|
blendMode: blendMode,
|
||||||
|
with: pma
|
||||||
|
)
|
||||||
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)] = try device.makeRenderPipelineState(descriptor: descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
increaseBuffersSize(to: SpineRenderer.defaultBufferSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
|
||||||
|
guard let spineView = view as? SpineUIView else { return }
|
||||||
|
|
||||||
|
sizeInPoints = CGSize(width: size.width / UIScreen.main.scale, height: size.height / UIScreen.main.scale)
|
||||||
|
viewPortSize = vector_uint2(UInt32(size.width), UInt32(size.height))
|
||||||
|
setTransform(
|
||||||
|
bounds: spineView.computedBounds,
|
||||||
|
mode: spineView.mode,
|
||||||
|
alignment: spineView.alignment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func draw(in view: MTKView) {
|
||||||
|
guard dataSource?.isPlaying(self) ?? false else {
|
||||||
|
lastDraw = CACurrentMediaTime()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
callNeedsUpdate()
|
||||||
|
|
||||||
|
// Tripple Buffering
|
||||||
|
// Source: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html#//apple_ref/doc/uid/TP40016642-CH5-SW1
|
||||||
|
bufferingSemaphore.wait()
|
||||||
|
currentBufferIndex = (currentBufferIndex + 1) % SpineRenderer.numberOfBuffers
|
||||||
|
|
||||||
|
guard let renderCommands = dataSource?.renderCommands(self),
|
||||||
|
let commandBuffer = commandQueue.makeCommandBuffer(),
|
||||||
|
let renderPassDescriptor = view.currentRenderPassDescriptor,
|
||||||
|
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate?.spineRendererWillDraw(self)
|
||||||
|
draw(renderCommands: renderCommands, renderEncoder: renderEncoder, in: view)
|
||||||
|
delegate?.spineRendererDidDraw(self)
|
||||||
|
|
||||||
|
renderEncoder.endEncoding()
|
||||||
|
view.currentDrawable.flatMap {
|
||||||
|
commandBuffer.present($0)
|
||||||
|
}
|
||||||
|
commandBuffer.addCompletedHandler { [bufferingSemaphore] _ in
|
||||||
|
bufferingSemaphore.signal()
|
||||||
|
}
|
||||||
|
commandBuffer.commit()
|
||||||
|
if waitUntilCompleted {
|
||||||
|
commandBuffer.waitUntilCompleted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setTransform(bounds: CGRect, mode: Spine.ContentMode, alignment: Spine.Alignment) {
|
||||||
|
let x = -bounds.minX - bounds.width / 2.0
|
||||||
|
let y = -bounds.minY - bounds.height / 2.0
|
||||||
|
|
||||||
|
var scaleX: CGFloat = 1.0
|
||||||
|
var scaleY: CGFloat = 1.0
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .fit:
|
||||||
|
scaleX = min(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
||||||
|
scaleY = scaleX
|
||||||
|
case .fill:
|
||||||
|
scaleX = max(sizeInPoints.width / bounds.width, sizeInPoints.height / bounds.height)
|
||||||
|
scaleY = scaleX
|
||||||
|
}
|
||||||
|
|
||||||
|
let offsetX = abs(sizeInPoints.width - bounds.width * scaleX) / 2 * alignment.x
|
||||||
|
let offsetY = abs(sizeInPoints.height - bounds.height * scaleY) / 2 * alignment.y
|
||||||
|
|
||||||
|
transform = SpineTransform(
|
||||||
|
translation: vector_float2(Float(x), Float(y)),
|
||||||
|
scale: vector_float2(Float(scaleX * UIScreen.main.scale), Float(scaleY * UIScreen.main.scale)),
|
||||||
|
offset: vector_float2(Float(offsetX * UIScreen.main.scale), Float(offsetY * UIScreen.main.scale))
|
||||||
|
)
|
||||||
|
|
||||||
|
delegate?.spineRendererDidUpdate(
|
||||||
|
self,
|
||||||
|
scaleX: scaleX,
|
||||||
|
scaleY: scaleY,
|
||||||
|
offsetX: x + offsetX / scaleX,
|
||||||
|
offsetY: y + offsetY / scaleY,
|
||||||
|
size: sizeInPoints
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func callNeedsUpdate() {
|
||||||
|
if lastDraw == 0 {
|
||||||
|
lastDraw = CACurrentMediaTime()
|
||||||
|
}
|
||||||
|
let delta = CACurrentMediaTime() - lastDraw
|
||||||
|
delegate?.spineRendererWillUpdate(self)
|
||||||
|
delegate?.spineRenderer(self, needsUpdate: delta)
|
||||||
|
lastDraw = CACurrentMediaTime()
|
||||||
|
delegate?.spineRendererDidUpdate(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func draw(renderCommands: [RenderCommand], renderEncoder: MTLRenderCommandEncoder, in view: MTKView) {
|
||||||
|
let allVertices = renderCommands.map { renderCommand in
|
||||||
|
Array(renderCommand.getVertices())
|
||||||
|
}
|
||||||
|
let vertices = allVertices.flatMap { $0 }
|
||||||
|
let verticesSize = MemoryLayout<SpineVertex>.stride * vertices.count
|
||||||
|
|
||||||
|
guard verticesSize > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var vertexBuffer = buffers[currentBufferIndex]
|
||||||
|
var vertexBufferSize = vertexBuffer.length
|
||||||
|
|
||||||
|
if vertexBufferSize < verticesSize {
|
||||||
|
increaseBuffersSize(to: verticesSize)
|
||||||
|
vertexBuffer = buffers[currentBufferIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEncoder.setViewport(
|
||||||
|
MTLViewport(
|
||||||
|
originX: 0.0,
|
||||||
|
originY: 0.0,
|
||||||
|
width: Double(viewPortSize.x),
|
||||||
|
height: Double(viewPortSize.y),
|
||||||
|
znear: 0.0,
|
||||||
|
zfar: 1.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
memcpy(vertexBuffer.contents(), vertices, verticesSize)
|
||||||
|
|
||||||
|
renderEncoder.setVertexBuffer(
|
||||||
|
vertexBuffer,
|
||||||
|
offset: 0,
|
||||||
|
index: Int(SpineVertexInputIndexVertices.rawValue)
|
||||||
|
)
|
||||||
|
renderEncoder.setVertexBytes(
|
||||||
|
&transform,
|
||||||
|
length: MemoryLayout.size(ofValue: transform),
|
||||||
|
index: Int(SpineVertexInputIndexTransform.rawValue)
|
||||||
|
)
|
||||||
|
renderEncoder.setVertexBytes(
|
||||||
|
&viewPortSize,
|
||||||
|
length: MemoryLayout.size(ofValue: viewPortSize),
|
||||||
|
index: Int(SpineVertexInputIndexViewportSize.rawValue)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Buffer Bindings
|
||||||
|
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html#//apple_ref/doc/uid/TP40016642-CH28-SW3
|
||||||
|
var vertexStart = 0
|
||||||
|
for (index, renderCommand) in renderCommands.enumerated() {
|
||||||
|
guard let pipelineState = getPipelineState(blendMode: renderCommand.blendMode) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
renderEncoder.setRenderPipelineState(pipelineState)
|
||||||
|
|
||||||
|
let vertices = allVertices[index]
|
||||||
|
|
||||||
|
let textureIndex = Int(renderCommand.atlasPage)
|
||||||
|
if textures.indices.contains(textureIndex) {
|
||||||
|
renderEncoder.setFragmentTexture(
|
||||||
|
textures[textureIndex],
|
||||||
|
index: Int(SpineTextureIndexBaseColor.rawValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEncoder.drawPrimitives(
|
||||||
|
type: .triangle,
|
||||||
|
vertexStart: vertexStart,
|
||||||
|
vertexCount: vertices.count
|
||||||
|
)
|
||||||
|
vertexStart += vertices.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getPipelineState(blendMode: BlendMode) -> MTLRenderPipelineState? {
|
||||||
|
pipelineStatesByBlendMode[Int(blendMode.rawValue)]
|
||||||
|
}
|
||||||
|
|
||||||
|
private func increaseBuffersSize(to size: Int) {
|
||||||
|
buffers = (0 ..< SpineRenderer.numberOfBuffers).map { _ in
|
||||||
|
device.makeBuffer(length: size, options: .storageModeShared)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension MTLRenderPipelineColorAttachmentDescriptor {
|
||||||
|
|
||||||
|
func apply(blendMode: BlendMode, with premultipliedAlpha: Bool) {
|
||||||
|
isBlendingEnabled = true
|
||||||
|
sourceRGBBlendFactor = blendMode.sourceRGBBlendFactor(premultipliedAlpha: premultipliedAlpha)
|
||||||
|
destinationRGBBlendFactor = blendMode.destinationRGBBlendFactor
|
||||||
|
destinationAlphaBlendFactor = .oneMinusSourceAlpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension BlendMode {
|
||||||
|
func sourceRGBBlendFactor(premultipliedAlpha: Bool) -> MTLBlendFactor {
|
||||||
|
switch self {
|
||||||
|
case SPINE_BLEND_MODE_NORMAL, SPINE_BLEND_MODE_ADDITIVE:
|
||||||
|
return premultipliedAlpha ? .one : .sourceAlpha
|
||||||
|
case SPINE_BLEND_MODE_MULTIPLY:
|
||||||
|
return .destinationColor
|
||||||
|
case SPINE_BLEND_MODE_SCREEN:
|
||||||
|
return .one
|
||||||
|
default:
|
||||||
|
return .one // Should never be called
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var destinationRGBBlendFactor: MTLBlendFactor {
|
||||||
|
switch self {
|
||||||
|
case SPINE_BLEND_MODE_NORMAL, SPINE_BLEND_MODE_ADDITIVE:
|
||||||
|
return .oneMinusSourceAlpha
|
||||||
|
case SPINE_BLEND_MODE_MULTIPLY:
|
||||||
|
return .one
|
||||||
|
case SPINE_BLEND_MODE_SCREEN:
|
||||||
|
return .oneMinusSourceColor
|
||||||
|
default:
|
||||||
|
return .one // Should never be called
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
spine-ios/Sources/Spine/Metal/SpineShaders.metal
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
typedef enum SpineVertexInputIndex {
|
||||||
|
SpineVertexInputIndexVertices = 0,
|
||||||
|
SpineVertexInputIndexTransform = 1,
|
||||||
|
SpineVertexInputIndexViewportSize = 2,
|
||||||
|
} SpineVertexInputIndex;
|
||||||
|
|
||||||
|
typedef enum SpineTextureIndex {
|
||||||
|
SpineTextureIndexBaseColor = 0,
|
||||||
|
} SpineTextureIndex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 position;
|
||||||
|
vector_float4 color;
|
||||||
|
vector_float2 uv;
|
||||||
|
} SpineVertex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 translation;
|
||||||
|
vector_float2 scale;
|
||||||
|
vector_float2 offset;
|
||||||
|
} SpineTransform;
|
||||||
|
|
||||||
|
struct RasterizerData {
|
||||||
|
float4 position [[position]];
|
||||||
|
float4 color;
|
||||||
|
float2 textureCoordinate;
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex RasterizerData
|
||||||
|
vertexShader(uint vertexID [[vertex_id]],
|
||||||
|
constant SpineVertex *vertices [[buffer(SpineVertexInputIndexVertices)]],
|
||||||
|
constant SpineTransform *transform [[buffer(SpineVertexInputIndexTransform)]],
|
||||||
|
constant vector_uint2 *viewportSizePointer [[buffer(SpineVertexInputIndexViewportSize)]])
|
||||||
|
{
|
||||||
|
RasterizerData out;
|
||||||
|
|
||||||
|
float2 pixelSpacePosition = vertices[vertexID].position.xy;
|
||||||
|
|
||||||
|
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
|
||||||
|
|
||||||
|
out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
out.position.xy = pixelSpacePosition;
|
||||||
|
out.position.xy *= transform->scale;
|
||||||
|
out.position.xy += transform->translation * transform->scale + transform->offset;
|
||||||
|
out.position.xy /= viewportSize / 2;
|
||||||
|
out.position.y *= -1;
|
||||||
|
|
||||||
|
out.color = vertices[vertexID].color;
|
||||||
|
|
||||||
|
out.textureCoordinate = vertices[vertexID].uv;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4
|
||||||
|
fragmentShader(RasterizerData in [[stage_in]],
|
||||||
|
texture2d<half> colorTexture [[ texture(SpineTextureIndexBaseColor) ]])
|
||||||
|
{
|
||||||
|
constexpr sampler textureSampler (mag_filter::nearest,
|
||||||
|
min_filter::nearest);
|
||||||
|
|
||||||
|
const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
|
||||||
|
|
||||||
|
return float4(colorSample) * in.color;
|
||||||
|
}
|
||||||
145
spine-ios/Sources/Spine/SkeletonDrawableWrapper.swift
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import Foundation
|
||||||
|
import Spine
|
||||||
|
import SpineCppLite
|
||||||
|
import CoreGraphics
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/// A ``SkeletonDrawableWrapper`` with ``SkeletonDrawable`` bundle loading, updating, and rendering an ``Atlas``, ``Skeleton``, and ``AnimationState``
|
||||||
|
/// into a single easy to use class.
|
||||||
|
///
|
||||||
|
/// Use the ``SkeletonDrawableWrapper/fromBundle(atlasFileName:skeletonFileName:bundle:)``, ``SkeletonDrawableWrapper/fromFile(atlasFile:skeletonFile:)``, or ``SkeletonDrawableWrapper/fromHttp(atlasURL:skeletonURL:)`` methods to construct a ``SkeletonDrawableWrapper``. To have
|
||||||
|
/// multiple skeleton drawable wrapper instances share the same ``Atlas`` and ``SkeletonData``, use the constructor.
|
||||||
|
///
|
||||||
|
/// You can then directly access the `skeletonDrawable` and with it `atlas`, `skeletonData`, `skeleton`, `animationStateData`, `animationState` and `animationStateWrapper`.
|
||||||
|
/// to query and animate the skeleton. Use the ``AnimationStateWrapper`` to queue animations on one or more tracks
|
||||||
|
/// via ``AnimationState/setAnimation(trackIndex:animation:loop:)`` or ``AnimationState/addAnimation(trackIndex:animation:loop:delay:)``.
|
||||||
|
///
|
||||||
|
/// To update the ``AnimationState`` and apply it to the ``Skeleton`` call the ``AnimationStateWrapper/update`` function, providing it
|
||||||
|
/// a delta time in seconds to advance the animations.
|
||||||
|
///
|
||||||
|
/// To render the current pose of the ``Skeleton`` as a `CGImage`, use ``SkeletonDrawableWrapper/renderToImage(size:backgroundColor:scaleFactor:)``.
|
||||||
|
///
|
||||||
|
/// When the skeleton drawable is no longer needed, call the ``SkeletonDrawableWrapper/dispose()`` method to release its resources. If
|
||||||
|
/// the skeleton drawable was constructed from a shared ``Atlas`` and ``SkeletonData``, make sure to dispose the
|
||||||
|
/// atlas and skeleton data as well, if no skeleton drawable references them anymore.
|
||||||
|
@objc(SpineSkeletonDrawableWrapper)
|
||||||
|
@objcMembers
|
||||||
|
public final class SkeletonDrawableWrapper: NSObject {
|
||||||
|
|
||||||
|
public let atlas: Atlas
|
||||||
|
public let atlasPages: [UIImage]
|
||||||
|
public let skeletonData: SkeletonData
|
||||||
|
|
||||||
|
public let skeletonDrawable: SkeletonDrawable
|
||||||
|
public let skeleton: Skeleton
|
||||||
|
public let animationStateData: AnimationStateData
|
||||||
|
public let animationState: AnimationState
|
||||||
|
public let animationStateWrapper: AnimationStateWrapper
|
||||||
|
|
||||||
|
internal var disposed = false
|
||||||
|
|
||||||
|
/// Constructs a new skeleton drawable from the `atlasFileName` and `skeletonFileName` from the `main` bundle
|
||||||
|
/// or the optionally provided `bundle`.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the data could not be loaded.
|
||||||
|
public static func fromBundle(atlasFileName: String, skeletonFileName: String, bundle: Bundle = .main) async throws -> SkeletonDrawableWrapper {
|
||||||
|
let atlasAndPages = try await Atlas.fromBundle(atlasFileName, bundle: bundle)
|
||||||
|
let skeletonData = try await SkeletonData.fromBundle(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonFileName: skeletonFileName,
|
||||||
|
bundle: bundle
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new skeleton drawable from the `atlasFile` and `skeletonFile`.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the data could not be loaded.
|
||||||
|
public static func fromFile(atlasFile: URL, skeletonFile: URL) async throws -> SkeletonDrawableWrapper {
|
||||||
|
let atlasAndPages = try await Atlas.fromFile(atlasFile)
|
||||||
|
let skeletonData = try await SkeletonData.fromFile(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonFile: skeletonFile
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new skeleton drawable wrapper from the http `atlasUrl` and `skeletonUrl`.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the data could not be loaded.
|
||||||
|
public static func fromHttp(atlasURL: URL, skeletonURL: URL) async throws -> SkeletonDrawableWrapper {
|
||||||
|
let atlasAndPages = try await Atlas.fromHttp(atlasURL)
|
||||||
|
let skeletonData = try await SkeletonData.fromHttp(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonURL: skeletonURL
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(atlas: Atlas, atlasPages: [UIImage], skeletonData: SkeletonData) throws {
|
||||||
|
self.atlas = atlas
|
||||||
|
self.atlasPages = atlasPages
|
||||||
|
self.skeletonData = skeletonData
|
||||||
|
|
||||||
|
guard let nativeSkeletonDrawable = spine_skeleton_drawable_create(skeletonData.wrappee) else {
|
||||||
|
throw "Could not load native skeleton drawable"
|
||||||
|
}
|
||||||
|
skeletonDrawable = SkeletonDrawable(nativeSkeletonDrawable)
|
||||||
|
|
||||||
|
guard let nativeSkeleton = spine_skeleton_drawable_get_skeleton(skeletonDrawable.wrappee) else {
|
||||||
|
throw "Could not load native skeleton"
|
||||||
|
}
|
||||||
|
skeleton = Skeleton(nativeSkeleton)
|
||||||
|
|
||||||
|
guard let nativeAnimationStateData = spine_skeleton_drawable_get_animation_state_data(skeletonDrawable.wrappee) else {
|
||||||
|
throw "Could not load native animation state data"
|
||||||
|
}
|
||||||
|
animationStateData = AnimationStateData(nativeAnimationStateData)
|
||||||
|
|
||||||
|
guard let nativeAnimationState = spine_skeleton_drawable_get_animation_state(skeletonDrawable.wrappee) else {
|
||||||
|
throw "Could not load native animation state"
|
||||||
|
}
|
||||||
|
animationState = AnimationState(nativeAnimationState)
|
||||||
|
animationStateWrapper = AnimationStateWrapper(
|
||||||
|
animationState: animationState,
|
||||||
|
aninationStateEvents: skeletonDrawable.animationStateEvents
|
||||||
|
)
|
||||||
|
skeleton.updateWorldTransform(physics: SPINE_PHYSICS_NONE)
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the ``AnimationState`` using the `delta` time given in seconds, applies the
|
||||||
|
/// animation state to the ``Skeleton`` and updates the world transforms of the skeleton
|
||||||
|
/// to calculate its current pose.
|
||||||
|
public func update(delta: Float) {
|
||||||
|
if disposed { return }
|
||||||
|
|
||||||
|
animationStateWrapper.update(delta: delta)
|
||||||
|
animationState.apply(skeleton: skeleton)
|
||||||
|
|
||||||
|
skeleton.update(delta: delta)
|
||||||
|
skeleton.updateWorldTransform(physics: SPINE_PHYSICS_UPDATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
if disposed { return }
|
||||||
|
disposed = true
|
||||||
|
|
||||||
|
atlas.dispose()
|
||||||
|
skeletonData.dispose()
|
||||||
|
|
||||||
|
skeletonDrawable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
315
spine-ios/Sources/Spine/Spine.Generated+Extensions.swift
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import SpineCppLite
|
||||||
|
|
||||||
|
public var version: String {
|
||||||
|
return "\(majorVersion).\(minorVersion)"
|
||||||
|
}
|
||||||
|
|
||||||
|
public var majorVersion: Int {
|
||||||
|
return Int(spine_major_version())
|
||||||
|
}
|
||||||
|
|
||||||
|
public var minorVersion: Int {
|
||||||
|
return Int(spine_minor_version())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ``Atlas`` data loaded from a `.atlas` file and its corresponding `.png` files. For each atlas image,
|
||||||
|
/// a corresponding `UIImage` is constructed, which is used when rendering a skeleton
|
||||||
|
/// that uses this atlas.
|
||||||
|
///
|
||||||
|
/// Use the static methods ``Atlas/fromBundle(_:bundle:)``, ``Atlas/fromFile(_:)``, and ``Atlas/fromHttp(_:)`` to load an atlas. Call ``Atlas/dispose()`
|
||||||
|
/// when the atlas is no longer in use to release its resources.
|
||||||
|
public extension Atlas {
|
||||||
|
|
||||||
|
/// Loads an ``Atlas`` from the file with name `atlasFileName` in the `main` bundle or the optionally provided [bundle].
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the atlas could not be loaded.
|
||||||
|
static func fromBundle(_ atlasFileName: String, bundle: Bundle = .main) async throws -> (Atlas, [UIImage]) {
|
||||||
|
let data = try await FileSource.bundle(fileName: atlasFileName, bundle: bundle).load()
|
||||||
|
return try await Self.fromData(data: data) { name in
|
||||||
|
return try await FileSource.bundle(fileName: name, bundle: bundle).load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads an ``Atlas`` from the file URL `atlasFile`.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the atlas could not be loaded.
|
||||||
|
static func fromFile(_ atlasFile: URL) async throws -> (Atlas, [UIImage]) {
|
||||||
|
let data = try await FileSource.file(atlasFile).load()
|
||||||
|
return try await Self.fromData(data: data) { name in
|
||||||
|
let dir = atlasFile.deletingLastPathComponent()
|
||||||
|
let file = dir.appendingPathComponent(name)
|
||||||
|
return try await FileSource.file(file).load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads an ``Atlas`` from the http URL `atlasURL`.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the atlas could not be loaded.
|
||||||
|
static func fromHttp(_ atlasURL: URL) async throws -> (Atlas, [UIImage]) {
|
||||||
|
let data = try await FileSource.http(atlasURL).load()
|
||||||
|
return try await Self.fromData(data: data) { name in
|
||||||
|
let dir = atlasURL.deletingLastPathComponent()
|
||||||
|
let file = dir.appendingPathComponent(name)
|
||||||
|
return try await FileSource.http(file).load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func fromData(data: Data, loadFile: (_ name: String) async throws -> Data) async throws -> (Atlas, [UIImage]) {
|
||||||
|
guard let atlasData = String(data: data, encoding: .utf8) as? NSString else {
|
||||||
|
throw "Couldn't read atlas bytes as utf8 string"
|
||||||
|
}
|
||||||
|
let atlasDataNative = UnsafeMutablePointer<CChar>(mutating: atlasData.utf8String)
|
||||||
|
guard let atlas = spine_atlas_load(atlasDataNative) else {
|
||||||
|
throw "Couldn't load atlas data"
|
||||||
|
}
|
||||||
|
|
||||||
|
if let error = spine_atlas_get_error(atlas) {
|
||||||
|
let message = String(cString: error)
|
||||||
|
spine_atlas_dispose(atlas)
|
||||||
|
throw "Couldn't load atlas: \(message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
var atlasPages = [UIImage]()
|
||||||
|
let numImagePaths = spine_atlas_get_num_image_paths(atlas);
|
||||||
|
|
||||||
|
for i in 0..<numImagePaths {
|
||||||
|
guard let atlasPageFilePointer = spine_atlas_get_image_path(atlas, i) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let atlasPageFile = String(cString: atlasPageFilePointer)
|
||||||
|
let imageData = try await loadFile(atlasPageFile)
|
||||||
|
guard let image = UIImage(data: imageData) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
atlasPages.append(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Atlas(atlas), atlasPages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension SkeletonData {
|
||||||
|
|
||||||
|
/// Loads a ``SkeletonData`` from the file with name `skeletonFileName` in the main bundle or the optionally provided `bundle`.
|
||||||
|
/// Uses the provided ``Atlas`` to resolve attachment images.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the skeleton data could not be loaded.
|
||||||
|
static func fromBundle(atlas: Atlas, skeletonFileName: String, bundle: Bundle = .main) async throws -> SkeletonData {
|
||||||
|
return try fromData(
|
||||||
|
atlas: atlas,
|
||||||
|
data: try await FileSource.bundle(fileName: skeletonFileName, bundle: bundle).load(),
|
||||||
|
isJson: skeletonFileName.hasSuffix(".json")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a ``SkeletonData`` from the file URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the skeleton data could not be loaded.
|
||||||
|
static func fromFile(atlas: Atlas, skeletonFile: URL) async throws -> SkeletonData {
|
||||||
|
return try fromData(
|
||||||
|
atlas: atlas,
|
||||||
|
data: try await FileSource.file(skeletonFile).load(),
|
||||||
|
isJson: skeletonFile.absoluteString.hasSuffix(".json")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a ``SkeletonData`` from the http URL `skeletonFile`. Uses the provided ``Atlas`` to resolve attachment images.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the skeleton data could not be loaded.
|
||||||
|
static func fromHttp(atlas: Atlas, skeletonURL: URL) async throws -> SkeletonData {
|
||||||
|
return try fromData(
|
||||||
|
atlas: atlas,
|
||||||
|
data: try await FileSource.http(skeletonURL).load(),
|
||||||
|
isJson: skeletonURL.absoluteString.hasSuffix(".json")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a ``SkeletonData`` from the ``binary`` skeleton `Data`, using the provided ``Atlas`` to resolve attachment images.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the skeleton data could not be loaded.
|
||||||
|
static func fromData(atlas: Atlas, data: Data) throws -> SkeletonData {
|
||||||
|
let binaryNative = try data.withUnsafeBytes { unsafeBytes in
|
||||||
|
guard let bytes = unsafeBytes.bindMemory(to: UInt8.self).baseAddress else {
|
||||||
|
throw "Couldn't read atlas binary"
|
||||||
|
}
|
||||||
|
return (data: bytes, length: Int32(unsafeBytes.count))
|
||||||
|
}
|
||||||
|
let result = spine_skeleton_data_load_binary(
|
||||||
|
atlas.wrappee,
|
||||||
|
binaryNative.data,
|
||||||
|
binaryNative.length
|
||||||
|
)
|
||||||
|
if let error = spine_skeleton_data_result_get_error(result) {
|
||||||
|
let message = String(cString: error)
|
||||||
|
spine_skeleton_data_result_dispose(result)
|
||||||
|
throw "Couldn't load skeleton data: \(message)"
|
||||||
|
}
|
||||||
|
guard let data = spine_skeleton_data_result_get_data(result) else {
|
||||||
|
throw "Couldn't load skeleton data from result"
|
||||||
|
}
|
||||||
|
spine_skeleton_data_result_dispose(result)
|
||||||
|
return SkeletonData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a ``SkeletonData`` from the `json` string, using the provided ``Atlas`` to resolve attachment
|
||||||
|
/// images.
|
||||||
|
///
|
||||||
|
/// Throws an `Error` in case the atlas could not be loaded.
|
||||||
|
static func fromJson(atlas: Atlas, json: String) throws -> SkeletonData {
|
||||||
|
let jsonNative = UnsafeMutablePointer<CChar>(mutating: (json as NSString).utf8String)
|
||||||
|
guard let result = spine_skeleton_data_load_json(atlas.wrappee, jsonNative) else {
|
||||||
|
throw "Couldn't load skeleton data json"
|
||||||
|
}
|
||||||
|
if let error = spine_skeleton_data_result_get_error(result) {
|
||||||
|
let message = String(cString: error)
|
||||||
|
spine_skeleton_data_result_dispose(result)
|
||||||
|
throw "Couldn't load skeleton data: \(message)"
|
||||||
|
}
|
||||||
|
guard let data = spine_skeleton_data_result_get_data(result) else {
|
||||||
|
throw "Couldn't load skeleton data from result"
|
||||||
|
}
|
||||||
|
spine_skeleton_data_result_dispose(result)
|
||||||
|
return SkeletonData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func fromData(atlas: Atlas, data: Data, isJson: Bool) throws -> SkeletonData {
|
||||||
|
if isJson {
|
||||||
|
guard let json = String(data: data, encoding: .utf8) else {
|
||||||
|
throw "Couldn't read skeleton data json string"
|
||||||
|
}
|
||||||
|
return try fromJson(atlas: atlas, json: json)
|
||||||
|
} else {
|
||||||
|
return try fromData(atlas: atlas, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal extension SkeletonDrawable {
|
||||||
|
|
||||||
|
func render() -> [RenderCommand] {
|
||||||
|
var commands = [RenderCommand]()
|
||||||
|
if disposed { return commands }
|
||||||
|
|
||||||
|
var nativeCmd = spine_skeleton_drawable_render(wrappee)
|
||||||
|
repeat {
|
||||||
|
if let ncmd = nativeCmd {
|
||||||
|
commands.append(RenderCommand(ncmd))
|
||||||
|
nativeCmd = spine_render_command_get_next(ncmd)
|
||||||
|
} else {
|
||||||
|
nativeCmd = nil
|
||||||
|
}
|
||||||
|
} while (nativeCmd != nil)
|
||||||
|
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal extension RenderCommand {
|
||||||
|
|
||||||
|
var numVertices: Int {
|
||||||
|
Int(spine_render_command_get_num_vertices(wrappee))
|
||||||
|
}
|
||||||
|
|
||||||
|
func positions(numVertices: Int) -> [Float] {
|
||||||
|
let num = numVertices * 2
|
||||||
|
let ptr = spine_render_command_get_positions(wrappee)
|
||||||
|
return (0..<num).compactMap { ptr?[$0] }
|
||||||
|
}
|
||||||
|
|
||||||
|
func uvs(numVertices: Int) -> [Float] {
|
||||||
|
let num = numVertices * 2
|
||||||
|
let ptr = spine_render_command_get_uvs(wrappee)
|
||||||
|
return (0..<num).compactMap { ptr?[$0] }
|
||||||
|
}
|
||||||
|
|
||||||
|
func colors(numVertices: Int) ->[Int32] {
|
||||||
|
let num = numVertices
|
||||||
|
let ptr = spine_render_command_get_colors(wrappee)
|
||||||
|
return (0..<num).compactMap { ptr?[$0] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Skin {
|
||||||
|
|
||||||
|
/// Constructs a new empty ``Skin`` using the given `name`. Skins constructed this way must be manually disposed via the `dispose` method
|
||||||
|
/// if they are no longer used.
|
||||||
|
static func create(name: String) -> Skin {
|
||||||
|
return Skin(spine_skin_create(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
|
||||||
|
public extension CGRect {
|
||||||
|
|
||||||
|
/// Construct a `CGRect` from ``Bounds``
|
||||||
|
init(bounds: Bounds) {
|
||||||
|
self = CGRect(
|
||||||
|
x: CGFloat(bounds.x),
|
||||||
|
y: CGFloat(bounds.y),
|
||||||
|
width: CGFloat(bounds.width),
|
||||||
|
height: CGFloat(bounds.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum FileSource {
|
||||||
|
case bundle(fileName: String, bundle: Bundle = .main)
|
||||||
|
case file(URL)
|
||||||
|
case http(URL)
|
||||||
|
|
||||||
|
internal func load() async throws -> Data {
|
||||||
|
switch self {
|
||||||
|
case .bundle(let fileName, let bundle):
|
||||||
|
let components = fileName.split(separator: ".")
|
||||||
|
guard components.count > 1, let ext = components.last else {
|
||||||
|
throw "Provide both file name and file extension"
|
||||||
|
}
|
||||||
|
let name = components.dropLast(1).joined(separator: ".")
|
||||||
|
|
||||||
|
guard let fileUrl = bundle.url(forResource: name, withExtension: String(ext)) else {
|
||||||
|
throw "Could not load file with name \(name) from bundle"
|
||||||
|
}
|
||||||
|
return try Data(contentsOf: fileUrl, options: [])
|
||||||
|
case .file(let fileUrl):
|
||||||
|
return try Data(contentsOf: fileUrl, options: [])
|
||||||
|
case .http(let url):
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
let (temp, response) = try await URLSession.shared.download(from: url)
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
return try Data(contentsOf: temp, options: [])
|
||||||
|
} else {
|
||||||
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
let task = URLSession.shared.downloadTask(with: url) { temp, response, error in
|
||||||
|
if let error {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
} else {
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||||
|
continuation.resume(throwing: URLError(.badServerResponse))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let temp else {
|
||||||
|
continuation.resume(throwing: "Could not download file.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
continuation.resume(returning: try Data(contentsOf: temp, options: []))
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String: Error {
|
||||||
|
|
||||||
|
}
|
||||||
3721
spine-ios/Sources/Spine/Spine.Generated.swift
Normal file
215
spine-ios/Sources/Spine/SpineController.swift
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import Foundation
|
||||||
|
import CoreGraphics
|
||||||
|
import QuartzCore
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public typealias SpineControllerCallback = (_ controller: SpineController) -> Void
|
||||||
|
|
||||||
|
/// Controls how the skeleton of a ``SpineUIView`` is animated and rendered.
|
||||||
|
///
|
||||||
|
/// Upon initialization of a ``SpineUIView`` the provided `onInitialized` callback method is called once. This method can be used
|
||||||
|
/// to setup the initial animation(s) of the skeleton, among other things.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the ``SpineUIView`` is rendered at the screen refresh rate. In each frame,
|
||||||
|
/// the ``AnimationState`` is updated and applied to the ``Skeleton``.
|
||||||
|
///
|
||||||
|
/// Next the optionally provided method `onBeforeUpdateWorldTransforms` is called, which can modify the
|
||||||
|
/// skeleton before its current pose is calculated using ``Skeleton/updateWorldTransform(physics:)``. After
|
||||||
|
/// ``Skeleton.updateWorldTransforms`` has completed, the optional `onAfterUpdateWorldTransforms` method is
|
||||||
|
/// called, which can modify the current pose before rendering the skeleton.
|
||||||
|
///
|
||||||
|
/// Before the skeleton's current pose is rendered by the ``SpineUIView`` the optional `onBeforePaint` is called,
|
||||||
|
/// which allows rendering backgrounds or other objects that should go behind the skeleton in your view hierarchy. The
|
||||||
|
/// ``SpineUIView`` then renderes the skeleton's current pose, and finally calls the optional `onAfterPaint`, after which you
|
||||||
|
/// can render additional objects on top of the skeleton in your view hierarchy.
|
||||||
|
///
|
||||||
|
/// The underlying ``Atlas``, ``SkeletonData``, ``Skeleton``, ``AnimationStateData``, ``AnimationState``, and ``SkeletonDrawable``
|
||||||
|
/// can be accessed through their respective getters to inspect and/or modify the skeleton and its associated data. Accessing
|
||||||
|
/// this data is only allowed if the ``SpineUIView`` and its data have been initialized and have not been disposed yet.
|
||||||
|
///
|
||||||
|
/// By default, the view updates and renders the skeleton every frame. The `pause` method can be used to pause updating
|
||||||
|
/// and rendering the skeleton. The `resume` method resumes updating and rendering the skeleton. The `isPlaying` property
|
||||||
|
/// reports the current state.
|
||||||
|
///
|
||||||
|
/// Per default, ``SkeletonDrawableWrapper`` is disposed when ``SpineController`` is deinitialized. You can disable this behaviour with the ``disposeDrawableOnDeInit`` contructor parameter.
|
||||||
|
@objcMembers
|
||||||
|
public final class SpineController: NSObject, ObservableObject {
|
||||||
|
|
||||||
|
public internal(set) var drawable: SkeletonDrawableWrapper!
|
||||||
|
|
||||||
|
private let onInitialized: SpineControllerCallback?
|
||||||
|
private let onBeforeUpdateWorldTransforms: SpineControllerCallback?
|
||||||
|
private let onAfterUpdateWorldTransforms: SpineControllerCallback?
|
||||||
|
private let onBeforePaint: SpineControllerCallback?
|
||||||
|
private let onAfterPaint: SpineControllerCallback?
|
||||||
|
private let disposeDrawableOnDeInit: Bool
|
||||||
|
|
||||||
|
private var scaleX: CGFloat = 1
|
||||||
|
private var scaleY: CGFloat = 1
|
||||||
|
private var offsetX: CGFloat = 0
|
||||||
|
private var offsetY: CGFloat = 0
|
||||||
|
|
||||||
|
@Published
|
||||||
|
public private(set) var isPlaying: Bool = true
|
||||||
|
|
||||||
|
@Published
|
||||||
|
public private(set) var viewSize: CGSize = .zero
|
||||||
|
|
||||||
|
/// Constructs a new ``SpineUIview`` controller. See the class documentation of ``SpineWidgetController`` for information on
|
||||||
|
/// the optional arguments.
|
||||||
|
public init(
|
||||||
|
onInitialized: SpineControllerCallback? = nil,
|
||||||
|
onBeforeUpdateWorldTransforms: SpineControllerCallback? = nil,
|
||||||
|
onAfterUpdateWorldTransforms: SpineControllerCallback? = nil,
|
||||||
|
onBeforePaint: SpineControllerCallback? = nil,
|
||||||
|
onAfterPaint: SpineControllerCallback? = nil,
|
||||||
|
disposeDrawableOnDeInit: Bool = true
|
||||||
|
) {
|
||||||
|
self.onInitialized = onInitialized
|
||||||
|
self.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms
|
||||||
|
self.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms
|
||||||
|
self.onBeforePaint = onBeforePaint
|
||||||
|
self.onAfterPaint = onAfterPaint
|
||||||
|
self.disposeDrawableOnDeInit = disposeDrawableOnDeInit
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if disposeDrawableOnDeInit {
|
||||||
|
drawable?.dispose() // TODO move drawable out of view?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ``Atlas`` from which images to render the skeleton are sourced.
|
||||||
|
public var atlas: Atlas {
|
||||||
|
drawable.atlas
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The setup-pose data used by the skeleton.
|
||||||
|
public var skeletonData: SkeletonData {
|
||||||
|
drawable.skeletonData
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ``Skeleton``
|
||||||
|
public var skeleton: Skeleton {
|
||||||
|
drawable.skeleton
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The mixing information used by the ``AnimationState``
|
||||||
|
public var animationStateData: AnimationStateData {
|
||||||
|
drawable.animationStateData
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ``AnimationState`` used to manage animations that are being applied to the
|
||||||
|
/// skeleton.
|
||||||
|
public var animationState: AnimationState {
|
||||||
|
drawable.animationState
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ``AnimationStateWrapper`` used to hold ``AnimationState``, register ``AnimationStateListener`` and call ``AnimationStateWrapper/update(delta:)``
|
||||||
|
public var animationStateWrapper: AnimationStateWrapper {
|
||||||
|
drawable.animationStateWrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms the coordinates given in the ``SpineUIView`` coordinate system in `position` to
|
||||||
|
/// the skeleton coordinate system. See the `IKFollowing.swift` example how to use this
|
||||||
|
/// to move a bone based on user touch input.
|
||||||
|
public func toSkeletonCoordinates(position: CGPoint) -> CGPoint {
|
||||||
|
let x = position.x;
|
||||||
|
let y = position.y;
|
||||||
|
return CGPoint(
|
||||||
|
x: (x - viewSize.width / 2) / scaleX - offsetX,
|
||||||
|
y: (y - viewSize.height / 2) / scaleY - offsetY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms the coordinates given in skeleton coordinate system to
|
||||||
|
/// the the ``SpineUIView`` coordinates. See the `DebugRendering.swift` example hot to use this to draw rectangles over skeleton bones for debugging purposes.
|
||||||
|
public func fromSkeletonCoordinates(position: CGPoint) -> CGPoint {
|
||||||
|
let x = position.x;
|
||||||
|
let y = position.y;
|
||||||
|
return CGPoint(
|
||||||
|
x: (x + offsetX) * scaleX,
|
||||||
|
y: (y + offsetY) * scaleY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pauses updating and rendering the skeleton.
|
||||||
|
public func pause() {
|
||||||
|
isPlaying = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resumes updating and rendering the skeleton.
|
||||||
|
public func resume() {
|
||||||
|
isPlaying = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func load(atlasFile: String, skeletonFile: String, bundle: Bundle = .main) async throws {
|
||||||
|
let atlasAndPages = try await Atlas.fromBundle(atlasFile, bundle: bundle)
|
||||||
|
let skeletonData = try await SkeletonData.fromBundle(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonFileName: skeletonFile,
|
||||||
|
bundle: bundle
|
||||||
|
)
|
||||||
|
try await MainActor.run {
|
||||||
|
let skeletonDrawableWrapper = try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
self.drawable = skeletonDrawableWrapper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func initialize() {
|
||||||
|
onInitialized?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpineController: SpineRendererDelegate {
|
||||||
|
|
||||||
|
func spineRendererWillDraw(_ spineRenderer: SpineRenderer) {
|
||||||
|
onBeforePaint?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func spineRendererDidDraw(_ spineRenderer: SpineRenderer) {
|
||||||
|
onAfterPaint?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer, scaleX: CGFloat, scaleY: CGFloat, offsetX: CGFloat, offsetY: CGFloat, size: CGSize) {
|
||||||
|
self.scaleX = scaleX
|
||||||
|
self.scaleY = scaleY
|
||||||
|
self.offsetX = offsetX
|
||||||
|
self.offsetY = offsetY
|
||||||
|
self.viewSize = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpineController: SpineRendererDataSource {
|
||||||
|
|
||||||
|
func spineRendererWillUpdate(_ spineRenderer: SpineRenderer) {
|
||||||
|
onBeforeUpdateWorldTransforms?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func spineRendererDidUpdate(_ spineRenderer: SpineRenderer) {
|
||||||
|
onAfterUpdateWorldTransforms?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func spineRenderer(_ spineRenderer: SpineRenderer, needsUpdate delta: TimeInterval) {
|
||||||
|
drawable?.update(delta: Float(delta))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPlaying(_ spineRenderer: SpineRenderer) -> Bool {
|
||||||
|
return isPlaying
|
||||||
|
}
|
||||||
|
|
||||||
|
func skeletonDrawable(_ spineRenderer: SpineRenderer) -> SkeletonDrawableWrapper {
|
||||||
|
return drawable
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCommands(_ spineRenderer: SpineRenderer) -> [RenderCommand] {
|
||||||
|
return drawable?.skeletonDrawable.render() ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
280
spine-ios/Sources/Spine/SpineUIView.swift
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
import UIKit
|
||||||
|
import MetalKit
|
||||||
|
|
||||||
|
/// A ``UIView`` to display a Spine skeleton. The skeleton can be loaded from a bundle, local files, http, or a pre-loaded ``SkeletonDrawableWrapper``.
|
||||||
|
///
|
||||||
|
/// The skeleton displayed by a ``SpineUIView`` can be controlled via a ``SpineController``.
|
||||||
|
///
|
||||||
|
/// The size of the widget can be derived from the bounds provided by a ``BoundsProvider``. If the view is not sized by the bounds
|
||||||
|
/// computed by the ``BoundsProvider``, the widget will use the computed bounds to fit the skeleton inside the view's dimensions.
|
||||||
|
///
|
||||||
|
/// This is a direct subclass of ``MTKView`` and is using `Metal` to render the skeleton.
|
||||||
|
@objc
|
||||||
|
public final class SpineUIView: MTKView {
|
||||||
|
|
||||||
|
let controller: SpineController
|
||||||
|
let mode: Spine.ContentMode
|
||||||
|
let alignment: Spine.Alignment
|
||||||
|
let boundsProvider: BoundsProvider
|
||||||
|
|
||||||
|
internal var computedBounds: CGRect = .zero
|
||||||
|
internal var renderer: SpineRenderer?
|
||||||
|
|
||||||
|
@objc internal init(
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.controller = controller
|
||||||
|
self.mode = mode
|
||||||
|
self.alignment = alignment
|
||||||
|
self.boundsProvider = boundsProvider
|
||||||
|
|
||||||
|
super.init(frame: .zero, device: SpineObjects.shared.device)
|
||||||
|
clearColor = MTLClearColor(backgroundColor)
|
||||||
|
isOpaque = backgroundColor != .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An initializer that constructs a new ``SpineUIView`` from a ``SpineViewSource``.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - from: Specifies the ``SpineViewSource`` from which to load `atlas` and `skeleton` data.
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
/// - backgroundColor: The background color of the view. Per defaut, `UIColor.clear` is used
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineUIView``.
|
||||||
|
public convenience init(
|
||||||
|
from source: SpineViewSource,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.init(controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
|
||||||
|
Task.detached(priority: .high) {
|
||||||
|
do {
|
||||||
|
let drawable = try await source.loadDrawable()
|
||||||
|
try await self.load(drawable: drawable)
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience initializer that constructs a new ``SpineUIView`` from bundled files.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - atlasFileName: Specifies the `.atlas` file to be loaded for the images used to render the skeleton
|
||||||
|
/// - skeletonFileName: Specifies either a Skeleton `.json` or `.skel` file containing the skeleton data
|
||||||
|
/// - bundle: Specifies from which bundle to load the files. Per default, it is `Bundle.main`
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
/// - backgroundColor: The background color of the view. Per defaut, `UIColor.clear` is used
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineUIView``.
|
||||||
|
@objc public convenience init(
|
||||||
|
atlasFileName: String,
|
||||||
|
skeletonFileName: String,
|
||||||
|
bundle: Bundle = .main,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.init(from: .bundle(atlasFileName: atlasFileName, skeletonFileName: skeletonFileName, bundle: bundle), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience initializer that constructs a new ``SpineUIView`` from file URLs.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - atlasFile: Specifies the `.atlas` file to be loaded for the images used to render the skeleton
|
||||||
|
/// - skeletonFile: Specifies either a Skeleton `.json` or `.skel` file containing the skeleton data
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
/// - backgroundColor: The background color of the view. Per defaut, `UIColor.clear` is used
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineUIView``.
|
||||||
|
@objc public convenience init(
|
||||||
|
atlasFile: URL,
|
||||||
|
skeletonFile: URL,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.init(from: .file(atlasFile: atlasFile, skeletonFile: skeletonFile), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience initializer that constructs a new ``SpineUIView`` from HTTP.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - atlasURL: Specifies the `.atlas` file http URL to be loaded for the images used to render the skeleton
|
||||||
|
/// - skeletonURL: Specifies either a Skeleton `.json` or `.skel` file http URL containing the skeleton data
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
/// - backgroundColor: The background color of the view. Per defaut, `UIColor.clear` is used
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineUIView``.
|
||||||
|
@objc public convenience init(
|
||||||
|
atlasURL: URL,
|
||||||
|
skeletonURL: URL,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.init(from: .http(atlasURL: atlasURL, skeletonURL: skeletonURL), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience initializer that constructs a new ``SpineUIView`` with a ``SkeletonDrawableWrapper``.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - drawable: The ``SkeletonDrawableWrapper`` provided directly to the ``SpineController``
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineUIView``.
|
||||||
|
@objc public convenience init(
|
||||||
|
drawable: SkeletonDrawableWrapper,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear
|
||||||
|
) {
|
||||||
|
self.init(from: .drawable(drawable), controller: controller, mode: mode, alignment: alignment, boundsProvider: boundsProvider, backgroundColor: backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override init(frame frameRect: CGRect, device: MTLDevice?) {
|
||||||
|
fatalError("init(frame: device:) has not been implemented. Use init() instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal required init(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented. Use init() instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU resources.
|
||||||
|
public var isRendering: Bool {
|
||||||
|
get { !super.isPaused }
|
||||||
|
set {
|
||||||
|
super.isPaused = !newValue
|
||||||
|
if !isPaused {
|
||||||
|
renderer?.lastDraw = CACurrentMediaTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpineUIView {
|
||||||
|
|
||||||
|
internal func load(drawable: SkeletonDrawableWrapper) throws {
|
||||||
|
controller.drawable = drawable
|
||||||
|
computedBounds = boundsProvider.computeBounds(for: drawable)
|
||||||
|
try initRenderer(
|
||||||
|
atlasPages: controller.drawable.atlasPages
|
||||||
|
)
|
||||||
|
controller.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initRenderer(atlasPages: [UIImage]) throws {
|
||||||
|
renderer = try SpineRenderer(
|
||||||
|
device: SpineObjects.shared.device,
|
||||||
|
commandQueue: SpineObjects.shared.commandQueue,
|
||||||
|
pixelFormat: colorPixelFormat,
|
||||||
|
atlasPages: atlasPages,
|
||||||
|
pma: controller.drawable.atlas.isPma
|
||||||
|
)
|
||||||
|
renderer?.delegate = controller
|
||||||
|
renderer?.dataSource = controller
|
||||||
|
renderer?.mtkView(self, drawableSizeWillChange: drawableSize)
|
||||||
|
delegate = renderer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines from which source the ``SkeletonDrawableWrapper`` holding `atlas` and `skeleton` data is loaded.
|
||||||
|
///
|
||||||
|
/// The following sources are supported:
|
||||||
|
/// - bundle: Provide file names of your `atlas` and `skeleton` files, including the file extension, to load them from a ``Bundle``. Per defailt, ``Bundle.main`` is used.
|
||||||
|
/// - file: Provide file URLs to the `atlas` and `skeleton` files.
|
||||||
|
/// - http: Provide http URLs to the `atlas` and `skeleton` files.
|
||||||
|
/// - drawable: Directly provide a ``SkeletonDrawableWrapper``
|
||||||
|
///
|
||||||
|
public enum SpineViewSource {
|
||||||
|
case bundle(atlasFileName: String, skeletonFileName: String, bundle: Bundle = .main)
|
||||||
|
case file(atlasFile: URL, skeletonFile: URL)
|
||||||
|
case http(atlasURL: URL, skeletonURL: URL)
|
||||||
|
case drawable(SkeletonDrawableWrapper)
|
||||||
|
|
||||||
|
internal func loadDrawable() async throws -> SkeletonDrawableWrapper {
|
||||||
|
switch self {
|
||||||
|
case .bundle(let atlasFileName, let skeletonFileName, let bundle):
|
||||||
|
let atlasAndPages = try await Atlas.fromBundle(atlasFileName, bundle: bundle)
|
||||||
|
let skeletonData = try await SkeletonData.fromBundle(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonFileName: skeletonFileName,
|
||||||
|
bundle: bundle
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
case .file(let atlasFile, let skeletonFile):
|
||||||
|
let atlasAndPages = try await Atlas.fromFile(atlasFile)
|
||||||
|
let skeletonData = try await SkeletonData.fromFile(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonFile: skeletonFile
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
case .http(let atlasURL, let skeletonURL):
|
||||||
|
let atlasAndPages = try await Atlas.fromHttp(atlasURL)
|
||||||
|
let skeletonData = try await SkeletonData.fromHttp(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
skeletonURL: skeletonURL
|
||||||
|
)
|
||||||
|
return try SkeletonDrawableWrapper(
|
||||||
|
atlas: atlasAndPages.0,
|
||||||
|
atlasPages: atlasAndPages.1,
|
||||||
|
skeletonData: skeletonData
|
||||||
|
)
|
||||||
|
case .drawable(let skeletonDrawableWrapper):
|
||||||
|
return skeletonDrawableWrapper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
spine-ios/Sources/Spine/SpineView.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// A `SwiftUI` `View` to display a Spine skeleton. The skeleton can be loaded from a bundle, local files, http, or a pre-loaded ``SkeletonDrawableWrapper``.
|
||||||
|
///
|
||||||
|
/// The skeleton displayed by a ``SpineUIView`` can be controlled via a ``SpineController``.
|
||||||
|
///
|
||||||
|
/// The size of the widget can be derived from the bounds provided by a ``BoundsProvider``. If the view is not sized by the bounds
|
||||||
|
/// computed by the ``BoundsProvider``, the widget will use the computed bounds to fit the skeleton inside the view's dimensions.
|
||||||
|
///
|
||||||
|
/// This is a ``UIViewRepresentable`` of `SpineUIView`.
|
||||||
|
public struct SpineView: UIViewRepresentable {
|
||||||
|
|
||||||
|
public typealias UIViewType = SpineUIView
|
||||||
|
|
||||||
|
private let source: SpineViewSource
|
||||||
|
private let controller: SpineController
|
||||||
|
private let mode: Spine.ContentMode
|
||||||
|
private let alignment: Spine.Alignment
|
||||||
|
private let boundsProvider: BoundsProvider
|
||||||
|
private let backgroundColor: UIColor // Not using `SwiftUI.Color`, as briging to `UIColor` prior iOS 14 might not always work.
|
||||||
|
|
||||||
|
@Binding
|
||||||
|
private var isRendering: Bool?
|
||||||
|
|
||||||
|
/// An initializer that constructs a new ``SpineView`` from a ``SpineViewSource``.
|
||||||
|
///
|
||||||
|
/// After initialization is complete, the provided `controller` is invoked as per the ``SpineController`` semantics, to allow
|
||||||
|
/// modifying how the skeleton inside the widget is animated and rendered.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - from: Specifies the ``SpineViewSource`` from which to load `atlas` and `skeleton` data.
|
||||||
|
/// - controller: The ``SpineController`` used to modify how the skeleton inside the view is animated and rendered.
|
||||||
|
/// - skeletonFileName: Specifies either a Skeleton `.json` or `.skel` file containing the skeleton data
|
||||||
|
/// - bundle: Specifies from which bundle to load the files. Per default, it is `Bundle.main`
|
||||||
|
/// - mode: How the skeleton is fitted inside ``SpineUIView``. Per default, it is `.fit`
|
||||||
|
/// - alignment: How the skeleton is alignment inside ``SpineUIView``. Per default, it is `.center`
|
||||||
|
/// - boundsProvider: The skeleton bounds must be computed via a ``BoundsProvider``. Per default, ``SetupPoseBounds`` is used.
|
||||||
|
/// - backgroundColor: The background color of the view. Per defaut, `UIColor.clear` is used
|
||||||
|
/// - isRendering: Bindgin to disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU resources.
|
||||||
|
///
|
||||||
|
/// - Returns: A new instance of ``SpineView``.
|
||||||
|
public init(
|
||||||
|
from source: SpineViewSource,
|
||||||
|
controller: SpineController = SpineController(),
|
||||||
|
mode: Spine.ContentMode = .fit,
|
||||||
|
alignment: Spine.Alignment = .center,
|
||||||
|
boundsProvider: BoundsProvider = SetupPoseBounds(),
|
||||||
|
backgroundColor: UIColor = .clear,
|
||||||
|
isRendering: Binding<Bool?> = .constant(nil)
|
||||||
|
) {
|
||||||
|
self.source = source
|
||||||
|
self.controller = controller
|
||||||
|
self.mode = mode
|
||||||
|
self.alignment = alignment
|
||||||
|
self.boundsProvider = boundsProvider
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
|
_isRendering = isRendering
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeUIView(context: Context) -> SpineUIView {
|
||||||
|
return SpineUIView(
|
||||||
|
from: source,
|
||||||
|
controller: controller,
|
||||||
|
mode: mode,
|
||||||
|
alignment: alignment,
|
||||||
|
boundsProvider: boundsProvider,
|
||||||
|
backgroundColor: backgroundColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIView(_ uiView: SpineUIView, context: Context) {
|
||||||
|
if let isRendering {
|
||||||
|
uiView.isRendering = isRendering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
spine-ios/Sources/SpineCppLite/include/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module SpineCppLite {
|
||||||
|
header "spine-cpp-lite.h"
|
||||||
|
export *
|
||||||
|
}
|
||||||
1
spine-ios/Sources/SpineCppLite/include/spine
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
./../../../../spine-cpp/spine-cpp/include/spine
|
||||||
1
spine-ios/Sources/SpineCppLite/include/spine-cpp-lite.h
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../../spine-cpp/spine-cpp-lite/spine-cpp-lite.h
|
||||||
1
spine-ios/Sources/SpineCppLite/spine
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
./../../../spine-cpp/spine-cpp/src/spine
|
||||||
1
spine-ios/Sources/SpineCppLite/spine-cpp-lite.cpp
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
./../../../spine-cpp/spine-cpp-lite/spine-cpp-lite.cpp
|
||||||
@ -0,0 +1 @@
|
|||||||
|
#include "SpineShadersStructs.h"
|
||||||
28
spine-ios/Sources/SpineShadersStructs/SpineShadersStructs.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef SpineShadersStructs_h
|
||||||
|
#define SpineShadersStructs_h
|
||||||
|
|
||||||
|
#include <simd/simd.h>
|
||||||
|
|
||||||
|
typedef enum SpineVertexInputIndex {
|
||||||
|
SpineVertexInputIndexVertices = 0,
|
||||||
|
SpineVertexInputIndexTransform = 1,
|
||||||
|
SpineVertexInputIndexViewportSize = 2,
|
||||||
|
} SpineVertexInputIndex;
|
||||||
|
|
||||||
|
typedef enum SpineTextureIndex {
|
||||||
|
SpineTextureIndexBaseColor = 0,
|
||||||
|
} SpineTextureIndex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 position;
|
||||||
|
vector_float4 color;
|
||||||
|
vector_float2 uv;
|
||||||
|
} SpineVertex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 translation;
|
||||||
|
vector_float2 scale;
|
||||||
|
vector_float2 offset;
|
||||||
|
} SpineTransform;
|
||||||
|
|
||||||
|
#endif /* SpineShadersStructs_h */
|
||||||
4
spine-ios/Sources/SpineShadersStructs/module.modulemap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module SpineShadersStructs {
|
||||||
|
header "SpineShadersStructs.h"
|
||||||
|
export *
|
||||||
|
}
|
||||||
4
spine-ios/setup.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
python3 ../spine-cpp/spine-cpp-lite/spine-cpp-lite-codegen.py > Sources/Spine/Spine.Generated.swift
|
||||||