/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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 com.badlogic.gdx.graphics.g2d;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.NumberUtils;
/** @author Stefan Bachmann
* @author Nathan Sweet */
public class PolygonSprite {
PolygonRegion region;
private float x, y;
private float width, height;
private float scaleX = 1f, scaleY = 1f;
private float rotation;
private float originX, originY;
private float[] vertices;
private boolean dirty;
private Rectangle bounds = new Rectangle();
private final Color color = new Color(1f, 1f, 1f, 1f);
public PolygonSprite (PolygonRegion region) {
setRegion(region);
setSize(region.region.regionWidth, region.region.regionHeight);
setOrigin(width / 2, height / 2);
}
/** Creates a sprite that is a copy in every way of the specified sprite. */
public PolygonSprite (PolygonSprite sprite) {
set(sprite);
}
public void set (PolygonSprite sprite) {
if (sprite == null) throw new IllegalArgumentException("sprite cannot be null.");
setRegion(sprite.region);
x = sprite.x;
y = sprite.y;
width = sprite.width;
height = sprite.height;
originX = sprite.originX;
originY = sprite.originY;
rotation = sprite.rotation;
scaleX = sprite.scaleX;
scaleY = sprite.scaleY;
color.set(sprite.color);
}
/** Sets the position and size of the sprite when drawn, before scaling and rotation are applied. If origin, rotation, or scale
* are changed, it is slightly more efficient to set the bounds after those operations. */
public void setBounds (float x, float y, float width, float height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
dirty = true;
}
/** Sets the size of the sprite when drawn, before scaling and rotation are applied. If origin, rotation, or scale are changed,
* it is slightly more efficient to set the size after those operations. If both position and size are to be changed, it is
* better to use {@link #setBounds(float, float, float, float)}. */
public void setSize (float width, float height) {
this.width = width;
this.height = height;
dirty = true;
}
/** Sets the position where the sprite will be drawn. If origin, rotation, or scale are changed, it is slightly more efficient
* to set the position after those operations. If both position and size are to be changed, it is better to use
* {@link #setBounds(float, float, float, float)}. */
public void setPosition (float x, float y) {
translate(x - this.x, y - this.y);
}
/** Sets the x position where the sprite will be drawn. If origin, rotation, or scale are changed, it is slightly more efficient
* to set the position after those operations. If both position and size are to be changed, it is better to use
* {@link #setBounds(float, float, float, float)}. */
public void setX (float x) {
translateX(x - this.x);
}
/** Sets the y position where the sprite will be drawn. If origin, rotation, or scale are changed, it is slightly more efficient
* to set the position after those operations. If both position and size are to be changed, it is better to use
* {@link #setBounds(float, float, float, float)}. */
public void setY (float y) {
translateY(y - this.y);
}
/** Sets the x position relative to the current position where the sprite will be drawn. If origin, rotation, or scale are
* changed, it is slightly more efficient to translate after those operations. */
public void translateX (float xAmount) {
this.x += xAmount;
if (dirty) return;
final float[] vertices = this.vertices;
for (int i = 0; i < vertices.length; i += Sprite.VERTEX_SIZE)
vertices[i] += xAmount;
}
/** Sets the y position relative to the current position where the sprite will be drawn. If origin, rotation, or scale are
* changed, it is slightly more efficient to translate after those operations. */
public void translateY (float yAmount) {
y += yAmount;
if (dirty) return;
final float[] vertices = this.vertices;
for (int i = 1; i < vertices.length; i += Sprite.VERTEX_SIZE)
vertices[i] += yAmount;
}
/** Sets the position relative to the current position where the sprite will be drawn. If origin, rotation, or scale are
* changed, it is slightly more efficient to translate after those operations. */
public void translate (float xAmount, float yAmount) {
x += xAmount;
y += yAmount;
if (dirty) return;
final float[] vertices = this.vertices;
for (int i = 0; i < vertices.length; i += Sprite.VERTEX_SIZE) {
vertices[i] += xAmount;
vertices[i + 1] += yAmount;
}
}
public void setColor (Color tint) {
color.set(tint);
float color = tint.toFloatBits();
final float[] vertices = this.vertices;
for (int i = 2; i < vertices.length; i += Sprite.VERTEX_SIZE)
vertices[i] = color;
}
public void setColor (float r, float g, float b, float a) {
color.set(r, g, b, a);
int intBits = ((int)(255 * a) << 24) | ((int)(255 * b) << 16) | ((int)(255 * g) << 8) | ((int)(255 * r));
float color = NumberUtils.intToFloatColor(intBits);
final float[] vertices = this.vertices;
for (int i = 2; i < vertices.length; i += Sprite.VERTEX_SIZE)
vertices[i] = color;
}
/** Sets the origin in relation to the sprite's position for scaling and rotation. */
public void setOrigin (float originX, float originY) {
this.originX = originX;
this.originY = originY;
dirty = true;
}
public void setRotation (float degrees) {
this.rotation = degrees;
dirty = true;
}
/** Sets the sprite's rotation relative to the current rotation. */
public void rotate (float degrees) {
rotation += degrees;
dirty = true;
}
public void setScale (float scaleXY) {
this.scaleX = scaleXY;
this.scaleY = scaleXY;
dirty = true;
}
public void setScale (float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
dirty = true;
}
/** Sets the sprite's scale relative to the current scale. */
public void scale (float amount) {
this.scaleX += amount;
this.scaleY += amount;
dirty = true;
}
/** Returns the packed vertices, colors, and texture coordinates for this sprite. */
public float[] getVertices () {
if (!dirty) return vertices;
dirty = false;
final float originX = this.originX;
final float originY = this.originY;
final float scaleX = this.scaleX;
final float scaleY = this.scaleY;
final PolygonRegion region = this.region;
final float[] vertices = this.vertices;
final float[] regionVertices = region.vertices;
final float worldOriginX = x + originX;
final float worldOriginY = y + originY;
final float sX = width / region.region.getRegionWidth();
final float sY = height / region.region.getRegionHeight();
final float cos = MathUtils.cosDeg(rotation);
final float sin = MathUtils.sinDeg(rotation);
float fx, fy;
for (int i = 0, v = 0, n = regionVertices.length; i < n; i += 2, v += 5) {
fx = (regionVertices[i] * sX - originX) * scaleX;
fy = (regionVertices[i + 1] * sY - originY) * scaleY;
vertices[v] = cos * fx - sin * fy + worldOriginX;
vertices[v + 1] = sin * fx + cos * fy + worldOriginY;
}
return vertices;
}
/** Returns the bounding axis aligned {@link Rectangle} that bounds this sprite. The rectangles x and y coordinates describe its
* bottom left corner. If you change the position or size of the sprite, you have to fetch the triangle again for it to be
* recomputed.
* @return the bounding Rectangle */
public Rectangle getBoundingRectangle () {
final float[] vertices = getVertices();
float minx = vertices[0];
float miny = vertices[1];
float maxx = vertices[0];
float maxy = vertices[1];
for (int i = 5; i < vertices.length; i += 5) {
float x = vertices[i];
float y = vertices[i + 1];
minx = minx > x ? x : minx;
maxx = maxx < x ? x : maxx;
miny = miny > y ? y : miny;
maxy = maxy < y ? y : maxy;
}
bounds.x = minx;
bounds.y = miny;
bounds.width = maxx - minx;
bounds.height = maxy - miny;
return bounds;
}
public void draw (PolygonSpriteBatch spriteBatch) {
final PolygonRegion region = this.region;
spriteBatch.draw(region.region.texture, getVertices(), 0, vertices.length, region.triangles, 0, region.triangles.length);
}
public void draw (PolygonSpriteBatch spriteBatch, float alphaModulation) {
Color color = getColor();
float oldAlpha = color.a;
color.a *= alphaModulation;
setColor(color);
draw(spriteBatch);
color.a = oldAlpha;
setColor(color);
}
public float getX () {
return x;
}
public float getY () {
return y;
}
public float getWidth () {
return width;
}
public float getHeight () {
return height;
}
public float getOriginX () {
return originX;
}
public float getOriginY () {
return originY;
}
public float getRotation () {
return rotation;
}
public float getScaleX () {
return scaleX;
}
public float getScaleY () {
return scaleY;
}
/** Returns the color of this sprite. Modifying the returned color will have unexpected effects unless {@link #setColor(Color)}
* or {@link #setColor(float, float, float, float)} is subsequently called before drawing this sprite. */
public Color getColor () {
return color;
}
/** Returns the actual color used in the vertices of this sprite. Modifying the returned color will have unexpected effects
* unless {@link #setColor(Color)} or {@link #setColor(float, float, float, float)} is subsequently called before drawing this
* sprite. */
public Color getVertexColor () {
int intBits = NumberUtils.floatToIntColor(vertices[2]);
Color color = this.color;
color.r = (intBits & 0xff) / 255f;
color.g = ((intBits >>> 8) & 0xff) / 255f;
color.b = ((intBits >>> 16) & 0xff) / 255f;
color.a = ((intBits >>> 24) & 0xff) / 255f;
return color;
}
public void setRegion (PolygonRegion region) {
this.region = region;
float[] regionVertices = region.vertices;
float[] textureCoords = region.textureCoords;
if (vertices == null || regionVertices.length != vertices.length) vertices = new float[(regionVertices.length / 2) * 5];
// Set the color and UVs in this sprite's vertices.
float floatColor = color.toFloatBits();
float[] vertices = this.vertices;
for (int i = 0, v = 2, n = regionVertices.length; i < n; i += 2, v += 5) {
vertices[v] = floatColor;
vertices[v + 1] = textureCoords[i];
vertices[v + 2] = textureCoords[i + 1];
}
dirty = true;
}
public PolygonRegion getRegion () {
return region;
}
}