Browse Source

[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
Mario Zechner 1 month ago
parent
commit
acbcfeb44d

+ 32 - 3
spine-cpp/include/spine/Atlas.h

@@ -36,6 +36,8 @@
 #include <spine/SpineString.h>
 #include <spine/HasRendererObject.h>
 #include "TextureRegion.h"
+#include "spine/MeshAttachment.h"
+#include "spine/RegionAttachment.h"
 
 namespace spine {
 	enum Format {
@@ -102,21 +104,35 @@ namespace spine {
 		explicit AtlasPage(const String &inName) : name(inName), format(Format_RGBA8888),
 												   minFilter(TextureFilter_Nearest),
 												   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 {
 		friend class Atlas;
+		friend class RegionAttachment;
+		friend class MeshAttachment;
+
+	RTTI_DECL
 
 	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() {}
 		AtlasPage *getPage() const { return _page; }
 		String getName() const { return _name; }
 		int getIndex() const { return _index; }
 		int getX() const { return _x; }
 		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> &getPads() { return _pads; }
 		Array<String> &getNames() { return _names; }
@@ -126,6 +142,14 @@ namespace spine {
 		void setIndex(int value) { _index = value; }
 		void setX(int value) { _x = 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 setPads(const Array<int> &value) { _pads = value; }
 		void setNames(const Array<String> &value) { _names = value; }
@@ -135,6 +159,11 @@ namespace spine {
 		String _name;
 		int _index;
 		int _x, _y;
+		float _offsetX, _offsetY;
+		int _packedWidth, _packedHeight;
+		int _originalWidth, _originalHeight;
+		bool _rotate;
+		int _degrees;
 		Array<int> _splits;
 		Array<int> _pads;
 		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
 		/// 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);
 
 		Array<AtlasPage *> &getPages();

+ 8 - 6
spine-cpp/include/spine/MeshAttachment.h

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

+ 4 - 6
spine-cpp/include/spine/RegionAttachment.h

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

+ 9 - 19
spine-cpp/include/spine/TextureRegion.h

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

+ 24 - 18
spine-cpp/src/spine/Atlas.cpp

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

+ 68 - 62
spine-cpp/src/spine/MeshAttachment.cpp

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

+ 48 - 29
spine-cpp/src/spine/RegionAttachment.cpp

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

+ 34 - 0
spine-cpp/src/spine/TextureRegion.cpp

@@ -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)

+ 1 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java

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