PolygonSprite的特性就是可以动态改变绘制Sprite的顶点数量,顶点数量越多意味着Sprite的三角化程度越细,绘制的空白像素也将越少,可以提高绘制效率,但顶点数量的增加会增加内存消耗。
当一张纹理的像素宽高比较大,空白区域或者接近透明的区域比较多,那么可以使用PolygonSprite。
一、使用示例
二、PolygonInfo:网格数据
// 保存了绘制一个图形需要的所有数据
// 1、顶点数据:3D网格,最简单的情况就是一个三角形
// 2、着色器
// 3、混合
// 4、纹理
// 5、材质
// 不过在这里,主要是处理顶点数据的变化,其他数据不做改变。
// 比如三角形数量增加,顶点数量增加(内存消耗增加),绘制的图形不变,结果是绘制的空白像素将减少,提升绘制效率。
// 这是以空间换时间。
// 当图片文件像素宽高较大,空白较多,那么使用PolygonSprite的效果将比较明显。
class CC_DLL PolygonInfo
{
public:
PolygonInfo(); // 空数据
PolygonInfo(const PolygonInfo& other); // 复制网格数据
PolygonInfo& operator= (const PolygonInfo &other); // 复制网格数据
~PolygonInfo();
// 设置为一个矩形的顶点数据,注意这些顶点数据是从外部传入,因为在这为weak reference
void setQuad(V3F_C4B_T2F_Quad *quad);
// 设置为numberOfQuads个矩形的顶点数据,其他同上。
void setQuads(V3F_C4B_T2F_Quad *quads, int numberOfQuads);
// 设置为一个三角形的顶点数据,其他同上。
void setTriangles(const TrianglesCommand::Triangles& triangles);
unsigned int getVertCount() const; // 顶点数量
unsigned int getTrianglesCount() const; // 三角形数量
float getArea() const; // 获得总面积(三角形总面积)
const Rect& getRect() const; // 如果是一个矩形,比如用来计算使用多边形之后,像素面积变化了多少
void setRect(const Rect& rect); // 又外部来设置
const std::string& getFilename() const; //
void setFilename(const std::string& filename ); //
protected:
bool _isVertsOwner; // 顶点数据是不是在这里管理
Rect _rect; // 一般是设置为如果是普通Sprite时的矩形框
std::string _filename;
TrianglesCommand::Triangles triangles; // 保存的顶点数据
private:
void releaseVertsAndIndices(); // 如果是_isVertsOwner,则要我们自己及时清楚顶点数据及其索引。
};
三、AutoPolygon:生成网格数据
// 是一个工具类,用于动态生成一个图像的2D(三角形)网格数据。
// 输出的就是上面的PolygonInfo。
class CC_DLL AutoPolygon
{
public:
// filename: 必须是一个32bit PNG格式。
AutoPolygon(const std::string &filename);
// 获得image中的rect区域中alpha > threshold的像素
// 沿着rect的outline顺时针遍历寻找。
std::vector<Vec2> trace(const cocos2d::Rect& rect, float threshold = 0.0f);
/**
* reduce the amount of points so its faster for GPU to process and draw
* based on Ramer-Douglas-Peucker algorithm
* @param points a vector of Vec2 points as input
* @param rect a texture rect for specify an area of the image to avoid over reduction
* @param epsilon the perpendicular distance where points smaller than this value will be discarded
* @return a vector of Vec2 of the remaining points in clockwise order
* @code
* auto ap = AutoPolygon();
* std::vector<Vec2> reduced = ap.reduce(inputPoints, rect);//default epsilon is 2
* @endcode
*/
//
std::vector<Vec2> reduce(const std::vector<Vec2>& points, const Rect& rect, float epsilon = 2.0f);
/**
* expand the points along their edge, useful after you reduce the points that cuts into
the sprite
* using ClipperLib
* @param points a vector of Vec2 points as input
* @param rect a texture rect for specify an area of the image, the expanded points will be clamped in this rect, ultimately resulting in a quad if the expansion is too great
* @param epsilon the distance which the edges will expand
* @return a vector of Vec2 as the result of the expansion
* @code
* auto ap = AutoPolygon();
* std::vector<Vec2> expanded = ap.expand(inputPoints, rect, 2.0);
* @endcode
*/
std::vector<Vec2> expand(const std::vector<Vec2>& points, const Rect& rect, float epsilon);
/**
* Triangulate the input points into triangles for rendering
* using poly2tri
* @warning points must be closed loop, cannot have 2 points sharing the same position and cannot intersect itself
* @param points a vector of vec2 points as input
* @return a Triangles object with points and indices
* @code
* auto ap = AutoPolygon();
* TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
* @endcode
*/
TrianglesCommand::Triangles triangulate(const std::vector<Vec2>& points);
/**
* calculate the UV coordinates for each points based on a texture rect
* @warning This method requires the AutoPolygon object to know the texture file dimension
* @param rect a texture rect to specify where to map the UV
* @param verts a pointer to the verts array, served both as input and output verts
* @param count the count for the verts array
* @code
* auto ap = AutoPolygon("grossini.png");
* TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
* ap.calculateUV(rect, myPolygons.verts, 20);
* @endcode
*/
void calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t count);
/**
* packing trace, reduce, expand, triangulate and calculate uv in one function
* @param rect texture rect, use Rect::ZERO for the size of the texture, default is Rect::ZERO
* @param epsilon the value used to reduce and expand, default to 2.0
* @param threshold the value where bigger than the threshold will be counted as opaque, used in trace
* @return a PolygonInfo, to use with sprite
* @code
* auto ap = AutoPolygon("grossini.png");
* PolygonInfo myInfo = ap.generateTriangles();//use all default values
* auto sp1 = Sprite::create(myInfo);
* polygonInfo myInfo2 = ap.generateTriangles(Rect::ZERO, 5.0, 0.1);//ap can be reused to generate another set of PolygonInfo with different settings
* auto sp2 = Sprite::create(myInfo2);
* @endcode
*/
PolygonInfo generateTriangles(const Rect& rect = Rect::ZERO, float epsilon = 2.0f, float threshold = 0.05f);
// 等价于下面:
// AutoPolygon ap(filename);
// ap.generateTriangles(rect, epsilon, threshold);
// 一般用于只需创建一次的情况,因为每次都要通过filename创建image,所以一般采用以下方法:
// AutoPolygon ap(filename); // 创建一次
// ap.generateTriangles(rect, epsilon, threshold); // 多次生成网格数据
// ap.generateTriangles(rect, epsilon, threshold); // 多次生成网格数据
static PolygonInfo generatePolygon(const std::string& filename,
const Rect& rect = Rect::ZERO,
float epsilon = 2.0f,
float threshold = 0.05f);
}