@petkoneo/phaser3-rex-plugins
Version:
866 lines (714 loc) • 29.2 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.rexshatterimageplugin = factory());
})(this, (function () { 'use strict';
const Mesh = Phaser.GameObjects.Mesh;
class MeshBase extends Mesh {
get tint() {
if (this.vertices.length === 0) {
return 0xffffff;
} else {
return this.vertices[0].color;
}
}
forceUpdate() {
this.dirtyCache[10] = 1;
return this;
}
}
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var delaunay = {exports: {}};
(function (module) {
// https://github.com/darkskyapp/delaunay-fast/blob/master/delaunay.js
var Delaunay;
(function() {
var EPSILON = 1.0 / 1048576.0;
function supertriangle(vertices) {
var xmin = Number.POSITIVE_INFINITY,
ymin = Number.POSITIVE_INFINITY,
xmax = Number.NEGATIVE_INFINITY,
ymax = Number.NEGATIVE_INFINITY,
i, dx, dy, dmax, xmid, ymid;
for(i = vertices.length; i--; ) {
if(vertices[i][0] < xmin) xmin = vertices[i][0];
if(vertices[i][0] > xmax) xmax = vertices[i][0];
if(vertices[i][1] < ymin) ymin = vertices[i][1];
if(vertices[i][1] > ymax) ymax = vertices[i][1];
}
dx = xmax - xmin;
dy = ymax - ymin;
dmax = Math.max(dx, dy);
xmid = xmin + dx * 0.5;
ymid = ymin + dy * 0.5;
return [
[xmid - 20 * dmax, ymid - dmax],
[xmid , ymid + 20 * dmax],
[xmid + 20 * dmax, ymid - dmax]
];
}
function circumcircle(vertices, i, j, k) {
var x1 = vertices[i][0],
y1 = vertices[i][1],
x2 = vertices[j][0],
y2 = vertices[j][1],
x3 = vertices[k][0],
y3 = vertices[k][1],
fabsy1y2 = Math.abs(y1 - y2),
fabsy2y3 = Math.abs(y2 - y3),
xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy;
/* Check for coincident points */
if(fabsy1y2 < EPSILON && fabsy2y3 < EPSILON)
throw new Error("Eek! Coincident points!");
if(fabsy1y2 < EPSILON) {
m2 = -((x3 - x2) / (y3 - y2));
mx2 = (x2 + x3) / 2.0;
my2 = (y2 + y3) / 2.0;
xc = (x2 + x1) / 2.0;
yc = m2 * (xc - mx2) + my2;
}
else if(fabsy2y3 < EPSILON) {
m1 = -((x2 - x1) / (y2 - y1));
mx1 = (x1 + x2) / 2.0;
my1 = (y1 + y2) / 2.0;
xc = (x3 + x2) / 2.0;
yc = m1 * (xc - mx1) + my1;
}
else {
m1 = -((x2 - x1) / (y2 - y1));
m2 = -((x3 - x2) / (y3 - y2));
mx1 = (x1 + x2) / 2.0;
mx2 = (x2 + x3) / 2.0;
my1 = (y1 + y2) / 2.0;
my2 = (y2 + y3) / 2.0;
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
yc = (fabsy1y2 > fabsy2y3) ?
m1 * (xc - mx1) + my1 :
m2 * (xc - mx2) + my2;
}
dx = x2 - xc;
dy = y2 - yc;
return {i: i, j: j, k: k, x: xc, y: yc, r: dx * dx + dy * dy};
}
function dedup(edges) {
var i, j, a, b, m, n;
for(j = edges.length; j; ) {
b = edges[--j];
a = edges[--j];
for(i = j; i; ) {
n = edges[--i];
m = edges[--i];
if((a === m && b === n) || (a === n && b === m)) {
edges.splice(j, 2);
edges.splice(i, 2);
break;
}
}
}
}
Delaunay = {
triangulate: function(vertices, key) {
var n = vertices.length,
i, j, indices, st, open, closed, edges, dx, dy, a, b, c;
/* Bail if there aren't enough vertices to form any triangles. */
if(n < 3)
return [];
/* Slice out the actual vertices from the passed objects. (Duplicate the
* array even if we don't, though, since we need to make a supertriangle
* later on!) */
vertices = vertices.slice(0);
if(key)
for(i = n; i--; )
vertices[i] = vertices[i][key];
/* Make an array of indices into the vertex array, sorted by the
* vertices' x-position. */
indices = new Array(n);
for(i = n; i--; )
indices[i] = i;
indices.sort(function(i, j) {
return vertices[j][0] - vertices[i][0];
});
/* Next, find the vertices of the supertriangle (which contains all other
* triangles), and append them onto the end of a (copy of) the vertex
* array. */
st = supertriangle(vertices);
vertices.push(st[0], st[1], st[2]);
/* Initialize the open list (containing the supertriangle and nothing
* else) and the closed list (which is empty since we havn't processed
* any triangles yet). */
open = [circumcircle(vertices, n + 0, n + 1, n + 2)];
closed = [];
edges = [];
/* Incrementally add each vertex to the mesh. */
for(i = indices.length; i--; edges.length = 0) {
c = indices[i];
/* For each open triangle, check to see if the current point is
* inside it's circumcircle. If it is, remove the triangle and add
* it's edges to an edge list. */
for(j = open.length; j--; ) {
/* If this point is to the right of this triangle's circumcircle,
* then this triangle should never get checked again. Remove it
* from the open list, add it to the closed list, and skip. */
dx = vertices[c][0] - open[j].x;
if(dx > 0.0 && dx * dx > open[j].r) {
closed.push(open[j]);
open.splice(j, 1);
continue;
}
/* If we're outside the circumcircle, skip this triangle. */
dy = vertices[c][1] - open[j].y;
if(dx * dx + dy * dy - open[j].r > EPSILON)
continue;
/* Remove the triangle and add it's edges to the edge list. */
edges.push(
open[j].i, open[j].j,
open[j].j, open[j].k,
open[j].k, open[j].i
);
open.splice(j, 1);
}
/* Remove any doubled edges. */
dedup(edges);
/* Add a new triangle for each edge. */
for(j = edges.length; j; ) {
b = edges[--j];
a = edges[--j];
open.push(circumcircle(vertices, a, b, c));
}
}
/* Copy any remaining open triangles to the closed list, and then
* remove any triangles that share a vertex with the supertriangle,
* building a list of triplets that represent triangles. */
for(i = open.length; i--; )
closed.push(open[i]);
open.length = 0;
for(i = closed.length; i--; )
if(closed[i].i < n && closed[i].j < n && closed[i].k < n)
open.push(closed[i].i, closed[i].j, closed[i].k);
/* Yay, we're done! */
return open;
},
contains: function(tri, p) {
/* Bounding box test first, for quick rejections. */
if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) ||
(p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) ||
(p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) ||
(p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1]))
return null;
var a = tri[1][0] - tri[0][0],
b = tri[2][0] - tri[0][0],
c = tri[1][1] - tri[0][1],
d = tri[2][1] - tri[0][1],
i = a * d - b * c;
/* Degenerate tri. */
if(i === 0.0)
return null;
var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i,
v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i;
/* If we're outside the tri, fail. */
if(u < 0.0 || v < 0.0 || (u + v) > 1.0)
return null;
return [u, v];
}
};
module.exports = Delaunay;
})();
} (delaunay));
var delaunayExports = delaunay.exports;
var Delaunay = /*@__PURE__*/getDefaultExportFromCjs(delaunayExports);
const Triangle = Phaser.Geom.Triangle;
var Triangulate = function (vertices, triangleResult) {
if (triangleResult === undefined) {
triangleResult = true;
}
var indices = Delaunay.triangulate(vertices);
if (triangleResult) {
var triangles = [];
for (var i = 0, cnt = indices.length; i < cnt; i += 3) {
var p0 = vertices[indices[i + 0]];
var p1 = vertices[indices[i + 1]];
var p2 = vertices[indices[i + 2]];
var triangle = new Triangle(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
triangles.push(triangle);
}
return triangles;
} else {
return {
vertices: vertices,
indices: indices
}
}
};
var IsFunction = function (obj) {
return obj && (typeof(obj) === 'function');
};
const GetValue$2 = Phaser.Utils.Objects.GetValue;
const Clamp = Phaser.Math.Clamp;
const DefaultRingRadiusList$1 = [1 / 27, 3 / 27, 9 / 27];
var ShatterRectangleToTriangles = function (config) {
var left, right, top, bottom, width, height;
var rectangle = config.rectangle;
if (rectangle) {
left = rectangle.x;
top = rectangle.y;
width = rectangle.width;
height = rectangle.height;
} else {
left = 0;
top = 0;
width = config.width;
height = config.height;
}
right = left + width;
bottom = top + height;
var center = config.center;
var centerX, centerY;
if (center === undefined) {
centerX = (left + right) / 2;
centerY = (top + bottom) / 2;
} else {
centerX = Clamp(center.x, left, right);
centerY = Clamp(center.y, top, bottom);
}
var ringRadiusList = GetValue$2(config, 'ringRadiusList', DefaultRingRadiusList$1);
var ringSamples = GetValue$2(config, 'samplesPerRing', 12);
var variation = GetValue$2(config, 'variation', 0.25);
var triangleOutput = GetValue$2(config, 'triangleOutput', true);
if (IsFunction(ringRadiusList)) {
ringRadiusList = ringRadiusList(width, height);
}
var randMin = 1 - variation,
randMax = 1 + variation;
for (var i = 0; i < 10; i++) {
// Can generate triangles 10 times
try {
var vertices = GenerateVertices(
centerX, centerY,
width, height, ringRadiusList, ringSamples,
randMin, randMax,
left, right, top, bottom
);
return Triangulate(vertices, triangleOutput);
} catch (e) {
}
}
throw new Error("Generate triangles fail");
};
var GenerateVertices = function (
centerX, centerY,
width, height, ringRadiusList, ringSamples,
randMin, randMax,
left, right, top, bottom
) {
var vertices = [];
vertices.push([centerX, centerY]);
var radius = Math.min(width, height);
for (var i = 0, cnt = ringRadiusList.length; i < cnt; i++) {
AddRingVertices(
vertices,
centerX, centerY, (radius * ringRadiusList[i]), ringSamples,
randMin, randMax,
left, right, top, bottom
);
}
// Vertices outside of rectangle
var radius = Math.max(width, height) * 2;
AddRingVertices(
vertices,
centerX, centerY, radius, ringSamples,
randMin, randMax,
left, right, top, bottom
);
return vertices;
};
const TWO_PI = Math.PI * 2;
var AddRingVertices = function (
vertices,
centerX, centerY, radius, amount,
randMin, randMax,
leftBound, rightBound, topBound, bottomBound
) {
for (var i = 0; i < amount; i++) {
var rad = (i / amount) * TWO_PI;
var x = centerX + Math.cos(rad) * radius * RandomRange(randMin, randMax);
var y = centerY + Math.sin(rad) * radius * RandomRange(randMin, randMax);
x = Clamp(x, leftBound, rightBound);
y = Clamp(y, topBound, bottomBound);
vertices.push([x, y]);
}
return vertices;
};
var RandomRange = function (min, max) {
if (min === max) {
return min;
} else {
return min + (max - min) * Math.random();
}
};
const Base = Phaser.Geom.Mesh.Face;
const DegToRad = Phaser.Math.DegToRad;
const RadToDeg = Phaser.Math.RadToDeg;
const RotateFace = Phaser.Geom.Mesh.RotateFace;
class Face extends Base {
constructor(vertex1, vertex2, vertex3) {
super(vertex1, vertex2, vertex3);
this._rotation = 0;
}
get rotation() {
return this._rotation;
}
set rotation(value) {
RotateFace(this, (value - this._rotation));
this._rotation = value;
}
setRotation(value) {
this.rotation = value;
return this;
}
get angle() {
return RadToDeg(this.rotation);
}
set angle(value) {
this.rotation = DegToRad(value);
}
setAngle(value) {
this.angle = value;
return this;
}
setAlpha(alpha) {
this.alpha = alpha;
return this;
}
get tint() {
var tint1 = this.vertex1.color;
var tint2 = this.vertex2.color;
var tint3 = this.vertex3.color;
return (((((tint1 >> 0) & 0xff) + ((tint2 >> 0) & 0xff) + ((tint3 >> 0) & 0xff)) / 3) << 0) +
(((((tint1 >> 8) & 0xff) + ((tint2 >> 8) & 0xff) + ((tint3 >> 8) & 0xff)) / 3) << 8) +
(((((tint1 >> 16) & 0xff) + ((tint2 >> 16) & 0xff) + ((tint3 >> 16) & 0xff)) / 3) << 16);
}
set tint(value) {
this.vertex1.color = value;
this.vertex2.color = value;
this.vertex3.color = value;
}
setTint(value) {
this.tint = value;
return this;
}
}
const RotateAround = Phaser.Math.RotateAround;
var WorldXYToLocalXY = function (gameObject, worldX, worldY) {
var ox = gameObject.width / 2;
var oy = gameObject.height / 2;
out.x = worldX - gameObject.x;
out.y = worldY - gameObject.y;
out.x /= gameObject.scaleX;
out.y /= gameObject.scaleY;
RotateAround(out, 0, 0, -gameObject.rotation);
out.x += ox;
out.y += oy;
return out;
};
var out = { x: 0, y: 0 };
const IsPlainObject$1 = Phaser.Utils.Objects.IsPlainObject;
const GetValue$1 = Phaser.Utils.Objects.GetValue;
const GenerateGridVerts = Phaser.Geom.Mesh.GenerateGridVerts;
const Vertex = Phaser.Geom.Mesh.Vertex;
const DistanceSquared = Phaser.Math.Distance.Squared;
const DefaultRingRadiusList = [1 / 27, 3 / 27, 9 / 27];
class ShatterImage extends MeshBase {
constructor(scene, x, y, key, frame, config) {
if (IsPlainObject$1(x)) {
config = x;
x = GetValue$1(config, 'x', 0);
y = GetValue$1(config, 'y', 0);
key = GetValue$1(config, 'key', null);
frame = GetValue$1(config, 'frame', null);
}
super(scene, x, y, key, frame);
this.type = 'rexShatterImage';
this.hideCCW = false;
this.resetImage();
this.shatterCenter = { x: null, y: null };
this.setRingRadiusList(GetValue$1(config, 'ringRadiusList', DefaultRingRadiusList));
this.setSamplesPerRing(GetValue$1(config, 'samplesPerRing', 12));
this.setVariation(GetValue$1(config, 'variation', 0.25));
}
setRingRadiusList(ringRadiusList) {
this.ringRadiusList = ringRadiusList;
return this;
}
setSamplesPerRing(samples) {
this.samplesPerRing = samples;
return this;
}
setVariation(variation) {
this.variation = variation;
return this;
}
resetImage() {
this.setSizeToFrame();
this.clear();
this.dirtyCache[9] = -1;
GenerateGridVerts({
mesh: this,
width: this.frame.cutWidth / this.height,
height: this.frame.cutHeight / this.height,
});
this.setOrtho(this.width / this.height, 1);
return this
}
shatter(centerX, centerY, config) {
if (IsPlainObject$1(centerX)) {
config = centerX;
centerX = undefined;
centerY = undefined;
}
if (IsPlainObject$1(config)) {
if (config.hasOwnProperty('centerX')) {
centerX = config.centerX;
}
if (config.hasOwnProperty('centerY')) {
centerY = config.centerY;
}
if (config.hasOwnProperty('ringRadiusList')) {
this.setRingRadiusList(config.ringRadiusList);
}
if (config.hasOwnProperty('samplesPerRing')) {
this.setSamplesPerRing(config.samplesPerRing);
}
if (config.hasOwnProperty('variation')) {
this.setVariation(config.variation);
}
}
if (centerX === undefined) {
centerX = this.width / 2;
centerY = this.height / 2;
} else {
var worldXY = WorldXYToLocalXY(this, centerX, centerY);
centerX = worldXY.x;
centerY = worldXY.y;
}
this.shatterCenter.x = centerX;
this.shatterCenter.y = centerY;
// Clear faces and vertices
this.clear();
this.dirtyCache[9] = -1;
if ((this.width === 0) || (this.height === 0)) {
return this;
}
var result = ShatterRectangleToTriangles({
width: this.width,
height: this.height,
center: this.shatterCenter,
triangleOutput: false,
ringRadiusList: this.ringRadiusList,
variation: this.variation,
samplesPerRing: this.samplesPerRing
});
var vertices = result.vertices;
var indices = result.indices;
// Calculate vertex data
var verticesData = [];
var srcWidth = this.width;
var srcHeight = this.height;
var vHalfWidth = (this.frame.cutWidth / srcHeight) / 2;
var vHalfHeight = (this.frame.cutHeight / srcHeight) / 2;
var frameU0 = this.frame.u0;
var frameU1 = this.frame.u1;
var frameV0 = this.frame.v0;
var frameV1 = this.frame.v1;
var frameU = frameU1 - frameU0;
var frameV = frameV1 - frameV0;
for (var i = 0, cnt = vertices.length; i < cnt; i++) {
var p = vertices[i];
var px = p[0];
var py = p[1];
verticesData.push({
g: DistanceSquared(centerX, centerY, px, py),
x: (px / srcHeight) - vHalfWidth,
y: (py / srcHeight) - vHalfHeight,
u: frameU0 + (frameU * (px / srcWidth)),
v: frameV0 + (frameV * (py / srcHeight))
});
}
// Build face
for (var i = 0, cnt = indices.length; i < cnt; i += 3) {
var v0 = verticesData[indices[i + 0]];
var v1 = verticesData[indices[i + 1]];
var v2 = verticesData[indices[i + 2]];
var vert1 = new Vertex(v0.x, -v0.y, 0, v0.u, v0.v);
var vert2 = new Vertex(v1.x, -v1.y, 0, v1.u, v1.v);
var vert3 = new Vertex(v2.x, -v2.y, 0, v2.u, v2.v);
var face = new Face(vert1, vert2, vert3);
this.vertices.push(vert1, vert2, vert3);
this.faces.push(face);
// Sort faces from center
face.g = Math.min(v0.g, v1.g, v2.g);
}
// Sort faces from center
this.faces.sort(function (faceA, faceB) {
return faceA.g - faceB.g;
});
return this;
}
startUpdate() {
this.ignoreDirtyCache = true;
return this;
}
stopUpdate() {
this.ignoreDirtyCache = false;
return this;
}
}
function ShatterImageFactory (x, y, texture, frame, config) {
var gameObject = new ShatterImage(this.scene, x, y, texture, frame, config);
this.scene.add.existing(gameObject);
return gameObject;
}
const GetAdvancedValue$1 = Phaser.Utils.Objects.GetAdvancedValue;
const BuildGameObject$1 = Phaser.GameObjects.BuildGameObject;
function ShatterImageCreator (config, addToScene) {
if (config === undefined) { config = {}; }
if (addToScene !== undefined) {
config.add = addToScene;
}
var key = GetAdvancedValue$1(config, 'key', null);
var frame = GetAdvancedValue$1(config, 'frame', null);
var gameObject = new ShatterImage(this.scene, 0, 0, key, frame, config);
BuildGameObject$1(this.scene, gameObject, config);
return gameObject;
}
const DynamicTexture = Phaser.Textures.DynamicTexture;
var CreateDynamicTexture = function (scene, width, height) {
if (width === undefined) {
width = 2;
}
if (height === undefined) {
height = 2;
}
var dt = new DynamicTexture(scene.sys.textures, null, width, height);
return dt;
};
const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
const GetValue = Phaser.Utils.Objects.GetValue;
class RenderTexture extends ShatterImage {
constructor(scene, x, y, width, height, config) {
if (IsPlainObject(x)) {
config = x;
x = GetValue(config, 'x', 0);
y = GetValue(config, 'y', 0);
width = GetValue(config, 'width', 32);
height = GetValue(config, 'height', 32);
}
// dynamic-texture -> quad-image
var texture = CreateDynamicTexture(scene, width, height);
super(scene, x, y, texture, null, config);
this.type = 'rexShatterRenderTexture';
this.rt = this.texture;
}
destroy(fromScene) {
// This Game Object has already been destroyed
if (!this.scene || this.ignoreDestroy) {
return;
}
super.destroy(fromScene);
this.rt.destroy();
this.rt = null;
}
}
function ShatterRenderTextureFactory (x, y, width, height, config) {
var gameObject = new RenderTexture(this.scene, x, y, width, height, config);
this.scene.add.existing(gameObject);
return gameObject;
}
const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
const BuildGameObject = Phaser.GameObjects.BuildGameObject;
function ShatterRenderTextureCreator (config, addToScene) {
if (config === undefined) { config = {}; }
if (addToScene !== undefined) {
config.add = addToScene;
}
var width = GetAdvancedValue(config, 'width', 32);
var height = GetAdvancedValue(config, 'height', 32);
var gameObject = new RenderTexture(this.scene, 0, 0, width, height, config);
BuildGameObject(this.scene, gameObject, config);
return gameObject;
}
var IsInValidKey = function (keys) {
return (keys == null) || (keys === '') || (keys.length === 0);
};
var GetEntry = function (target, keys, defaultEntry) {
var entry = target;
if (IsInValidKey(keys)) ; else {
if (typeof (keys) === 'string') {
keys = keys.split('.');
}
var key;
for (var i = 0, cnt = keys.length; i < cnt; i++) {
key = keys[i];
if ((entry[key] == null) || (typeof (entry[key]) !== 'object')) {
var newEntry;
if (i === cnt - 1) {
if (defaultEntry === undefined) {
newEntry = {};
} else {
newEntry = defaultEntry;
}
} else {
newEntry = {};
}
entry[key] = newEntry;
}
entry = entry[key];
}
}
return entry;
};
var SetValue = function (target, keys, value, delimiter) {
if (delimiter === undefined) {
delimiter = '.';
}
// no object
if (typeof (target) !== 'object') {
return;
}
// invalid key
else if (IsInValidKey(keys)) {
// don't erase target
if (value == null) {
return;
}
// set target to another object
else if (typeof (value) === 'object') {
target = value;
}
} else {
if (typeof (keys) === 'string') {
keys = keys.split(delimiter);
}
var lastKey = keys.pop();
var entry = GetEntry(target, keys);
entry[lastKey] = value;
}
return target;
};
class ShatterImagePlugin extends Phaser.Plugins.BasePlugin {
constructor(pluginManager) {
super(pluginManager);
// Register our new Game Object type
pluginManager.registerGameObject('rexShatterImage', ShatterImageFactory, ShatterImageCreator);
pluginManager.registerGameObject('rexShatterRenderTexture', ShatterRenderTextureFactory, ShatterRenderTextureCreator);
}
start() {
var eventEmitter = this.game.events;
eventEmitter.on('destroy', this.destroy, this);
}
}
SetValue(window, 'RexPlugins.GameObjects.ShatterImage', ShatterImage);
SetValue(window, 'RexPlugins.GameObjects.ShatterRenderTexture', RenderTexture);
return ShatterImagePlugin;
}));