[cpp] Still compute RegionAttachment offsets when the region is null, see #2887

- Includes a massive refactor of the TextureRegion/AtlasRegion hierarchy to be in line with Java implementation
- Exact line by line port of RegionAttachment#updateRegion and MeshAttachment#updateRegion using new region implementations
This commit is contained in:
Mario Zechner 2025-07-15 14:20:49 +02:00
parent 15addc9db5
commit acbcfeb44d
9 changed files with 228 additions and 144 deletions

View File

@ -36,6 +36,8 @@
#include <spine/SpineString.h> #include <spine/SpineString.h>
#include <spine/HasRendererObject.h> #include <spine/HasRendererObject.h>
#include "TextureRegion.h" #include "TextureRegion.h"
#include "spine/MeshAttachment.h"
#include "spine/RegionAttachment.h"
namespace spine { namespace spine {
enum Format { enum Format {
@ -102,21 +104,35 @@ namespace spine {
explicit AtlasPage(const String &inName) : name(inName), format(Format_RGBA8888), explicit AtlasPage(const String &inName) : name(inName), format(Format_RGBA8888),
minFilter(TextureFilter_Nearest), minFilter(TextureFilter_Nearest),
magFilter(TextureFilter_Nearest), uWrap(TextureWrap_ClampToEdge), magFilter(TextureFilter_Nearest), uWrap(TextureWrap_ClampToEdge),
vWrap(TextureWrap_ClampToEdge), width(0), height(0), pma(false), index(0), texture(NULL) { vWrap(TextureWrap_ClampToEdge), width(0), height(0), pma(false), index(0), texture(nullptr) {
} }
}; };
class SP_API AtlasRegion : public TextureRegion { class SP_API AtlasRegion : public TextureRegion {
friend class Atlas; friend class Atlas;
friend class RegionAttachment;
friend class MeshAttachment;
RTTI_DECL
public: public:
AtlasRegion() : TextureRegion(), _page(nullptr), _name(""), _index(0), _x(0), _y(0) {} AtlasRegion() : TextureRegion(), _page(nullptr), _name(""), _index(0), _x(0), _y(0),
_offsetX(0), _offsetY(0), _packedWidth(0), _packedHeight(0),
_originalWidth(0), _originalHeight(0), _rotate(false), _degrees(0) {}
~AtlasRegion() {} ~AtlasRegion() {}
AtlasPage *getPage() const { return _page; } AtlasPage *getPage() const { return _page; }
String getName() const { return _name; } String getName() const { return _name; }
int getIndex() const { return _index; } int getIndex() const { return _index; }
int getX() const { return _x; } int getX() const { return _x; }
int getY() const { return _y; } int getY() const { return _y; }
float getOffsetX() const { return _offsetX; }
float getOffsetY() const { return _offsetY; }
int getPackedWidth() const { return _packedWidth; }
int getPackedHeight() const { return _packedHeight; }
int getOriginalWidth() const { return _originalWidth; }
int getOriginalHeight() const { return _originalHeight; }
bool getRotate() const { return _rotate; }
int getDegrees() const { return _degrees; }
Array<int> &getSplits() { return _splits; } Array<int> &getSplits() { return _splits; }
Array<int> &getPads() { return _pads; } Array<int> &getPads() { return _pads; }
Array<String> &getNames() { return _names; } Array<String> &getNames() { return _names; }
@ -126,6 +142,14 @@ namespace spine {
void setIndex(int value) { _index = value; } void setIndex(int value) { _index = value; }
void setX(int value) { _x = value; } void setX(int value) { _x = value; }
void setY(int value) { _y = value; } void setY(int value) { _y = value; }
void setOffsetX(float value) { _offsetX = value; }
void setOffsetY(float value) { _offsetY = value; }
void setPackedWidth(int value) { _packedWidth = value; }
void setPackedHeight(int value) { _packedHeight = value; }
void setOriginalWidth(int value) { _originalWidth = value; }
void setOriginalHeight(int value) { _originalHeight = value; }
void setRotate(bool value) { _rotate = value; }
void setDegrees(int value) { _degrees = value; }
void setSplits(const Array<int> &value) { _splits = value; } void setSplits(const Array<int> &value) { _splits = value; }
void setPads(const Array<int> &value) { _pads = value; } void setPads(const Array<int> &value) { _pads = value; }
void setNames(const Array<String> &value) { _names = value; } void setNames(const Array<String> &value) { _names = value; }
@ -135,6 +159,11 @@ namespace spine {
String _name; String _name;
int _index; int _index;
int _x, _y; int _x, _y;
float _offsetX, _offsetY;
int _packedWidth, _packedHeight;
int _originalWidth, _originalHeight;
bool _rotate;
int _degrees;
Array<int> _splits; Array<int> _splits;
Array<int> _pads; Array<int> _pads;
Array<String> _names; Array<String> _names;
@ -155,7 +184,7 @@ namespace spine {
/// Returns the first region found with the specified name. This method uses String comparison to find the region, so the result /// Returns the first region found with the specified name. This method uses String comparison to find the region, so the result
/// should be cached rather than calling this method multiple times. /// should be cached rather than calling this method multiple times.
/// @return The region, or NULL. /// @return The region, or nullptr.
AtlasRegion *findRegion(const String &name); AtlasRegion *findRegion(const String &name);
Array<AtlasPage *> &getPages(); Array<AtlasPage *> &getPages();

View File

@ -111,17 +111,19 @@ namespace spine {
MeshAttachment *newLinkedMesh(); MeshAttachment *newLinkedMesh();
private: private:
MeshAttachment *_parentMesh; TextureRegion *_region;
Array<float> _uvs;
Array<float> _regionUVs;
Array<unsigned short> _triangles;
Array<unsigned short> _edges;
String _path; String _path;
Array<float> _regionUVs;
Array<float> _uvs;
Array<unsigned short> _triangles;
Color _color; Color _color;
int _hullLength; int _hullLength;
int _width, _height; MeshAttachment *_parentMesh;
TextureRegion *_region;
Sequence *_sequence; Sequence *_sequence;
// Nonessential.
Array<unsigned short> _edges;
int _width, _height;
}; };
} }

View File

@ -38,8 +38,6 @@
#include <spine/HasRendererObject.h> #include <spine/HasRendererObject.h>
#define NUM_UVS 8
namespace spine { namespace spine {
class Bone; class Bone;
class Slot; class Slot;
@ -128,12 +126,12 @@ namespace spine {
static const int BRX; static const int BRX;
static const int BRY; static const int BRY;
float _x, _y, _rotation, _scaleX, _scaleY, _width, _height;
Array<float> _offset;
Array<float> _uvs;
String _path;
Color _color;
TextureRegion *_region; TextureRegion *_region;
String _path;
float _x, _y, _scaleX, _scaleY, _rotation, _width, _height;
Array<float> _uvs;
Array<float> _offset;
Color _color;
Sequence *_sequence; Sequence *_sequence;
}; };
} }

View File

@ -31,6 +31,7 @@
#define Spine_TextureRegion_h #define Spine_TextureRegion_h
#include <spine/Array.h> #include <spine/Array.h>
#include <spine/RTTI.h>
namespace spine { namespace spine {
class SP_API TextureRegion : public SpineObject { class SP_API TextureRegion : public SpineObject {
@ -40,8 +41,10 @@ namespace spine {
friend class AtlasRegion; friend class AtlasRegion;
friend class SkeletonRenderer; friend class SkeletonRenderer;
RTTI_DECL_NOPARENT
public: public:
TextureRegion(): _rendererObject(NULL), _u(0), _v(0), _u2(0), _v2(0), _degrees(0), _offsetX(0), _offsetY(0), _width(0), _height(0), _originalWidth(0), _originalHeight(0) {}; TextureRegion(): _rendererObject(nullptr), _u(0), _v(0), _u2(0), _v2(0), _regionWidth(0), _regionHeight(0) {};
~TextureRegion() {}; ~TextureRegion() {};
float getU() const { return _u; }; float getU() const { return _u; };
@ -52,28 +55,15 @@ namespace spine {
void setU2(float value) { _u2 = value; } void setU2(float value) { _u2 = value; }
float getV2() const { return _v2; } float getV2() const { return _v2; }
void setV2(float value) { _v2 = value; } void setV2(float value) { _v2 = value; }
int getDegrees() const { return _degrees; } int getRegionWidth() const { return _regionWidth; };
void setDegrees(int value) { _degrees = value; } void setRegionWidth(int value) { _regionWidth = value; }
float getOffsetX() const { return _offsetX; } int getRegionHeight() const { return _regionHeight; }
void setOffsetX(float value) { _offsetX = value; } void setRegionHeight(int value) { _regionHeight = value; }
float getOffsetY() const { return _offsetY; }
void setOffsetY(float value) { _offsetY = value; }
int getRegionWidth() const { return _width; };
void setRegionWidth(int value) { _width = value; }
int getRegionHeight() const { return _height; }
void setRegionHeight(int value) { _height = value; }
int getOriginalWidth() const { return _originalWidth; };
void setOriginalWidth(int value) { _originalWidth = value; }
int getOriginalHeight() const { return _originalHeight; };
void setOriginalHeight(int value) { _originalHeight = value; }
private: private:
void *_rendererObject; void *_rendererObject;
float _u, _v, _u2, _v2; float _u, _v, _u2, _v2;
int _degrees; int _regionWidth, _regionHeight;
float _offsetX, _offsetY;
int _width, _height;
int _originalWidth, _originalHeight;
}; };
} }

View File

@ -35,6 +35,8 @@
using namespace spine; using namespace spine;
RTTI_IMPL(AtlasRegion, TextureRegion)
Atlas::Atlas(const String &path, TextureLoader *textureLoader, bool createTexture) : _textureLoader(textureLoader) { Atlas::Atlas(const String &path, TextureLoader *textureLoader, bool createTexture) : _textureLoader(textureLoader) {
int dirLength; int dirLength;
char *dir; char *dir;
@ -88,7 +90,7 @@ void Atlas::flipV() {
AtlasRegion *Atlas::findRegion(const String &name) { AtlasRegion *Atlas::findRegion(const String &name) {
for (size_t i = 0, n = _regions.size(); i < n; ++i) for (size_t i = 0, n = _regions.size(); i < n; ++i)
if (_regions[i]->_name == name) return _regions[i]; if (_regions[i]->_name == name) return _regions[i];
return NULL; return nullptr;
} }
Array<AtlasPage *> &Atlas::getPages() { Array<AtlasPage *> &Atlas::getPages() {
@ -197,7 +199,7 @@ struct AtlasInput {
} }
static int readEntry(SimpleString entry[5], SimpleString *line) { static int readEntry(SimpleString entry[5], SimpleString *line) {
if (line == NULL) return 0; if (line == nullptr) return 0;
line->trim(); line->trim();
if (line->length == 0) return 0; if (line->length == 0) return 0;
@ -234,24 +236,24 @@ void Atlas::load(const char *begin, int length, const char *dir, bool createText
int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\'; int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
AtlasInput reader(begin, length); AtlasInput reader(begin, length);
SimpleString entry[5]; SimpleString entry[5];
AtlasPage *page = NULL; AtlasPage *page = nullptr;
SimpleString *line = reader.readLine(); SimpleString *line = reader.readLine();
while (line != NULL && line->length == 0) while (line != nullptr && line->length == 0)
line = reader.readLine(); line = reader.readLine();
while (true) { while (true) {
if (line == NULL || line->length == 0) break; if (line == nullptr || line->length == 0) break;
if (reader.readEntry(entry, line) == 0) break; if (reader.readEntry(entry, line) == 0) break;
line = reader.readLine(); line = reader.readLine();
} }
while (true) { while (true) {
if (line == NULL) break; if (line == nullptr) break;
if (line->trim().length == 0) { if (line->trim().length == 0) {
page = NULL; page = nullptr;
line = reader.readLine(); line = reader.readLine();
} else if (page == NULL) { } else if (page == nullptr) {
char *name = line->copy(); char *name = line->copy();
char *path = SpineExtension::calloc<char>(dirLength + needsSlash + strlen(name) + 1, __FILE__, __LINE__); char *path = SpineExtension::calloc<char>(dirLength + needsSlash + strlen(name) + 1, __FILE__, __LINE__);
memcpy(path, dir, dirLength); memcpy(path, dir, dirLength);
@ -302,13 +304,13 @@ void Atlas::load(const char *begin, int length, const char *dir, bool createText
region->_x = entry[1].toInt(); region->_x = entry[1].toInt();
region->_y = entry[2].toInt(); region->_y = entry[2].toInt();
} else if (entry[0].equals("size")) { } else if (entry[0].equals("size")) {
region->_width = entry[1].toInt(); region->_packedWidth = entry[1].toInt();
region->_height = entry[2].toInt(); region->_packedHeight = entry[2].toInt();
} else if (entry[0].equals("bounds")) { } else if (entry[0].equals("bounds")) {
region->_x = entry[1].toInt(); region->_x = entry[1].toInt();
region->_y = entry[2].toInt(); region->_y = entry[2].toInt();
region->_width = entry[3].toInt(); region->_packedWidth = entry[3].toInt();
region->_height = entry[4].toInt(); region->_packedHeight = entry[4].toInt();
} else if (entry[0].equals("offset")) { } else if (entry[0].equals("offset")) {
region->_offsetX = entry[1].toInt(); region->_offsetX = entry[1].toInt();
region->_offsetY = entry[2].toInt(); region->_offsetY = entry[2].toInt();
@ -326,6 +328,7 @@ void Atlas::load(const char *begin, int length, const char *dir, bool createText
} else if (!entry[1].equals("false")) { } else if (!entry[1].equals("false")) {
region->_degrees = entry[1].toInt(); region->_degrees = entry[1].toInt();
} }
region->_rotate = region->_degrees == 90;
} else if (entry[0].equals("index")) { } else if (entry[0].equals("index")) {
region->_index = entry[1].toInt(); region->_index = entry[1].toInt();
} else { } else {
@ -336,19 +339,22 @@ void Atlas::load(const char *begin, int length, const char *dir, bool createText
} }
} }
if (region->_originalWidth == 0 && region->_originalHeight == 0) { if (region->_originalWidth == 0 && region->_originalHeight == 0) {
region->_originalWidth = region->_width; region->_originalWidth = region->_packedWidth;
region->_originalHeight = region->_height; region->_originalHeight = region->_packedHeight;
} }
region->_u = (float) region->_x / page->width; region->_u = (float) region->_x / page->width;
region->_v = (float) region->_y / page->height; region->_v = (float) region->_y / page->height;
if (region->_degrees == 90) { if (region->_degrees == 90) {
region->_u2 = (float) (region->_x + region->_height) / page->width; region->_u2 = (float) (region->_x + region->_packedHeight) / page->width;
region->_v2 = (float) (region->_y + region->_width) / page->height; region->_v2 = (float) (region->_y + region->_packedWidth) / page->height;
} else { } else {
region->_u2 = (float) (region->_x + region->_width) / page->width; region->_u2 = (float) (region->_x + region->_packedWidth) / page->width;
region->_v2 = (float) (region->_y + region->_height) / page->height; region->_v2 = (float) (region->_y + region->_packedHeight) / page->height;
} }
// Calculate regionWidth/Height from UV coordinates
region->_regionWidth = abs((int) ((region->_u2 - region->_u) * page->width));
region->_regionHeight = abs((int) ((region->_v2 - region->_v) * page->height));
_regions.add(region); _regions.add(region);
} }
} }

View File

@ -28,6 +28,7 @@
*****************************************************************************/ *****************************************************************************/
#include <spine/MeshAttachment.h> #include <spine/MeshAttachment.h>
#include <spine/Atlas.h>
#include <spine/Slot.h> #include <spine/Slot.h>
using namespace spine; using namespace spine;
@ -35,83 +36,88 @@ using namespace spine;
RTTI_IMPL(MeshAttachment, VertexAttachment) RTTI_IMPL(MeshAttachment, VertexAttachment)
MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name), MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name),
_parentMesh(NULL), _region(NULL),
_path(), _path(),
_color(1, 1, 1, 1), _color(1, 1, 1, 1),
_hullLength(0), _hullLength(0),
_parentMesh(NULL),
_sequence(NULL),
_width(0), _width(0),
_height(0), _height(0) {}
_region(NULL),
_sequence(NULL) {}
MeshAttachment::~MeshAttachment() { MeshAttachment::~MeshAttachment() {
if (_sequence) delete _sequence; if (_sequence) delete _sequence;
} }
void MeshAttachment::updateRegion() { void MeshAttachment::updateRegion() {
if (_uvs.size() != _regionUVs.size()) { if (_uvs.size() != _regionUVs.size()) _uvs.setSize(_regionUVs.size(), 0);
_uvs.setSize(_regionUVs.size(), 0); int n = (int) _regionUVs.size();
} float u, v, width, height;
if (_region != nullptr && _region->rtti.instanceOf(AtlasRegion::rtti)) {
AtlasRegion *atlasRegion = static_cast<AtlasRegion *>(_region);
u = _region->_u;
v = _region->_v;
if (_region == nullptr) { float textureWidth = atlasRegion->_packedWidth / (_region->_u2 - _region->_u);
return; float textureHeight = atlasRegion->_packedHeight / (_region->_v2 - _region->_v);
}
int i = 0, n = (int) _regionUVs.size(); switch (atlasRegion->_degrees) {
float u = _region->_u, v = _region->_v; case 90: {
float width = 0, height = 0; textureWidth = atlasRegion->_packedHeight / (_region->_u2 - _region->_u);
switch (_region->_degrees) { textureHeight = atlasRegion->_packedWidth / (_region->_v2 - _region->_v);
case 90: { u -= (atlasRegion->_originalHeight - atlasRegion->_offsetY - atlasRegion->_packedHeight) / textureWidth;
float textureWidth = _region->_height / (_region->_u2 - _region->_u); v -= (atlasRegion->_originalWidth - atlasRegion->_offsetX - atlasRegion->_packedWidth) / textureHeight;
float textureHeight = _region->_width / (_region->_v2 - _region->_v); width = atlasRegion->_originalHeight / textureWidth;
u -= (_region->_originalHeight - _region->_offsetY - _region->_height) / textureWidth; height = atlasRegion->_originalWidth / textureHeight;
v -= (_region->_originalWidth - _region->_offsetX - _region->_width) / textureHeight; for (int i = 0; i < n; i += 2) {
width = _region->_originalHeight / textureWidth; _uvs[i] = u + _regionUVs[i + 1] * width;
height = _region->_originalWidth / textureHeight; _uvs[i + 1] = v + (1 - _regionUVs[i]) * height;
for (i = 0; i < n; i += 2) { }
_uvs[i] = u + _regionUVs[i + 1] * width; return;
_uvs[i + 1] = v + (1 - _regionUVs[i]) * height;
} }
return; case 180: {
} u -= (atlasRegion->_originalWidth - atlasRegion->_offsetX - atlasRegion->_packedWidth) / textureWidth;
case 180: { v -= atlasRegion->_offsetY / textureHeight;
float textureWidth = _region->_width / (_region->_u2 - _region->_u); width = atlasRegion->_originalWidth / textureWidth;
float textureHeight = _region->_height / (_region->_v2 - _region->_v); height = atlasRegion->_originalHeight / textureHeight;
u -= (_region->_originalWidth - _region->_offsetX - _region->_width) / textureWidth; for (int i = 0; i < n; i += 2) {
v -= _region->_offsetY / textureHeight; _uvs[i] = u + (1 - _regionUVs[i]) * width;
width = _region->_originalWidth / textureWidth; _uvs[i + 1] = v + (1 - _regionUVs[i + 1]) * height;
height = _region->_originalHeight / textureHeight; }
for (i = 0; i < n; i += 2) { return;
_uvs[i] = u + (1 - _regionUVs[i]) * width;
_uvs[i + 1] = v + (1 - _regionUVs[i + 1]) * height;
} }
return; case 270: {
} textureHeight = atlasRegion->_packedHeight / (_region->_v2 - _region->_v);
case 270: { textureWidth = atlasRegion->_packedWidth / (_region->_u2 - _region->_u);
float textureHeight = _region->_height / (_region->_v2 - _region->_v); u -= atlasRegion->_offsetY / textureWidth;
float textureWidth = _region->_width / (_region->_u2 - _region->_u); v -= atlasRegion->_offsetX / textureHeight;
u -= _region->_offsetY / textureWidth; width = atlasRegion->_originalHeight / textureWidth;
v -= _region->_offsetX / textureHeight; height = atlasRegion->_originalWidth / textureHeight;
width = _region->_originalHeight / textureWidth; for (int i = 0; i < n; i += 2) {
height = _region->_originalWidth / textureHeight; _uvs[i] = u + (1 - _regionUVs[i + 1]) * width;
for (i = 0; i < n; i += 2) { _uvs[i + 1] = v + _regionUVs[i] * height;
_uvs[i] = u + (1 - _regionUVs[i + 1]) * width; }
_uvs[i + 1] = v + _regionUVs[i] * height; return;
} }
return; default: {
} u -= atlasRegion->_offsetX / textureWidth;
default: { v -= (atlasRegion->_originalHeight - atlasRegion->_offsetY - atlasRegion->_packedHeight) / textureHeight;
float textureWidth = _region->_width / (_region->_u2 - _region->_u); width = atlasRegion->_originalWidth / textureWidth;
float textureHeight = _region->_height / (_region->_v2 - _region->_v); height = atlasRegion->_originalHeight / textureHeight;
u -= _region->_offsetX / textureWidth;
v -= (_region->_originalHeight - _region->_offsetY - _region->_height) / textureHeight;
width = _region->_originalWidth / textureWidth;
height = _region->_originalHeight / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i] * width;
_uvs[i + 1] = v + _regionUVs[i + 1] * height;
} }
} }
} else if (_region == nullptr) {
u = v = 0;
width = height = 1;
} else {
u = _region->_u;
v = _region->_v;
width = _region->_u2 - u;
height = _region->_v2 - v;
}
for (int i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i] * width;
_uvs[i + 1] = v + _regionUVs[i + 1] * height;
} }
} }

View File

@ -29,6 +29,7 @@
#include <spine/RegionAttachment.h> #include <spine/RegionAttachment.h>
#include <spine/Atlas.h>
#include <spine/Bone.h> #include <spine/Bone.h>
#include <spine/Slot.h> #include <spine/Slot.h>
@ -48,19 +49,19 @@ const int RegionAttachment::BRX = 6;
const int RegionAttachment::BRY = 7; const int RegionAttachment::BRY = 7;
RegionAttachment::RegionAttachment(const String &name) : Attachment(name), RegionAttachment::RegionAttachment(const String &name) : Attachment(name),
_region(NULL),
_path(),
_x(0), _x(0),
_y(0), _y(0),
_rotation(0),
_scaleX(1), _scaleX(1),
_scaleY(1), _scaleY(1),
_rotation(0),
_width(0), _width(0),
_height(0), _height(0),
_path(),
_color(1, 1, 1, 1), _color(1, 1, 1, 1),
_region(NULL),
_sequence(NULL) { _sequence(NULL) {
_offset.setSize(NUM_UVS, 0); _offset.setSize(8, 0);
_uvs.setSize(NUM_UVS, 0); _uvs.setSize(8, 0);
} }
RegionAttachment::~RegionAttachment() { RegionAttachment::~RegionAttachment() {
@ -68,24 +69,33 @@ RegionAttachment::~RegionAttachment() {
} }
void RegionAttachment::updateRegion() { void RegionAttachment::updateRegion() {
if (_region == NULL) { float width = getWidth(), height = getHeight();
_uvs[BLX] = 0; float localX2 = width / 2;
_uvs[BLY] = 0; float localY2 = height / 2;
_uvs[ULX] = 0; float localX = -localX2;
_uvs[ULY] = 1; float localY = -localY2;
_uvs[URX] = 1; bool rotated = false;
_uvs[URY] = 1; AtlasRegion *atlasRegion = NULL;
_uvs[BRX] = 1; if (_region != NULL) {
_uvs[BRY] = 0; atlasRegion = _region->rtti.isExactly(AtlasRegion::rtti) ? static_cast<AtlasRegion *>(_region) : NULL;
return;
} }
if (atlasRegion) {
float regionScaleX = _width / _region->_originalWidth * _scaleX; localX += atlasRegion->_offsetX / atlasRegion->_originalWidth * width;
float regionScaleY = _height / _region->_originalHeight * _scaleY; localY += atlasRegion->_offsetY / atlasRegion->_originalHeight * height;
float localX = -_width / 2 * _scaleX + _region->_offsetX * regionScaleX; if (atlasRegion->_degrees == 90) {
float localY = -_height / 2 * _scaleY + _region->_offsetY * regionScaleY; rotated = true;
float localX2 = localX + _region->_width * regionScaleX; localX2 -= (atlasRegion->_originalWidth - atlasRegion->_offsetX - atlasRegion->_packedHeight) / atlasRegion->_originalWidth * width;
float localY2 = localY + _region->_height * regionScaleY; localY2 -= (atlasRegion->_originalHeight - atlasRegion->_offsetY - atlasRegion->_packedWidth) / atlasRegion->_originalHeight * height;
} else {
localX2 -= (atlasRegion->_originalWidth - atlasRegion->_offsetX - atlasRegion->_packedWidth) / atlasRegion->_originalWidth * width;
localY2 -= (atlasRegion->_originalHeight - atlasRegion->_offsetY - atlasRegion->_packedHeight) / atlasRegion->_originalHeight * height;
}
}
float scaleX = getScaleX(), scaleY = getScaleY();
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float cos = MathUtil::cosDeg(_rotation); float cos = MathUtil::cosDeg(_rotation);
float sin = MathUtil::sinDeg(_rotation); float sin = MathUtil::sinDeg(_rotation);
float localXCos = localX * cos + _x; float localXCos = localX * cos + _x;
@ -106,24 +116,33 @@ void RegionAttachment::updateRegion() {
_offset[BRX] = localX2Cos - localYSin; _offset[BRX] = localX2Cos - localYSin;
_offset[BRY] = localYCos + localX2Sin; _offset[BRY] = localYCos + localX2Sin;
if (_region->_degrees == 90) { if (_region == NULL) {
_uvs[URX] = _region->_u; _uvs[BLX] = 0;
_uvs[URY] = _region->_v2; _uvs[BLY] = 0;
_uvs[BRX] = _region->_u; _uvs[ULX] = 0;
_uvs[BRY] = _region->_v; _uvs[ULY] = 1;
_uvs[URX] = 1;
_uvs[URY] = 1;
_uvs[BRX] = 1;
_uvs[BRY] = 0;
} else if (rotated) {
_uvs[BLX] = _region->_u2; _uvs[BLX] = _region->_u2;
_uvs[BLY] = _region->_v; _uvs[BLY] = _region->_v;
_uvs[ULX] = _region->_u2; _uvs[ULX] = _region->_u2;
_uvs[ULY] = _region->_v2; _uvs[ULY] = _region->_v2;
_uvs[URX] = _region->_u;
_uvs[URY] = _region->_v2;
_uvs[BRX] = _region->_u;
_uvs[BRY] = _region->_v;
} else { } else {
_uvs[BLX] = _region->_u2;
_uvs[BLY] = _region->_v2;
_uvs[ULX] = _region->_u; _uvs[ULX] = _region->_u;
_uvs[ULY] = _region->_v2; _uvs[ULY] = _region->_v2;
_uvs[URX] = _region->_u; _uvs[URX] = _region->_u;
_uvs[URY] = _region->_v; _uvs[URY] = _region->_v;
_uvs[BRX] = _region->_u2; _uvs[BRX] = _region->_u2;
_uvs[BRY] = _region->_v; _uvs[BRY] = _region->_v;
_uvs[BLX] = _region->_u2;
_uvs[BLY] = _region->_v2;
} }
} }

View File

@ -0,0 +1,34 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/TextureRegion.h>
using namespace spine;
RTTI_IMPL_NOPARENT(TextureRegion)

View File

@ -106,7 +106,7 @@ public class Animation {
this.duration = duration; this.duration = duration;
} }
public IntArray getBones() { public IntArray getBones () {
return bones; return bones;
} }