Skip to content

Commit

Permalink
Fix calculations of UV coordinates for rotated polygonal trimmed imag…
Browse files Browse the repository at this point in the history
…es (#8874)

* Fix calculations of UV coordinates for rotated polygonal trimmed images

* Added Atlas engine documentation

* fix
  • Loading branch information
JCash committed May 2, 2024
1 parent 18680bd commit 9141d9d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,19 @@ public static SpriteGeometry buildConvexHull(BufferedImage image, SpriteTrimming
private static SpriteGeometry.Builder createSpriteGeometryFromRect(Rect rect) {
SpriteGeometry.Builder builder = SpriteGeometry.newBuilder();

int imageWidth = rect.getWidth();
int imageHeight = rect.getHeight();
boolean rotated = rect.getRotated();
builder.setRotated(rotated);

if (rotated)
{
// for legacy reasons, we need to rotate it back
// The geometry wants the size in unrotated form
builder.setWidth(imageHeight);
builder.setHeight(imageWidth);
}
else
{
builder.setWidth(imageWidth);
builder.setHeight(imageHeight);
}
// may be rotated
int imageWidth = rect.getWidth();
int imageHeight = rect.getHeight();

int originalImageWidth = rotated ? imageHeight : imageWidth;
int originalImageHeight = rotated ? imageWidth : imageHeight;

// The geometry wants the size in unrotated form
builder.setWidth(originalImageWidth);
builder.setHeight(originalImageHeight);

TextureSetLayout.Point center = rect.getCenter();
builder.setCenterX(center.x);
Expand All @@ -291,8 +287,8 @@ private static SpriteGeometry.Builder createSpriteGeometryFromRect(Rect rect) {
// Also convert from image space (texels) to local UV space
int index = 0;
for (TextureSetLayout.Pointi vertex : rect.getVertices()) {
float localX = vertex.x / (float)imageWidth - 0.5f;
float localY = vertex.y / (float)imageHeight - 0.5f;
float localX = vertex.x / (float)originalImageWidth - 0.5f;
float localY = vertex.y / (float)originalImageHeight - 0.5f;
builder.addVertices(localX);
builder.addVertices(localY);
index += 2;
Expand All @@ -302,33 +298,40 @@ private static SpriteGeometry.Builder createSpriteGeometryFromRect(Rect rect) {
}

// From the vertices and layout, generate UV coordinates
// Note: The UV calculation is mostly legacy code, and only used by the editor for rendering (in collections, animation previews)
private static SpriteGeometry.Builder createPolygonUVs(SpriteGeometry.Builder geometryBuilder, Rect rect, float width, float height) {

boolean rotated = rect.getRotated();
int originalRectWidth = (rotated ? rect.getHeight() : rect.getWidth());
int originalRectHeight = (rotated ? rect.getWidth() : rect.getHeight());

float centerX = geometryBuilder.getCenterX();
float centerY = geometryBuilder.getCenterY();

geometryBuilder.setCenterX(centerX);
geometryBuilder.setCenterY(centerY);
TextureSetLayout.Point center = rect.getCenter();
geometryBuilder.setCenterX(center.x);
geometryBuilder.setCenterY(center.y);

float centerX = center.x;
float centerY = center.y;

geometryBuilder.setRotated(rotated);

// boolean debug = rect.getId() == "boy_slash6";
// if (debug) {
// System.out.println(String.format("createPolygonUVs - %s", rect.id));
// System.out.println(String.format(" cx/cy: %f, %f ow/oh: %d, %d numPoints: %d", centerX, centerY, originalRectWidth, originalRectHeight, geometry.getVerticesCount() / 2));
// System.out.println(String.format(" %d %d", rect.width, rect.height));
// System.out.println(String.format("createPolygonUVs - %s", rect.getId()));
// System.out.println(String.format(" page w/h: %f %f", width, height));
// System.out.println(String.format(" rect x/y/w/h: %d %d %d %d", rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()));
// System.out.println(String.format(" cx/cy: %f, %f ow/oh: %d, %d numPoints: %d", centerX, centerY, originalRectWidth, originalRectHeight, geometryBuilder.getVerticesCount() / 2));
// }
int numPoints = geometryBuilder.getVerticesCount() / 2;

geometryBuilder.clearUvs();

for (int i = 0; i < numPoints; ++i) {

// the points are in object space, where origin is at the center of the sprite image
// the points are in sprite image space, not rotated,
// in units [-0.5,0.5]
// The polygon has a CCW orientation
// where origin is at the center of the sprite image (i.e. at [0,0])
// The polygons (see indices) has a CCW orientation
float localU = geometryBuilder.getVertices(i * 2 + 0);
float localV = geometryBuilder.getVertices(i * 2 + 1);
float localX = localU * originalRectWidth;
Expand All @@ -338,11 +341,10 @@ private static SpriteGeometry.Builder createPolygonUVs(SpriteGeometry.Builder ge

localY = -localY;

if (rotated) {
// rotate 90 degrees ccw
// where cos(pi/2)==0 and sin(pi/2)==1
// xp = x * cos(a) - y * sin(a) = -y
// yp = y * cos(a) + x * sin(a) = x
// A rotated image is stored with a 90 deg CW rotation in the final texture
// so we need to convert the vertices into the uv space of that texture
if (rotated) // rotate 90 degrees CW
{
float tmp = localX;
localX = -localY;
localY = tmp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static class Rect {
private int index; // for easier keeping the original order
private int page;
private Rectanglei rect; // The final placement in the texture. May lay outside of the texture image.
private boolean rotated; // True if rotated 90 deg (CCW)
private boolean rotated; // True if rotated 90 deg (CW)

// Texel coordinates within the original image.
// Origin is top left corner of original image.
Expand Down
34 changes: 34 additions & 0 deletions engine/docs/ATLAS_FORMAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Atlas format

This document describes the atlas format and its conventions

## The TextureSetLayout.SourceImage struct

This is our new public interface for extensions, and holds the sprite image geometry and the final (trimmed and rotated) rectangle in the atlas.
The `indices` member is a list of of indices, where each group of 3 define a CCW triangle.
The `vertices` member is a list of Point's
The points are in source image space: [(0,0) .. (image width, image height)] (i.e. not rotated)


## The TextureSetLayout.Rect struct

While originally a much more simple rectangle structure, it now is holding data for a single sprite image and its geometry.
It also holds the page index, and if the image is rotated (90 degrees CW) in the texture.

## Texture

The texture holds all packed images, and may span over several "pages". Each packed Rect has an index into which page it is located.

Rotated images are stored in a 90 degrees CW rotation.

## Runtime Sprite Geometry

The runtime sprite geometry is defined by the struct `SpriteGeometry` in [texture_set_ddf.proto](../../gamesys/proto/gamesys/texture_set_ddf.proto)
The vertices are in local UV space [-0.5, 0.5] where the origin (0,0) is at the image center.
The vertices are also not rotated, as they're used the final vertex calculation.
They're also used at runtime to calculate the actual UV coordinates in the texture.

## Deprecated

The editor currently uses the precalculated UV coordinates of the output file format.
Since the engine doesn't need the data, we intend to remove it from the file format, and the editor will have to render the sprites the same way as the engine does.

0 comments on commit 9141d9d

Please sign in to comment.