All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dermetfan.gdx.graphics.g2d.Box2DPolygonSprite Maven / Gradle / Ivy

There is a newer version: 0.13.4
Show newest version
/** Copyright 2014 Robin Stumm ([email protected], http://dermetfan.net)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. */

package net.dermetfan.gdx.graphics.g2d;

import java.util.Comparator;
import java.util.Iterator;

import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.PolygonRegion;
import com.badlogic.gdx.graphics.g2d.PolygonSprite;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.Pools;
import net.dermetfan.utils.Function;
import net.dermetfan.gdx.math.GeometryUtils;

import static net.dermetfan.gdx.physics.box2d.utils.Box2DUtils.height;
import static net.dermetfan.gdx.physics.box2d.utils.Box2DUtils.minX;
import static net.dermetfan.gdx.physics.box2d.utils.Box2DUtils.minY;
import static net.dermetfan.gdx.physics.box2d.utils.Box2DUtils.position;
import static net.dermetfan.gdx.physics.box2d.utils.Box2DUtils.width;

/** A {@link Box2DPolygonSprite} is a {@link PolygonSprite} with additional drawing information and the ability to draw itself on a given {@link Body} or {@link Fixture}.
 *  It is supposed to be put in the user data of {@link Fixture Fixtures} or {@link Body Bodies}. Because geometrical information about bodies cannot be cached, it is faster to put Box2DPolygonSprites in the user data of Fixtures.
 *  @since 0.5.0
 *  @author dermetfan */
public class Box2DPolygonSprite extends PolygonSprite {

	/** the z index for sorted drawing */
	private float zIndex;

	/** if the width and height should be adjusted to those of the {@link Body} or {@link Fixture} this {@link Box2DPolygonSprite} is attached to (true by default) */
	private boolean adjustWidth = true, adjustHeight = true;

	/** if the size should be adjusted to match the polygon onto the body or fixture (true by default) */
	private boolean adjustToPolygon = true;

	/** if the origin of this {@link Box2DPolygonSprite} should be used when it's drawn (false by default) */
	private boolean useOriginX, useOriginY;

	/** for internal, temporary usage */
	private static final Vector2 vec2 = new Vector2();

	/** for internal, temporary usage */
	private static final Vector3 vec3 = new Vector3();

	/** @see PolygonSprite#PolygonSprite(PolygonRegion) */
	public Box2DPolygonSprite(PolygonRegion region) {
		super(region);
	}

	/** @see PolygonSprite#PolygonSprite(PolygonSprite) */
	public Box2DPolygonSprite(PolygonSprite sprite) {
		super(sprite);
	}

	/** the {@link #userDataAccessor} used by default */
	public static final Function defaultUserDataAccessor = new Function() {
		@Override
		public Box2DPolygonSprite apply(Object userData) {
			return userData instanceof Box2DPolygonSprite ? (Box2DPolygonSprite) userData : null;
		}
	};

	/** the {@link Function} used to get a {@link Box2DPolygonSprite} from the user data of a body or fixture */
	private static Function userDataAccessor = defaultUserDataAccessor;

	/** a {@link Comparator} used to sort {@link Box2DPolygonSprite Box2DPolygonSprites} by their {@link Box2DPolygonSprite#zIndex z index} in {@link #draw(Batch, World)} */
	private static Comparator zComparator = new Comparator() {
		@Override
		public int compare(Box2DPolygonSprite s1, Box2DPolygonSprite s2) {
			return s1.zIndex - s2.zIndex > 0 ? 1 : s1.zIndex - s2.zIndex < 0 ? -1 : 0;
		}
	};

	/** @see #draw(Batch, World, boolean) */
	public static void draw(Batch batch, World world) {
		draw(batch, world, false);
	}

