feat: add builder configuration file
feat: add cocos-service configuration file feat: add device configuration file feat: add engine configuration file feat: add information configuration file feat: add program configuration file feat: add project configuration file feat: add TypeScript configuration file
This commit is contained in:
325
assets/script/SplitRender/SplitRender.ts
Normal file
325
assets/script/SplitRender/SplitRender.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
import { SplitRenderHelper } from "./SplitRenderHelper";
|
||||
import { _decorator, Node, Vec3, IAssembler, ccenum, Vec2, Mat4, Texture2D, v2, v3, EventTouch, gfx, SpriteFrame, UIRenderer } from 'cc';
|
||||
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
const vec3_temps: Vec3[] = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
vec3_temps.push(new Vec3());
|
||||
}
|
||||
|
||||
class AssemblerSplit implements IAssembler {
|
||||
createData(com: SplitRender) {
|
||||
let vertexCount = 4;
|
||||
let indexCount = 6;
|
||||
|
||||
const renderData = com.requestRenderData();
|
||||
renderData.dataLength = vertexCount;
|
||||
renderData.resize(vertexCount, indexCount);
|
||||
return renderData;
|
||||
}
|
||||
|
||||
resetData(com: SplitRender) {
|
||||
let points = com.polygon;
|
||||
if (!points || points.length < 3) return;
|
||||
|
||||
let vertexCount = points.length;
|
||||
let indexCount = vertexCount + (vertexCount - 3) * 2;
|
||||
|
||||
com.renderData.clear();
|
||||
com.renderData.dataLength = vertexCount;
|
||||
com.renderData.resize(vertexCount, indexCount);
|
||||
|
||||
let material = com.renderData.material;
|
||||
com.renderData.material = material;
|
||||
}
|
||||
|
||||
updateRenderData(com: SplitRender) {
|
||||
// dynamicAtlasManager.packToDynamicAtlas(com, frame);
|
||||
const renderData = com.renderData;
|
||||
if (renderData.vertDirty) {
|
||||
this.resetData(com);
|
||||
this.updateVertexData(com);
|
||||
this.updateUvs(com);
|
||||
this.updateColor(com);
|
||||
renderData.updateRenderData(com, com.spriteFrame);
|
||||
}
|
||||
}
|
||||
|
||||
updateWorldVerts(com: SplitRender, verts: Float32Array) {
|
||||
let floatsPerVert = 9;
|
||||
|
||||
let matrix: Mat4 = com.node.worldMatrix;
|
||||
let a = matrix.m00, b = matrix.m01, c = matrix.m04, d = matrix.m05,
|
||||
tx = matrix.m12, ty = matrix.m13;
|
||||
|
||||
let justTranslate = a === 1 && b === 0 && c === 0 && d === 1;
|
||||
if (justTranslate) {
|
||||
let polygon = com.polygon;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
verts[i * floatsPerVert] = polygon[i].x + tx;
|
||||
verts[i * floatsPerVert + 1] = polygon[i].y + ty;
|
||||
}
|
||||
} else {
|
||||
let polygon = com.polygon;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
verts[i * floatsPerVert] = a * polygon[i].x + c * polygon[i].y + tx;
|
||||
verts[i * floatsPerVert + 1] = b * polygon[i].x + d * polygon[i].y + ty;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
com.node._uiProps.uiTransformDirty = false;
|
||||
}
|
||||
|
||||
fillBuffers(com: SplitRender, renderer: any) {
|
||||
const chunk = com.renderData.chunk;
|
||||
// indices generated
|
||||
let indicesArr = SplitRenderHelper.splitPolygon(com.polygon);
|
||||
this.updateWorldVerts(com, chunk.vb);
|
||||
|
||||
// quick version
|
||||
const bid = chunk.bufferId;
|
||||
const vid = chunk.vertexOffset;
|
||||
const meshBuffer = chunk.vertexAccessor.getMeshBuffer(bid);
|
||||
const ib = chunk.vertexAccessor.getIndexBuffer(bid);
|
||||
let indexOffset = meshBuffer.indexOffset;
|
||||
|
||||
// fill indices
|
||||
for (let i = 0, l = indicesArr.length; i < l; i++) {
|
||||
ib[indexOffset++] = vid + indicesArr[i];
|
||||
}
|
||||
meshBuffer.indexOffset += indicesArr.length;
|
||||
}
|
||||
|
||||
updateVertexData(com: SplitRender) {
|
||||
const renderData = com.renderData;
|
||||
if (!renderData) {
|
||||
return;
|
||||
}
|
||||
const dataList = renderData.data;
|
||||
|
||||
let polygon = com.polygon;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
dataList[i].x = polygon[i].x;
|
||||
dataList[i].y = polygon[i].y;
|
||||
}
|
||||
|
||||
const chunk = com.renderData.chunk;
|
||||
const vid = chunk.vertexOffset;
|
||||
const ib = chunk.ib as any;
|
||||
|
||||
let indicesArr = SplitRenderHelper.splitPolygon(com.polygon);
|
||||
for (let i = 0, l = indicesArr.length; i < l; i++) {
|
||||
ib[i] = vid + indicesArr[i];
|
||||
}
|
||||
}
|
||||
|
||||
updateUvs(com: SplitRender) {
|
||||
let uvOffset = 3, floatsPerVert = 9;
|
||||
const vData = com.renderData.chunk.vb;
|
||||
|
||||
let uvs = [];
|
||||
if (com.spriteFrame.texture) {
|
||||
uvs = SplitRenderHelper.computeUv(com.polygon, com.spriteFrame.texture.width, com.spriteFrame.texture.height)
|
||||
}
|
||||
|
||||
let polygon = com.polygon;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
vData[uvOffset] = uvs[i].x;
|
||||
vData[uvOffset + 1] = uvs[i].y;
|
||||
uvOffset += floatsPerVert;
|
||||
}
|
||||
}
|
||||
|
||||
updateColor(com: SplitRender) {
|
||||
const renderData = com.renderData!;
|
||||
|
||||
let colorOffset = 5, floatsPerVert = renderData.floatStride;
|
||||
let vData = renderData.chunk.vb;
|
||||
|
||||
const color = com.color;
|
||||
const colorR = color.r / 255;
|
||||
const colorG = color.g / 255;
|
||||
const colorB = color.b / 255;
|
||||
const colorA = color.a / 255;
|
||||
|
||||
let polygon = com.polygon;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
vData![colorOffset] = colorR;
|
||||
vData![colorOffset + 1] = colorG;
|
||||
vData![colorOffset + 2] = colorB;
|
||||
vData![colorOffset + 3] = colorA;
|
||||
colorOffset += floatsPerVert;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum TextureType {
|
||||
Cut, // 裁剪
|
||||
Stretch // 拉伸, 暂未实现
|
||||
}
|
||||
ccenum(TextureType);
|
||||
|
||||
let _vec2_temp = new Vec2();
|
||||
let _mat4_temp = new Mat4();
|
||||
|
||||
@ccclass('SplitRender')
|
||||
@executeInEditMode
|
||||
export class SplitRender extends UIRenderer {
|
||||
static Type = TextureType;
|
||||
|
||||
@property({ type: SpriteFrame, serializable: true })
|
||||
protected _spriteFrame: SpriteFrame | null = null;
|
||||
@property({ type: SpriteFrame, serializable: true })
|
||||
get spriteFrame() {
|
||||
return this._spriteFrame;
|
||||
}
|
||||
|
||||
set spriteFrame(value) {
|
||||
if (!value || this._spriteFrame === value) {
|
||||
this._spriteFrame = value;
|
||||
return;
|
||||
}
|
||||
|
||||
this._spriteFrame = value;
|
||||
|
||||
let l = -value.width / 2, b = -value.height / 2, t = value.height / 2, r = value.width / 2;
|
||||
this.polygon = [v2(l, b), v2(r, b), v2(r, t), v2(l, t)];
|
||||
|
||||
this.markForUpdateRenderData(false);
|
||||
this._applySpriteSize();
|
||||
}
|
||||
|
||||
@property({ type: TextureType, serializable: true })
|
||||
_type: TextureType = 0;
|
||||
@property({ type: TextureType, serializable: true })
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
set type(val: TextureType) {
|
||||
this._type = val;
|
||||
this.markForUpdateRenderData();
|
||||
}
|
||||
|
||||
@property
|
||||
editing: boolean = false;
|
||||
|
||||
@property({ type: [Vec2], serializable: true })
|
||||
_polygon: Vec2[] = [];
|
||||
@property({ type: [Vec2], serializable: true })
|
||||
public get polygon() {
|
||||
return this._polygon;
|
||||
}
|
||||
public set polygon(points: Vec2[]) {
|
||||
this._polygon = points;
|
||||
this.markForUpdateRenderData();
|
||||
}
|
||||
|
||||
protected _assembler: IAssembler = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
onLoad() {
|
||||
this._renderEntity.setNode(this.node);
|
||||
this.node['_hitTest'] = this._hitTest.bind(this);
|
||||
}
|
||||
|
||||
start() {
|
||||
// this.node.on(Node.EventType.TOUCH_START, (e: EventTouch) => {
|
||||
// console.log("click texture plus -");
|
||||
// }, this);
|
||||
|
||||
// this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
|
||||
// console.log("click texture plus +");
|
||||
// this.node.setPosition(v3(this.node.position.x + e.getDeltaX(),
|
||||
// this.node.position.y + e.getDeltaY(),
|
||||
// this.node.position.z));
|
||||
// }, this);
|
||||
// console.log(this.node.uuid);
|
||||
}
|
||||
|
||||
_hitTest(cameraPt: Vec2) {
|
||||
let node = this.node;
|
||||
let testPt = _vec2_temp;
|
||||
|
||||
node.updateWorldTransform();
|
||||
// If scale is 0, it can't be hit.
|
||||
if (!Mat4.invert(_mat4_temp, node.worldMatrix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2.transformMat4(testPt, cameraPt, _mat4_temp);
|
||||
return SplitRenderHelper.isPointInsidePolygon(testPt, this.polygon);
|
||||
}
|
||||
|
||||
private _applySpriteSize() {
|
||||
if (this._spriteFrame) {
|
||||
const size = this._spriteFrame.originalSize;
|
||||
this.node._uiProps.uiTransformComp!.setContentSize(size);
|
||||
}
|
||||
|
||||
this._activateMaterial();
|
||||
}
|
||||
|
||||
private _activateMaterial() {
|
||||
const spriteFrame = this._spriteFrame;
|
||||
const material = this.getRenderMaterial(0);
|
||||
if (spriteFrame) {
|
||||
if (material) {
|
||||
this.markForUpdateRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.renderData) {
|
||||
this.renderData.material = material;
|
||||
}
|
||||
}
|
||||
|
||||
protected _render(render: any) {
|
||||
render.commitComp(this, this.renderData, this._spriteFrame, this._assembler!);
|
||||
}
|
||||
|
||||
protected _canRender() {
|
||||
if (!super._canRender()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const spriteFrame = this._spriteFrame;
|
||||
if (!spriteFrame || !spriteFrame.texture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected _flushAssembler(): void {
|
||||
if (this._assembler == null) {
|
||||
this.destroyRenderData();
|
||||
this._assembler = new AssemblerSplit();
|
||||
}
|
||||
|
||||
if (!this.renderData) {
|
||||
if (this._assembler && this._assembler.createData) {
|
||||
this._renderData = this._assembler.createData(this);
|
||||
this.renderData!.material = this.getRenderMaterial(0);
|
||||
this.markForUpdateRenderData();
|
||||
this._updateColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected updateMaterial() {
|
||||
if (this._customMaterial) {
|
||||
this.setSharedMaterial(this._customMaterial, 0);
|
||||
// this._customMaterial.overridePipelineStates({ priority: 128 }, 0);
|
||||
// this._blendHash = -1;
|
||||
return;
|
||||
}
|
||||
const mat = this._updateBuiltinMaterial();
|
||||
this.setSharedMaterial(mat, 0);
|
||||
this._updateBlendFunc();
|
||||
}
|
||||
}
|
||||
9
assets/script/SplitRender/SplitRender.ts.meta
Normal file
9
assets/script/SplitRender/SplitRender.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ccd6bd1c-e0c8-4fa3-a9e3-60b1929a51d0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
375
assets/script/SplitRender/SplitRenderHelper.ts
Normal file
375
assets/script/SplitRender/SplitRenderHelper.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
import { math, v2, Vec2, Vec3 } from 'cc';
|
||||
|
||||
export class SplitRenderHelper {
|
||||
//ab与ac的叉积
|
||||
static ab_cross_ac(a, b, c) {
|
||||
return SplitRenderHelper.cross(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
|
||||
}
|
||||
static dot(x1, y1, x2, y2) {
|
||||
return x1 * x2 + y1 * y2;
|
||||
}
|
||||
static cross(x1, y1, x2, y2) {
|
||||
return x1 * y2 - x2 * y1;
|
||||
}
|
||||
static dblcmp(a: number, b: number) {
|
||||
if (Math.abs(a - b) <= 0.000001) return 0;
|
||||
if (a > b) return 1;
|
||||
else return -1;
|
||||
}
|
||||
//求a点是不是在线段上,>0不在,=0与端点重合,<0在。
|
||||
static point_on_line(a, p1, p2) {
|
||||
return SplitRenderHelper.dblcmp(SplitRenderHelper.dot(p1.x - a.x, p1.y - a.y, p2.x - a.x, p2.y - a.y), 0);
|
||||
}
|
||||
// 判断一个点是否在三角形内
|
||||
static isInTriangle(point: Vec2, triA: Vec2, triB: Vec2, triC: Vec2) {
|
||||
let AB: Vec2 = new Vec2();
|
||||
Vec2.subtract(AB, triB, triA);
|
||||
|
||||
let AC: Vec2 = new Vec2();
|
||||
Vec2.subtract(AC, triC, triA);
|
||||
|
||||
let BC: Vec2 = new Vec2();
|
||||
Vec2.subtract(BC, triC, triB);
|
||||
|
||||
let AD: Vec2 = new Vec2();
|
||||
Vec2.subtract(AD, point, triA);
|
||||
|
||||
let BD: Vec2 = new Vec2();
|
||||
Vec2.subtract(BD, point, triB);
|
||||
|
||||
//@ts-ignore
|
||||
return (AB.cross(AC) >= 0 ^ AB.cross(AD) < 0) && (AB.cross(AC) >= 0 ^ AC.cross(AD) >= 0) && (BC.cross(AB) > 0 ^ BC.cross(BD) >= 0);
|
||||
}
|
||||
static isInPolygon(checkPoint: Vec2, polygonPoints: Vec2[]) {
|
||||
var counter = 0;
|
||||
var i: number;
|
||||
var xinters;
|
||||
var p1: Vec2, p2: Vec2;
|
||||
var pointCount = polygonPoints.length;
|
||||
p1 = polygonPoints[0];
|
||||
|
||||
for (i = 1; i <= pointCount; i++) {
|
||||
p2 = polygonPoints[i % pointCount];
|
||||
if (
|
||||
checkPoint.x > Math.min(p1.x, p2.x) &&
|
||||
checkPoint.x <= Math.max(p1.x, p2.x)
|
||||
) {
|
||||
if (checkPoint.y <= Math.max(p1.y, p2.y)) {
|
||||
if (p1.x != p2.x) {
|
||||
xinters = (checkPoint.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
|
||||
if (p1.y == p2.y || checkPoint.y <= xinters) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
|
||||
if (counter % 2 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
static computeUv(points: Vec2[], width: number, height: number) {
|
||||
let uvs: Vec2[] = [];
|
||||
for (const p of points) {
|
||||
// uv原点是左上角
|
||||
let x = math.clamp(0, 1, (p.x + width / 2) / width);
|
||||
let y = math.clamp(0, 1, 1. - (p.y + height / 2) / height);
|
||||
uvs.push(v2(x, y));
|
||||
}
|
||||
return uvs;
|
||||
}
|
||||
static splitPolygon(points: Vec2[]): number[] {
|
||||
if (points.length <= 3) return [0, 1, 2];
|
||||
let pointMap: { [key: string]: number } = {}; // point与idx的映射
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let p = points[i];
|
||||
pointMap[`${p.x}-${p.y}`] = i;
|
||||
}
|
||||
const getIdx = (p: Vec2) => {
|
||||
return pointMap[`${p.x}-${p.y}`]
|
||||
}
|
||||
points = points.concat([]);
|
||||
let idxs: number[] = [];
|
||||
|
||||
let index = 0;
|
||||
while (points.length > 3) {
|
||||
let p1 = points[(index) % points.length]
|
||||
, p2 = points[(index + 1) % points.length]
|
||||
, p3 = points[(index + 2) % points.length];
|
||||
let splitPoint = (index + 1) % points.length;
|
||||
|
||||
let v1: Vec2 = new Vec2();
|
||||
Vec2.subtract(v1, p2, p1);
|
||||
let v2: Vec2 = new Vec2();
|
||||
Vec2.subtract(v2, p3, p2);
|
||||
|
||||
if (v1.cross(v2) < 0) { // 是一个凹角, 寻找下一个
|
||||
index = (index + 1) % points.length;
|
||||
continue;
|
||||
}
|
||||
let hasPoint = false;
|
||||
for (const p of points) {
|
||||
if (p != p1 && p != p2 && p != p3 && this.isInTriangle(p, p1, p2, p3)) {
|
||||
hasPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasPoint) { // 当前三角形包含其他点, 寻找下一个
|
||||
index = (index + 1) % points.length;
|
||||
continue;
|
||||
}
|
||||
// 找到了耳朵, 切掉
|
||||
idxs.push(getIdx(p1), getIdx(p2), getIdx(p3));
|
||||
points.splice(splitPoint, 1);
|
||||
}
|
||||
for (const p of points) {
|
||||
idxs.push(getIdx(p));
|
||||
}
|
||||
return idxs;
|
||||
}
|
||||
//点发出的右射线和线段的关系
|
||||
// 返回值: -1:不相交, 0:相交, 1:点在线段上
|
||||
static rayPointToLine(point: Vec2, linePA: Vec2, linePB: Vec2) {
|
||||
// 定义最小和最大的X Y轴值
|
||||
let minX = Math.min(linePA.x, linePB.x);
|
||||
let maxX = Math.max(linePA.x, linePB.x);
|
||||
let minY = Math.min(linePA.y, linePB.y);
|
||||
let maxY = Math.max(linePA.y, linePB.y);
|
||||
|
||||
// 射线与边无交点的其他情况
|
||||
if (point.y < minY || point.y > maxY || point.x > maxX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 剩下的情况, 计算射线与边所在的直线的交点的横坐标
|
||||
let x0 = linePA.x + ((linePB.x - linePA.x) / (linePB.y - linePA.y)) * (point.y - linePA.y);
|
||||
if (x0 > point.x) {
|
||||
return 0;
|
||||
}
|
||||
if (x0 == point.x) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//点和多边形的关系
|
||||
//返回值: -1:在多边形外部, 0:在多边形内部, 1:在多边形边线内, 2:跟多边形某个顶点重合
|
||||
static relationPointToPolygon(point: Vec2, polygon: Vec2[]) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < polygon.length; ++i) {
|
||||
if (polygon[i].equals(point)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
let pa = polygon[i];
|
||||
let pb = polygon[0];
|
||||
if (i < polygon.length - 1) {
|
||||
pb = polygon[i + 1];
|
||||
}
|
||||
|
||||
let re = SplitRenderHelper.rayPointToLine(point, pa, pb);
|
||||
if (re == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (re == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count % 2 == 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//求两条线段的交点
|
||||
//返回值:[n,p] n:0相交,1在共有点,-1不相交 p:交点
|
||||
static lineCrossPoint(p1: Vec2, p2: Vec2, q1: Vec2, q2: Vec2): [number, Vec2] {
|
||||
let a = p1, b = p2, c = q1, d = q2;
|
||||
let s1, s2, s3, s4;
|
||||
let d1, d2, d3, d4;
|
||||
let p: Vec2 = new Vec2(0, 0);
|
||||
|
||||
d1 = SplitRenderHelper.dblcmp(s1 = SplitRenderHelper.ab_cross_ac(a, b, c), 0);
|
||||
d2 = SplitRenderHelper.dblcmp(s2 = SplitRenderHelper.ab_cross_ac(a, b, d), 0);
|
||||
d3 = SplitRenderHelper.dblcmp(s3 = SplitRenderHelper.ab_cross_ac(c, d, a), 0);
|
||||
d4 = SplitRenderHelper.dblcmp(s4 = SplitRenderHelper.ab_cross_ac(c, d, b), 0);
|
||||
|
||||
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) {
|
||||
p.x = (c.x * s2 - d.x * s1) / (s2 - s1);
|
||||
p.y = (c.y * s2 - d.y * s1) / (s2 - s1);
|
||||
return [0, p];
|
||||
}
|
||||
|
||||
if (d1 == 0 && SplitRenderHelper.point_on_line(c, a, b) <= 0) {
|
||||
p = c;
|
||||
return [1, p];
|
||||
}
|
||||
if (d2 == 0 && SplitRenderHelper.point_on_line(d, a, b) <= 0) {
|
||||
p = d;
|
||||
return [1, p];
|
||||
}
|
||||
if (d3 == 0 && SplitRenderHelper.point_on_line(a, c, d) <= 0) {
|
||||
p = a;
|
||||
return [1, p];
|
||||
}
|
||||
if (d4 == 0 && SplitRenderHelper.point_on_line(b, c, d) <= 0) {
|
||||
p = b;
|
||||
return [1, p];
|
||||
}
|
||||
return [-1, null];
|
||||
}
|
||||
//线段对多边形进行切割
|
||||
//返回多边形数组
|
||||
//如果没有被切割,返回空数组
|
||||
static lineCutPolygon(pa: Vec2, pb: Vec2, polygon: Vec2[]) {
|
||||
// 检查切割线的端点是否在多边形内部
|
||||
const extendPoint = (point: Vec2, direction: Vec2) => {
|
||||
const extendedPoint = new Vec2(point.x + direction.x * 1000, point.y + direction.y * 1000);
|
||||
return extendedPoint;
|
||||
};
|
||||
|
||||
if (SplitRenderHelper.isInPolygon(pa, polygon)) {
|
||||
const direction = Vec2.subtract(new Vec2(), pa, pb).normalize();
|
||||
pa = extendPoint(pa, direction);
|
||||
}
|
||||
if (SplitRenderHelper.isInPolygon(pb, polygon)) {
|
||||
const direction = Vec2.subtract(new Vec2(), pb, pa).normalize();
|
||||
pb = extendPoint(pb, direction);
|
||||
}
|
||||
|
||||
let ret: Array<Vec2[]> = [];
|
||||
let points: Vec2[] = [];
|
||||
let pointIndex: number[] = [];
|
||||
|
||||
for (let i = 0; i < polygon.length; ++i) {
|
||||
points.push(polygon[i]);
|
||||
|
||||
let a = polygon[i];
|
||||
let b = polygon[0];
|
||||
if (i < polygon.length - 1) b = polygon[i + 1];
|
||||
|
||||
let c = SplitRenderHelper.lineCrossPoint(pa, pb, a, b);
|
||||
if (c[0] == 0) {
|
||||
pointIndex.push(points.length);
|
||||
points.push(c[1] as Vec2);
|
||||
}
|
||||
else if (c[0] > 0) {
|
||||
if ((c[1] as Vec2).equals(a)) {
|
||||
pointIndex.push(points.length - 1);
|
||||
}
|
||||
else {
|
||||
pointIndex.push(points.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pointIndex.length > 1) {
|
||||
let cp0 = points[pointIndex[0]];
|
||||
let cp1 = points[pointIndex[1]];
|
||||
|
||||
let r = SplitRenderHelper.relationPointToPolygon(new Vec2((cp0.x + cp1.x) / 2, (cp0.y + cp1.y) / 2), polygon);
|
||||
let inPolygon: boolean = r >= 0;
|
||||
|
||||
let cp0_cp1: Vec2 = new Vec2();
|
||||
let len_0_1 = Vec2.subtract(cp0_cp1, cp0, cp1).length()
|
||||
|
||||
let cp0_cp: Vec2 = new Vec2();
|
||||
let len_0_ = Vec2.subtract(cp0_cp, cp0, points[pointIndex[pointIndex.length - 1]]).length()
|
||||
|
||||
if (pointIndex.length > 2 && len_0_1 > len_0_) {
|
||||
cp1 = points[pointIndex[pointIndex.length - 1]];
|
||||
r = SplitRenderHelper.relationPointToPolygon(new Vec2((cp0.x + cp1.x) / 2, (cp0.y + cp1.y) / 2), polygon);
|
||||
inPolygon = r < 0;
|
||||
}
|
||||
|
||||
let firstInPolygon = inPolygon;
|
||||
|
||||
let index = 0;
|
||||
let startIndex = pointIndex[index];
|
||||
let oldPoints = [];
|
||||
let newPoints = [];
|
||||
let count = 0;
|
||||
|
||||
oldPoints.push(points[startIndex]);
|
||||
if (inPolygon) {
|
||||
newPoints.push(points[startIndex]);
|
||||
}
|
||||
|
||||
index++;
|
||||
count++;
|
||||
startIndex++;
|
||||
|
||||
while (count < points.length) {
|
||||
if (startIndex == points.length) startIndex = 0;
|
||||
let p = points[startIndex];
|
||||
if (index >= 0 && startIndex == pointIndex[index]) {
|
||||
index++;
|
||||
if (index >= pointIndex.length) index = 0;
|
||||
if (inPolygon) {
|
||||
newPoints.push(p);
|
||||
ret.push(newPoints);
|
||||
newPoints = [];
|
||||
}
|
||||
else {
|
||||
newPoints = [];
|
||||
newPoints.push(p);
|
||||
}
|
||||
oldPoints.push(p);
|
||||
inPolygon = !inPolygon;
|
||||
}
|
||||
else {
|
||||
if (inPolygon) {
|
||||
newPoints.push(p);
|
||||
}
|
||||
else {
|
||||
oldPoints.push(p);
|
||||
}
|
||||
}
|
||||
startIndex++;
|
||||
count++;
|
||||
}
|
||||
if (inPolygon) {
|
||||
if (!firstInPolygon && newPoints.length > 1) {
|
||||
newPoints.push(points[pointIndex[0]]);
|
||||
ret.push(newPoints);
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < newPoints.length; ++i) {
|
||||
oldPoints.push(newPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ret.push(oldPoints);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// 实用函数
|
||||
static isPointInsidePolygon(p: Vec2, polygon: Vec2[]) {
|
||||
let windingNumber = 0;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
const a = polygon[i];
|
||||
const b = polygon[(i + 1) % polygon.length];
|
||||
if (a.y <= p.y) {
|
||||
if (b.y > p.y && (b.x - a.x) * (p.y - a.y) > (p.x - a.x) * (b.y - a.y))
|
||||
windingNumber++;
|
||||
} else {
|
||||
if (b.y <= p.y && (b.x - a.x) * (p.y - a.y) < (p.x - a.x) * (b.y - a.y))
|
||||
windingNumber--;
|
||||
}
|
||||
}
|
||||
return windingNumber !== 0;
|
||||
}
|
||||
static calculatePolygonArea(polygon: Vec2[]) {
|
||||
let area = 0;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
const j = (i + 1) % polygon.length;
|
||||
area += polygon[i].x * polygon[j].y;
|
||||
area -= polygon[j].x * polygon[i].y;
|
||||
}
|
||||
return Math.abs(area) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/script/SplitRender/SplitRenderHelper.ts.meta
Normal file
9
assets/script/SplitRender/SplitRenderHelper.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e6b2c70f-62b1-4940-988f-c42a322beca3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
219
assets/script/SplitRender/ToolsSplit.ts
Normal file
219
assets/script/SplitRender/ToolsSplit.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import { SplitRenderHelper } from "./SplitRenderHelper";
|
||||
import { SplitRender } from "./SplitRender";
|
||||
|
||||
import { _decorator, Component, Node, Texture2D, Graphics, Vec2, view, Vec3, SpriteFrame, EventTouch, v2, tween, Camera, color, Layers, profiler, UITransform, Sprite, RigidBody2D, ERigidBody2DType, PolygonCollider2D, Physics2DUtils, PhysicsSystem2D, ERaycast2DType, UIOpacity, Collider, Collider2D, director, Director, misc, Label, Color } from 'cc';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('ToolsSplit')
|
||||
export default class ToolsSplit extends Component {
|
||||
@property(Node)
|
||||
textureRoot: Node = null;
|
||||
|
||||
@property(Graphics)
|
||||
|
||||
graphics: Graphics = null;
|
||||
|
||||
@property(SpriteFrame)
|
||||
pic: SpriteFrame = null;
|
||||
|
||||
@property(Camera)
|
||||
cam: Camera = null;
|
||||
|
||||
@property(Label)
|
||||
moveStepLabel: Label = null;
|
||||
|
||||
private textures: SplitRender[] = [];
|
||||
private startPoint: Vec2 = null;
|
||||
private endPoint: Vec2 = null;
|
||||
|
||||
gameWidth: number = 0;
|
||||
gameHeight: number = 0;
|
||||
|
||||
canTouch: boolean = true;
|
||||
|
||||
protected onLoad(): void {
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
profiler.hideStats();
|
||||
|
||||
this.init();
|
||||
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
|
||||
this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
|
||||
this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
|
||||
this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
|
||||
this.graphics.node.setPosition(new Vec3(-view.getVisibleSize().width / 2, -view.getVisibleSize().height / 2));
|
||||
}
|
||||
|
||||
init() {
|
||||
// let node = new Node();
|
||||
// let t = node.addComponent(SplitRender);
|
||||
// node.parent = this.textureRoot;
|
||||
// node.layer = Layers.Enum.UI_2D;
|
||||
// t.spriteFrame = this.pic;
|
||||
// this.textures.push(t);
|
||||
|
||||
this.gameWidth = view.getVisibleSize().width;
|
||||
this.gameHeight = view.getVisibleSize().height;
|
||||
|
||||
this.node.getComponent(UITransform).setContentSize(this.gameWidth, this.gameHeight);
|
||||
}
|
||||
|
||||
setTextures(textures: SplitRender[]) {
|
||||
for (let i = 0; i < textures.length; i++) {
|
||||
const texture = textures[i];
|
||||
this.textures.push(texture);
|
||||
}
|
||||
}
|
||||
|
||||
setPerTexture(texture: SplitRender) {
|
||||
this.textures.push(texture);
|
||||
}
|
||||
|
||||
onTouchStart(e: EventTouch) {
|
||||
if (this.moveStepLabel.string == "0") {
|
||||
return;
|
||||
}
|
||||
if (!this.canTouch) {
|
||||
return;
|
||||
}
|
||||
this.startPoint = e.getUILocation();
|
||||
}
|
||||
|
||||
onTouchMove(e: EventTouch) {
|
||||
if (this.moveStepLabel.string == "0") {
|
||||
return;
|
||||
}
|
||||
if (!this.canTouch) {
|
||||
return;
|
||||
}
|
||||
this.graphics.clear();
|
||||
this.graphics.moveTo(this.startPoint.x, this.startPoint.y);
|
||||
let p = e.getUILocation();
|
||||
this.graphics.lineTo(p.x, p.y);
|
||||
this.graphics.stroke();
|
||||
}
|
||||
|
||||
onTouchEnd(e: EventTouch) {
|
||||
if (this.moveStepLabel.string == "0") {
|
||||
return;
|
||||
}
|
||||
if (!this.canTouch) {
|
||||
return;
|
||||
}
|
||||
this.canTouch = false;
|
||||
this.graphics.clear();
|
||||
this.endPoint = e.getUILocation();
|
||||
}
|
||||
|
||||
|
||||
cutPolygonCollider2D(originPolygon: Vec2[], startPoint: Vec2, endPoint: Vec2) {
|
||||
let newPolygon = SplitRenderHelper.lineCutPolygon(startPoint, endPoint, originPolygon);
|
||||
return newPolygon;
|
||||
}
|
||||
|
||||
private doSplit() {
|
||||
let h = this.pic.height, w = this.pic.width;
|
||||
for (let i = 0; i < 15; i++) {
|
||||
let p0 = v2(-(w / 2 + 10), (Math.random() * h) - h / 2);
|
||||
let p1 = v2(w / 2 + 10, (Math.random() * h) - h / 2);
|
||||
this.useLineCutPolygon(p0, p1, false);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
let p0 = v2(Math.random() * w - w / 2, -(h / 2 + 10));
|
||||
let p1 = v2(Math.random() * w - w / 2, (h / 2 + 10));
|
||||
this.useLineCutPolygon(p0, p1, false);
|
||||
}
|
||||
}
|
||||
|
||||
private useLineCutPolygon(p0: Vec2, p1: Vec2, isWorld = true) {
|
||||
for (let i = this.textures.length - 1; i >= 0; i--) {
|
||||
let texture = this.textures[i];
|
||||
let pa = p0.clone();
|
||||
let pb = p1.clone();
|
||||
if (isWorld) {
|
||||
let mat = texture.node.worldMatrix.clone().invert();
|
||||
pa = pa.transformMat4(mat);
|
||||
pb = pb.transformMat4(mat);
|
||||
}
|
||||
let polygons = SplitRenderHelper.lineCutPolygon(pa, pb, texture.polygon);
|
||||
if (polygons.length <= 0) {
|
||||
console.log("No Polygon")
|
||||
continue
|
||||
};
|
||||
this.splitTexture(texture, polygons);
|
||||
}
|
||||
}
|
||||
|
||||
private splitTexture(texture: SplitRender, polygons: Vec2[][]) {
|
||||
texture.polygon = polygons[0];
|
||||
let newnode: Node = null;
|
||||
for (let i = 1; i < polygons.length; i++) {
|
||||
let node = new Node("Split");
|
||||
newnode = node;
|
||||
node.layer = Layers.Enum.UI_2D;
|
||||
let t = node.addComponent(SplitRender);
|
||||
node.parent = this.textureRoot;
|
||||
node.setPosition(new Vec3(texture.node.position.x, texture.node.position.y));
|
||||
node.setRotationFromEuler(texture.node.eulerAngles.x, texture.node.eulerAngles.y, texture.node.eulerAngles.z);
|
||||
t.spriteFrame = texture.spriteFrame;
|
||||
t.polygon = polygons[i];
|
||||
|
||||
this.textures.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
onClickFly() {
|
||||
for (let i = 0; i < this.textures.length; i++) {
|
||||
let center = this.getPolygonCenter(this.textures[i].polygon);
|
||||
let dir = center.normalize();
|
||||
tween(this.textures[i].node).by(0.5, { position: new Vec3(dir.x * 100, dir.y * 100, 0) }).start();
|
||||
}
|
||||
}
|
||||
|
||||
onClickReset() {
|
||||
for (let i = 0; i < this.textures.length; i++) {
|
||||
let center = this.getPolygonCenter(this.textures[i].polygon);
|
||||
let dir = center.normalize();
|
||||
tween(this.textures[i].node).by(0.5, { position: new Vec3(-dir.x * 100, -dir.y * 100, 0) }).call(() => {
|
||||
if (i === this.textures.length - 1) {
|
||||
this.textureRoot.destroyAllChildren();
|
||||
this.textureRoot.removeAllChildren();
|
||||
this.textures = [];
|
||||
this.init();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
onFallDown() {
|
||||
for (let i = 0; i < this.textures.length; i++) {
|
||||
let center = this.getPolygonCenter(this.textures[i].polygon);
|
||||
tween(this.textures[i].node).delay((center.y + this.pic.height) / this.pic.height).by(2, { position: new Vec3(0, -500, 0) }, { easing: 'circIn' }).start();
|
||||
}
|
||||
}
|
||||
onResetFallDown() {
|
||||
this.textureRoot.destroyAllChildren();
|
||||
this.textureRoot.removeAllChildren();
|
||||
this.textures = [];
|
||||
this.init();
|
||||
}
|
||||
|
||||
private getPolygonCenter(polygon: Vec2[]) {
|
||||
let x = 0, y = 0;
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
x += polygon[i].x;
|
||||
y += polygon[i].y;
|
||||
}
|
||||
x = x / polygon.length;
|
||||
y = y / polygon.length;
|
||||
return v2(x, y)
|
||||
}
|
||||
|
||||
getTextureList() {
|
||||
return this.textures;
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/script/SplitRender/ToolsSplit.ts.meta
Normal file
9
assets/script/SplitRender/ToolsSplit.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0ad0fcaa-5dac-47ec-8cc4-e0c36da51296",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user