/*******************************************************************************
* 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 java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.GdxRuntimeException;
/**
* Defines a polygon shape on top of a #TextureRegion for minimising pixel drawing. Can either be constructed through a
* .psh file from an external editor or programmatically through a list of vertices defining a polygon.
*
* THIS STUFF IS WIP
*
* @author Stefan Bachmann
*/
public class PolygonRegion {
// texture coordinates in atlas coordinates
private float[] texCoords;
// pixel coordinates relative to source image.
private float[] localVertices;
// the underlying TextureRegion
private final TextureRegion region;
/**
* Creates a PolygonRegion by reading in the vertices and texture coordinates from the external file. TextureRegion
* can come from an atlas.
*
* @param region
* the region used for drawing
* @param file
* polygon shape definition file
*/
public PolygonRegion(TextureRegion region, FileHandle file) {
this.region = region;
if (file == null)
throw new IllegalArgumentException("region cannot be null.");
loadPolygonDefinition(file);
}
/**
* Creates a PolygonRegin by triangulating the polygon coordinates in vertices and calculates uvs based on that.
* TextureRegion can come from an atlas.
*
* @param region
* the region used for drawing
* @param vertices
* contains 2D polygon coordinates in pixels relative to source region
*/
public PolygonRegion(TextureRegion region, float[] vertices) {
this.region = region;
EarClippingTriangulator ect = new EarClippingTriangulator();
List<Vector2> polygonVectors = new ArrayList<Vector2>();
for (int i = 0; i < vertices.length; i += 2) {
polygonVectors.add(new Vector2(vertices[i], vertices[i + 1]));
}
List<Vector2> triangulatedVectors = ect.computeTriangles(polygonVectors);
localVertices = new float[triangulatedVectors.size() * 2];
texCoords = new float[triangulatedVectors.size() * 2];
float uvWidth = region.u2 - region.u;
float uvHeight = region.v2 - region.v;
for (int i = 0; i < triangulatedVectors.size(); i++) {
localVertices[i * 2] = triangulatedVectors.get(i).x;
localVertices[i * 2 + 1] = triangulatedVectors.get(i).y;
texCoords[i * 2] = region.u + (localVertices[i * 2] - region.getRegionX()) / region.getRegionWidth();
texCoords[i * 2 + 1] = region.v
+ (1 - (localVertices[i * 2 + 1] - region.getRegionY()) / region.getRegionHeight());
}
}
/**
* Loads the vertices and texture data from an external file. The file should look something like this:
*
* ------------ // Triangulated vertices data (x, y) in pixel coordinates with origin bottom-left, y-up v 230.0,
* 230.0, ... // UVs with origin top-left u 0.23, 0.123, ... -------------
*
* Anything not prefixed with "u" or "v" will be ignored.
*
* @param file
* file handle to the shape definition file
*/
private void loadPolygonDefinition(FileHandle file) {
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(file.read()), 64);
try {
while (true) {
line = reader.readLine();
if (line == null)
break;
else if (line.startsWith("v")) {
// read in vertices
String[] vertices = line.substring(1).trim().split(",");
localVertices = new float[vertices.length];
for (int i = 0; i < vertices.length; i += 2) {
localVertices[i] = Float.parseFloat(vertices[i]);
localVertices[i + 1] = Float.parseFloat(vertices[i + 1]);
}
} else if (line.startsWith("u")) {
// read in uvs
String[] texCoords = line.substring(1).trim().split(",");
float localTexCoords[] = new float[texCoords.length];
for (int i = 0; i < texCoords.length; i += 2) {
localTexCoords[i] = Float.parseFloat(texCoords[i]);
localTexCoords[i + 1] = Float.parseFloat(texCoords[i + 1]);
}
this.texCoords = calculateAtlasTexCoords(localTexCoords);
}
}
} catch (IOException ex) {
throw new GdxRuntimeException("Error reading polygon shape file: " + file);
} finally {
try {
reader.close();
} catch (IOException ignored) {
}
}
}
/**
* @param localTexCoords
* texture coordinates relative to the image
* @return the texture coordinates relative to the Texture (atlas) the region is from
*/
private float[] calculateAtlasTexCoords(float[] localTexCoords) {
float uvWidth = this.region.u2 - this.region.u;
float uvHeight = this.region.v2 - this.region.v;
for (int i = 0; i < localTexCoords.length; i += 2) {
localTexCoords[i] = this.region.u + localTexCoords[i] * uvWidth;
localTexCoords[i + 1] = this.region.v + localTexCoords[i + 1] * uvHeight;
}
return localTexCoords;
}
// Returns the vertices in local space
public float[] getLocalVertices() {
return localVertices;
}
// Returns the texture coordinates
public float[] getTextureCoords() {
return texCoords;
}
// Returns the underlying TextureRegion
public TextureRegion getRegion() {
return region;
}
}