	/** draws all the {@link Box2DPolygonSprite Box2DPolygonSprites} on the {@link Body} or {@link Fixture} that hold them in their user data in the given {@link World} */
	public static void draw(Batch batch, World world, boolean sortByZ) {
		@SuppressWarnings("unchecked")
		Array tmpBodies = Pools.obtain(Array.class);
		world.getBodies(tmpBodies);

		if(sortByZ) {
			@SuppressWarnings("unchecked")
			ObjectMap tmpZMap = Pools.obtain(ObjectMap.class);
			tmpZMap.clear();
			for(Body body : tmpBodies) {
				Box2DPolygonSprite tmpBox2DPolygonSprite;
				if((tmpBox2DPolygonSprite = userDataAccessor.apply(body.getUserData())) != null)
					tmpZMap.put(tmpBox2DPolygonSprite, body);
				for(Fixture fixture : body.getFixtureList())
					if((tmpBox2DPolygonSprite = userDataAccessor.apply(fixture.getUserData())) != null)
						tmpZMap.put(tmpBox2DPolygonSprite, fixture);
			}

			@SuppressWarnings("unchecked")
			Array tmpKeys = Pools.obtain(Array.class);
			Iterator keys = tmpZMap.keys();
			while(keys.hasNext())
				tmpKeys.add(keys.next());
			tmpKeys.sort(zComparator);
			for(Box2DPolygonSprite key : tmpKeys) {
				Object value = tmpZMap.get(key);
				if(value instanceof Body)
					key.draw(batch, (Body) value);
				else
					key.draw(batch, (Fixture) value);
			}

			tmpKeys.clear();
			tmpZMap.clear();
			Pools.free(tmpKeys);
			Pools.free(tmpZMap);
		} else
			for(Body body : tmpBodies) {
				Box2DPolygonSprite tmpBox2DPolygonSprite;
				if((tmpBox2DPolygonSprite = userDataAccessor.apply(body.getUserData())) != null)
					tmpBox2DPolygonSprite.draw(batch, body);
				for(Fixture fixture : body.getFixtureList())
					if((tmpBox2DPolygonSprite = userDataAccessor.apply(fixture.getUserData())) != null)
						tmpBox2DPolygonSprite.draw(batch, fixture);
			}

		tmpBodies.clear();
		Pools.free(tmpBodies);
	}

	/** draws this {@link Box2DPolygonSprite} on the given {@link Fixture} */
	public void draw(Batch batch, Fixture fixture) {
		vec2.set(position(fixture));
		draw(batch, vec2.x, vec2.y, width(fixture), height(fixture), fixture.getBody().getAngle());
	}

	/** draws this {@link Box2DPolygonSprite} on the given {@link Body} */
	public void draw(Batch batch, Body body) {
		float width = width(body), height = height(body);
		vec2.set(minX(body) + width / 2, minY(body) + height / 2);
		vec2.set(body.getWorldPoint(vec2));
		draw(batch, vec2.x, vec2.y, width, height, body.getAngle());
	}

