[SOLVED] Shear transformation in libgdx

I recently started experimenting with libgdx. Creating a SpriteBatch drawing sprites on it. Work like a sharm. It is possible to resize and rotate an sprite but I can’t find a method to performe a shear transformation on a sprite, like it is possible in Java2D: http://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html

Can somebody help me out on how to perform a shear operation on a sprite in libgdx?

One option is to apply a shear transformation matrix to the sprite. Another option is to transform the vertices yourself and submit them with the float[] parameter.

Here is an implementation of the first option:

	private Matrix4 idt = new Matrix4();
	private Matrix4 shear = new Matrix4();
	private OrthographicCamera cam;
	
	public static Matrix4 toShear(Matrix4 m, float shx, float shy) {
		m.idt();
		m.val[Matrix4.M01] = shx;
		m.val[Matrix4.M10] = shy;
		return m;
	}
	
	@Override
	public void render () {
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		cam.setToOrtho(false);
		spriteBatch.setProjectionMatrix( cam.combined );
		spriteBatch.begin();
		
		//draw your regular sprites with identity transform...
		spriteBatch.setTransformMatrix( idt );
		spriteBatch.draw(texture, 0, 0, 25, 25);
		
		//now draw your sheared sprites
		toShear(shear, 0.80f, 0.0f);
		spriteBatch.setTransformMatrix( shear );
		
      spriteBatch.draw(texture, 50, 50);
      spriteBatch.end();
	}

More info on the shear matrix here:
http://cs.fit.edu/~wds/classes/cse5255/thesis/shear/shear.html

Keep in mind that changing the transform/projection matrices will flush your batch. You should order your calls to minimize batch flushing, see here:

Setting a shear transformation on the SpriteBatch is not an option for me. I have many sprites that are sheared different ways. This will (like you say) kill the SpriteBatch. I’am going to try manipulation the vertices. I will keep you posted.

I talk a little about setting individual vertices using Sprite or SpriteBatch in this post:
http://www.badlogicgames.com/forum/viewtopic.php?p=42550#p42550

It is for a gradient (individual vertex colors), but the same concept should apply for individual vertex positions. The downside is that you won’t get the nice rotation/scale/etc of SpriteBatch.draw() utilities.

Fixed. I now use the standard Java AfineTransform to change the vertices. This makes converting my existing code (Java2D) much easier because AfineTransform is used there quite frequently. See the example code below.

And of course. Thanks for the help :slight_smile:


public class GdxPerf implements ApplicationListener {
	private OrthographicCamera camera;
	private SpriteBatch batch;
	private Texture texture;
	private Sprite sprite;
	private AffineTransform transform = new AffineTransform();
	
	private float[] verticesSrc = null;
	private float[] verticesDst = null;
	private float[] src = new float[8];
	private float[] dst = new float[8];
	
	@Override
	public void create() {		
		camera = new OrthographicCamera();
		
		batch = new SpriteBatch();
		
		texture = new Texture(Gdx.files.internal("data/Alice.png"));
		texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		
		TextureRegion region = new TextureRegion(texture, 0, 0, 512, 512);
		
		sprite = new Sprite(region);
		sprite.setPosition(0, 0);
		
		verticesSrc = sprite.getVertices();
		verticesDst = Arrays.copyOf(sprite.getVertices(), sprite.getVertices().length);
		
		transform = new AffineTransform();
	}

	@Override
	public void dispose() {
		batch.dispose();
		texture.dispose();
	}

	@Override
	public void render() {		
		Gdx.gl.glClearColor(0, 0, 0, 0);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		transform.setToIdentity();
		
		transform.translate(50.0d, 50.0d);
		transform.scale(1.5d, 1.5d);
		transform.shear(0.25d, 0.10d);
		transform.rotate(-Math.PI / 8.0d);
		
		src[0] = verticesSrc[SpriteBatch.X1];
		src[1] = verticesSrc[SpriteBatch.Y1];
		src[2] = verticesSrc[SpriteBatch.X2];
		src[3] = verticesSrc[SpriteBatch.Y2];
		src[4] = verticesSrc[SpriteBatch.X3];
		src[5] = verticesSrc[SpriteBatch.Y3];
		src[6] = verticesSrc[SpriteBatch.X4];
		src[7] = verticesSrc[SpriteBatch.Y4];

		transform.transform(src, 0, dst, 0, 4);
		
		verticesDst[SpriteBatch.X1] = dst[0];
		verticesDst[SpriteBatch.Y1] = dst[1];
		verticesDst[SpriteBatch.X2] = dst[2];
		verticesDst[SpriteBatch.Y2] = dst[3];
		verticesDst[SpriteBatch.X3] = dst[4];
		verticesDst[SpriteBatch.Y3] = dst[5];
		verticesDst[SpriteBatch.X4] = dst[6];
		verticesDst[SpriteBatch.Y4] = dst[7];

		camera.setToOrtho(false);
		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		
		batch.draw(texture, verticesDst, 0, verticesDst.length);
				
		batch.end();
	}

	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}
}

Keep in mind that the java2D package won’t work on WebGL, iOS, or Android. Nor will it work if you use a desktop backend like JGLFW.

Indeed. Looks like I have to make my own AffineTransform implementation that behaves like the Java2D one.

Why not just use the shear matrix transform I posted earlier, along with standard LibGDX camera / matrix? :stuck_out_tongue:

My original Java2D application that I want to convert to libGdx uses a different shear transformation for every drawed image. Using the SpriteBatch.setTransformMatrix is not an option because it will render the SpriteBatch useless (flushes each draw to the GPU).

I tought about extending the Sprite class or build a wrapper around it. Problem with this solution is that it can become implemetation dependent on the Sprite class real fast.

I noticed that the transformations of the Sprite class are a bit different from the Java2D implementation. For example: rotate in Java2D rotates default around the bottom left point of an image. Sprite rotates around the center of the image. Of course this can all be solved.

Making my own AffineTransform implementation has the benefit that the original code doesn’t have to be rewritten much.

In the end it is a decision with both pro’s and con’s. Rewrite more parts of the original code or create an easy way to use libGdx for this application. In this case I choose not to rewrite big parts of the original code.