	/** Used internally. Draws this {@code Box2DPolygonSprite} in classic sprite coordinate system fashion with the given Box2D coordinates (combined with its own position, size and rotation).
* If {@link #useOriginX useOriginX/Y} is enabled, the {@link #originX origin} will be used instead of calculating an appropriate one for the given Box2D coordinates.
* If {@link #adjustWidth adjustWidth/Height} is disabled, the size of the drawing area of the sprite will be {@link #width} * {@link #height} instead of the given size.
* The drawing position of the sprite is always the bottom left of the body or fixture. * @param batch The Batch to draw on. Redirects to {@link #draw(PolygonSpriteBatch, float, float, float, float, float)} if this is an instance of {@link PolygonSpriteBatch}. * @param box2dX the x coordinate (center) of the body or fixture * @param box2dY the y coordinate (center) of the body or fixture * @param box2dWidth the width of the body or fixture * @param box2dHeight the height of the body or fixture * @param box2dRotation the rotation of the body or fixture */ public void draw(Batch batch, float box2dX, float box2dY, float box2dWidth, float box2dHeight, float box2dRotation) { if(batch instanceof PolygonSpriteBatch) draw((PolygonSpriteBatch) batch, box2dX, box2dY, box2dWidth, box2dHeight, box2dRotation); else { batch.setColor(getColor()); batch.draw(getRegion().getRegion(), box2dX - box2dWidth / 2 + getX(), box2dY - box2dHeight / 2 + getY(), useOriginX ? getOriginX() : box2dWidth / 2, useOriginY ? getOriginY() : box2dHeight / 2, adjustWidth ? box2dWidth : getWidth(), adjustHeight ? box2dHeight : getHeight(), getScaleX(), getScaleY(), box2dRotation * MathUtils.radDeg + getRotation()); } } /** @see #draw(Batch, float, float, float, float, float) */ public void draw(PolygonSpriteBatch batch, float box2dX, float box2dY, float box2dWidth, float box2dHeight, float box2dRotation) { batch.setColor(getColor()); float x, y, originX, originY, width, height; if(adjustToPolygon) { FloatArray vertices = Pools.obtain(FloatArray.class); vertices.clear(); vertices.addAll(getRegion().getVertices()); float polygonWidth = GeometryUtils.width(vertices), polygonHeight = GeometryUtils.height(vertices); float polygonWidthRatio = getRegion().getRegion().getRegionWidth() / polygonWidth, polygonHeightRatio = getRegion().getRegion().getRegionHeight() / polygonHeight; width = box2dWidth * polygonWidthRatio; height = box2dHeight * polygonHeightRatio; float polygonX = GeometryUtils.minX(vertices), polygonY = GeometryUtils.minY(vertices); float polygonXRatio = getRegion().getRegion().getRegionWidth() / polygonX, polygonYRatio = getRegion().getRegion().getRegionHeight() / polygonY; float offsetX = width / polygonXRatio, offsetY = height / polygonYRatio; x = box2dX - offsetX - width / 2 / polygonWidthRatio; y = box2dY - offsetY - height / 2 / polygonHeightRatio; originX = offsetX + box2dWidth / 2; originY = offsetY + box2dHeight / 2; } else { x = box2dX - box2dWidth / 2; y = box2dY - box2dHeight / 2; originX = box2dWidth / 2; originY = box2dHeight / 2; width = box2dWidth; height = box2dHeight; } batch.draw(getRegion(), x + getX(), y + getY(), useOriginX ? getOriginX() : originX, useOriginY ? getOriginY() : originY, adjustWidth ? width : getWidth(), adjustHeight ? height : getHeight(), getScaleX(), getScaleY(), box2dRotation * MathUtils.radDeg + getRotation()); } // getters and setters /** @return the {@link #zIndex} */ public float getZIndex() { return zIndex; } /** @param zIndex the {@link #zIndex} to set */ public void setZIndex(float zIndex) { this.zIndex = zIndex; } /** @return the {@link #adjustWidth} */ public boolean isAdjustWidth() { return adjustWidth; } /** @param adjustWidth the {@link #adjustWidth} to set */ public void setAdjustWidth(boolean adjustWidth) { this.adjustWidth = adjustWidth; } /** @return the {@link #adjustHeight} */ public boolean isAdjustHeight() { return adjustHeight; } /** @param adjustHeight the {@link #adjustHeight} to set */ public void setAdjustHeight(boolean adjustHeight) { this.adjustHeight = adjustHeight; } /** @param adjustSize the {@link #adjustWidth} and {@link #adjustHeight} to set */ public void setAdjustSize(boolean adjustSize) { adjustWidth = adjustHeight = adjustSize; } /** @return the {@link #adjustToPolygon} */ public boolean isAdjustToPolygon() { return adjustToPolygon; } /** @param adjustToPolygon the {@link #adjustToPolygon} to set */ public void setAdjustToPolygon(boolean adjustToPolygon) { this.adjustToPolygon = adjustToPolygon; } /** @return the {@link #useOriginX} */ public boolean isUseOriginX() { return useOriginX; } /** @param useOriginX the {@link #useOriginX} to set */ public void setUseOriginX(boolean useOriginX) { this.useOriginX = useOriginX; } /** @return the {@link #useOriginY} */ public boolean isUseOriginY() { return useOriginY; } /** @param useOriginY the {@link #useOriginY} to set */ public void setUseOriginY(boolean useOriginY) { this.useOriginY = useOriginY; } /** @param useOrigin the {@link #useOriginX} and {@link #useOriginY} to set */ public void setUseOrigin(boolean useOrigin) { useOriginX = useOriginY = useOrigin; } /** @see Sprite#setSize(float, float) */ public void setWidth(float width) { setSize(width, getHeight()); } /** @see Sprite#setSize(float, float) */ public void setHeight(float height) { setSize(getWidth(), height); } /** @return the {@link #zComparator} */ public static Comparator getZComparator() { return zComparator; } /** @param zComparator the {@link #zComparator} to set */ public static void setZComparator(Comparator zComparator) { if(zComparator == null) throw new IllegalArgumentException("zComparator must not be null"); Box2DPolygonSprite.zComparator = zComparator; } /** @return the {@link #userDataAccessor} */ public static Function getUserDataAccessor() { return userDataAccessor; } /** @param userDataAccessor the {@link #userDataAccessor} to set */ public static void setUserDataAccessor(Function userDataAccessor) { Box2DPolygonSprite.userDataAccessor = userDataAccessor != null ? userDataAccessor : defaultUserDataAccessor; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy