资源拆分
This commit is contained in:
1 parent
f810e712ec
commit
83890faaea
38 files changed
+4586
-4144
No files matched your search
Vendored
-4
@@ -10,9 +10,5 @@
|
|||||||
{
|
{
|
||||||
"name": "粒子-序列帧-拖尾-碰撞-路径-spine",
|
"name": "粒子-序列帧-拖尾-碰撞-路径-spine",
|
||||||
"file": "粒子-序列帧-拖尾-碰撞-路径-spine.json"
|
"file": "粒子-序列帧-拖尾-碰撞-路径-spine.json"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "烟花金币spine",
|
|
||||||
"file": "烟花金币spine.json"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
.help-overlay[data-v-ae69ecd7]{position:absolute;top:0;right:0;bottom:0;left:0;z-index:40;display:flex;align-items:center;justify-content:center;padding:72px 24px 28px;box-sizing:border-box;background:#05080fb8;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.help-dialog[data-v-ae69ecd7]{width:min(820px,100%);max-height:100%;display:flex;flex-direction:column;overflow:hidden;border:1px solid #3a4864;border-radius:14px;background:#141a27;color:#b8c3d8;box-shadow:0 22px 70px #0000009e}.help-head[data-v-ae69ecd7]{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:18px 20px 15px;border-bottom:1px solid #2b354a;background:linear-gradient(135deg,#202942,#181e2d)}.help-head h2[data-v-ae69ecd7]{margin:0;color:#eef2ff;font-size:19px}.help-head p[data-v-ae69ecd7]{margin:5px 0 0;color:#7786a3;font-size:11px}.help-close[data-v-ae69ecd7]{width:28px;height:28px;flex:0 0 28px;border:1px solid #43516b;border-radius:6px;background:#273249;color:#ccd7ed;cursor:pointer}.help-close[data-v-ae69ecd7]:hover{border-color:#7774ff;background:#3b4670;color:#fff}.help-body[data-v-ae69ecd7]{min-height:0;overflow:auto;padding:18px 20px 21px}.help-section[data-v-ae69ecd7]{padding:14px 15px;border:1px solid #2c3850;border-radius:10px;background:#181f2e}.help-section h3[data-v-ae69ecd7]{margin:0 0 11px;color:#9fb2ff;font-size:13px}.help-start ol[data-v-ae69ecd7]{margin:0;padding-left:21px;color:#aab6cc;font-size:12px;line-height:1.75}.help-start li[data-v-ae69ecd7]::marker{color:#7774ff;font-weight:700}.help-section b[data-v-ae69ecd7]{color:#dce4f7}.help-grid[data-v-ae69ecd7]{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(250px,.85fr);gap:12px;margin-top:12px}.help-list[data-v-ae69ecd7]{display:grid;gap:0;margin:0}.help-list>div[data-v-ae69ecd7]{display:grid;grid-template-columns:78px 1fr;gap:9px;padding:7px 0;border-bottom:1px solid #263146}.help-list>div[data-v-ae69ecd7]:last-child{border-bottom:0}.help-list dt[data-v-ae69ecd7]{color:#d3dcf0;font-size:11px;font-weight:700}.help-list dd[data-v-ae69ecd7]{margin:0;color:#8795ae;font-size:11px;line-height:1.5}.shortcut-list[data-v-ae69ecd7]{display:grid;gap:7px}.shortcut-list>div[data-v-ae69ecd7]{display:flex;align-items:center;min-height:25px;gap:5px}.shortcut-list kbd[data-v-ae69ecd7]{min-width:24px;box-sizing:border-box;padding:3px 6px;border:1px solid #465470;border-bottom-width:2px;border-radius:5px;background:#101623;color:#e0e7f6;font:10px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;text-align:center}.shortcut-list span[data-v-ae69ecd7]{color:#65728b;font-size:10px}.shortcut-list em[data-v-ae69ecd7]{margin-left:auto;color:#9ca9c1;font-size:11px;font-style:normal}.help-note[data-v-ae69ecd7]{margin:11px 0 0;padding-top:9px;border-top:1px solid #2a3549;color:#697791;font-size:10px;line-height:1.55}.help-export[data-v-ae69ecd7]{margin-top:12px}.help-export p[data-v-ae69ecd7]{margin:0;color:#8997b0;font-size:11px;line-height:1.65}.help-doc-title[data-v-ae69ecd7]{margin:16px 2px 2px;color:#9fb2ff;font-size:13px;font-weight:700;letter-spacing:.5px}.help-module[data-v-ae69ecd7]{margin-top:12px}.help-module-head[data-v-ae69ecd7]{display:flex;align-items:center;gap:8px;width:100%;padding:0;border:0;background:transparent;color:#9fb2ff;font:inherit;font-size:13px;font-weight:700;text-align:left;cursor:pointer}.help-module-head[data-v-ae69ecd7]:hover{color:#fff}.help-module-head .mtitle[data-v-ae69ecd7]{flex:1;min-width:0}.help-module-head .caret[data-v-ae69ecd7]{flex:0 0 auto;color:#7b86a2;font-size:12px}.help-module.open .help-module-head[data-v-ae69ecd7]{color:#fff}.help-module.open .help-module-head .caret[data-v-ae69ecd7]{color:#9fb2ff}.help-module .mnum[data-v-ae69ecd7]{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;background:linear-gradient(135deg,#4a53b8,#6a5fd0);color:#fff;font-size:12px;font-weight:700}.help-module.help-connector[data-v-ae69ecd7]{border-color:#4a4f8a;background:linear-gradient(135deg,#1d2442,#181d2f)}.help-module.help-connector .help-module-head[data-v-ae69ecd7]{color:#b8c2ff}.help-module .connector-intro[data-v-ae69ecd7]{margin:0 0 10px;color:#9aa8c4;font-size:11px;line-height:1.6}.help-module .connector-intro b[data-v-ae69ecd7]{color:#dce4f7}.help-module .dl-link[data-v-ae69ecd7]{display:inline-flex;flex-direction:column;align-items:flex-start;gap:2px;margin:0 10px 6px 0;padding:6px 12px;border:1px solid #5652b5;border-radius:6px;background:#4540a0;color:#fff;font-size:12px;font-weight:700;text-decoration:none}.help-module .dl-link[data-v-ae69ecd7]:hover{border-color:#8982ff;background:#554dc2}.help-module .dl-link small[data-v-ae69ecd7]{color:#cfe0ff;font-weight:500;font-size:9px}.help-module-body[data-v-ae69ecd7]{padding-top:10px}@media(max-width:760px){.help-overlay[data-v-ae69ecd7]{padding:66px 12px 14px}.help-grid[data-v-ae69ecd7]{grid-template-columns:1fr}}.stage-wrap[data-v-373478e7]{position:relative;width:100%;height:100%;overfloLine truncated
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
.help-overlay[data-v-12773018]{position:absolute;top:0;right:0;bottom:0;left:0;z-index:40;display:flex;align-items:center;justify-content:center;padding:72px 24px 28px;box-sizing:border-box;background:#05080fb8;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.help-dialog[data-v-12773018]{width:min(820px,100%);max-height:100%;display:flex;flex-direction:column;overflow:hidden;border:1px solid #3a4864;border-radius:14px;background:#141a27;color:#b8c3d8;box-shadow:0 22px 70px #0000009e}.help-head[data-v-12773018]{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:18px 20px 15px;border-bottom:1px solid #2b354a;background:linear-gradient(135deg,#202942,#181e2d)}.help-head h2[data-v-12773018]{margin:0;color:#eef2ff;font-size:19px}.help-head p[data-v-12773018]{margin:5px 0 0;color:#7786a3;font-size:11px}.help-close[data-v-12773018]{width:28px;height:28px;flex:0 0 28px;border:1px solid #43516b;border-radius:6px;background:#273249;color:#ccd7ed;cursor:pointer}.help-close[data-v-12773018]:hover{border-color:#7774ff;background:#3b4670;color:#fff}.help-body[data-v-12773018]{min-height:0;overflow:auto;padding:18px 20px 21px}.help-section[data-v-12773018]{padding:14px 15px;border:1px solid #2c3850;border-radius:10px;background:#181f2e}.help-section h3[data-v-12773018]{margin:0 0 11px;color:#9fb2ff;font-size:13px}.help-start ol[data-v-12773018]{margin:0;padding-left:21px;color:#aab6cc;font-size:12px;line-height:1.75}.help-start li[data-v-12773018]::marker{color:#7774ff;font-weight:700}.help-section b[data-v-12773018]{color:#dce4f7}.help-grid[data-v-12773018]{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(250px,.85fr);gap:12px;margin-top:12px}.help-list[data-v-12773018]{display:grid;gap:0;margin:0}.help-list>div[data-v-12773018]{display:grid;grid-template-columns:78px 1fr;gap:9px;padding:7px 0;border-bottom:1px solid #263146}.help-list>div[data-v-12773018]:last-child{border-bottom:0}.help-list dt[data-v-12773018]{color:#d3dcf0;font-size:11px;font-weight:700}.help-list dd[data-v-12773018]{margin:0;color:#8795ae;font-size:11px;line-height:1.5}.shortcut-list[data-v-12773018]{display:grid;gap:7px}.shortcut-list>div[data-v-12773018]{display:flex;align-items:center;min-height:25px;gap:5px}.shortcut-list kbd[data-v-12773018]{min-width:24px;box-sizing:border-box;padding:3px 6px;border:1px solid #465470;border-bottom-width:2px;border-radius:5px;background:#101623;color:#e0e7f6;font:10px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;text-align:center}.shortcut-list span[data-v-12773018]{color:#65728b;font-size:10px}.shortcut-list em[data-v-12773018]{margin-left:auto;color:#9ca9c1;font-size:11px;font-style:normal}.help-note[data-v-12773018]{margin:11px 0 0;padding-top:9px;border-top:1px solid #2a3549;color:#697791;font-size:10px;line-height:1.55}.help-export[data-v-12773018]{margin-top:12px}.help-export p[data-v-12773018]{margin:0;color:#8997b0;font-size:11px;line-height:1.65}.help-doc-title[data-v-12773018]{margin:16px 2px 2px;color:#9fb2ff;font-size:13px;font-weight:700;letter-spacing:.5px}.help-module[data-v-12773018]{margin-top:12px}.help-module-head[data-v-12773018]{display:flex;align-items:center;gap:8px;width:100%;padding:0;border:0;background:transparent;color:#9fb2ff;font:inherit;font-size:13px;font-weight:700;text-align:left;cursor:pointer}.help-module-head[data-v-12773018]:hover{color:#fff}.help-module-head .mtitle[data-v-12773018]{flex:1;min-width:0}.help-module-head .caret[data-v-12773018]{flex:0 0 auto;color:#7b86a2;font-size:12px}.help-module.open .help-module-head[data-v-12773018]{color:#fff}.help-module.open .help-module-head .caret[data-v-12773018]{color:#9fb2ff}.help-module .mnum[data-v-12773018]{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;background:linear-gradient(135deg,#4a53b8,#6a5fd0);color:#fff;font-size:12px;font-weight:700}.help-module.help-connector[data-v-12773018]{border-color:#4a4f8a;background:linear-gradient(135deg,#1d2442,#181d2f)}.help-module.help-connector .help-module-head[data-v-12773018]{color:#b8c2ff}.help-module .connector-intro[data-v-12773018]{margin:0 0 10px;color:#9aa8c4;font-size:11px;line-height:1.6}.help-module .connector-intro b[data-v-12773018]{color:#dce4f7}.help-module .dl-link[data-v-12773018]{display:inline-flex;flex-direction:column;align-items:flex-start;gap:2px;margin:0 10px 6px 0;padding:6px 12px;border:1px solid #5652b5;border-radius:6px;background:#4540a0;color:#fff;font-size:12px;font-weight:700;text-decoration:none}.help-module .dl-link[data-v-12773018]:hover{border-color:#8982ff;background:#554dc2}.help-module .dl-link small[data-v-12773018]{color:#cfe0ff;font-weight:500;font-size:9px}.help-module-body[data-v-12773018]{padding-top:10px}@media(max-width:760px){.help-overlay[data-v-12773018]{padding:66px 12px 14px}.help-grid[data-v-12773018]{grid-template-columns:1fr}}.stage-wrap[data-v-659e6fc2]{position:relative;width:100%;height:100%;overfloLine truncated
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
var ft=Object.defineProperty;var gt=(W,t,e)=>t in W?ft(W,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):W[t]=e;var T=(W,t,e)=>gt(W,typeof t!="symbol"?t+"":t,e);import{f as ht,R as pt,N as dt,ac as ut,Z as mt,b4 as yt,O as rt,b5 as at,U as D,B as Tt}from"./index-D08n0lXm.js";import{$ as Rt,a0 as Yt,a1 as Ft,a2 as Et,y as _t,a3 as Ot,a4 as kt,a5 as Xt,a6 as Bt,a7 as Nt,a8 as Dt,a9 as $t,aa as zt,ab as Wt,ad as qt,H as Gt,ae as Jt,af as Ht,ag as Zt,ah as Kt,ai as Qt,aj as jt,ak as te,al as ee,am as se,an as ne,ao as ie,ap as oe,aq as re,ar as ae,as as le,at as ce,au as he,av as pe,aw as de,ax as ue,ay as fe,az as ge,aA as me,aB as ye,aC as Te,aD as be,aE as we,aF as xe,aG as Ae,aH as Pe,aI as Ie,aJ as Ce,aK as Ve,aL as Le,aM as Se,aN as Me,aO as ve,aP as Ue,aQ as Re,aR as Ye,aS as Fe,aT as Ee,x as _e,aU as Oe,aV as ke,aW as Xe,aX as Be,aY as Ne,aZ as De,a_ as $e,a$ as ze,b0 as We,b1 as qe,b2 as Ge,b3 as Je,b6 as He,b7 as Ze,b8 as Ke,b9 as Qe,ba as je,bb as ts,bc as es,bd as ss,be as ns,bf as is,bg as os,bh as rs,bi as as,bj as ls,bk as cs,bl as hs,bm as ps,bn as ds,bo as us,bp as fs,bq as gs,br as ms,bs as ys,bt as Ts,bu as bs,w as ws,X as xs,J as As,Y as Ps,z as Is,bv as Cs,bw as Vs,bx as Ls,by as Ss,bz as Ms,bA as vs,bB as Us,bC as Rs,bD as Ys,bE as Fs,bF as Es,bG as _s,T as Os,bH as ks,bI as Xs,b as Bs,bJ as Ns,c as Ds,bK as $s,bL as zs,bM as Ws,bN as qs,bO as Gs,bP as Js,bQ as Hs,bR as Zs,bS as Ks,bT as Qs,bU as js,bV as tn,bW as en,bX as sn,bY as nn,bZ as on,b_ as rn,V as an,b$ as ln,c0 as cn,c1 as hn,c2 as pn,c3 as dn}from"./index-D08n0lXm.js";class It{constructor(t){T(this,"skeletonData");T(this,"animationToMixTime",{});T(this,"defaultMix",0);if(!t)throw new Error("skeletonData cannot be null.");this.skeletonData=t}setMix(t,e,n){return typeof t=="string"?this.setMix1(t,e,n):this.setMix2(t,e,n)}setMix1(t,e,n){const s=this.skeletonData.findAnimation(t);if(!s)throw new Error(`Animation not found: ${t}`);const i=this.skeletonData.findAnimation(e);if(!i)throw new Error(`Animation not found: ${e}`);this.setMix2(s,i,n)}setMix2(t,e,n){if(!t)throw new Error("from cannot be null.");if(!e)throw new Error("to cannot be null.");const s=`${t.name}.${e.name}`;this.animationToMixTime[s]=n}getMix(t,e){const n=`${t.name}.${e.name}`,s=this.animationToMixTime[n];return s===void 0?this.defaultMix:s}}class Ct{constructor(t,e="",n=new bt,s=new lt){T(this,"textureLoader");T(this,"pathPrefix");T(this,"downloader");T(this,"cache");T(this,"errors",{});T(this,"toLoad",0);T(this,"loaded",0);T(this,"texturePmaInfo",{});this.textureLoader=t,this.pathPrefix=e,this.downloader=n,this.cache=s}start(t){return this.toLoad++,this.pathPrefix+t}success(t,e,n){this.toLoad--,this.loaded++,this.cache.assets[e]=n,this.cache.assetsRefCount[e]=(this.cache.assetsRefCount[e]||0)+1,t&&t(e,n)}error(t,e,n){this.toLoad--,this.loaded++,this.errors[e]=n,t&&t(e,n)}loadAll(){return new Promise((e,n)=>{const s=()=>{if(this.isLoadingComplete()){this.hasErrors()?n(this.errors):e(this);return}requestAnimationFrame(s)};requestAnimationFrame(s)})}setRawDataURI(t,e){this.downloader.rawDataUris[this.pathPrefix+t]=e}loadBinary(t,e=()=>{},n=()=>{}){t=this.start(t),!this.reuseAssets(t,e,n)&&(this.cache.assetsLoaded[t]=new Promise((s,i)=>{this.downloader.downloadBinary(t,o=>{this.success(e,t,o),s(o)},(o,r)=>{const l=`Couldn't load binary ${t}: status ${o}, ${r}`;this.error(n,t,l),i(l)})}))}loadText(t,e=()=>{},n=()=>{}){t=this.start(t),this.downloader.downloadText(t,s=>{this.success(e,t,s)},(s,i)=>{this.error(n,t,`Couldn't load text ${t}: status ${s}, ${i}`)})}loadJson(t,e=()=>{},n=()=>{}){t=this.start(t),!this.reuseAssets(t,e,n)&&(this.cache.assetsLoaded[t]=new Promise((s,i)=>{this.downloader.downloadJson(t,o=>{this.success(e,t,o),s(o)},(o,r)=>{const l=`Couldn't load JSON ${t}: status ${o}, ${r}`;this.error(n,t,l),i(l)})}))}reuseAssets(t,e=()=>{},n=()=>{}){const s=this.cache.getAsset(t),i=s!==void 0;return i&&(this.cache.assetsLoaded[t]=s.then(o=>(o=o instanceof Image||o instanceof ImageBitmap?this.textureLoader(o):o,this.success(e,t,o),o)).catch(o=>{this.error(n,t,o)})),i}loadTexture(t,e=()=>{},n=()=>{}){if(t=this.start(t),this.reuseAssets(t,e,n))return;const s=this.texturePmaInfo[t];this.cache.assetsLoaded[t]=new Promise((i,o)=>{if(!!!(typeof window<"u"&&typeof navigator<"u"&&window.document))fetch(t,{mode:"cors"}).then(c=>{if(c.ok)return c.blob();const a=`Couldn't load image: ${t}`;this.error(n,t,`Couldn't load image: ${t}`),o(a)}).then(c=>c?createImageBitmap(c,{premultiplyAlpha:"none",colorSpaceConversion:"none"}):null).then(c=>{if(c){const a=this.createTexture(t,s,c);this.success(e,t,a),i(a)}});else{const c=new Image;c.crossOrigin="anonymous",c.onload=()=>{const a=this.createTexture(t,s,c);this.success(e,t,a),i(a)},c.onerror=()=>{const a=`Couldn't load image: ${t}`;this.error(n,t,a),o(a)},this.downloader.rawDataUris[t]&&(t=this.downloader.rawDataUris[t]),c.src=t}})}loadTextureAtlas(t,e=()=>{},n=()=>{},s){const i=t.lastIndexOf("/"),o=i>=0?t.substring(0,i+1)Line truncated
|
var ft=Object.defineProperty;var gt=(W,t,e)=>t in W?ft(W,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):W[t]=e;var T=(W,t,e)=>gt(W,typeof t!="symbol"?t+"":t,e);import{f as ht,R as pt,N as dt,ac as ut,Z as mt,b4 as yt,O as rt,b5 as at,U as D,B as Tt}from"./index-CxN-rEmg.js";import{$ as Rt,a0 as Yt,a1 as Ft,a2 as Et,y as _t,a3 as Ot,a4 as kt,a5 as Xt,a6 as Bt,a7 as Nt,a8 as Dt,a9 as $t,aa as zt,ab as Wt,ad as qt,H as Gt,ae as Jt,af as Ht,ag as Zt,ah as Kt,ai as Qt,aj as jt,ak as te,al as ee,am as se,an as ne,ao as ie,ap as oe,aq as re,ar as ae,as as le,at as ce,au as he,av as pe,aw as de,ax as ue,ay as fe,az as ge,aA as me,aB as ye,aC as Te,aD as be,aE as we,aF as xe,aG as Ae,aH as Pe,aI as Ie,aJ as Ce,aK as Ve,aL as Le,aM as Se,aN as Me,aO as ve,aP as Ue,aQ as Re,aR as Ye,aS as Fe,aT as Ee,x as _e,aU as Oe,aV as ke,aW as Xe,aX as Be,aY as Ne,aZ as De,a_ as $e,a$ as ze,b0 as We,b1 as qe,b2 as Ge,b3 as Je,b6 as He,b7 as Ze,b8 as Ke,b9 as Qe,ba as je,bb as ts,bc as es,bd as ss,be as ns,bf as is,bg as os,bh as rs,bi as as,bj as ls,bk as cs,bl as hs,bm as ps,bn as ds,bo as us,bp as fs,bq as gs,br as ms,bs as ys,bt as Ts,bu as bs,w as ws,X as xs,J as As,Y as Ps,z as Is,bv as Cs,bw as Vs,bx as Ls,by as Ss,bz as Ms,bA as vs,bB as Us,bC as Rs,bD as Ys,bE as Fs,bF as Es,bG as _s,T as Os,bH as ks,bI as Xs,b as Bs,bJ as Ns,c as Ds,bK as $s,bL as zs,bM as Ws,bN as qs,bO as Gs,bP as Js,bQ as Hs,bR as Zs,bS as Ks,bT as Qs,bU as js,bV as tn,bW as en,bX as sn,bY as nn,bZ as on,b_ as rn,V as an,b$ as ln,c0 as cn,c1 as hn,c2 as pn,c3 as dn}from"./index-CxN-rEmg.js";class It{constructor(t){T(this,"skeletonData");T(this,"animationToMixTime",{});T(this,"defaultMix",0);if(!t)throw new Error("skeletonData cannot be null.");this.skeletonData=t}setMix(t,e,n){return typeof t=="string"?this.setMix1(t,e,n):this.setMix2(t,e,n)}setMix1(t,e,n){const s=this.skeletonData.findAnimation(t);if(!s)throw new Error(`Animation not found: ${t}`);const i=this.skeletonData.findAnimation(e);if(!i)throw new Error(`Animation not found: ${e}`);this.setMix2(s,i,n)}setMix2(t,e,n){if(!t)throw new Error("from cannot be null.");if(!e)throw new Error("to cannot be null.");const s=`${t.name}.${e.name}`;this.animationToMixTime[s]=n}getMix(t,e){const n=`${t.name}.${e.name}`,s=this.animationToMixTime[n];return s===void 0?this.defaultMix:s}}class Ct{constructor(t,e="",n=new bt,s=new lt){T(this,"textureLoader");T(this,"pathPrefix");T(this,"downloader");T(this,"cache");T(this,"errors",{});T(this,"toLoad",0);T(this,"loaded",0);T(this,"texturePmaInfo",{});this.textureLoader=t,this.pathPrefix=e,this.downloader=n,this.cache=s}start(t){return this.toLoad++,this.pathPrefix+t}success(t,e,n){this.toLoad--,this.loaded++,this.cache.assets[e]=n,this.cache.assetsRefCount[e]=(this.cache.assetsRefCount[e]||0)+1,t&&t(e,n)}error(t,e,n){this.toLoad--,this.loaded++,this.errors[e]=n,t&&t(e,n)}loadAll(){return new Promise((e,n)=>{const s=()=>{if(this.isLoadingComplete()){this.hasErrors()?n(this.errors):e(this);return}requestAnimationFrame(s)};requestAnimationFrame(s)})}setRawDataURI(t,e){this.downloader.rawDataUris[this.pathPrefix+t]=e}loadBinary(t,e=()=>{},n=()=>{}){t=this.start(t),!this.reuseAssets(t,e,n)&&(this.cache.assetsLoaded[t]=new Promise((s,i)=>{this.downloader.downloadBinary(t,o=>{this.success(e,t,o),s(o)},(o,r)=>{const l=`Couldn't load binary ${t}: status ${o}, ${r}`;this.error(n,t,l),i(l)})}))}loadText(t,e=()=>{},n=()=>{}){t=this.start(t),this.downloader.downloadText(t,s=>{this.success(e,t,s)},(s,i)=>{this.error(n,t,`Couldn't load text ${t}: status ${s}, ${i}`)})}loadJson(t,e=()=>{},n=()=>{}){t=this.start(t),!this.reuseAssets(t,e,n)&&(this.cache.assetsLoaded[t]=new Promise((s,i)=>{this.downloader.downloadJson(t,o=>{this.success(e,t,o),s(o)},(o,r)=>{const l=`Couldn't load JSON ${t}: status ${o}, ${r}`;this.error(n,t,l),i(l)})}))}reuseAssets(t,e=()=>{},n=()=>{}){const s=this.cache.getAsset(t),i=s!==void 0;return i&&(this.cache.assetsLoaded[t]=s.then(o=>(o=o instanceof Image||o instanceof ImageBitmap?this.textureLoader(o):o,this.success(e,t,o),o)).catch(o=>{this.error(n,t,o)})),i}loadTexture(t,e=()=>{},n=()=>{}){if(t=this.start(t),this.reuseAssets(t,e,n))return;const s=this.texturePmaInfo[t];this.cache.assetsLoaded[t]=new Promise((i,o)=>{if(!!!(typeof window<"u"&&typeof navigator<"u"&&window.document))fetch(t,{mode:"cors"}).then(c=>{if(c.ok)return c.blob();const a=`Couldn't load image: ${t}`;this.error(n,t,`Couldn't load image: ${t}`),o(a)}).then(c=>c?createImageBitmap(c,{premultiplyAlpha:"none",colorSpaceConversion:"none"}):null).then(c=>{if(c){const a=this.createTexture(t,s,c);this.success(e,t,a),i(a)}});else{const c=new Image;c.crossOrigin="anonymous",c.onload=()=>{const a=this.createTexture(t,s,c);this.success(e,t,a),i(a)},c.onerror=()=>{const a=`Couldn't load image: ${t}`;this.error(n,t,a),o(a)},this.downloader.rawDataUris[t]&&(t=this.downloader.rawDataUris[t]),c.src=t}})}loadTextureAtlas(t,e=()=>{},n=()=>{},s){const i=t.lastIndexOf("/"),o=i>=0?t.substring(0,i+1)Line truncated
|
||||||
${n}`:""}`)}remove(t){t=this.pathPrefix+t;const e=this.cache.assets[t];return e.dispose&&e.dispose(),delete this.cache.assets[t],delete this.cache.assetsRefCount[t],delete this.cache.assetsLoaded[t],e}removeAll(){for(const t in this.cache.assets){const e=this.cache.assets[t];e.dispose&&e.dispose()}this.cache.assets={},this.cache.assetsLoaded={},this.cache.assetsRefCount={}}isLoadingComplete(){return this.toLoad===0}getToLoad(){return this.toLoad}getLoaded(){return this.loaded}dispose(){this.removeAll()}disposeAsset(t){const e=this.cache.assets[t];if(e instanceof ht){e.dispose();return}this.disposeAssetInternal(t)}hasErrors(){return Object.keys(this.errors).length>0}getErrors(){return this.errors}disposeAssetInternal(t){if(this.cache.assetsRefCount[t]>0&&--this.cache.assetsRefCount[t]===0)return this.remove(t)}createTextureAtlas(t,e,n,s){const i=new ht(t);i.dispose=()=>{var o;if(!(this.cache.assetsRefCount[n]<=0)){this.disposeAssetInternal(n);for(const r of i.pages)(o=r.texture)==null||o.dispose()}};for(const o of i.pages){const r=this.texturePath(e,o.name,s);this.texturePmaInfo[this.pathPrefix+r]=o.pma}return i}createTexture(t,e,n){const s=this.textureLoader(n,e),i=s.dispose.bind(s);return s.dispose=()=>{this.disposeAssetInternal(t)&&i()},s}texturePath(t,e,n){return n?n[e]:t+e}}const nt=class nt{constructor(){T(this,"assets",{});T(this,"assetsRefCount",{});T(this,"assetsLoaded",{})}static getCache(t){const e=nt.AVAILABLE_CACHES.get(t);if(e)return e;const n=new nt;return nt.AVAILABLE_CACHES.set(t,n),n}async addAsset(t,e){return this.assetsLoaded[t]=Promise.resolve(e),this.assets[t]=e,e}getAsset(t){return this.assetsLoaded[t]}};T(nt,"AVAILABLE_CACHES",new Map);let lt=nt;class bt{constructor(){T(this,"callbacks",{});T(this,"rawDataUris",{})}dataUriToString(t){if(!t.startsWith("data:"))throw new Error("Not a data URI.");let e=t.indexOf("base64,");return e!==-1?(e+=7,atob(t.substr(e))):t.substr(t.indexOf(",")+1)}base64ToUint8Array(t){var e=window.atob(t),n=e.length,s=new Uint8Array(n);for(let i=0;i<n;i++)s[i]=e.charCodeAt(i);return s}dataUriToUint8Array(t){if(!t.startsWith("data:"))throw new Error("Not a data URI.");let e=t.indexOf("base64,");if(e===-1)throw new Error("Not a binary data URI.");return e+=7,this.base64ToUint8Array(t.substr(e))}downloadText(t,e,n){if(this.start(t,e,n))return;const s=this.rawDataUris[t];if(s&&!s.includes(".")){try{this.finish(t,200,this.dataUriToString(s))}catch(r){this.finish(t,400,JSON.stringify(r))}return}const i=new XMLHttpRequest;i.overrideMimeType("text/html"),i.open("GET",s||t,!0);const o=()=>{this.finish(t,i.status,i.responseText)};i.onload=o,i.onerror=o,i.send()}downloadJson(t,e,n){this.downloadText(t,s=>{e(JSON.parse(s))},n)}downloadBinary(t,e,n){if(this.start(t,e,n))return;const s=this.rawDataUris[t];if(s&&!s.includes(".")){try{this.finish(t,200,this.dataUriToUint8Array(s))}catch(r){this.finish(t,400,JSON.stringify(r))}return}const i=new XMLHttpRequest;i.open("GET",s||t,!0),i.responseType="arraybuffer";const o=()=>{this.finish(t,i.status,i.response)};i.onload=()=>{i.status===200||i.status===0?this.finish(t,200,new Uint8Array(i.response)):o()},i.onerror=o,i.send()}start(t,e,n){let s=this.callbacks[t];try{if(s)return!0;this.callbacks[t]=s=[]}finally{s.push(e,n)}}finish(t,e,n){const s=this.callbacks[t];if(delete this.callbacks[t],e===200||e===0)for(let i=0,o=s.length;i<o;i+=2)s[i](n);else for(let i=1,o=s.length;i<o;i+=2)s[i](e,n)}}class Vt{constructor(t,e=!1){T(this,"atlas");T(this,"allowMissingRegions");this.atlas=t,this.allowMissingRegions=e}findRegions(t,e,n){const s=n.regions;for(let i=0,o=s.length;i<o;i++)s[i]=this.findRegion(t,n.getPath(e,i))}findRegion(t,e){const n=this.atlas.findRegion(e);if(!n&&!this.allowMissingRegions)throw new Error(`Region not found in atlas: ${e} (attachment: ${t})`);return n}newRegionAttachment(t,e,n,s,i){return this.findRegions(n,s,i),new pt(n,i)}newMeshAttachment(t,e,n,s,i){return this.findRegions(n,s,i),new dt(n,i)}newBoundingBoxAttachment(t,e,n){return new ut(n)}newPathAttachment(t,e,n){return new mt(n)}newPointAttachment(t,e,n){return new yt(n)}newClippingAttachment(t,e,n){return new rt(n)}}class Lt{constructor(){T(this,"minX",0);T(this,"minY",0);T(this,"maxX",0);T(this,"maxY",0);T(this,"boundingBoxes",[]);T(this,"polygons",[]);T(this,"polygonPool",new at(()=>D.newFloatArray(16)))}update(t,e){if(!t)throw new Error("skeleton cannot be null.");const n=this.boundingBoxes,s=this.polygons,i=this.polygonPool,o=t.slots,r=o.length;n.length=0,i.freeAll(s),s.length=0;for(let l=0;l<r;l++){const c=o[l];if(!c.bone.active)continue;const a=c.appliedPose.attachment;if(a instanceof ut){n.push(a);let h=i.obtain();h.length!==a.worldVerticesLength&&(h=D.newFloatArray(a.worldVerticesLength)),s.push(h),a.computeWorldVertices(t,c,0,a.worldVerticesLength,h,0,2)}}e?this.aabbCompute():(this.minX=Number.POSITIVE_INFINITY,this.minY=Number.POSITIVE_INFINITY,this.maxX=Number.NEGATIVE_INFINITY,this.maxY=Number.NEGATIVE_INFINITY)}aabbCompute(){let t=Number.POSITIVE_INFLine truncated
|
${n}`:""}`)}remove(t){t=this.pathPrefix+t;const e=this.cache.assets[t];return e.dispose&&e.dispose(),delete this.cache.assets[t],delete this.cache.assetsRefCount[t],delete this.cache.assetsLoaded[t],e}removeAll(){for(const t in this.cache.assets){const e=this.cache.assets[t];e.dispose&&e.dispose()}this.cache.assets={},this.cache.assetsLoaded={},this.cache.assetsRefCount={}}isLoadingComplete(){return this.toLoad===0}getToLoad(){return this.toLoad}getLoaded(){return this.loaded}dispose(){this.removeAll()}disposeAsset(t){const e=this.cache.assets[t];if(e instanceof ht){e.dispose();return}this.disposeAssetInternal(t)}hasErrors(){return Object.keys(this.errors).length>0}getErrors(){return this.errors}disposeAssetInternal(t){if(this.cache.assetsRefCount[t]>0&&--this.cache.assetsRefCount[t]===0)return this.remove(t)}createTextureAtlas(t,e,n,s){const i=new ht(t);i.dispose=()=>{var o;if(!(this.cache.assetsRefCount[n]<=0)){this.disposeAssetInternal(n);for(const r of i.pages)(o=r.texture)==null||o.dispose()}};for(const o of i.pages){const r=this.texturePath(e,o.name,s);this.texturePmaInfo[this.pathPrefix+r]=o.pma}return i}createTexture(t,e,n){const s=this.textureLoader(n,e),i=s.dispose.bind(s);return s.dispose=()=>{this.disposeAssetInternal(t)&&i()},s}texturePath(t,e,n){return n?n[e]:t+e}}const nt=class nt{constructor(){T(this,"assets",{});T(this,"assetsRefCount",{});T(this,"assetsLoaded",{})}static getCache(t){const e=nt.AVAILABLE_CACHES.get(t);if(e)return e;const n=new nt;return nt.AVAILABLE_CACHES.set(t,n),n}async addAsset(t,e){return this.assetsLoaded[t]=Promise.resolve(e),this.assets[t]=e,e}getAsset(t){return this.assetsLoaded[t]}};T(nt,"AVAILABLE_CACHES",new Map);let lt=nt;class bt{constructor(){T(this,"callbacks",{});T(this,"rawDataUris",{})}dataUriToString(t){if(!t.startsWith("data:"))throw new Error("Not a data URI.");let e=t.indexOf("base64,");return e!==-1?(e+=7,atob(t.substr(e))):t.substr(t.indexOf(",")+1)}base64ToUint8Array(t){var e=window.atob(t),n=e.length,s=new Uint8Array(n);for(let i=0;i<n;i++)s[i]=e.charCodeAt(i);return s}dataUriToUint8Array(t){if(!t.startsWith("data:"))throw new Error("Not a data URI.");let e=t.indexOf("base64,");if(e===-1)throw new Error("Not a binary data URI.");return e+=7,this.base64ToUint8Array(t.substr(e))}downloadText(t,e,n){if(this.start(t,e,n))return;const s=this.rawDataUris[t];if(s&&!s.includes(".")){try{this.finish(t,200,this.dataUriToString(s))}catch(r){this.finish(t,400,JSON.stringify(r))}return}const i=new XMLHttpRequest;i.overrideMimeType("text/html"),i.open("GET",s||t,!0);const o=()=>{this.finish(t,i.status,i.responseText)};i.onload=o,i.onerror=o,i.send()}downloadJson(t,e,n){this.downloadText(t,s=>{e(JSON.parse(s))},n)}downloadBinary(t,e,n){if(this.start(t,e,n))return;const s=this.rawDataUris[t];if(s&&!s.includes(".")){try{this.finish(t,200,this.dataUriToUint8Array(s))}catch(r){this.finish(t,400,JSON.stringify(r))}return}const i=new XMLHttpRequest;i.open("GET",s||t,!0),i.responseType="arraybuffer";const o=()=>{this.finish(t,i.status,i.response)};i.onload=()=>{i.status===200||i.status===0?this.finish(t,200,new Uint8Array(i.response)):o()},i.onerror=o,i.send()}start(t,e,n){let s=this.callbacks[t];try{if(s)return!0;this.callbacks[t]=s=[]}finally{s.push(e,n)}}finish(t,e,n){const s=this.callbacks[t];if(delete this.callbacks[t],e===200||e===0)for(let i=0,o=s.length;i<o;i+=2)s[i](n);else for(let i=1,o=s.length;i<o;i+=2)s[i](e,n)}}class Vt{constructor(t,e=!1){T(this,"atlas");T(this,"allowMissingRegions");this.atlas=t,this.allowMissingRegions=e}findRegions(t,e,n){const s=n.regions;for(let i=0,o=s.length;i<o;i++)s[i]=this.findRegion(t,n.getPath(e,i))}findRegion(t,e){const n=this.atlas.findRegion(e);if(!n&&!this.allowMissingRegions)throw new Error(`Region not found in atlas: ${e} (attachment: ${t})`);return n}newRegionAttachment(t,e,n,s,i){return this.findRegions(n,s,i),new pt(n,i)}newMeshAttachment(t,e,n,s,i){return this.findRegions(n,s,i),new dt(n,i)}newBoundingBoxAttachment(t,e,n){return new ut(n)}newPathAttachment(t,e,n){return new mt(n)}newPointAttachment(t,e,n){return new yt(n)}newClippingAttachment(t,e,n){return new rt(n)}}class Lt{constructor(){T(this,"minX",0);T(this,"minY",0);T(this,"maxX",0);T(this,"maxY",0);T(this,"boundingBoxes",[]);T(this,"polygons",[]);T(this,"polygonPool",new at(()=>D.newFloatArray(16)))}update(t,e){if(!t)throw new Error("skeleton cannot be null.");const n=this.boundingBoxes,s=this.polygons,i=this.polygonPool,o=t.slots,r=o.length;n.length=0,i.freeAll(s),s.length=0;for(let l=0;l<r;l++){const c=o[l];if(!c.bone.active)continue;const a=c.appliedPose.attachment;if(a instanceof ut){n.push(a);let h=i.obtain();h.length!==a.worldVerticesLength&&(h=D.newFloatArray(a.worldVerticesLength)),s.push(h),a.computeWorldVertices(t,c,0,a.worldVerticesLength,h,0,2)}}e?this.aabbCompute():(this.minX=Number.POSITIVE_INFINITY,this.minY=Number.POSITIVE_INFINITY,this.maxX=Number.NEGATIVE_INFINITY,this.maxY=Number.NEGATIVE_INFINITY)}aabbCompute(){let t=Number.POSITIVE_INFLine truncated
|
||||||
+57
-57
@@ -1,4 +1,4 @@
|
|||||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-B1oGFzi8.js","assets/index-BUbc0qUx.js"])))=>i.map(i=>d[i]);
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-hYCzV0U1.js","assets/index-Cr68vmVa.js"])))=>i.map(i=>d[i]);
|
||||||
var ES=Object.defineProperty;var CS=(s,e,t)=>e in s?ES(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var x=(s,e,t)=>CS(s,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))i(n);new MutationObserver(n=>{for(const r of n)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&i(a)}).observe(document,{childList:!0,subtree:!0});function t(n){const r={};return n.integrity&&(r.integrity=n.integrity),n.referrerPolicy&&(r.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?r.credentials="include":n.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(n){if(n.ep)return;n.ep=!0;const r=t(n);fetch(n.href,r)}})();/**
|
var ES=Object.defineProperty;var CS=(s,e,t)=>e in s?ES(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var x=(s,e,t)=>CS(s,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))i(n);new MutationObserver(n=>{for(const r of n)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&i(a)}).observe(document,{childList:!0,subtree:!0});function t(n){const r={};return n.integrity&&(r.integrity=n.integrity),n.referrerPolicy&&(r.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?r.credentials="include":n.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(n){if(n.ep)return;n.ep=!0;const r=t(n);fetch(n.href,r)}})();/**
|
||||||
* @vue/shared v3.5.41
|
* @vue/shared v3.5.41
|
||||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
@@ -7,37 +7,37 @@ var ES=Object.defineProperty;var CS=(s,e,t)=>e in s?ES(s,e,{enumerable:!0,config
|
|||||||
* @vue/reactivity v3.5.41
|
* @vue/reactivity v3.5.41
|
||||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
* @license MIT
|
* @license MIT
|
||||||
**/let Pi;class Cy{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!e&&Pi&&(Pi.active?(this.parent=Pi,this.index=(Pi.scopes||(Pi.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes){const i=this.scopes.slice();for(e=0,t=i.length;e<t;e++)i[e].pause()}for(e=0,t=this.effects.length;e<t;e++)this.effects[e].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let e,t;if(this.scopes){const n=this.scopes.slice();for(e=0,t=n.length;e<t;e++)n[e].resume()}const i=this.effects.slice();for(e=0,t=i.length;e<t;e++)i[e].resume()}}run(e){if(this._active){const t=Pi;try{return Pi=this,e()}finally{Pi=t}}}on(){++this._on===1&&(this.prevScope=Pi,Pi=this)}off(){if(this._on>0&&--this._on===0){if(Pi===this)Pi=this.prevScope;else{let e=Pi;for(;e;){if(e.prevScope===this){e.prevScope=this.prevScope;break}e=e.prevScope}}this.prevScope=void 0}}stop(e){if(this._active){this._active=!1;let t,i;for(t=0,i=this.effects.length;t<i;t++)this.effects[t].stop();for(this.effects.length=0,t=0,i=this.cleanups.length;t<i;t++)this.cleanups[t]();if(this.cleanups.length=0,this.scopes){const n=this.scopes.slice();for(t=0,i=n.length;t<i;t++)n[t].stop(!0);this.scopes.length=0}if(!this.detached&&this.parent&&!e){const n=this.parent.scopes.pop();n&&n!==this&&(this.parent.scopes[this.index]=n,n.index=this.index)}this.parent=void 0}}}function Iy(s){return new Cy(s)}function Ry(){return Pi}function VS(s,e=!1){Pi&&Pi.cleanups.push(s)}let ai;const Lu=new WeakSet;class Py{constructor(e){this.fn=e,this.deps=void 0,this.depsTail=void 0,this.flags=5,this.next=void 0,this.cleanup=void 0,this.scheduler=void 0,Pi&&(Pi.active?Pi.effects.push(this):this.flags&=-2)}pause(){this.flags|=64}resume(){this.flags&64&&(this.flags&=-65,Lu.has(this)&&(Lu.delete(this),this.trigger()))}notify(){this.flags&2&&!(this.flags&32)||this.flags&8||Fy(this)}run(){if(!(this.flags&1))return this.fn();this.flags|=2,Gp(this),Oy(this);const e=ai,t=rn;ai=this,rn=!0;try{return this.fn()}finally{By(this),ai=e,rn=t,this.flags&=-3}}stop(){if(this.flags&1){for(let e=this.deps;e;e=e.nextDep)ep(e);this.deps=this.depsTail=void 0,Gp(this),this.onStop&&this.onStop(),this.flags&=-2}}trigger(){this.flags&64?Lu.add(this):this.scheduler?this.scheduler():this.runIfDirty()}runIfDirty(){If(this)&&this.run()}get dirty(){return If(this)}}let ky=0,hl,ul;function Fy(s,e=!1){if(s.flags|=8,e){s.next=ul,ul=s;return}s.next=hl,hl=s}function Jm(){ky++}function Qm(){if(--ky>0)return;if(ul){let e=ul;for(ul=void 0;e;){const t=e.next;e.next=void 0,e.flags&=-9,e=t}}let s;for(;hl;){let e=hl;for(hl=void 0;e;){const t=e.next;if(e.next=void 0,e.flags&=-9,e.flags&1)try{e.trigger()}catch(i){s||(s=i)}e=t}}if(s)throw s}function Oy(s){for(let e=s.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function By(s){let e,t=s.depsTail,i=t;for(;i;){const n=i.prevDep;i.version===-1?(i===t&&(t=n),ep(i),YS(i)):e=i,i.dep.activeLink=i.prevActiveLink,i.prevActiveLink=void 0,i=n}s.deps=e,s.depsTail=t}function If(s){for(let e=s.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(Ly(e.dep.computed)||e.dep.version!==e.version))return!0;return!!s._dirty}function Ly(s){if(s.flags&4&&!(s.flags&16)||(s.flags&=-17,s.globalVersion===_l)||(s.globalVersion=_l,!s.isSSR&&s.flags&128&&(!s.deps&&!s._dirty||!If(s))))return;s.flags|=2;const e=s.dep,t=ai,i=rn;ai=s,rn=!0;try{Oy(s);const n=s.fn(s._value);(e.version===0||In(n,s._value))&&(s.flags|=128,s._value=n,e.version++)}catch(n){throw e.version++,n}finally{ai=t,rn=i,By(s),s.flags&=-3}}function ep(s,e=!1){const{dep:t,prevSub:i,nextSub:n}=s;if(i&&(i.nextSub=n,s.prevSub=void 0),n&&(n.prevSub=i,s.nextSub=void 0),t.subs===s&&(t.subs=i,!i&&t.computed)){t.computed.flags&=-5;for(let r=t.computed.deps;r;r=r.nextDep)ep(r,!0)}!e&&!--t.sc&&t.map&&t.map.delete(t.key)}function YS(s){const{prevDep:e,nextDep:t}=s;e&&(e.nextDep=t,s.prevDep=void 0),t&&(t.prevDep=e,s.nextDep=void 0)}let rn=!0;const Dy=[];function dr(){Dy.push(rn),rn=!1}function fr(){const s=Dy.pop();rn=s===void 0?!0:s}function Gp(s){const{cleanup:e}=s;if(s.cleanup=void 0,e){const t=ai;ai=void 0;try{e()}finally{ai=t}}}let _l=0;class $S{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class tp{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!ai||!rn||ai===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==ai)t=this.activeLink=new $S(ai,this),ai.deps?(t.prevDep=ai.depsTail,ai.depsTail.nextDep=t,ai.depsTail=t):ai.deps=ai.depsTail=t,Ny(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){const i=t.nextDep;i.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=i),t.prevDep=ai.depsTailLine truncated
|
**/let Pi;class Cy{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!e&&Pi&&(Pi.active?(this.parent=Pi,this.index=(Pi.scopes||(Pi.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes){const i=this.scopes.slice();for(e=0,t=i.length;e<t;e++)i[e].pause()}for(e=0,t=this.effects.length;e<t;e++)this.effects[e].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let e,t;if(this.scopes){const n=this.scopes.slice();for(e=0,t=n.length;e<t;e++)n[e].resume()}const i=this.effects.slice();for(e=0,t=i.length;e<t;e++)i[e].resume()}}run(e){if(this._active){const t=Pi;try{return Pi=this,e()}finally{Pi=t}}}on(){++this._on===1&&(this.prevScope=Pi,Pi=this)}off(){if(this._on>0&&--this._on===0){if(Pi===this)Pi=this.prevScope;else{let e=Pi;for(;e;){if(e.prevScope===this){e.prevScope=this.prevScope;break}e=e.prevScope}}this.prevScope=void 0}}stop(e){if(this._active){this._active=!1;let t,i;for(t=0,i=this.effects.length;t<i;t++)this.effects[t].stop();for(this.effects.length=0,t=0,i=this.cleanups.length;t<i;t++)this.cleanups[t]();if(this.cleanups.length=0,this.scopes){const n=this.scopes.slice();for(t=0,i=n.length;t<i;t++)n[t].stop(!0);this.scopes.length=0}if(!this.detached&&this.parent&&!e){const n=this.parent.scopes.pop();n&&n!==this&&(this.parent.scopes[this.index]=n,n.index=this.index)}this.parent=void 0}}}function Iy(s){return new Cy(s)}function Ry(){return Pi}function VS(s,e=!1){Pi&&Pi.cleanups.push(s)}let ai;const Lu=new WeakSet;class Py{constructor(e){this.fn=e,this.deps=void 0,this.depsTail=void 0,this.flags=5,this.next=void 0,this.cleanup=void 0,this.scheduler=void 0,Pi&&(Pi.active?Pi.effects.push(this):this.flags&=-2)}pause(){this.flags|=64}resume(){this.flags&64&&(this.flags&=-65,Lu.has(this)&&(Lu.delete(this),this.trigger()))}notify(){this.flags&2&&!(this.flags&32)||this.flags&8||Fy(this)}run(){if(!(this.flags&1))return this.fn();this.flags|=2,Gp(this),Oy(this);const e=ai,t=rn;ai=this,rn=!0;try{return this.fn()}finally{By(this),ai=e,rn=t,this.flags&=-3}}stop(){if(this.flags&1){for(let e=this.deps;e;e=e.nextDep)ep(e);this.deps=this.depsTail=void 0,Gp(this),this.onStop&&this.onStop(),this.flags&=-2}}trigger(){this.flags&64?Lu.add(this):this.scheduler?this.scheduler():this.runIfDirty()}runIfDirty(){If(this)&&this.run()}get dirty(){return If(this)}}let ky=0,hl,ul;function Fy(s,e=!1){if(s.flags|=8,e){s.next=ul,ul=s;return}s.next=hl,hl=s}function Jm(){ky++}function Qm(){if(--ky>0)return;if(ul){let e=ul;for(ul=void 0;e;){const t=e.next;e.next=void 0,e.flags&=-9,e=t}}let s;for(;hl;){let e=hl;for(hl=void 0;e;){const t=e.next;if(e.next=void 0,e.flags&=-9,e.flags&1)try{e.trigger()}catch(i){s||(s=i)}e=t}}if(s)throw s}function Oy(s){for(let e=s.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function By(s){let e,t=s.depsTail,i=t;for(;i;){const n=i.prevDep;i.version===-1?(i===t&&(t=n),ep(i),YS(i)):e=i,i.dep.activeLink=i.prevActiveLink,i.prevActiveLink=void 0,i=n}s.deps=e,s.depsTail=t}function If(s){for(let e=s.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(Ly(e.dep.computed)||e.dep.version!==e.version))return!0;return!!s._dirty}function Ly(s){if(s.flags&4&&!(s.flags&16)||(s.flags&=-17,s.globalVersion===_l)||(s.globalVersion=_l,!s.isSSR&&s.flags&128&&(!s.deps&&!s._dirty||!If(s))))return;s.flags|=2;const e=s.dep,t=ai,i=rn;ai=s,rn=!0;try{Oy(s);const n=s.fn(s._value);(e.version===0||In(n,s._value))&&(s.flags|=128,s._value=n,e.version++)}catch(n){throw e.version++,n}finally{ai=t,rn=i,By(s),s.flags&=-3}}function ep(s,e=!1){const{dep:t,prevSub:i,nextSub:n}=s;if(i&&(i.nextSub=n,s.prevSub=void 0),n&&(n.prevSub=i,s.nextSub=void 0),t.subs===s&&(t.subs=i,!i&&t.computed)){t.computed.flags&=-5;for(let r=t.computed.deps;r;r=r.nextDep)ep(r,!0)}!e&&!--t.sc&&t.map&&t.map.delete(t.key)}function YS(s){const{prevDep:e,nextDep:t}=s;e&&(e.nextDep=t,s.prevDep=void 0),t&&(t.prevDep=e,s.nextDep=void 0)}let rn=!0;const Dy=[];function dr(){Dy.push(rn),rn=!1}function fr(){const s=Dy.pop();rn=s===void 0?!0:s}function Gp(s){const{cleanup:e}=s;if(s.cleanup=void 0,e){const t=ai;ai=void 0;try{e()}finally{ai=t}}}let _l=0;class $S{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class tp{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!ai||!rn||ai===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==ai)t=this.activeLink=new $S(ai,this),ai.deps?(t.prevDep=ai.depsTail,ai.depsTail.nextDep=t,ai.depsTail=t):ai.deps=ai.depsTail=t,Ny(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){const i=t.nextDep;i.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=i),t.prevDep=ai.depsTailLine truncated
|
||||||
* @vue/runtime-core v3.5.41
|
* @vue/runtime-core v3.5.41
|
||||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
* @license MIT
|
* @license MIT
|
||||||
**/function Yl(s,e,t,i){try{return i?s(...i):s()}catch(n){ou(n,e,t)}}function Vs(s,e,t,i){if(Mt(s)){const n=Yl(s,e,t,i);return n&&by(n)&&n.catch(r=>{ou(r,e,t)}),n}if(ft(s)){const n=[];for(let r=0;r<s.length;r++)n.push(Vs(s[r],e,t,i));return n}}function ou(s,e,t,i=!0){const n=e?e.vnode:null,{errorHandler:r,throwUnhandledErrorInProduction:a}=e&&e.appContext.config||ti;if(e){let o=e.parent;const h=e.proxy,l=`https://vuejs.org/error-reference/#runtime-${t}`;for(;o;){const c=o.ec;if(c){for(let u=0;u<c.length;u++)if(c[u](s,h,l)===!1)return}o=o.parent}if(r){dr(),Yl(r,null,10,[s,h,l]),fr();return}}vM(s,t,n,i,a)}function vM(s,e,t,i=!0,n=!1){if(n)throw s;console.error(s)}const ls=[];let bn=-1;const po=[];let Er=null,to=0;const jy=Promise.resolve();let nh=null;function lu(s){const e=nh||jy;return s?e.then(this?s.bind(this):s):e}function yM(s){let e=bn+1,t=ls.length;for(;e<t;){const i=e+t>>>1,n=ls[i],r=Al(n);r<s||r===s&&n.flags&2?e=i+1:t=i}return e}function np(s){if(!(s.flags&1)){const e=Al(s),t=ls[ls.length-1];!t||!(s.flags&2)&&e>=Al(t)?ls.push(s):ls.splice(yM(e),0,s),s.flags|=1,Wy()}}function Wy(){nh||(nh=jy.then(qy))}function xM(s){if(!ft(s))Er&&s.id===-1?Er.splice(to+1,0,s):s.flags&1||(po.push(s),s.flags|=1);else for(let e=0;e<s.length;e++)po.push(s[e]);Wy()}function jp(s,e,t=bn+1){for(;t<ls.length;t++){const i=ls[t];if(i&&i.flags&2){if(s&&i.id!==s.uid)continue;ls.splice(t,1),t--,i.flags&4&&(i.flags&=-2),i(),i.flags&4||(i.flags&=-2)}}}function zy(s){if(po.length){const e=[...new Set(po)].sort((t,i)=>Al(t)-Al(i));if(po.length=0,Er){for(let t=0;t<e.length;t++)Er.push(e[t]);return}for(Er=e,to=0;to<Er.length;to++){const t=Er[to];t.flags&4&&(t.flags&=-2),t.flags&8||t(),t.flags&=-2}Er=null,to=0}}const Al=s=>s.id==null?s.flags&2?-1:1/0:s.id;function qy(s){try{for(bn=0;bn<ls.length;bn++){const e=ls[bn];e&&!(e.flags&8)&&(e.flags&4&&(e.flags&=-2),Yl(e,e.i,e.i?15:14),e.flags&4||(e.flags&=-2))}}finally{for(;bn<ls.length;bn++){const e=ls[bn];e&&(e.flags&=-2)}bn=-1,ls.length=0,zy(),nh=null,(ls.length||po.length)&&qy()}}let is=null,Ky=null;function rh(s){const e=is;return is=s,Ky=s&&s.type.__scopeId||null,e}function ah(s,e=is,t){if(!e||s._n)return s;const i=(...n)=>{i._d&&hh(-1);const r=rh(e),a=hr.length;let o;try{o=s(...n)}finally{for(let h=hr.length;h>a;h--)lp();rh(r),i._d&&hh(1)}return o};return i._n=!0,i._c=!0,i._d=!0,i}function ye(s,e){if(is===null)return s;const t=mu(is),i=s.dirs||(s.dirs=[]);for(let n=0;n<e.length;n++){let[r,a,o,h=ti]=e[n];r&&(Mt(r)&&(r={mounted:r,updated:r}),r.deep&&er(a),i.push({dir:r,instance:t,value:a,oldValue:void 0,arg:o,modifiers:h}))}return s}function ia(s,e,t,i){const n=s.dirs,r=e&&e.dirs;for(let a=0;a<n.length;a++){const o=n[a];r&&(o.oldValue=r[a].value);let h=o.dir[i];h&&(dr(),Vs(h,t,8,[s.el,o,s,e]),fr())}}function bM(s,e){if(cs){let t=cs.provides;const i=cs.parent&&cs.parent.provides;i===t&&(t=cs.provides=Object.create(i)),t[s]=e}}function dl(s,e,t=!1){const i=cp();if(i||Aa){let n=Aa?Aa._context.provides:i?i.parent==null||i.ce?i.vnode.appContext&&i.vnode.appContext.provides:i.parent.provides:void 0;if(n&&s in n)return n[s];if(arguments.length>1)return t&&Mt(e)?e.call(i&&i.proxy):e}}function wM(){return!!(cp()||Aa)}const SM=Symbol.for("v-scx"),MM=()=>dl(SM);function Jo(s,e){return rp(s,null,e)}function fs(s,e,t){return rp(s,e,t)}function rp(s,e,t=ti){const{immediate:i,deep:n,flush:r,once:a}=t,o=Oi({},t),h=e&&i||!e&&r!=="post";let l;if(Pl){if(r==="sync"){const f=MM();l=f.__watcherHandles||(f.__watcherHandles=[])}else if(!h){const f=()=>{};return f.stop=On,f.resume=On,f.pause=On,f}}const c=cs;o.call=(f,p,m)=>Vs(f,c,p,m);let u=!1;r==="post"?o.scheduler=f=>{ms(f,c&&c.suspense)}:r!=="sync"&&(u=!0,o.scheduler=(f,p)=>{p?f():np(f)}),o.augmentJob=f=>{e&&(f.flags|=4),u&&(f.flags|=2,c&&(f.id=c.uid,f.i=c))};const d=gM(s,e,o);return Pl&&(l?l.push(d):h&&d()),d}function _M(s,e,t){const i=this.proxy,n=vi(s)?s.includes(".")?Zy(i,s):()=>i[s]:s.bind(i,i);let r;Mt(e)?r=e:(r=e.handler,t=e);const a=Hl(this),o=rp(n,r.bind(i),t);return a(),o}function Zy(s,e){const t=e.split(".");return()=>{let i=s;for(let n=0;n<t.length&&i;n++)i=i[t[n]];return i}}const TM=Symbol("_vte"),cu=s=>s.__isTeleport,Ds=Symbol("_leaveCb"),$o=Symbol("_enterCb");function AM(){const s={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Kr(()=>{s.isMounted=!0}),$l(()=>{s.isUnmounting=!0}),s}const Fs=[Function,Array],Jy={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Fs,onEnter:Fs,onAfterEnter:Fs,onEnterCancelled:Fs,onBeforeLeave:Fs,onLeave:Fs,onAfterLeave:Fs,onLeaveCancelled:Fs,onBeforeAppear:Fs,onAppear:Fs,onAfterAppear:Fs,onAppearCancelled:Fs},Qy=s=>{const e=s.subTree;return e.component?Qy(e.component):e},EM={name:"BaseTransition",props:Jy,setup(s,{slots:e}){const t=cp(),i=AM();return()=>{const n=e.default&&ix(e.default(),!0),r=n&&n.length?ex(n):t.subTree?Ve():void 0;if(!r)return;const a=$t(s),{mode:o}=a;if(i.isLeaving)return Uu(r);const h=oh(r);if(!h)return Uu(r);let l=Ff(h,a,i,t,u=>l=u);h.type!==ts&&El(h,l);let
Line truncated
|
**/function Yl(s,e,t,i){try{return i?s(...i):s()}catch(n){ou(n,e,t)}}function Vs(s,e,t,i){if(Mt(s)){const n=Yl(s,e,t,i);return n&&by(n)&&n.catch(r=>{ou(r,e,t)}),n}if(ft(s)){const n=[];for(let r=0;r<s.length;r++)n.push(Vs(s[r],e,t,i));return n}}function ou(s,e,t,i=!0){const n=e?e.vnode:null,{errorHandler:r,throwUnhandledErrorInProduction:a}=e&&e.appContext.config||ti;if(e){let o=e.parent;const h=e.proxy,l=`https://vuejs.org/error-reference/#runtime-${t}`;for(;o;){const c=o.ec;if(c){for(let u=0;u<c.length;u++)if(c[u](s,h,l)===!1)return}o=o.parent}if(r){dr(),Yl(r,null,10,[s,h,l]),fr();return}}v2(s,t,n,i,a)}function v2(s,e,t,i=!0,n=!1){if(n)throw s;console.error(s)}const ls=[];let bn=-1;const po=[];let Er=null,to=0;const jy=Promise.resolve();let nh=null;function lu(s){const e=nh||jy;return s?e.then(this?s.bind(this):s):e}function y2(s){let e=bn+1,t=ls.length;for(;e<t;){const i=e+t>>>1,n=ls[i],r=Al(n);r<s||r===s&&n.flags&2?e=i+1:t=i}return e}function np(s){if(!(s.flags&1)){const e=Al(s),t=ls[ls.length-1];!t||!(s.flags&2)&&e>=Al(t)?ls.push(s):ls.splice(y2(e),0,s),s.flags|=1,Wy()}}function Wy(){nh||(nh=jy.then(qy))}function x2(s){if(!ft(s))Er&&s.id===-1?Er.splice(to+1,0,s):s.flags&1||(po.push(s),s.flags|=1);else for(let e=0;e<s.length;e++)po.push(s[e]);Wy()}function jp(s,e,t=bn+1){for(;t<ls.length;t++){const i=ls[t];if(i&&i.flags&2){if(s&&i.id!==s.uid)continue;ls.splice(t,1),t--,i.flags&4&&(i.flags&=-2),i(),i.flags&4||(i.flags&=-2)}}}function zy(s){if(po.length){const e=[...new Set(po)].sort((t,i)=>Al(t)-Al(i));if(po.length=0,Er){for(let t=0;t<e.length;t++)Er.push(e[t]);return}for(Er=e,to=0;to<Er.length;to++){const t=Er[to];t.flags&4&&(t.flags&=-2),t.flags&8||t(),t.flags&=-2}Er=null,to=0}}const Al=s=>s.id==null?s.flags&2?-1:1/0:s.id;function qy(s){try{for(bn=0;bn<ls.length;bn++){const e=ls[bn];e&&!(e.flags&8)&&(e.flags&4&&(e.flags&=-2),Yl(e,e.i,e.i?15:14),e.flags&4||(e.flags&=-2))}}finally{for(;bn<ls.length;bn++){const e=ls[bn];e&&(e.flags&=-2)}bn=-1,ls.length=0,zy(),nh=null,(ls.length||po.length)&&qy()}}let is=null,Ky=null;function rh(s){const e=is;return is=s,Ky=s&&s.type.__scopeId||null,e}function ah(s,e=is,t){if(!e||s._n)return s;const i=(...n)=>{i._d&&hh(-1);const r=rh(e),a=hr.length;let o;try{o=s(...n)}finally{for(let h=hr.length;h>a;h--)lp();rh(r),i._d&&hh(1)}return o};return i._n=!0,i._c=!0,i._d=!0,i}function ye(s,e){if(is===null)return s;const t=mu(is),i=s.dirs||(s.dirs=[]);for(let n=0;n<e.length;n++){let[r,a,o,h=ti]=e[n];r&&(Mt(r)&&(r={mounted:r,updated:r}),r.deep&&er(a),i.push({dir:r,instance:t,value:a,oldValue:void 0,arg:o,modifiers:h}))}return s}function ia(s,e,t,i){const n=s.dirs,r=e&&e.dirs;for(let a=0;a<n.length;a++){const o=n[a];r&&(o.oldValue=r[a].value);let h=o.dir[i];h&&(dr(),Vs(h,t,8,[s.el,o,s,e]),fr())}}function b2(s,e){if(cs){let t=cs.provides;const i=cs.parent&&cs.parent.provides;i===t&&(t=cs.provides=Object.create(i)),t[s]=e}}function dl(s,e,t=!1){const i=cp();if(i||Aa){let n=Aa?Aa._context.provides:i?i.parent==null||i.ce?i.vnode.appContext&&i.vnode.appContext.provides:i.parent.provides:void 0;if(n&&s in n)return n[s];if(arguments.length>1)return t&&Mt(e)?e.call(i&&i.proxy):e}}function w2(){return!!(cp()||Aa)}const S2=Symbol.for("v-scx"),M2=()=>dl(S2);function Jo(s,e){return rp(s,null,e)}function fs(s,e,t){return rp(s,e,t)}function rp(s,e,t=ti){const{immediate:i,deep:n,flush:r,once:a}=t,o=Oi({},t),h=e&&i||!e&&r!=="post";let l;if(Pl){if(r==="sync"){const f=M2();l=f.__watcherHandles||(f.__watcherHandles=[])}else if(!h){const f=()=>{};return f.stop=On,f.resume=On,f.pause=On,f}}const c=cs;o.call=(f,p,m)=>Vs(f,c,p,m);let u=!1;r==="post"?o.scheduler=f=>{ms(f,c&&c.suspense)}:r!=="sync"&&(u=!0,o.scheduler=(f,p)=>{p?f():np(f)}),o.augmentJob=f=>{e&&(f.flags|=4),u&&(f.flags|=2,c&&(f.id=c.uid,f.i=c))};const d=g2(s,e,o);return Pl&&(l?l.push(d):h&&d()),d}function _2(s,e,t){const i=this.proxy,n=vi(s)?s.includes(".")?Zy(i,s):()=>i[s]:s.bind(i,i);let r;Mt(e)?r=e:(r=e.handler,t=e);const a=Hl(this),o=rp(n,r.bind(i),t);return a(),o}function Zy(s,e){const t=e.split(".");return()=>{let i=s;for(let n=0;n<t.length&&i;n++)i=i[t[n]];return i}}const T2=Symbol("_vte"),cu=s=>s.__isTeleport,Ds=Symbol("_leaveCb"),$o=Symbol("_enterCb");function A2(){const s={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Kr(()=>{s.isMounted=!0}),$l(()=>{s.isUnmounting=!0}),s}const Fs=[Function,Array],Jy={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Fs,onEnter:Fs,onAfterEnter:Fs,onEnterCancelled:Fs,onBeforeLeave:Fs,onLeave:Fs,onAfterLeave:Fs,onLeaveCancelled:Fs,onBeforeAppear:Fs,onAppear:Fs,onAfterAppear:Fs,onAppearCancelled:Fs},Qy=s=>{const e=s.subTree;return e.component?Qy(e.component):e},E2={name:"BaseTransition",props:Jy,setup(s,{slots:e}){const t=cp(),i=A2();return()=>{const n=e.default&&ix(e.default(),!0),r=n&&n.length?ex(n):t.subTree?Ve():void 0;if(!r)return;const a=$t(s),{mode:o}=a;if(i.isLeaving)return Uu(r);const h=oh(r);if(!h)return Uu(r);let l=Ff(h,a,i,t,u=>l=u);h.type!==ts&&El(h,l);let
Line truncated
|
||||||
* @vue/runtime-dom v3.5.41
|
* @vue/runtime-dom v3.5.41
|
||||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||||
* @license MIT
|
* @license MIT
|
||||||
**/let Nf;const rg=typeof window<"u"&&window.trustedTypes;if(rg)try{Nf=rg.createPolicy("vue",{createHTML:s=>s})}catch{}const Ex=Nf?s=>Nf.createHTML(s):s=>s,A_="http://www.w3.org/2000/svg",E_="http://www.w3.org/1998/Math/MathML",Wn=typeof document<"u"?document:null,ag=Wn&&Wn.createElement("template"),C_={insert:(s,e,t)=>{e.insertBefore(s,t||null)},remove:s=>{const e=s.parentNode;e&&e.removeChild(s)},createElement:(s,e,t,i)=>{const n=e==="svg"?Wn.createElementNS(A_,s):e==="mathml"?Wn.createElementNS(E_,s):t?Wn.createElement(s,{is:t}):Wn.createElement(s);return s==="select"&&i&&i.multiple!=null&&n.setAttribute("multiple",i.multiple),n},createText:s=>Wn.createTextNode(s),createComment:s=>Wn.createComment(s),setText:(s,e)=>{s.nodeValue=e},setElementText:(s,e)=>{s.textContent=e},parentNode:s=>s.parentNode,nextSibling:s=>s.nextSibling,querySelector:s=>Wn.querySelector(s),setScopeId(s,e){s.setAttribute(e,"")},insertStaticContent(s,e,t,i,n,r){const a=t?t.previousSibling:e.lastChild;if(n&&(n===r||n.nextSibling))for(;e.insertBefore(n.cloneNode(!0),t),!(n===r||!(n=n.nextSibling)););else{ag.innerHTML=Ex(i==="svg"?`<svg>${s}</svg>`:i==="mathml"?`<math>${s}</math>`:s);const o=ag.content;if(i==="svg"||i==="mathml"){const h=o.firstChild;for(;h.firstChild;)o.appendChild(h.firstChild);o.removeChild(h)}e.insertBefore(o,t)}return[a?a.nextSibling:e.firstChild,t?t.previousSibling:e.lastChild]}},wr="transition",Go="animation",kl=Symbol("_vtc"),Cx={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},I_=Oi({},Jy,Cx),R_=s=>(s.displayName="Transition",s.props=I_,s),P_=R_((s,{slots:e})=>__(CM,k_(s),e)),na=(s,e=[])=>{ft(s)?s.forEach(t=>t(...e)):s&&s(...e)},og=s=>s?ft(s)?s.some(e=>e.length>1):s.length>1:!1;function k_(s){const e={};for(const k in s)k in Cx||(e[k]=s[k]);if(s.css===!1)return e;const{name:t="v",type:i,duration:n,enterFromClass:r=`${t}-enter-from`,enterActiveClass:a=`${t}-enter-active`,enterToClass:o=`${t}-enter-to`,appearFromClass:h=r,appearActiveClass:l=a,appearToClass:c=o,leaveFromClass:u=`${t}-leave-from`,leaveActiveClass:d=`${t}-leave-active`,leaveToClass:f=`${t}-leave-to`}=s,p=F_(n),m=p&&p[0],g=p&&p[1],{onBeforeEnter:v,onEnter:b,onEnterCancelled:y,onLeave:w,onLeaveCancelled:A,onBeforeAppear:T=v,onAppear:_=b,onAppearCancelled:R=y}=e,P=(k,B,D,U)=>{k._enterCancelled=U,ra(k,B?c:o),ra(k,B?l:a),D&&D()},F=(k,B)=>{k._isLeaving=!1,ra(k,u),ra(k,f),ra(k,d),B&&B()},I=k=>(B,D)=>{const U=k?_:b,L=()=>P(B,k,D);na(U,[B,L]),lg(()=>{ra(B,k?h:r),$n(B,k?c:o),og(U)||cg(B,i,m,L)})};return Oi(e,{onBeforeEnter(k){na(v,[k]),$n(k,r),$n(k,a)},onBeforeAppear(k){na(T,[k]),$n(k,h),$n(k,l)},onEnter:I(!1),onAppear:I(!0),onLeave(k,B){k._isLeaving=!0;const D=()=>F(k,B);$n(k,u),k._enterCancelled?($n(k,d),dg(k)):(dg(k),$n(k,d)),lg(()=>{k._isLeaving&&(ra(k,u),$n(k,f),og(w)||cg(k,i,g,D))}),na(w,[k,D])},onEnterCancelled(k){P(k,!1,void 0,!0),na(y,[k])},onAppearCancelled(k){P(k,!0,void 0,!0),na(R,[k])},onLeaveCancelled(k){F(k),na(A,[k])}})}function F_(s){if(s==null)return null;if(qt(s))return[Yu(s.enter),Yu(s.leave)];{const e=Yu(s);return[e,e]}}function Yu(s){return FS(s)}function $n(s,e){e.split(/\s+/).forEach(t=>t&&s.classList.add(t)),(s[kl]||(s[kl]=new Set)).add(e)}function ra(s,e){e.split(/\s+/).forEach(i=>i&&s.classList.remove(i));const t=s[kl];t&&(t.delete(e),t.size||(s[kl]=void 0))}function lg(s){requestAnimationFrame(()=>{requestAnimationFrame(s)})}let O_=0;function cg(s,e,t,i){const n=s._endId=++O_,r=()=>{n===s._endId&&i()};if(t!=null)return setTimeout(r,t);const{type:a,timeout:o,propCount:h}=B_(s,e);if(!a)return i();const l=a+"end";let c=0;const u=()=>{s.removeEventListener(l,d),r()},d=f=>{f.target===s&&++c>=h&&u()};setTimeout(()=>{c<h&&u()},o+1),s.addEventListener(l,d)}function B_(s,e){const t=window.getComputedStyle(s),i=p=>(t[p]||"").split(", "),n=i(`${wr}Delay`),r=i(`${wr}Duration`),a=hg(n,r),o=i(`${Go}Delay`),h=i(`${Go}Duration`),l=hg(o,h);let c=null,u=0,d=0;e===wr?a>0&&(c=wr,u=a,d=r.length):e===Go?l>0&&(c=Go,u=l,d=h.length):(u=Math.max(a,l),c=u>0?a>l?wr:Go:null,d=c?c===wr?r.length:h.length:0);const f=c===wr&&/\b(?:transform|all)(?:,|$)/.test(i(`${wr}Property`).toString());return{type:c,timeout:u,propCount:d,hasTransform:f}}function hg(s,e){for(;s.length<e.length;)s=s.concat(s);return Math.max(...e.map((t,i)=>ug(t)+ug(s[i])))}function ug(s){return s==="auto"?0:Number(s.slice(0,-1).replace(",","."))*1e3}function dg(s){return(s?s.ownerDocument:document).body.offsetHeight}function L_(s,e,t){const i=s[kl];i&&(e=(e?[e,...i]:[...i]).join(" ")),e==null?s.removeAttribute("class"):t?s.setAttribute("class",e):s.className=e}const fh=Symbol("_vod"),Ix=Symbol("_vsh"),Yi={name:"show",beforeMount(s,{value:e},{transition:t}){s[fh]=s.style.display==="none"?"":s.style.display,t&&e?t.beforeEnter(s):Ho(s,e)},mounted(s,{value:e},{tra
Line truncated
|
**/let Nf;const rg=typeof window<"u"&&window.trustedTypes;if(rg)try{Nf=rg.createPolicy("vue",{createHTML:s=>s})}catch{}const Ex=Nf?s=>Nf.createHTML(s):s=>s,AM="http://www.w3.org/2000/svg",EM="http://www.w3.org/1998/Math/MathML",Wn=typeof document<"u"?document:null,ag=Wn&&Wn.createElement("template"),CM={insert:(s,e,t)=>{e.insertBefore(s,t||null)},remove:s=>{const e=s.parentNode;e&&e.removeChild(s)},createElement:(s,e,t,i)=>{const n=e==="svg"?Wn.createElementNS(AM,s):e==="mathml"?Wn.createElementNS(EM,s):t?Wn.createElement(s,{is:t}):Wn.createElement(s);return s==="select"&&i&&i.multiple!=null&&n.setAttribute("multiple",i.multiple),n},createText:s=>Wn.createTextNode(s),createComment:s=>Wn.createComment(s),setText:(s,e)=>{s.nodeValue=e},setElementText:(s,e)=>{s.textContent=e},parentNode:s=>s.parentNode,nextSibling:s=>s.nextSibling,querySelector:s=>Wn.querySelector(s),setScopeId(s,e){s.setAttribute(e,"")},insertStaticContent(s,e,t,i,n,r){const a=t?t.previousSibling:e.lastChild;if(n&&(n===r||n.nextSibling))for(;e.insertBefore(n.cloneNode(!0),t),!(n===r||!(n=n.nextSibling)););else{ag.innerHTML=Ex(i==="svg"?`<svg>${s}</svg>`:i==="mathml"?`<math>${s}</math>`:s);const o=ag.content;if(i==="svg"||i==="mathml"){const h=o.firstChild;for(;h.firstChild;)o.appendChild(h.firstChild);o.removeChild(h)}e.insertBefore(o,t)}return[a?a.nextSibling:e.firstChild,t?t.previousSibling:e.lastChild]}},wr="transition",Go="animation",kl=Symbol("_vtc"),Cx={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},IM=Oi({},Jy,Cx),RM=s=>(s.displayName="Transition",s.props=IM,s),PM=RM((s,{slots:e})=>_M(C2,kM(s),e)),na=(s,e=[])=>{ft(s)?s.forEach(t=>t(...e)):s&&s(...e)},og=s=>s?ft(s)?s.some(e=>e.length>1):s.length>1:!1;function kM(s){const e={};for(const k in s)k in Cx||(e[k]=s[k]);if(s.css===!1)return e;const{name:t="v",type:i,duration:n,enterFromClass:r=`${t}-enter-from`,enterActiveClass:a=`${t}-enter-active`,enterToClass:o=`${t}-enter-to`,appearFromClass:h=r,appearActiveClass:l=a,appearToClass:c=o,leaveFromClass:u=`${t}-leave-from`,leaveActiveClass:d=`${t}-leave-active`,leaveToClass:f=`${t}-leave-to`}=s,p=FM(n),m=p&&p[0],g=p&&p[1],{onBeforeEnter:v,onEnter:b,onEnterCancelled:y,onLeave:w,onLeaveCancelled:A,onBeforeAppear:T=v,onAppear:_=b,onAppearCancelled:R=y}=e,P=(k,B,D,U)=>{k._enterCancelled=U,ra(k,B?c:o),ra(k,B?l:a),D&&D()},F=(k,B)=>{k._isLeaving=!1,ra(k,u),ra(k,f),ra(k,d),B&&B()},I=k=>(B,D)=>{const U=k?_:b,L=()=>P(B,k,D);na(U,[B,L]),lg(()=>{ra(B,k?h:r),$n(B,k?c:o),og(U)||cg(B,i,m,L)})};return Oi(e,{onBeforeEnter(k){na(v,[k]),$n(k,r),$n(k,a)},onBeforeAppear(k){na(T,[k]),$n(k,h),$n(k,l)},onEnter:I(!1),onAppear:I(!0),onLeave(k,B){k._isLeaving=!0;const D=()=>F(k,B);$n(k,u),k._enterCancelled?($n(k,d),dg(k)):(dg(k),$n(k,d)),lg(()=>{k._isLeaving&&(ra(k,u),$n(k,f),og(w)||cg(k,i,g,D))}),na(w,[k,D])},onEnterCancelled(k){P(k,!1,void 0,!0),na(y,[k])},onAppearCancelled(k){P(k,!0,void 0,!0),na(R,[k])},onLeaveCancelled(k){F(k),na(A,[k])}})}function FM(s){if(s==null)return null;if(qt(s))return[Yu(s.enter),Yu(s.leave)];{const e=Yu(s);return[e,e]}}function Yu(s){return FS(s)}function $n(s,e){e.split(/\s+/).forEach(t=>t&&s.classList.add(t)),(s[kl]||(s[kl]=new Set)).add(e)}function ra(s,e){e.split(/\s+/).forEach(i=>i&&s.classList.remove(i));const t=s[kl];t&&(t.delete(e),t.size||(s[kl]=void 0))}function lg(s){requestAnimationFrame(()=>{requestAnimationFrame(s)})}let OM=0;function cg(s,e,t,i){const n=s._endId=++OM,r=()=>{n===s._endId&&i()};if(t!=null)return setTimeout(r,t);const{type:a,timeout:o,propCount:h}=BM(s,e);if(!a)return i();const l=a+"end";let c=0;const u=()=>{s.removeEventListener(l,d),r()},d=f=>{f.target===s&&++c>=h&&u()};setTimeout(()=>{c<h&&u()},o+1),s.addEventListener(l,d)}function BM(s,e){const t=window.getComputedStyle(s),i=p=>(t[p]||"").split(", "),n=i(`${wr}Delay`),r=i(`${wr}Duration`),a=hg(n,r),o=i(`${Go}Delay`),h=i(`${Go}Duration`),l=hg(o,h);let c=null,u=0,d=0;e===wr?a>0&&(c=wr,u=a,d=r.length):e===Go?l>0&&(c=Go,u=l,d=h.length):(u=Math.max(a,l),c=u>0?a>l?wr:Go:null,d=c?c===wr?r.length:h.length:0);const f=c===wr&&/\b(?:transform|all)(?:,|$)/.test(i(`${wr}Property`).toString());return{type:c,timeout:u,propCount:d,hasTransform:f}}function hg(s,e){for(;s.length<e.length;)s=s.concat(s);return Math.max(...e.map((t,i)=>ug(t)+ug(s[i])))}function ug(s){return s==="auto"?0:Number(s.slice(0,-1).replace(",","."))*1e3}function dg(s){return(s?s.ownerDocument:document).body.offsetHeight}function LM(s,e,t){const i=s[kl];i&&(e=(e?[e,...i]:[...i]).join(" ")),e==null?s.removeAttribute("class"):t?s.setAttribute("class",e):s.className=e}const fh=Symbol("_vod"),Ix=Symbol("_vsh"),Yi={name:"show",beforeMount(s,{value:e},{transition:t}){s[fh]=s.style.display==="none"?"":s.style.display,t&&e?t.beforeEnter(s):Ho(s,e)},mounted(s,{value:e},{tra
Line truncated
|
||||||
`))),s[Rn]=Eo(n);const r=i||n.props&&n.props.type==="number";Fr(s,e?"change":"input",a=>{a.target.composing||s[Rn](Hu(s.value,t,r))}),(t||r)&&Fr(s,"change",()=>{s.value=Hu(s.value,t,r)}),e||(Fr(s,"compositionstart",Q_),Fr(s,"compositionend",bg),Fr(s,"change",bg))},mounted(s,{value:e,modifiers:{trim:t,number:i}}){const n=e??"",r=s[rc];delete s[rc],r!==void 0&&(s.type==="text"||s.type==="textarea")&&s.value!==r?s[Rn](Hu(s.value,t,i)):s.value=n},beforeUpdate(s,{value:e,oldValue:t,modifiers:{lazy:i,trim:n,number:r}},a){if(s[Rn]=Eo(a),s.composing)return;const o=(r||s.type==="number")&&!/^0\d/.test(s.value)?su(s.value):s.value,h=e??"";if(o===h)return;const l=s.getRootNode();(l instanceof Document||l instanceof ShadowRoot)&&l.activeElement===s&&s.type!=="range"&&(i&&e===t||n&&s.value.trim()===h)||(s.value=h)}},ei={deep:!0,created(s,e,t){s[Rn]=Eo(t),Fr(s,"change",()=>{const i=s._modelValue,n=Fl(s),r=s.checked,a=s[Rn];if(ft(i)){const o=Zm(i,n),h=o!==-1;if(r&&!h)a(i.concat(n));else if(!r&&h){const l=[...i];l.splice(o,1),a(l)}}else if(Oo(i)){const o=new Set(i);r?o.add(n):o.delete(n),a(o)}else a(Rx(s,r))})},mounted:wg,beforeUpdate(s,e,t){s[Rn]=Eo(t),wg(s,e,t)}};function wg(s,{value:e,oldValue:t},i){s._modelValue=e;let n;if(ft(e))n=Zm(e,i.props.value)>-1;else if(Oo(e))n=e.has(i.props.value);else{if(e===t)return;n=Bo(e,Rx(s,!0))}s.checked!==n&&(s.checked=n)}const Ht={deep:!0,created(s,{value:e,modifiers:{number:t}},i){s._modelValue=e,Fr(s,"change",()=>{const n=Array.prototype.filter.call(s.options,r=>r.selected).map(r=>t?su(Fl(r)):Fl(r));s[Rn](s.multiple?Oo(s._modelValue)?new Set(n):n:n[0]),s._assigning=!0,lu(()=>{s._assigning=!1})}),s[Rn]=Eo(i)},mounted(s,{value:e}){Sg(s,e)},beforeUpdate(s,{value:e},t){s._modelValue=e,s[Rn]=Eo(t)},updated(s,{value:e}){s._assigning||Sg(s,e)}};function Sg(s,e){const t=s.multiple,i=ft(e);if(!(t&&!i&&!Oo(e))){for(let n=0,r=s.options.length;n<r;n++){const a=s.options[n],o=Fl(a);if(t)if(i){const h=typeof o;h==="string"||h==="number"?a.selected=e.some(l=>String(l)===String(o)):a.selected=Zm(e,o)>-1}else a.selected=e.has(o);else if(Bo(Fl(a),e)){s.selectedIndex!==n&&(s.selectedIndex=n);return}}!t&&s.selectedIndex!==-1&&(s.selectedIndex=-1)}}function Fl(s){return"_value"in s?s._value:s.value}function Rx(s,e){const t=e?"_trueValue":"_falseValue";return t in s?s[t]:e}const eT=["ctrl","shift","alt","meta"],tT={stop:s=>s.stopPropagation(),prevent:s=>s.preventDefault(),self:s=>s.target!==s.currentTarget,ctrl:s=>!s.ctrlKey,shift:s=>!s.shiftKey,alt:s=>!s.altKey,meta:s=>!s.metaKey,left:s=>"button"in s&&s.button!==0,middle:s=>"button"in s&&s.button!==1,right:s=>"button"in s&&s.button!==2,exact:(s,e)=>eT.some(t=>s[`${t}Key`]&&!e.includes(t))},bt=(s,e)=>{if(!s)return s;const t=s._withMods||(s._withMods={}),i=e.join(".");return t[i]||(t[i]=((n,...r)=>{for(let a=0;a<e.length;a++){const o=tT[e[a]];if(o&&o(n,e))return}return s(n,...r)}))},iT={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},Ea=(s,e)=>{const t=s._withKeys||(s._withKeys={}),i=e.join(".");return t[i]||(t[i]=(n=>{if(!("key"in n))return;const r=qr(n.key);if(e.some(a=>a===r||iT[a]===r))return s(n)}))},sT=Oi({patchProp:K_},C_);let Mg;function nT(){return Mg||(Mg=l_(sT))}const rT=((...s)=>{const e=nT().createApp(...s),{mount:t}=e;return e.mount=i=>{const n=oT(i);if(!n)return;const r=e._component;!Mt(r)&&!r.render&&!r.template&&(r.template=n.innerHTML),n.nodeType===1&&(n.textContent="");const a=t(n,!1,aT(n));return n instanceof Element&&(n.removeAttribute("v-cloak"),n.setAttribute("data-v-app","")),a},e});function aT(s){if(s instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&s instanceof MathMLElement)return"mathml"}function oT(s){return vi(s)?document.querySelector(s):s}/*!
|
`))),s[Rn]=Eo(n);const r=i||n.props&&n.props.type==="number";Fr(s,e?"change":"input",a=>{a.target.composing||s[Rn](Hu(s.value,t,r))}),(t||r)&&Fr(s,"change",()=>{s.value=Hu(s.value,t,r)}),e||(Fr(s,"compositionstart",QM),Fr(s,"compositionend",bg),Fr(s,"change",bg))},mounted(s,{value:e,modifiers:{trim:t,number:i}}){const n=e??"",r=s[rc];delete s[rc],r!==void 0&&(s.type==="text"||s.type==="textarea")&&s.value!==r?s[Rn](Hu(s.value,t,i)):s.value=n},beforeUpdate(s,{value:e,oldValue:t,modifiers:{lazy:i,trim:n,number:r}},a){if(s[Rn]=Eo(a),s.composing)return;const o=(r||s.type==="number")&&!/^0\d/.test(s.value)?su(s.value):s.value,h=e??"";if(o===h)return;const l=s.getRootNode();(l instanceof Document||l instanceof ShadowRoot)&&l.activeElement===s&&s.type!=="range"&&(i&&e===t||n&&s.value.trim()===h)||(s.value=h)}},ei={deep:!0,created(s,e,t){s[Rn]=Eo(t),Fr(s,"change",()=>{const i=s._modelValue,n=Fl(s),r=s.checked,a=s[Rn];if(ft(i)){const o=Zm(i,n),h=o!==-1;if(r&&!h)a(i.concat(n));else if(!r&&h){const l=[...i];l.splice(o,1),a(l)}}else if(Oo(i)){const o=new Set(i);r?o.add(n):o.delete(n),a(o)}else a(Rx(s,r))})},mounted:wg,beforeUpdate(s,e,t){s[Rn]=Eo(t),wg(s,e,t)}};function wg(s,{value:e,oldValue:t},i){s._modelValue=e;let n;if(ft(e))n=Zm(e,i.props.value)>-1;else if(Oo(e))n=e.has(i.props.value);else{if(e===t)return;n=Bo(e,Rx(s,!0))}s.checked!==n&&(s.checked=n)}const Ht={deep:!0,created(s,{value:e,modifiers:{number:t}},i){s._modelValue=e,Fr(s,"change",()=>{const n=Array.prototype.filter.call(s.options,r=>r.selected).map(r=>t?su(Fl(r)):Fl(r));s[Rn](s.multiple?Oo(s._modelValue)?new Set(n):n:n[0]),s._assigning=!0,lu(()=>{s._assigning=!1})}),s[Rn]=Eo(i)},mounted(s,{value:e}){Sg(s,e)},beforeUpdate(s,{value:e},t){s._modelValue=e,s[Rn]=Eo(t)},updated(s,{value:e}){s._assigning||Sg(s,e)}};function Sg(s,e){const t=s.multiple,i=ft(e);if(!(t&&!i&&!Oo(e))){for(let n=0,r=s.options.length;n<r;n++){const a=s.options[n],o=Fl(a);if(t)if(i){const h=typeof o;h==="string"||h==="number"?a.selected=e.some(l=>String(l)===String(o)):a.selected=Zm(e,o)>-1}else a.selected=e.has(o);else if(Bo(Fl(a),e)){s.selectedIndex!==n&&(s.selectedIndex=n);return}}!t&&s.selectedIndex!==-1&&(s.selectedIndex=-1)}}function Fl(s){return"_value"in s?s._value:s.value}function Rx(s,e){const t=e?"_trueValue":"_falseValue";return t in s?s[t]:e}const e_=["ctrl","shift","alt","meta"],t_={stop:s=>s.stopPropagation(),prevent:s=>s.preventDefault(),self:s=>s.target!==s.currentTarget,ctrl:s=>!s.ctrlKey,shift:s=>!s.shiftKey,alt:s=>!s.altKey,meta:s=>!s.metaKey,left:s=>"button"in s&&s.button!==0,middle:s=>"button"in s&&s.button!==1,right:s=>"button"in s&&s.button!==2,exact:(s,e)=>e_.some(t=>s[`${t}Key`]&&!e.includes(t))},bt=(s,e)=>{if(!s)return s;const t=s._withMods||(s._withMods={}),i=e.join(".");return t[i]||(t[i]=((n,...r)=>{for(let a=0;a<e.length;a++){const o=t_[e[a]];if(o&&o(n,e))return}return s(n,...r)}))},i_={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},Ea=(s,e)=>{const t=s._withKeys||(s._withKeys={}),i=e.join(".");return t[i]||(t[i]=(n=>{if(!("key"in n))return;const r=qr(n.key);if(e.some(a=>a===r||i_[a]===r))return s(n)}))},s_=Oi({patchProp:KM},CM);let Mg;function n_(){return Mg||(Mg=lM(s_))}const r_=((...s)=>{const e=n_().createApp(...s),{mount:t}=e;return e.mount=i=>{const n=o_(i);if(!n)return;const r=e._component;!Mt(r)&&!r.render&&!r.template&&(r.template=n.innerHTML),n.nodeType===1&&(n.textContent="");const a=t(n,!1,a_(n));return n instanceof Element&&(n.removeAttribute("v-cloak"),n.setAttribute("data-v-app","")),a},e});function a_(s){if(s instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&s instanceof MathMLElement)return"mathml"}function o_(s){return vi(s)?document.querySelector(s):s}/*!
|
||||||
* pinia v4.0.3
|
* pinia v4.0.3
|
||||||
* (c) 2026 Eduardo San Martin Morote
|
* (c) 2026 Eduardo San Martin Morote
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/let Px;const pu=s=>Px=s,kx=Symbol();function _g(s){return s&&typeof s=="object"&&Object.prototype.toString.call(s)==="[object Object]"&&typeof s.toJSON!="function"}function lT(){const s=Iy(!0),e=s.run(()=>lt({}));let t=[],i=[];const n=Dr({install(r){pu(n),n._a=r,r.provide(kx,n),r.config.globalProperties.$pinia=n,i.forEach(a=>t.push(a)),i=[]},use(r){return this._a?t.push(r):i.push(r),this},_p:t,_a:null,_e:s,_s:new Map,state:e});return n}const Uf=()=>{};function Tg(s,e,t,i=Uf){s.add(e);const n=()=>{s.delete(e)&&i()};return!t&&Ry()&&VS(n),n}function Wa(s,...e){s.forEach(t=>{t(...e)})}const cT=s=>s(),Ag=Symbol(),ju=Symbol();function Xf(s,e){s instanceof Map&&e instanceof Map?e.forEach((t,i)=>s.set(i,t)):s instanceof Set&&e instanceof Set&&e.forEach(s.add,s);for(const t in e){if(!Object.hasOwn(e,t))continue;const i=e[t],n=s[t];_g(n)&&_g(i)&&Object.hasOwn(s,t)&&!Si(i)&&!cr(i)?s[t]=Xf(n,i):s[t]=i}return s}const hT=Symbol();function uT(s){return!s||typeof s!="object"||!Object.hasOwn(s,hT)}const{assign:_r}=Object;function dT(s){return!!(Si(s)&&s.effect)}function fT(s,e,t,i){const{state:n,actions:r,getters:a}=e,o=t.state.value[s];let h;function l(){o||(t.state.value[s]=n?n():{});const c=hM(t.state.value[s]);return _r(c,r,Object.keys(a||{}).reduce((u,d)=>(u[d]=Dr(vt(()=>{pu(t);const f=t._s.get(s);return a[d].call(f,f)})),u),{}))}return h=Fx(s,l,e,t,i,!0),h}function Fx(s,e,t={},i,n,r){let a;const o=_r({actions:{}},t),h={deep:!0};let l,c,u=new Set,d=new Set,f;const p=i.state.value[s];!r&&!p&&(i.state.value[s]={});let m;function g(_){let R;l=c=!1,typeof _=="function"?(_(i.state.value[s]),R={type:"patch function",storeId:s,events:f}):(Xf(i.state.value[s],_),R={type:"patch object",payload:_,storeId:s,events:f});const P=m=Symbol();lu().then(()=>{m===P&&(l=!0)}),c=!0,Wa(u,R,i.state.value[s])}const v=r?function(){const{state:R}=t,P=R?R():{};this.$patch(F=>{_r(F,P)})}:Uf;function b(){a.stop(),u.clear(),d.clear(),i._s.delete(s)}const y=(_,R="")=>{if(Ag in _)return _[ju]=R,_;const P=function(){pu(i);const F=Array.from(arguments),I=new Set,k=new Set;function B(L){I.add(L)}function D(L){k.add(L)}Wa(d,{args:F,name:P[ju],store:A,after:B,onError:D});let U;try{U=_.apply(this&&this.$id===s?this:A,F)}catch(L){throw Wa(k,L),L}return U instanceof Promise?U.then(L=>(Wa(I,L),L)).catch(L=>(Wa(k,L),Promise.reject(L))):(Wa(I,U),U)};return P[Ag]=!0,P[ju]=R,P},w={_p:i,$id:s,$onAction:Tg.bind(null,d),$patch:g,$reset:v,$subscribe(_,R={}){if(u.has(_))return Uf;const P=Tg(u,_,R.detached,()=>F()),F=a.run(()=>fs(()=>i.state.value[s],I=>{(R.flush==="sync"?c:l)&&_({storeId:s,type:"direct",events:f},I)},_r({},h,R)));return P},$dispose:b},A=Da(w);i._s.set(s,A);const T=(i._a&&i._a.runWithContext||cT)(()=>i._e.run(()=>(a=Iy()).run(()=>e({action:y}))));for(const _ in T){const R=T[_];Si(R)&&!dT(R)||cr(R)?r||(p&&uT(R)&&(Si(R)?R.value=p[_]:((R instanceof Set||R instanceof Map)&&R.clear(),Xf(R,p[_]))),i.state.value[s][_]=R):typeof R=="function"&&(T[_]=y(R,_),o.actions[_]=R)}return _r(A,T),_r($t(A),T),Object.defineProperty(A,"$state",{get:()=>i.state.value[s],set:_=>{g(R=>{_r(R,_)})}}),i._p.forEach(_=>{const R=a.run(()=>_({store:A,app:i._a,pinia:i,options:o}));_r(A,R)}),p&&r&&t.hydrate&&t.hydrate(A.$state,p),l=!0,c=!0,A}/*! #__NO_SIDE_EFFECTS__ */function mT(s,e,t){let i;const n=typeof e=="function";i=n?t:e;function r(a,o){const h=wM();return a=a||(h?dl(kx,null):null),a&&pu(a),a=Px,a._s.has(s)||(n?Fx(s,e,i,a):fT(s,i,a)),a._s.get(s)}return r.$id=s,r}var Na=(s=>(s[s.WEBGL_LEGACY=0]="WEBGL_LEGACY",s[s.WEBGL=1]="WEBGL",s[s.WEBGL2=2]="WEBGL2",s))(Na||{}),Ox=(s=>(s[s.UNKNOWN=0]="UNKNOWN",s[s.WEBGL=1]="WEBGL",s[s.CANVAS=2]="CANVAS",s))(Ox||{}),Vf=(s=>(s[s.COLOR=16384]="COLOR",s[s.DEPTH=256]="DEPTH",s[s.STENCIL=1024]="STENCIL",s))(Vf||{}),dt=(s=>(s[s.NORMAL=0]="NORMAL",s[s.ADD=1]="ADD",s[s.MULTIPLY=2]="MULTIPLY",s[s.SCREEN=3]="SCREEN",s[s.OVERLAY=4]="OVERLAY",s[s.DARKEN=5]="DARKEN",s[s.LIGHTEN=6]="LIGHTEN",s[s.COLOR_DODGE=7]="COLOR_DODGE",s[s.COLOR_BURN=8]="COLOR_BURN",s[s.HARD_LIGHT=9]="HARD_LIGHT",s[s.SOFT_LIGHT=10]="SOFT_LIGHT",s[s.DIFFERENCE=11]="DIFFERENCE",s[s.EXCLUSION=12]="EXCLUSION",s[s.HUE=13]="HUE",s[s.SATURATION=14]="SATURATION",s[s.COLOR=15]="COLOR",s[s.LUMINOSITY=16]="LUMINOSITY",s[s.NORMAL_NPM=17]="NORMAL_NPM",s[s.ADD_NPM=18]="ADD_NPM",s[s.SCREEN_NPM=19]="SCREEN_NPM",s[s.NONE=20]="NONE",s[s.SRC_OVER=0]="SRC_OVER",s[s.SRC_IN=21]="SRC_IN",s[s.SRC_OUT=22]="SRC_OUT",s[s.SRC_ATOP=23]="SRC_ATOP",s[s.DST_OVER=24]="DST_OVER",s[s.DST_IN=25]="DST_IN",s[s.DST_OUT=26]="DST_OUT",s[s.DST_ATOP=27]="DST_ATOP",s[s.ERASE=26]="ERASE",s[s.SUBTRACT=28]="SUBTRACT",s[s.XOR=29]="XOR",s))(dt||{}),Pn=(s=>(s[s.POINTS=0]="POINTS",s[s.LINES=1]="LINES",s[s.LINE_LOOP=2]="LINE_LOOP",s[s.LINE_STRIP=3]="LINE_STRIP",s[s.TRIANGLES=4]="TRIANGLES",s[s.TRIANGLE_STRIP=5]="TRIANGLE_STRIP",s[s.TRIANGLE_FAN=6]="TRIANGLE_FAN",s))(Pn||{}),Ne=(s=>(s[s.RGBA=6408]="RGBA",s[s.RGB=6407]="RGB",s[s.RG=33319]="RG",s[s.RED=6403]="RED",s[s.RGBA_INTEGER=36249]="RGBA_INTEGER",s[s.RGB_INTEGER=36248]="RGB_INTELine truncated
|
*/let Px;const pu=s=>Px=s,kx=Symbol();function _g(s){return s&&typeof s=="object"&&Object.prototype.toString.call(s)==="[object Object]"&&typeof s.toJSON!="function"}function l_(){const s=Iy(!0),e=s.run(()=>lt({}));let t=[],i=[];const n=Dr({install(r){pu(n),n._a=r,r.provide(kx,n),r.config.globalProperties.$pinia=n,i.forEach(a=>t.push(a)),i=[]},use(r){return this._a?t.push(r):i.push(r),this},_p:t,_a:null,_e:s,_s:new Map,state:e});return n}const Uf=()=>{};function Tg(s,e,t,i=Uf){s.add(e);const n=()=>{s.delete(e)&&i()};return!t&&Ry()&&VS(n),n}function Wa(s,...e){s.forEach(t=>{t(...e)})}const c_=s=>s(),Ag=Symbol(),ju=Symbol();function Xf(s,e){s instanceof Map&&e instanceof Map?e.forEach((t,i)=>s.set(i,t)):s instanceof Set&&e instanceof Set&&e.forEach(s.add,s);for(const t in e){if(!Object.hasOwn(e,t))continue;const i=e[t],n=s[t];_g(n)&&_g(i)&&Object.hasOwn(s,t)&&!Si(i)&&!cr(i)?s[t]=Xf(n,i):s[t]=i}return s}const h_=Symbol();function u_(s){return!s||typeof s!="object"||!Object.hasOwn(s,h_)}const{assign:_r}=Object;function d_(s){return!!(Si(s)&&s.effect)}function f_(s,e,t,i){const{state:n,actions:r,getters:a}=e,o=t.state.value[s];let h;function l(){o||(t.state.value[s]=n?n():{});const c=h2(t.state.value[s]);return _r(c,r,Object.keys(a||{}).reduce((u,d)=>(u[d]=Dr(vt(()=>{pu(t);const f=t._s.get(s);return a[d].call(f,f)})),u),{}))}return h=Fx(s,l,e,t,i,!0),h}function Fx(s,e,t={},i,n,r){let a;const o=_r({actions:{}},t),h={deep:!0};let l,c,u=new Set,d=new Set,f;const p=i.state.value[s];!r&&!p&&(i.state.value[s]={});let m;function g(_){let R;l=c=!1,typeof _=="function"?(_(i.state.value[s]),R={type:"patch function",storeId:s,events:f}):(Xf(i.state.value[s],_),R={type:"patch object",payload:_,storeId:s,events:f});const P=m=Symbol();lu().then(()=>{m===P&&(l=!0)}),c=!0,Wa(u,R,i.state.value[s])}const v=r?function(){const{state:R}=t,P=R?R():{};this.$patch(F=>{_r(F,P)})}:Uf;function b(){a.stop(),u.clear(),d.clear(),i._s.delete(s)}const y=(_,R="")=>{if(Ag in _)return _[ju]=R,_;const P=function(){pu(i);const F=Array.from(arguments),I=new Set,k=new Set;function B(L){I.add(L)}function D(L){k.add(L)}Wa(d,{args:F,name:P[ju],store:A,after:B,onError:D});let U;try{U=_.apply(this&&this.$id===s?this:A,F)}catch(L){throw Wa(k,L),L}return U instanceof Promise?U.then(L=>(Wa(I,L),L)).catch(L=>(Wa(k,L),Promise.reject(L))):(Wa(I,U),U)};return P[Ag]=!0,P[ju]=R,P},w={_p:i,$id:s,$onAction:Tg.bind(null,d),$patch:g,$reset:v,$subscribe(_,R={}){if(u.has(_))return Uf;const P=Tg(u,_,R.detached,()=>F()),F=a.run(()=>fs(()=>i.state.value[s],I=>{(R.flush==="sync"?c:l)&&_({storeId:s,type:"direct",events:f},I)},_r({},h,R)));return P},$dispose:b},A=Da(w);i._s.set(s,A);const T=(i._a&&i._a.runWithContext||c_)(()=>i._e.run(()=>(a=Iy()).run(()=>e({action:y}))));for(const _ in T){const R=T[_];Si(R)&&!d_(R)||cr(R)?r||(p&&u_(R)&&(Si(R)?R.value=p[_]:((R instanceof Set||R instanceof Map)&&R.clear(),Xf(R,p[_]))),i.state.value[s][_]=R):typeof R=="function"&&(T[_]=y(R,_),o.actions[_]=R)}return _r(A,T),_r($t(A),T),Object.defineProperty(A,"$state",{get:()=>i.state.value[s],set:_=>{g(R=>{_r(R,_)})}}),i._p.forEach(_=>{const R=a.run(()=>_({store:A,app:i._a,pinia:i,options:o}));_r(A,R)}),p&&r&&t.hydrate&&t.hydrate(A.$state,p),l=!0,c=!0,A}/*! #__NO_SIDE_EFFECTS__ */function m_(s,e,t){let i;const n=typeof e=="function";i=n?t:e;function r(a,o){const h=w2();return a=a||(h?dl(kx,null):null),a&&pu(a),a=Px,a._s.has(s)||(n?Fx(s,e,i,a):f_(s,i,a)),a._s.get(s)}return r.$id=s,r}var Na=(s=>(s[s.WEBGL_LEGACY=0]="WEBGL_LEGACY",s[s.WEBGL=1]="WEBGL",s[s.WEBGL2=2]="WEBGL2",s))(Na||{}),Ox=(s=>(s[s.UNKNOWN=0]="UNKNOWN",s[s.WEBGL=1]="WEBGL",s[s.CANVAS=2]="CANVAS",s))(Ox||{}),Vf=(s=>(s[s.COLOR=16384]="COLOR",s[s.DEPTH=256]="DEPTH",s[s.STENCIL=1024]="STENCIL",s))(Vf||{}),dt=(s=>(s[s.NORMAL=0]="NORMAL",s[s.ADD=1]="ADD",s[s.MULTIPLY=2]="MULTIPLY",s[s.SCREEN=3]="SCREEN",s[s.OVERLAY=4]="OVERLAY",s[s.DARKEN=5]="DARKEN",s[s.LIGHTEN=6]="LIGHTEN",s[s.COLOR_DODGE=7]="COLOR_DODGE",s[s.COLOR_BURN=8]="COLOR_BURN",s[s.HARD_LIGHT=9]="HARD_LIGHT",s[s.SOFT_LIGHT=10]="SOFT_LIGHT",s[s.DIFFERENCE=11]="DIFFERENCE",s[s.EXCLUSION=12]="EXCLUSION",s[s.HUE=13]="HUE",s[s.SATURATION=14]="SATURATION",s[s.COLOR=15]="COLOR",s[s.LUMINOSITY=16]="LUMINOSITY",s[s.NORMAL_NPM=17]="NORMAL_NPM",s[s.ADD_NPM=18]="ADD_NPM",s[s.SCREEN_NPM=19]="SCREEN_NPM",s[s.NONE=20]="NONE",s[s.SRC_OVER=0]="SRC_OVER",s[s.SRC_IN=21]="SRC_IN",s[s.SRC_OUT=22]="SRC_OUT",s[s.SRC_ATOP=23]="SRC_ATOP",s[s.DST_OVER=24]="DST_OVER",s[s.DST_IN=25]="DST_IN",s[s.DST_OUT=26]="DST_OUT",s[s.DST_ATOP=27]="DST_ATOP",s[s.ERASE=26]="ERASE",s[s.SUBTRACT=28]="SUBTRACT",s[s.XOR=29]="XOR",s))(dt||{}),Pn=(s=>(s[s.POINTS=0]="POINTS",s[s.LINES=1]="LINES",s[s.LINE_LOOP=2]="LINE_LOOP",s[s.LINE_STRIP=3]="LINE_STRIP",s[s.TRIANGLES=4]="TRIANGLES",s[s.TRIANGLE_STRIP=5]="TRIANGLE_STRIP",s[s.TRIANGLE_FAN=6]="TRIANGLE_FAN",s))(Pn||{}),Ne=(s=>(s[s.RGBA=6408]="RGBA",s[s.RGB=6407]="RGB",s[s.RG=33319]="RG",s[s.RED=6403]="RED",s[s.RGBA_INTEGER=36249]="RGBA_INTEGER",s[s.RGB_INTEGER=36248]="RGB_INTELine truncated
|
||||||
`)>=0)return!1;return!0}function zi(ce,de){var Rt;if(ce.indent===" ")Rt=" ";else if(typeof ce.indent=="number"&&ce.indent>0)Rt=R.call(Array(ce.indent+1)," ");else return null;return{base:Rt,prev:R.call(Array(de+1),Rt)}}function dn(ce,de){if(ce.length===0)return"";var Rt=`
|
`)>=0)return!1;return!0}function zi(ce,de){var Rt;if(ce.indent===" ")Rt=" ";else if(typeof ce.indent=="number"&&ce.indent>0)Rt=R.call(Array(ce.indent+1)," ");else return null;return{base:Rt,prev:R.call(Array(de+1),Rt)}}function dn(ce,de){if(ce.length===0)return"";var Rt=`
|
||||||
`+de.prev+de.base;return Rt+R.call(ce,","+Rt)+`
|
`+de.prev+de.base;return Rt+R.call(ce,","+Rt)+`
|
||||||
`+de.prev}function di(ce,de){var Rt=me(ce),Wt=[];if(Rt){Wt.length=ce.length;for(var Zt=0;Zt<ce.length;Zt++)Wt[Zt]=he(ce,Zt)?de(ce[Zt],ce):""}var pt=typeof k=="function"?k(ce):[],Rs;if(D){Rs={};for(var Hs=0;Hs<pt.length;Hs++)Rs["$"+pt[Hs]]=pt[Hs]}for(var Ui in ce)he(ce,Ui)&&(Rt&&String(Number(Ui))===Ui&&Ui<ce.length||D&&Rs["$"+Ui]instanceof Symbol||(T.call(/[^\w$]/,Ui)?Wt.push(de(Ui,ce)+": "+de(ce[Ui],ce)):Wt.push(Ui+": "+de(ce[Ui],ce))));if(typeof k=="function")for(var Ps=0;Ps<pt.length;Ps++)L.call(ce,pt[Ps])&&Wt.push("["+de(pt[Ps])+"]: "+de(ce[pt[Ps]],ce));return Wt}return Zu}var Ju,Gg;function IT(){if(Gg)return Ju;Gg=1;var s=gu(),e=Lo(),t=function(o,h,l){for(var c=o,u;(u=c.next)!=null;c=u)if(u.key===h)return c.next=u.next,l||(u.next=o.next,o.next=u),u},i=function(o,h){if(o){var l=t(o,h);return l&&l.value}},n=function(o,h,l){var c=t(o,h);c?c.value=l:o.next={key:h,next:o.next,value:l}},r=function(o,h){return o?!!t(o,h):!1},a=function(o,h){if(o)return t(o,h,!0)};return Ju=function(){var h,l={assert:function(c){if(!l.has(c))throw new e("Side channel does not contain "+s(c))},delete:function(c){var u=a(h,c);return u&&h&&!h.next&&(h=void 0),!!u},get:function(c){return i(h,c)},has:function(c){return r(h,c)},set:function(c,u){h||(h={next:void 0}),n(h,c,u)}};return l},Ju}var Qu,Hg;function Lx(){return Hg||(Hg=1,Qu=Object),Qu}var ed,jg;function RT(){return jg||(jg=1,ed=Error),ed}var td,Wg;function PT(){return Wg||(Wg=1,td=EvalError),td}var id,zg;function kT(){return zg||(zg=1,id=RangeError),id}var sd,qg;function FT(){return qg||(qg=1,sd=ReferenceError),sd}var nd,Kg;function OT(){return Kg||(Kg=1,nd=SyntaxError),nd}var rd,Zg;function BT(){return Zg||(Zg=1,rd=URIError),rd}var ad,Jg;function LT(){return Jg||(Jg=1,ad=Math.abs),ad}var od,Qg;function DT(){return Qg||(Qg=1,od=Math.floor),od}var ld,e0;function NT(){return e0||(e0=1,ld=Math.max),ld}var cd,t0;function UT(){return t0||(t0=1,cd=Math.min),cd}var hd,i0;function XT(){return i0||(i0=1,hd=Math.pow),hd}var ud,s0;function VT(){return s0||(s0=1,ud=Math.round),ud}var dd,n0;function YT(){return n0||(n0=1,dd=Number.isNaN||function(e){return e!==e}),dd}var fd,r0;function $T(){if(r0)return fd;r0=1;var s=YT();return fd=function(t){return s(t)||t===0?t:t<0?-1:1},fd}var md,a0;function GT(){return a0||(a0=1,md=Object.getOwnPropertyDescriptor),md}var pd,o0;function Dx(){if(o0)return pd;o0=1;var s=GT();if(s)try{s([],"length")}catch{s=null}return pd=s,pd}var gd,l0;function Nx(){if(l0)return gd;l0=1;var s=Object.defineProperty||!1;if(s)try{s({},"a",{value:1})}catch{s=!1}return gd=s,gd}var vd,c0;function HT(){return c0||(c0=1,vd=function(){if(typeof Symbol!="function"||typeof Object.getOwnPropertySymbols!="function")return!1;if(typeof Symbol.iterator=="symbol")return!0;var e={},t=Symbol("test"),i=Object(t);if(typeof t=="string"||Object.prototype.toString.call(t)!=="[object Symbol]"||Object.prototype.toString.call(i)!=="[object Symbol]")return!1;var n=42;e[t]=n;for(var r in e)return!1;if(typeof Object.keys=="function"&&Object.keys(e).length!==0||typeof Object.getOwnPropertyNames=="function"&&Object.getOwnPropertyNames(e).length!==0)return!1;var a=Object.getOwnPropertySymbols(e);if(a.length!==1||a[0]!==t||!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if(typeof Object.getOwnPropertyDescriptor=="function"){var o=Object.getOwnPropertyDescriptor(e,t);if(o.value!==n||o.enumerable!==!0)return!1}return!0}),vd}var yd,h0;function jT(){if(h0)return yd;h0=1;var s=typeof Symbol<"u"&&Symbol,e=HT();return yd=function(){return typeof s!="function"||typeof Symbol!="function"||typeof s("foo")!="symbol"||typeof Symbol("bar")!="symbol"?!1:e()},yd}var xd,u0;function Ux(){return u0||(u0=1,xd=typeof Reflect<"u"&&Reflect.getPrototypeOf||null),xd}var bd,d0;function Xx(){if(d0)return bd;d0=1;var s=Lx();return bd=s.getPrototypeOf||null,bd}var wd,f0;function WT(){if(f0)return wd;f0=1;var s="Function.prototype.bind called on incompatible ",e=Object.prototype.toString,t=Math.max,i="[object Function]",n=function(h,l){for(var c=[],u=0;u<h.length;u+=1)c[u]=h[u];for(var d=0;d<l.length;d+=1)c[d+h.length]=l[d];return c},r=function(h,l){for(var c=[],u=l,d=0;u<h.length;u+=1,d+=1)c[d]=h[u];return c},a=function(o,h){for(var l="",c=0;c<o.length;c+=1)l+=o[c],c+1<o.length&&(l+=h);return l};return wd=function(h){var l=this;if(typeof l!="function"||e.apply(l)!==i)throw new TypeError(s+l);for(var c=r(arguments,1),u,d=function(){if(this instanceof u){var v=l.apply(this,n(c,arguments));return Object(v)===v?v:this}return l.apply(h,n(c,arguments))},f=t(0,l.length-c.length),p=[],m=0;m<f;m++)p[m]="$"+m;if(u=Function("binder","return function ("+a(p,",")+"){ return binder.apply(this,arguments); }")(d),l.prototype){var g=function(){};g.prototype=l.prototype,u.prototype=new g,g.prototype=null}return u},wd}var Sd,m0;function vu(){if(m0)return Sd;m0=1;var s=WT();return Sd=Function.prototype.bind||s,Sd}var Md,p0;function up(){return p0||(p0=1,Md=Function.prototype.call),Md}var _d,g0;function Vx(){return g0||(g0=1,_d=Function.p
Line truncated
|
`+de.prev}function di(ce,de){var Rt=me(ce),Wt=[];if(Rt){Wt.length=ce.length;for(var Zt=0;Zt<ce.length;Zt++)Wt[Zt]=he(ce,Zt)?de(ce[Zt],ce):""}var pt=typeof k=="function"?k(ce):[],Rs;if(D){Rs={};for(var Hs=0;Hs<pt.length;Hs++)Rs["$"+pt[Hs]]=pt[Hs]}for(var Ui in ce)he(ce,Ui)&&(Rt&&String(Number(Ui))===Ui&&Ui<ce.length||D&&Rs["$"+Ui]instanceof Symbol||(T.call(/[^\w$]/,Ui)?Wt.push(de(Ui,ce)+": "+de(ce[Ui],ce)):Wt.push(Ui+": "+de(ce[Ui],ce))));if(typeof k=="function")for(var Ps=0;Ps<pt.length;Ps++)L.call(ce,pt[Ps])&&Wt.push("["+de(pt[Ps])+"]: "+de(ce[pt[Ps]],ce));return Wt}return Zu}var Ju,Gg;function I_(){if(Gg)return Ju;Gg=1;var s=gu(),e=Lo(),t=function(o,h,l){for(var c=o,u;(u=c.next)!=null;c=u)if(u.key===h)return c.next=u.next,l||(u.next=o.next,o.next=u),u},i=function(o,h){if(o){var l=t(o,h);return l&&l.value}},n=function(o,h,l){var c=t(o,h);c?c.value=l:o.next={key:h,next:o.next,value:l}},r=function(o,h){return o?!!t(o,h):!1},a=function(o,h){if(o)return t(o,h,!0)};return Ju=function(){var h,l={assert:function(c){if(!l.has(c))throw new e("Side channel does not contain "+s(c))},delete:function(c){var u=a(h,c);return u&&h&&!h.next&&(h=void 0),!!u},get:function(c){return i(h,c)},has:function(c){return r(h,c)},set:function(c,u){h||(h={next:void 0}),n(h,c,u)}};return l},Ju}var Qu,Hg;function Lx(){return Hg||(Hg=1,Qu=Object),Qu}var ed,jg;function R_(){return jg||(jg=1,ed=Error),ed}var td,Wg;function P_(){return Wg||(Wg=1,td=EvalError),td}var id,zg;function k_(){return zg||(zg=1,id=RangeError),id}var sd,qg;function F_(){return qg||(qg=1,sd=ReferenceError),sd}var nd,Kg;function O_(){return Kg||(Kg=1,nd=SyntaxError),nd}var rd,Zg;function B_(){return Zg||(Zg=1,rd=URIError),rd}var ad,Jg;function L_(){return Jg||(Jg=1,ad=Math.abs),ad}var od,Qg;function D_(){return Qg||(Qg=1,od=Math.floor),od}var ld,e0;function N_(){return e0||(e0=1,ld=Math.max),ld}var cd,t0;function U_(){return t0||(t0=1,cd=Math.min),cd}var hd,i0;function X_(){return i0||(i0=1,hd=Math.pow),hd}var ud,s0;function V_(){return s0||(s0=1,ud=Math.round),ud}var dd,n0;function Y_(){return n0||(n0=1,dd=Number.isNaN||function(e){return e!==e}),dd}var fd,r0;function $_(){if(r0)return fd;r0=1;var s=Y_();return fd=function(t){return s(t)||t===0?t:t<0?-1:1},fd}var md,a0;function G_(){return a0||(a0=1,md=Object.getOwnPropertyDescriptor),md}var pd,o0;function Dx(){if(o0)return pd;o0=1;var s=G_();if(s)try{s([],"length")}catch{s=null}return pd=s,pd}var gd,l0;function Nx(){if(l0)return gd;l0=1;var s=Object.defineProperty||!1;if(s)try{s({},"a",{value:1})}catch{s=!1}return gd=s,gd}var vd,c0;function H_(){return c0||(c0=1,vd=function(){if(typeof Symbol!="function"||typeof Object.getOwnPropertySymbols!="function")return!1;if(typeof Symbol.iterator=="symbol")return!0;var e={},t=Symbol("test"),i=Object(t);if(typeof t=="string"||Object.prototype.toString.call(t)!=="[object Symbol]"||Object.prototype.toString.call(i)!=="[object Symbol]")return!1;var n=42;e[t]=n;for(var r in e)return!1;if(typeof Object.keys=="function"&&Object.keys(e).length!==0||typeof Object.getOwnPropertyNames=="function"&&Object.getOwnPropertyNames(e).length!==0)return!1;var a=Object.getOwnPropertySymbols(e);if(a.length!==1||a[0]!==t||!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if(typeof Object.getOwnPropertyDescriptor=="function"){var o=Object.getOwnPropertyDescriptor(e,t);if(o.value!==n||o.enumerable!==!0)return!1}return!0}),vd}var yd,h0;function j_(){if(h0)return yd;h0=1;var s=typeof Symbol<"u"&&Symbol,e=H_();return yd=function(){return typeof s!="function"||typeof Symbol!="function"||typeof s("foo")!="symbol"||typeof Symbol("bar")!="symbol"?!1:e()},yd}var xd,u0;function Ux(){return u0||(u0=1,xd=typeof Reflect<"u"&&Reflect.getPrototypeOf||null),xd}var bd,d0;function Xx(){if(d0)return bd;d0=1;var s=Lx();return bd=s.getPrototypeOf||null,bd}var wd,f0;function W_(){if(f0)return wd;f0=1;var s="Function.prototype.bind called on incompatible ",e=Object.prototype.toString,t=Math.max,i="[object Function]",n=function(h,l){for(var c=[],u=0;u<h.length;u+=1)c[u]=h[u];for(var d=0;d<l.length;d+=1)c[d+h.length]=l[d];return c},r=function(h,l){for(var c=[],u=l,d=0;u<h.length;u+=1,d+=1)c[d]=h[u];return c},a=function(o,h){for(var l="",c=0;c<o.length;c+=1)l+=o[c],c+1<o.length&&(l+=h);return l};return wd=function(h){var l=this;if(typeof l!="function"||e.apply(l)!==i)throw new TypeError(s+l);for(var c=r(arguments,1),u,d=function(){if(this instanceof u){var v=l.apply(this,n(c,arguments));return Object(v)===v?v:this}return l.apply(h,n(c,arguments))},f=t(0,l.length-c.length),p=[],m=0;m<f;m++)p[m]="$"+m;if(u=Function("binder","return function ("+a(p,",")+"){ return binder.apply(this,arguments); }")(d),l.prototype){var g=function(){};g.prototype=l.prototype,u.prototype=new g,g.prototype=null}return u},wd}var Sd,m0;function vu(){if(m0)return Sd;m0=1;var s=W_();return Sd=Function.prototype.bind||s,Sd}var Md,p0;function up(){return p0||(p0=1,Md=Function.prototype.call),Md}var _d,g0;function Vx(){return g0||(g0=1,_d=Function.p
Line truncated
|
||||||
`," "],a=["{","}","|","\\","^","`"].concat(r),o=["'"].concat(a),h=["%","/","?",";","#"].concat(o),l=["/","?","#"],c=255,u=/^[+a-z0-9A-Z_-]{0,63}$/,d=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,f={javascript:!0,"javascript:":!0},p={javascript:!0,"javascript:":!0},m={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},g=iA();function v(A,T,_){if(A&&typeof A=="object"&&A instanceof e)return A;var R=new e;return R.parse(A,T,_),R}e.prototype.parse=function(A,T,_){if(typeof A!="string")throw new TypeError("Parameter 'url' must be a string, not "+typeof A);var R=A.indexOf("?"),P=R!==-1&&R<A.indexOf("#")?"?":"#",F=A.split(P),I=/\\/g;F[0]=F[0].replace(I,"/"),A=F.join(P);var k=A;if(k=k.trim(),!_&&A.split("#").length===1){var B=n.exec(k);if(B)return this.path=k,this.href=k,this.pathname=B[1],B[2]?(this.search=B[2],T?this.query=g.parse(this.search.substr(1)):this.query=this.search.substr(1)):T&&(this.search="",this.query={}),this}var D=t.exec(k);if(D){D=D[0];var U=D.toLowerCase();this.protocol=U,k=k.substr(D.length)}if(_||D||k.match(/^\/\/[^@/]+@[^@/]+/)){var L=k.substr(0,2)==="//";L&&!(D&&p[D])&&(k=k.substr(2),this.slashes=!0)}if(!p[D]&&(L||D&&!m[D])){for(var N=-1,O=0;O<l.length;O++){var X=k.indexOf(l[O]);X!==-1&&(N===-1||X<N)&&(N=X)}var E,V;N===-1?V=k.lastIndexOf("@"):V=k.lastIndexOf("@",N),V!==-1&&(E=k.slice(0,V),k=k.slice(V+1),this.auth=decodeURIComponent(E)),N=-1;for(var O=0;O<h.length;O++){var X=k.indexOf(h[O]);X!==-1&&(N===-1||X<N)&&(N=X)}N===-1&&(N=k.length),this.host=k.slice(0,N),k=k.slice(N),this.parseHost(),this.hostname=this.hostname||"";var q=this.hostname[0]==="["&&this.hostname[this.hostname.length-1]==="]";if(!q)for(var W=this.hostname.split(/\./),O=0,J=W.length;O<J;O++){var K=W[O];if(K&&!K.match(u)){for(var fe="",me=0,we=K.length;me<we;me++)K.charCodeAt(me)>127?fe+="x":fe+=K[me];if(!fe.match(u)){var Ee=W.slice(0,O),pe=W.slice(O+1),Ae=K.match(d);Ae&&(Ee.push(Ae[1]),pe.unshift(Ae[2])),pe.length&&(k="/"+pe.join(".")+k),this.hostname=Ee.join(".");break}}}this.hostname.length>c?this.hostname="":this.hostname=this.hostname.toLowerCase(),q||(this.hostname=s.toASCII(this.hostname));var G=this.port?":"+this.port:"",Z=this.hostname||"";this.host=Z+G,this.href+=this.host,q&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),k[0]!=="/"&&(k="/"+k))}if(!f[U])for(var O=0,J=o.length;O<J;O++){var z=o[O];if(k.indexOf(z)!==-1){var j=encodeURIComponent(z);j===z&&(j=escape(z)),k=k.split(z).join(j)}}var te=k.indexOf("#");te!==-1&&(this.hash=k.substr(te),k=k.slice(0,te));var he=k.indexOf("?");if(he!==-1?(this.search=k.substr(he),this.query=k.substr(he+1),T&&(this.query=g.parse(this.query)),k=k.slice(0,he)):T&&(this.search="",this.query={}),k&&(this.pathname=k),m[U]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var G=this.pathname||"",xe=this.search||"";this.path=G+xe}return this.href=this.format(),this};function b(A){return typeof A=="string"&&(A=v(A)),A instanceof e?A.format():e.prototype.format.call(A)}e.prototype.format=function(){var A=this.auth||"";A&&(A=encodeURIComponent(A),A=A.replace(/%3A/i,":"),A+="@");var T=this.protocol||"",_=this.pathname||"",R=this.hash||"",P=!1,F="";this.host?P=A+this.host:this.hostname&&(P=A+(this.hostname.indexOf(":")===-1?this.hostname:"["+this.hostname+"]"),this.port&&(P+=":"+this.port)),this.query&&typeof this.query=="object"&&Object.keys(this.query).length&&(F=g.stringify(this.query,{arrayFormat:"repeat",addQueryPrefix:!1}));var I=this.search||F&&"?"+F||"";return T&&T.substr(-1)!==":"&&(T+=":"),this.slashes||(!T||m[T])&&P!==!1?(P="//"+(P||""),_&&_.charAt(0)!=="/"&&(_="/"+_)):P||(P=""),R&&R.charAt(0)!=="#"&&(R="#"+R),I&&I.charAt(0)!=="?"&&(I="?"+I),_=_.replace(/[?#]/g,function(k){return encodeURIComponent(k)}),I=I.replace("#","%23"),T+P+_+I+R};function y(A,T){return v(A,!1,!0).resolve(T)}e.prototype.resolve=function(A){return this.resolveObject(v(A,!1,!0)).format()};function w(A,T){return A?v(A,!1,!0).resolveObject(T):T}return e.prototype.resolveObject=function(A){if(typeof A=="string"){var T=new e;T.parse(A,!1,!0),A=T}for(var _=new e,R=Object.keys(this),P=0;P<R.length;P++){var F=R[P];_[F]=this[F]}if(_.hash=A.hash,A.href==="")return _.href=_.format(),_;if(A.slashes&&!A.protocol){for(var I=Object.keys(A),k=0;k<I.length;k++){var B=I[k];B!=="protocol"&&(_[B]=A[B])}return m[_.protocol]&&_.hostname&&!_.pathname&&(_.pathname="/",_.path=_.pathname),_.href=_.format(),_}if(A.protocol&&A.protocol!==_.protocol){if(!m[A.protocol]){for(var D=Object.keys(A),U=0;U<D.length;U++){var L=D[U];_[L]=A[L]}return _.href=_.format(),_}if(_.protocol=A.protocol,!A.host&&!p[A.protocol]){for(var J=(A.pathname||"").split("/");J.length&&!(A.host=J.shift()););A.host||(A.host=""),A.hostname||(A.hostname=""),J[0]!==""&&J.unshift(""),J.length<2&&J.unshift(""),_.pathname=J.join("/")}else _.pathname=A.pathname;if(_.search=A.search,_.query=A.query,_.host=A.host||"",_.auth=A.auth,_.hostname=A.hostname||A.host,_.port=A.port,_.pathname||_.search){va
Line truncated
|
`," "],a=["{","}","|","\\","^","`"].concat(r),o=["'"].concat(a),h=["%","/","?",";","#"].concat(o),l=["/","?","#"],c=255,u=/^[+a-z0-9A-Z_-]{0,63}$/,d=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,f={javascript:!0,"javascript:":!0},p={javascript:!0,"javascript:":!0},m={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},g=iT();function v(A,T,_){if(A&&typeof A=="object"&&A instanceof e)return A;var R=new e;return R.parse(A,T,_),R}e.prototype.parse=function(A,T,_){if(typeof A!="string")throw new TypeError("Parameter 'url' must be a string, not "+typeof A);var R=A.indexOf("?"),P=R!==-1&&R<A.indexOf("#")?"?":"#",F=A.split(P),I=/\\/g;F[0]=F[0].replace(I,"/"),A=F.join(P);var k=A;if(k=k.trim(),!_&&A.split("#").length===1){var B=n.exec(k);if(B)return this.path=k,this.href=k,this.pathname=B[1],B[2]?(this.search=B[2],T?this.query=g.parse(this.search.substr(1)):this.query=this.search.substr(1)):T&&(this.search="",this.query={}),this}var D=t.exec(k);if(D){D=D[0];var U=D.toLowerCase();this.protocol=U,k=k.substr(D.length)}if(_||D||k.match(/^\/\/[^@/]+@[^@/]+/)){var L=k.substr(0,2)==="//";L&&!(D&&p[D])&&(k=k.substr(2),this.slashes=!0)}if(!p[D]&&(L||D&&!m[D])){for(var N=-1,O=0;O<l.length;O++){var X=k.indexOf(l[O]);X!==-1&&(N===-1||X<N)&&(N=X)}var E,V;N===-1?V=k.lastIndexOf("@"):V=k.lastIndexOf("@",N),V!==-1&&(E=k.slice(0,V),k=k.slice(V+1),this.auth=decodeURIComponent(E)),N=-1;for(var O=0;O<h.length;O++){var X=k.indexOf(h[O]);X!==-1&&(N===-1||X<N)&&(N=X)}N===-1&&(N=k.length),this.host=k.slice(0,N),k=k.slice(N),this.parseHost(),this.hostname=this.hostname||"";var q=this.hostname[0]==="["&&this.hostname[this.hostname.length-1]==="]";if(!q)for(var W=this.hostname.split(/\./),O=0,J=W.length;O<J;O++){var K=W[O];if(K&&!K.match(u)){for(var fe="",me=0,we=K.length;me<we;me++)K.charCodeAt(me)>127?fe+="x":fe+=K[me];if(!fe.match(u)){var Ee=W.slice(0,O),pe=W.slice(O+1),Ae=K.match(d);Ae&&(Ee.push(Ae[1]),pe.unshift(Ae[2])),pe.length&&(k="/"+pe.join(".")+k),this.hostname=Ee.join(".");break}}}this.hostname.length>c?this.hostname="":this.hostname=this.hostname.toLowerCase(),q||(this.hostname=s.toASCII(this.hostname));var G=this.port?":"+this.port:"",Z=this.hostname||"";this.host=Z+G,this.href+=this.host,q&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),k[0]!=="/"&&(k="/"+k))}if(!f[U])for(var O=0,J=o.length;O<J;O++){var z=o[O];if(k.indexOf(z)!==-1){var j=encodeURIComponent(z);j===z&&(j=escape(z)),k=k.split(z).join(j)}}var te=k.indexOf("#");te!==-1&&(this.hash=k.substr(te),k=k.slice(0,te));var he=k.indexOf("?");if(he!==-1?(this.search=k.substr(he),this.query=k.substr(he+1),T&&(this.query=g.parse(this.query)),k=k.slice(0,he)):T&&(this.search="",this.query={}),k&&(this.pathname=k),m[U]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var G=this.pathname||"",xe=this.search||"";this.path=G+xe}return this.href=this.format(),this};function b(A){return typeof A=="string"&&(A=v(A)),A instanceof e?A.format():e.prototype.format.call(A)}e.prototype.format=function(){var A=this.auth||"";A&&(A=encodeURIComponent(A),A=A.replace(/%3A/i,":"),A+="@");var T=this.protocol||"",_=this.pathname||"",R=this.hash||"",P=!1,F="";this.host?P=A+this.host:this.hostname&&(P=A+(this.hostname.indexOf(":")===-1?this.hostname:"["+this.hostname+"]"),this.port&&(P+=":"+this.port)),this.query&&typeof this.query=="object"&&Object.keys(this.query).length&&(F=g.stringify(this.query,{arrayFormat:"repeat",addQueryPrefix:!1}));var I=this.search||F&&"?"+F||"";return T&&T.substr(-1)!==":"&&(T+=":"),this.slashes||(!T||m[T])&&P!==!1?(P="//"+(P||""),_&&_.charAt(0)!=="/"&&(_="/"+_)):P||(P=""),R&&R.charAt(0)!=="#"&&(R="#"+R),I&&I.charAt(0)!=="?"&&(I="?"+I),_=_.replace(/[?#]/g,function(k){return encodeURIComponent(k)}),I=I.replace("#","%23"),T+P+_+I+R};function y(A,T){return v(A,!1,!0).resolve(T)}e.prototype.resolve=function(A){return this.resolveObject(v(A,!1,!0)).format()};function w(A,T){return A?v(A,!1,!0).resolveObject(T):T}return e.prototype.resolveObject=function(A){if(typeof A=="string"){var T=new e;T.parse(A,!1,!0),A=T}for(var _=new e,R=Object.keys(this),P=0;P<R.length;P++){var F=R[P];_[F]=this[F]}if(_.hash=A.hash,A.href==="")return _.href=_.format(),_;if(A.slashes&&!A.protocol){for(var I=Object.keys(A),k=0;k<I.length;k++){var B=I[k];B!=="protocol"&&(_[B]=A[B])}return m[_.protocol]&&_.hostname&&!_.pathname&&(_.pathname="/",_.path=_.pathname),_.href=_.format(),_}if(A.protocol&&A.protocol!==_.protocol){if(!m[A.protocol]){for(var D=Object.keys(A),U=0;U<D.length;U++){var L=D[U];_[L]=A[L]}return _.href=_.format(),_}if(_.protocol=A.protocol,!A.host&&!p[A.protocol]){for(var J=(A.pathname||"").split("/");J.length&&!(A.host=J.shift()););A.host||(A.host=""),A.hostname||(A.hostname=""),J[0]!==""&&J.unshift(""),J.length<2&&J.unshift(""),_.pathname=J.join("/")}else _.pathname=A.pathname;if(_.search=A.search,_.query=A.query,_.host=A.host||"",_.auth=A.auth,_.hostname=A.hostname||A.host,_.port=A.port,_.pathname||_.search){va
Line truncated
|
||||||
Deprecated since v${s}`):(i=i.split(`
|
Deprecated since v${s}`):(i=i.split(`
|
||||||
`).splice(t).join(`
|
`).splice(t).join(`
|
||||||
`),console.groupCollapsed?(console.groupCollapsed("%cPixiJS Deprecation Warning: %c%s","color:#614108;background:#fffbe6","font-weight:normal;color:#614108;background:#fffbe6",`${e}
|
`),console.groupCollapsed?(console.groupCollapsed("%cPixiJS Deprecation Warning: %c%s","color:#614108;background:#fffbe6","font-weight:normal;color:#614108;background:#fffbe6",`${e}
|
||||||
Deprecated since v${s}`),console.warn(i),console.groupEnd()):(console.warn("PixiJS Deprecation Warning: ",`${e}
|
Deprecated since v${s}`),console.warn(i),console.groupEnd()):(console.warn("PixiJS Deprecation Warning: ",`${e}
|
||||||
Deprecated since v${s}`),console.warn(i))),O0[e]=!0}function zs(s){if(typeof s!="string")throw new TypeError(`Path must be a string. Received ${JSON.stringify(s)}`)}function jo(s){return s.split("?")[0].split("#")[0]}function nA(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function rA(s,e,t){return s.replace(new RegExp(nA(e),"g"),t)}function aA(s,e){let t="",i=0,n=-1,r=0,a=-1;for(let o=0;o<=s.length;++o){if(o<s.length)a=s.charCodeAt(o);else{if(a===47)break;a=47}if(a===47){if(!(n===o-1||r===1))if(n!==o-1&&r===2){if(t.length<2||i!==2||t.charCodeAt(t.length-1)!==46||t.charCodeAt(t.length-2)!==46){if(t.length>2){const h=t.lastIndexOf("/");if(h!==t.length-1){h===-1?(t="",i=0):(t=t.slice(0,h),i=t.length-1-t.lastIndexOf("/")),n=o,r=0;continue}}else if(t.length===2||t.length===1){t="",i=0,n=o,r=0;continue}}}else t.length>0?t+=`/${s.slice(n+1,o)}`:t=s.slice(n+1,o),i=o-n-1;n=o,r=0}else a===46&&r!==-1?++r:r=-1}return t}const wi={toPosix(s){return rA(s,"\\","/")},isUrl(s){return/^https?:/.test(this.toPosix(s))},isDataUrl(s){return/^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i.test(s)},isBlobUrl(s){return s.startsWith("blob:")},hasProtocol(s){return/^[^/:]+:/.test(this.toPosix(s))},getProtocol(s){zs(s),s=this.toPosix(s);const e=/^file:\/\/\//.exec(s);if(e)return e[0];const t=/^[^/:]+:\/{0,2}/.exec(s);return t?t[0]:""},toAbsolute(s,e,t){if(zs(s),this.isDataUrl(s)||this.isBlobUrl(s))return s;const i=jo(this.toPosix(e??We.ADAPTER.getBaseUrl())),n=jo(this.toPosix(t??this.rootname(i)));return s=this.toPosix(s),s.startsWith("/")?wi.join(n,s.slice(1)):this.isAbsolute(s)?s:this.join(i,s)},normalize(s){if(zs(s),s.length===0)return".";if(this.isDataUrl(s)||this.isBlobUrl(s))return s;s=this.toPosix(s);let e="";const t=s.startsWith("/");this.hasProtocol(s)&&(e=this.rootname(s),s=s.slice(e.length));const i=s.endsWith("/");return s=aA(s),s.length>0&&i&&(s+="/"),t?`/${s}`:e+s},isAbsolute(s){return zs(s),s=this.toPosix(s),this.hasProtocol(s)?!0:s.startsWith("/")},join(...s){if(s.length===0)return".";let e;for(let t=0;t<s.length;++t){const i=s[t];if(zs(i),i.length>0)if(e===void 0)e=i;else{const n=s[t-1]??"";this.joinExtensions.includes(this.extname(n).toLowerCase())?e+=`/../${i}`:e+=`/${i}`}}return e===void 0?".":this.normalize(e)},dirname(s){if(zs(s),s.length===0)return".";s=this.toPosix(s);let e=s.charCodeAt(0);const t=e===47;let i=-1,n=!0;const r=this.getProtocol(s),a=s;s=s.slice(r.length);for(let o=s.length-1;o>=1;--o)if(e=s.charCodeAt(o),e===47){if(!n){i=o;break}}else n=!1;return i===-1?t?"/":this.isUrl(a)?r+s:r:t&&i===1?"//":r+s.slice(0,i)},rootname(s){zs(s),s=this.toPosix(s);let e="";if(s.startsWith("/")?e="/":e=this.getProtocol(s),this.isUrl(s)){const t=s.indexOf("/",e.length);t!==-1?e=s.slice(0,t):e=s,e.endsWith("/")||(e+="/")}return e},basename(s,e){zs(s),e&&zs(e),s=jo(this.toPosix(s));let t=0,i=-1,n=!0,r;if(e!==void 0&&e.length>0&&e.length<=s.length){if(e.length===s.length&&e===s)return"";let a=e.length-1,o=-1;for(r=s.length-1;r>=0;--r){const h=s.charCodeAt(r);if(h===47){if(!n){t=r+1;break}}else o===-1&&(n=!1,o=r+1),a>=0&&(h===e.charCodeAt(a)?--a===-1&&(i=r):(a=-1,i=o))}return t===i?i=o:i===-1&&(i=s.length),s.slice(t,i)}for(r=s.length-1;r>=0;--r)if(s.charCodeAt(r)===47){if(!n){t=r+1;break}}else i===-1&&(n=!1,i=r+1);return i===-1?"":s.slice(t,i)},extname(s){zs(s),s=jo(this.toPosix(s));let e=-1,t=0,i=-1,n=!0,r=0;for(let a=s.length-1;a>=0;--a){const o=s.charCodeAt(a);if(o===47){if(!n){t=a+1;break}continue}i===-1&&(n=!1,i=a+1),o===46?e===-1?e=a:r!==1&&(r=1):e!==-1&&(r=-1)}return e===-1||i===-1||r===0||r===1&&e===i-1&&e===t+1?"":s.slice(e,i)},parse(s){zs(s);const e={root:"",dir:"",base:"",ext:"",name:""};if(s.length===0)return e;s=jo(this.toPosix(s));let t=s.charCodeAt(0);const i=this.isAbsolute(s);let n;e.root=this.rootname(s),i||this.hasProtocol(s)?n=1:n=0;let r=-1,a=0,o=-1,h=!0,l=s.length-1,c=0;for(;l>=n;--l){if(t=s.charCodeAt(l),t===47){if(!h){a=l+1;break}continue}o===-1&&(h=!1,o=l+1),t===46?r===-1?r=l:c!==1&&(c=1):r!==-1&&(c=-1)}return r===-1||o===-1||c===0||c===1&&r===o-1&&r===a+1?o!==-1&&(a===0&&i?e.base=e.name=s.slice(1,o):e.base=e.name=s.slice(a,o)):(a===0&&i?(e.name=s.slice(1,r),e.base=s.slice(1,o)):(e.name=s.slice(a,r),e.base=s.slice(a,o)),e.ext=s.slice(r,o)),e.dir=this.dirname(s),e},sep:"/",delimiter:":",joinExtensions:[".html"]};let Vd;async function oA(){return Vd??(Vd=(async()=>{var r;const s=document.createElement("canvas").getContext("webgl");if(!s)return Ni.UNPACK;const e=await new Promise(a=>{const o=document.createElement("video");o.onloadeddata=()=>a(o),o.onerror=()=>a(null),o.autoplay=!1,o.crossOrigin="anonymous",o.preload="auto",o.src="data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEAAAAAAAHTEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggEXTbuMU6uEHFO7a1OsggG97AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Line truncated
|
Deprecated since v${s}`),console.warn(i))),O0[e]=!0}function zs(s){if(typeof s!="string")throw new TypeError(`Path must be a string. Received ${JSON.stringify(s)}`)}function jo(s){return s.split("?")[0].split("#")[0]}function nT(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function rT(s,e,t){return s.replace(new RegExp(nT(e),"g"),t)}function aT(s,e){let t="",i=0,n=-1,r=0,a=-1;for(let o=0;o<=s.length;++o){if(o<s.length)a=s.charCodeAt(o);else{if(a===47)break;a=47}if(a===47){if(!(n===o-1||r===1))if(n!==o-1&&r===2){if(t.length<2||i!==2||t.charCodeAt(t.length-1)!==46||t.charCodeAt(t.length-2)!==46){if(t.length>2){const h=t.lastIndexOf("/");if(h!==t.length-1){h===-1?(t="",i=0):(t=t.slice(0,h),i=t.length-1-t.lastIndexOf("/")),n=o,r=0;continue}}else if(t.length===2||t.length===1){t="",i=0,n=o,r=0;continue}}}else t.length>0?t+=`/${s.slice(n+1,o)}`:t=s.slice(n+1,o),i=o-n-1;n=o,r=0}else a===46&&r!==-1?++r:r=-1}return t}const wi={toPosix(s){return rT(s,"\\","/")},isUrl(s){return/^https?:/.test(this.toPosix(s))},isDataUrl(s){return/^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i.test(s)},isBlobUrl(s){return s.startsWith("blob:")},hasProtocol(s){return/^[^/:]+:/.test(this.toPosix(s))},getProtocol(s){zs(s),s=this.toPosix(s);const e=/^file:\/\/\//.exec(s);if(e)return e[0];const t=/^[^/:]+:\/{0,2}/.exec(s);return t?t[0]:""},toAbsolute(s,e,t){if(zs(s),this.isDataUrl(s)||this.isBlobUrl(s))return s;const i=jo(this.toPosix(e??We.ADAPTER.getBaseUrl())),n=jo(this.toPosix(t??this.rootname(i)));return s=this.toPosix(s),s.startsWith("/")?wi.join(n,s.slice(1)):this.isAbsolute(s)?s:this.join(i,s)},normalize(s){if(zs(s),s.length===0)return".";if(this.isDataUrl(s)||this.isBlobUrl(s))return s;s=this.toPosix(s);let e="";const t=s.startsWith("/");this.hasProtocol(s)&&(e=this.rootname(s),s=s.slice(e.length));const i=s.endsWith("/");return s=aT(s),s.length>0&&i&&(s+="/"),t?`/${s}`:e+s},isAbsolute(s){return zs(s),s=this.toPosix(s),this.hasProtocol(s)?!0:s.startsWith("/")},join(...s){if(s.length===0)return".";let e;for(let t=0;t<s.length;++t){const i=s[t];if(zs(i),i.length>0)if(e===void 0)e=i;else{const n=s[t-1]??"";this.joinExtensions.includes(this.extname(n).toLowerCase())?e+=`/../${i}`:e+=`/${i}`}}return e===void 0?".":this.normalize(e)},dirname(s){if(zs(s),s.length===0)return".";s=this.toPosix(s);let e=s.charCodeAt(0);const t=e===47;let i=-1,n=!0;const r=this.getProtocol(s),a=s;s=s.slice(r.length);for(let o=s.length-1;o>=1;--o)if(e=s.charCodeAt(o),e===47){if(!n){i=o;break}}else n=!1;return i===-1?t?"/":this.isUrl(a)?r+s:r:t&&i===1?"//":r+s.slice(0,i)},rootname(s){zs(s),s=this.toPosix(s);let e="";if(s.startsWith("/")?e="/":e=this.getProtocol(s),this.isUrl(s)){const t=s.indexOf("/",e.length);t!==-1?e=s.slice(0,t):e=s,e.endsWith("/")||(e+="/")}return e},basename(s,e){zs(s),e&&zs(e),s=jo(this.toPosix(s));let t=0,i=-1,n=!0,r;if(e!==void 0&&e.length>0&&e.length<=s.length){if(e.length===s.length&&e===s)return"";let a=e.length-1,o=-1;for(r=s.length-1;r>=0;--r){const h=s.charCodeAt(r);if(h===47){if(!n){t=r+1;break}}else o===-1&&(n=!1,o=r+1),a>=0&&(h===e.charCodeAt(a)?--a===-1&&(i=r):(a=-1,i=o))}return t===i?i=o:i===-1&&(i=s.length),s.slice(t,i)}for(r=s.length-1;r>=0;--r)if(s.charCodeAt(r)===47){if(!n){t=r+1;break}}else i===-1&&(n=!1,i=r+1);return i===-1?"":s.slice(t,i)},extname(s){zs(s),s=jo(this.toPosix(s));let e=-1,t=0,i=-1,n=!0,r=0;for(let a=s.length-1;a>=0;--a){const o=s.charCodeAt(a);if(o===47){if(!n){t=a+1;break}continue}i===-1&&(n=!1,i=a+1),o===46?e===-1?e=a:r!==1&&(r=1):e!==-1&&(r=-1)}return e===-1||i===-1||r===0||r===1&&e===i-1&&e===t+1?"":s.slice(e,i)},parse(s){zs(s);const e={root:"",dir:"",base:"",ext:"",name:""};if(s.length===0)return e;s=jo(this.toPosix(s));let t=s.charCodeAt(0);const i=this.isAbsolute(s);let n;e.root=this.rootname(s),i||this.hasProtocol(s)?n=1:n=0;let r=-1,a=0,o=-1,h=!0,l=s.length-1,c=0;for(;l>=n;--l){if(t=s.charCodeAt(l),t===47){if(!h){a=l+1;break}continue}o===-1&&(h=!1,o=l+1),t===46?r===-1?r=l:c!==1&&(c=1):r!==-1&&(c=-1)}return r===-1||o===-1||c===0||c===1&&r===o-1&&r===a+1?o!==-1&&(a===0&&i?e.base=e.name=s.slice(1,o):e.base=e.name=s.slice(a,o)):(a===0&&i?(e.name=s.slice(1,r),e.base=s.slice(1,o)):(e.name=s.slice(a,r),e.base=s.slice(a,o)),e.ext=s.slice(r,o)),e.dir=this.dirname(s),e},sep:"/",delimiter:":",joinExtensions:[".html"]};let Vd;async function oT(){return Vd??(Vd=(async()=>{var r;const s=document.createElement("canvas").getContext("webgl");if(!s)return Ni.UNPACK;const e=await new Promise(a=>{const o=document.createElement("video");o.onloadeddata=()=>a(o),o.onerror=()=>a(null),o.autoplay=!1,o.crossOrigin="anonymous",o.preload="auto",o.src="data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEAAAAAAAHTEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggEXTbuMU6uEHFO7a1OsggG97AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Line truncated
|
||||||
`);function CA(s){let e="";for(let t=0;t<s;++t)t>0&&(e+=`
|
`);function CT(s){let e="";for(let t=0;t<s;++t)t>0&&(e+=`
|
||||||
else `),t<s-1&&(e+=`if(test == ${t}.0){}`);return e}function IA(s,e){if(s===0)throw new Error("Invalid value of `0` passed to `checkMaxIfStatementsInShader`");const t=e.createShader(e.FRAGMENT_SHADER);for(;;){const i=EA.replace(/%forloop%/gi,CA(s));if(e.shaderSource(t,i),e.compileShader(t),!e.getShaderParameter(t,e.COMPILE_STATUS))s=s/2|0;else break}return s}const jd=0,Wd=1,zd=2,qd=3,Kd=4,Zd=5;class Un{constructor(){this.data=0,this.blendMode=dt.NORMAL,this.polygonOffset=0,this.blend=!0,this.depthMask=!0}get blend(){return!!(this.data&1<<jd)}set blend(e){!!(this.data&1<<jd)!==e&&(this.data^=1<<jd)}get offsets(){return!!(this.data&1<<Wd)}set offsets(e){!!(this.data&1<<Wd)!==e&&(this.data^=1<<Wd)}get culling(){return!!(this.data&1<<zd)}set culling(e){!!(this.data&1<<zd)!==e&&(this.data^=1<<zd)}get depthTest(){return!!(this.data&1<<qd)}set depthTest(e){!!(this.data&1<<qd)!==e&&(this.data^=1<<qd)}get depthMask(){return!!(this.data&1<<Zd)}set depthMask(e){!!(this.data&1<<Zd)!==e&&(this.data^=1<<Zd)}get clockwiseFrontFace(){return!!(this.data&1<<Kd)}set clockwiseFrontFace(e){!!(this.data&1<<Kd)!==e&&(this.data^=1<<Kd)}get blendMode(){return this._blendMode}set blendMode(e){this.blend=e!==dt.NONE,this._blendMode=e}get polygonOffset(){return this._polygonOffset}set polygonOffset(e){this.offsets=!!e,this._polygonOffset=e}static for2d(){const e=new Un;return e.depthTest=!1,e.blend=!0,e}}Un.prototype.toString=function(){return`[@pixi/core:State blendMode=${this.blendMode} clockwiseFrontFace=${this.clockwiseFrontFace} culling=${this.culling} depthMask=${this.depthMask} polygonOffset=${this.polygonOffset}]`};const Wf=[];function Qx(s,e){if(!s)return null;let t="";if(typeof s=="string"){const i=/\.(\w{3,4})(?:$|\?|#)/i.exec(s);i&&(t=i[1].toLowerCase())}for(let i=Wf.length-1;i>=0;--i){const n=Wf[i];if(n.test&&n.test(s,t))return new n(s,e)}throw new Error("Unrecognized source type to auto-detect Resource")}class on{constructor(e){this.items=[],this._name=e,this._aliasCount=0}emit(e,t,i,n,r,a,o,h){if(arguments.length>8)throw new Error("max arguments reached");const{name:l,items:c}=this;this._aliasCount++;for(let u=0,d=c.length;u<d;u++)c[u][l](e,t,i,n,r,a,o,h);return c===this.items&&this._aliasCount--,this}ensureNonAliasedItems(){this._aliasCount>0&&this.items.length>1&&(this._aliasCount=0,this.items=this.items.slice(0))}add(e){return e[this._name]&&(this.ensureNonAliasedItems(),this.remove(e),this.items.push(e)),this}remove(e){const t=this.items.indexOf(e);return t!==-1&&(this.ensureNonAliasedItems(),this.items.splice(t,1)),this}contains(e){return this.items.includes(e)}removeAll(){return this.ensureNonAliasedItems(),this.items.length=0,this}destroy(){this.removeAll(),this.items.length=0,this._name=""}get empty(){return this.items.length===0}get name(){return this._name}}Object.defineProperties(on.prototype,{dispatch:{value:on.prototype.emit},run:{value:on.prototype.emit}});class Ol{constructor(e=0,t=0){this._width=e,this._height=t,this.destroyed=!1,this.internal=!1,this.onResize=new on("setRealSize"),this.onUpdate=new on("update"),this.onError=new on("onError")}bind(e){this.onResize.add(e),this.onUpdate.add(e),this.onError.add(e),(this._width||this._height)&&this.onResize.emit(this._width,this._height)}unbind(e){this.onResize.remove(e),this.onUpdate.remove(e),this.onError.remove(e)}resize(e,t){(e!==this._width||t!==this._height)&&(this._width=e,this._height=t,this.onResize.emit(e,t))}get valid(){return!!this._width&&!!this._height}update(){this.destroyed||this.onUpdate.emit()}load(){return Promise.resolve(this)}get width(){return this._width}get height(){return this._height}style(e,t,i){return!1}dispose(){}destroy(){this.destroyed||(this.destroyed=!0,this.dispose(),this.onError.removeAll(),this.onError=null,this.onResize.removeAll(),this.onResize=null,this.onUpdate.removeAll(),this.onUpdate=null)}static test(e,t){return!1}}class yu extends Ol{constructor(e,t){const{width:i,height:n}=t||{};if(!i||!n)throw new Error("BufferResource width or height invalid");super(i,n),this.data=e,this.unpackAlignment=t.unpackAlignment??4}upload(e,t,i){const n=e.gl;n.pixelStorei(n.UNPACK_ALIGNMENT,this.unpackAlignment),n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL,t.alphaMode===Ni.UNPACK);const r=t.realWidth,a=t.realHeight;return i.width===r&&i.height===a?n.texSubImage2D(t.target,0,0,0,r,a,t.format,i.type,this.data):(i.width=r,i.height=a,n.texImage2D(t.target,0,i.internalFormat,r,a,0,t.format,i.type,this.data)),!0}dispose(){this.data=null}static test(e){return e===null||e instanceof Int8Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array}}const RA={scaleMode:sn.NEAREST,alphaMode:Ni.NPM},zf=class so extends jl{constructor(e=null,t=null){super(),t=Object.assign({},so.defaultOptions,t);const{alphaMode:i,mipmap:n,anisotropicLevel:r,scaleMode:a,width:o,height:h,wrapMode:l,format:c,type:u,target:d,resolution:
Line truncated
|
else `),t<s-1&&(e+=`if(test == ${t}.0){}`);return e}function IT(s,e){if(s===0)throw new Error("Invalid value of `0` passed to `checkMaxIfStatementsInShader`");const t=e.createShader(e.FRAGMENT_SHADER);for(;;){const i=ET.replace(/%forloop%/gi,CT(s));if(e.shaderSource(t,i),e.compileShader(t),!e.getShaderParameter(t,e.COMPILE_STATUS))s=s/2|0;else break}return s}const jd=0,Wd=1,zd=2,qd=3,Kd=4,Zd=5;class Un{constructor(){this.data=0,this.blendMode=dt.NORMAL,this.polygonOffset=0,this.blend=!0,this.depthMask=!0}get blend(){return!!(this.data&1<<jd)}set blend(e){!!(this.data&1<<jd)!==e&&(this.data^=1<<jd)}get offsets(){return!!(this.data&1<<Wd)}set offsets(e){!!(this.data&1<<Wd)!==e&&(this.data^=1<<Wd)}get culling(){return!!(this.data&1<<zd)}set culling(e){!!(this.data&1<<zd)!==e&&(this.data^=1<<zd)}get depthTest(){return!!(this.data&1<<qd)}set depthTest(e){!!(this.data&1<<qd)!==e&&(this.data^=1<<qd)}get depthMask(){return!!(this.data&1<<Zd)}set depthMask(e){!!(this.data&1<<Zd)!==e&&(this.data^=1<<Zd)}get clockwiseFrontFace(){return!!(this.data&1<<Kd)}set clockwiseFrontFace(e){!!(this.data&1<<Kd)!==e&&(this.data^=1<<Kd)}get blendMode(){return this._blendMode}set blendMode(e){this.blend=e!==dt.NONE,this._blendMode=e}get polygonOffset(){return this._polygonOffset}set polygonOffset(e){this.offsets=!!e,this._polygonOffset=e}static for2d(){const e=new Un;return e.depthTest=!1,e.blend=!0,e}}Un.prototype.toString=function(){return`[@pixi/core:State blendMode=${this.blendMode} clockwiseFrontFace=${this.clockwiseFrontFace} culling=${this.culling} depthMask=${this.depthMask} polygonOffset=${this.polygonOffset}]`};const Wf=[];function Qx(s,e){if(!s)return null;let t="";if(typeof s=="string"){const i=/\.(\w{3,4})(?:$|\?|#)/i.exec(s);i&&(t=i[1].toLowerCase())}for(let i=Wf.length-1;i>=0;--i){const n=Wf[i];if(n.test&&n.test(s,t))return new n(s,e)}throw new Error("Unrecognized source type to auto-detect Resource")}class on{constructor(e){this.items=[],this._name=e,this._aliasCount=0}emit(e,t,i,n,r,a,o,h){if(arguments.length>8)throw new Error("max arguments reached");const{name:l,items:c}=this;this._aliasCount++;for(let u=0,d=c.length;u<d;u++)c[u][l](e,t,i,n,r,a,o,h);return c===this.items&&this._aliasCount--,this}ensureNonAliasedItems(){this._aliasCount>0&&this.items.length>1&&(this._aliasCount=0,this.items=this.items.slice(0))}add(e){return e[this._name]&&(this.ensureNonAliasedItems(),this.remove(e),this.items.push(e)),this}remove(e){const t=this.items.indexOf(e);return t!==-1&&(this.ensureNonAliasedItems(),this.items.splice(t,1)),this}contains(e){return this.items.includes(e)}removeAll(){return this.ensureNonAliasedItems(),this.items.length=0,this}destroy(){this.removeAll(),this.items.length=0,this._name=""}get empty(){return this.items.length===0}get name(){return this._name}}Object.defineProperties(on.prototype,{dispatch:{value:on.prototype.emit},run:{value:on.prototype.emit}});class Ol{constructor(e=0,t=0){this._width=e,this._height=t,this.destroyed=!1,this.internal=!1,this.onResize=new on("setRealSize"),this.onUpdate=new on("update"),this.onError=new on("onError")}bind(e){this.onResize.add(e),this.onUpdate.add(e),this.onError.add(e),(this._width||this._height)&&this.onResize.emit(this._width,this._height)}unbind(e){this.onResize.remove(e),this.onUpdate.remove(e),this.onError.remove(e)}resize(e,t){(e!==this._width||t!==this._height)&&(this._width=e,this._height=t,this.onResize.emit(e,t))}get valid(){return!!this._width&&!!this._height}update(){this.destroyed||this.onUpdate.emit()}load(){return Promise.resolve(this)}get width(){return this._width}get height(){return this._height}style(e,t,i){return!1}dispose(){}destroy(){this.destroyed||(this.destroyed=!0,this.dispose(),this.onError.removeAll(),this.onError=null,this.onResize.removeAll(),this.onResize=null,this.onUpdate.removeAll(),this.onUpdate=null)}static test(e,t){return!1}}class yu extends Ol{constructor(e,t){const{width:i,height:n}=t||{};if(!i||!n)throw new Error("BufferResource width or height invalid");super(i,n),this.data=e,this.unpackAlignment=t.unpackAlignment??4}upload(e,t,i){const n=e.gl;n.pixelStorei(n.UNPACK_ALIGNMENT,this.unpackAlignment),n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL,t.alphaMode===Ni.UNPACK);const r=t.realWidth,a=t.realHeight;return i.width===r&&i.height===a?n.texSubImage2D(t.target,0,0,0,r,a,t.format,i.type,this.data):(i.width=r,i.height=a,n.texImage2D(t.target,0,i.internalFormat,r,a,0,t.format,i.type,this.data)),!0}dispose(){this.data=null}static test(e){return e===null||e instanceof Int8Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int16Array||e instanceof Uint16Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Float32Array}}const RT={scaleMode:sn.NEAREST,alphaMode:Ni.NPM},zf=class so extends jl{constructor(e=null,t=null){super(),t=Object.assign({},so.defaultOptions,t);const{alphaMode:i,mipmap:n,anisotropicLevel:r,scaleMode:a,width:o,height:h,wrapMode:l,format:c,type:u,target:d,resolution:
Line truncated
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
|
|
||||||
void main(void){
|
void main(void){
|
||||||
gl_FragColor *= texture2D(uSampler, vTextureCoord);
|
gl_FragColor *= texture2D(uSampler, vTextureCoord);
|
||||||
}`,XA=`attribute vec2 aVertexPosition;
|
}`,XT=`attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -48,7 +48,7 @@ void main(void){
|
|||||||
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||||
vTextureCoord = aTextureCoord;
|
vTextureCoord = aTextureCoord;
|
||||||
}
|
}
|
||||||
`;function K0(s,e,t){const i=s.createShader(e);return s.shaderSource(i,t),s.compileShader(i),i}function Jd(s){const e=new Array(s);for(let t=0;t<e.length;t++)e[t]=!1;return e}function ib(s,e){switch(s){case"float":return 0;case"vec2":return new Float32Array(2*e);case"vec3":return new Float32Array(3*e);case"vec4":return new Float32Array(4*e);case"int":case"uint":case"sampler2D":case"sampler2DArray":return 0;case"ivec2":return new Int32Array(2*e);case"ivec3":return new Int32Array(3*e);case"ivec4":return new Int32Array(4*e);case"uvec2":return new Uint32Array(2*e);case"uvec3":return new Uint32Array(3*e);case"uvec4":return new Uint32Array(4*e);case"bool":return!1;case"bvec2":return Jd(2*e);case"bvec3":return Jd(3*e);case"bvec4":return Jd(4*e);case"mat2":return new Float32Array([1,0,0,1]);case"mat3":return new Float32Array([1,0,0,0,1,0,0,0,1]);case"mat4":return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])}return null}const xo=[{test:s=>s.type==="float"&&s.size===1&&!s.isArray,code:s=>`
|
`;function K0(s,e,t){const i=s.createShader(e);return s.shaderSource(i,t),s.compileShader(i),i}function Jd(s){const e=new Array(s);for(let t=0;t<e.length;t++)e[t]=!1;return e}function i1(s,e){switch(s){case"float":return 0;case"vec2":return new Float32Array(2*e);case"vec3":return new Float32Array(3*e);case"vec4":return new Float32Array(4*e);case"int":case"uint":case"sampler2D":case"sampler2DArray":return 0;case"ivec2":return new Int32Array(2*e);case"ivec3":return new Int32Array(3*e);case"ivec4":return new Int32Array(4*e);case"uvec2":return new Uint32Array(2*e);case"uvec3":return new Uint32Array(3*e);case"uvec4":return new Uint32Array(4*e);case"bool":return!1;case"bvec2":return Jd(2*e);case"bvec3":return Jd(3*e);case"bvec4":return Jd(4*e);case"mat2":return new Float32Array([1,0,0,1]);case"mat3":return new Float32Array([1,0,0,0,1,0,0,0,1]);case"mat4":return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])}return null}const xo=[{test:s=>s.type==="float"&&s.size===1&&!s.isArray,code:s=>`
|
||||||
if(uv["${s}"] !== ud["${s}"].value)
|
if(uv["${s}"] !== ud["${s}"].value)
|
||||||
{
|
{
|
||||||
ud["${s}"].value = uv["${s}"]
|
ud["${s}"].value = uv["${s}"]
|
||||||
@@ -168,7 +168,7 @@ void main(void){
|
|||||||
cv[3] = v[3];
|
cv[3] = v[3];
|
||||||
|
|
||||||
gl.uniform4f(ud["${s}"].location, v[0], v[1], v[2], v[3])
|
gl.uniform4f(ud["${s}"].location, v[0], v[1], v[2], v[3])
|
||||||
}`}],VA={float:`
|
}`}],VT={float:`
|
||||||
if (cv !== v)
|
if (cv !== v)
|
||||||
{
|
{
|
||||||
cu.value = v;
|
cu.value = v;
|
||||||
@@ -304,7 +304,7 @@ void main(void){
|
|||||||
cu.value = v;
|
cu.value = v;
|
||||||
|
|
||||||
gl.uniform1i(location, v);
|
gl.uniform1i(location, v);
|
||||||
}`},YA={float:"gl.uniform1fv(location, v)",vec2:"gl.uniform2fv(location, v)",vec3:"gl.uniform3fv(location, v)",vec4:"gl.uniform4fv(location, v)",mat4:"gl.uniformMatrix4fv(location, false, v)",mat3:"gl.uniformMatrix3fv(location, false, v)",mat2:"gl.uniformMatrix2fv(location, false, v)",int:"gl.uniform1iv(location, v)",ivec2:"gl.uniform2iv(location, v)",ivec3:"gl.uniform3iv(location, v)",ivec4:"gl.uniform4iv(location, v)",uint:"gl.uniform1uiv(location, v)",uvec2:"gl.uniform2uiv(location, v)",uvec3:"gl.uniform3uiv(location, v)",uvec4:"gl.uniform4uiv(location, v)",bool:"gl.uniform1iv(location, v)",bvec2:"gl.uniform2iv(location, v)",bvec3:"gl.uniform3iv(location, v)",bvec4:"gl.uniform4iv(location, v)",sampler2D:"gl.uniform1iv(location, v)",samplerCube:"gl.uniform1iv(location, v)",sampler2DArray:"gl.uniform1iv(location, v)"};function $A(s,e){var i;const t=[`
|
}`},YT={float:"gl.uniform1fv(location, v)",vec2:"gl.uniform2fv(location, v)",vec3:"gl.uniform3fv(location, v)",vec4:"gl.uniform4fv(location, v)",mat4:"gl.uniformMatrix4fv(location, false, v)",mat3:"gl.uniformMatrix3fv(location, false, v)",mat2:"gl.uniformMatrix2fv(location, false, v)",int:"gl.uniform1iv(location, v)",ivec2:"gl.uniform2iv(location, v)",ivec3:"gl.uniform3iv(location, v)",ivec4:"gl.uniform4iv(location, v)",uint:"gl.uniform1uiv(location, v)",uvec2:"gl.uniform2uiv(location, v)",uvec3:"gl.uniform3uiv(location, v)",uvec4:"gl.uniform4uiv(location, v)",bool:"gl.uniform1iv(location, v)",bvec2:"gl.uniform2iv(location, v)",bvec3:"gl.uniform3iv(location, v)",bvec4:"gl.uniform4iv(location, v)",sampler2D:"gl.uniform1iv(location, v)",samplerCube:"gl.uniform1iv(location, v)",sampler2DArray:"gl.uniform1iv(location, v)"};function $T(s,e){var i;const t=[`
|
||||||
var v = null;
|
var v = null;
|
||||||
var cv = null;
|
var cv = null;
|
||||||
var cu = null;
|
var cu = null;
|
||||||
@@ -314,18 +314,18 @@ void main(void){
|
|||||||
renderer.shader.syncUniformBufferGroup(uv.${n}, '${n}');
|
renderer.shader.syncUniformBufferGroup(uv.${n}, '${n}');
|
||||||
`):t.push(`
|
`):t.push(`
|
||||||
renderer.shader.syncUniformGroup(uv.${n}, syncData);
|
renderer.shader.syncUniformGroup(uv.${n}, syncData);
|
||||||
`));continue}const a=s.uniforms[n];let o=!1;for(let h=0;h<xo.length;h++)if(xo[h].test(r,a)){t.push(xo[h].code(n,a)),o=!0;break}if(!o){const h=(r.size===1&&!r.isArray?VA:YA)[r.type].replace("location",`ud["${n}"].location`);t.push(`
|
`));continue}const a=s.uniforms[n];let o=!1;for(let h=0;h<xo.length;h++)if(xo[h].test(r,a)){t.push(xo[h].code(n,a)),o=!0;break}if(!o){const h=(r.size===1&&!r.isArray?VT:YT)[r.type].replace("location",`ud["${n}"].location`);t.push(`
|
||||||
cu = ud["${n}"];
|
cu = ud["${n}"];
|
||||||
cv = cu.value;
|
cv = cu.value;
|
||||||
v = uv["${n}"];
|
v = uv["${n}"];
|
||||||
${h};`)}}return new Function("ud","uv","renderer","syncData",t.join(`
|
${h};`)}}return new Function("ud","uv","renderer","syncData",t.join(`
|
||||||
`))}const sb={};let qa=sb;function GA(){if(qa===sb||qa!=null&&qa.isContextLost()){const s=We.ADAPTER.createCanvas();let e;We.PREFER_ENV>=Na.WEBGL2&&(e=s.getContext("webgl2",{})),e||(e=s.getContext("webgl",{})||s.getContext("experimental-webgl",{}),e?e.getExtension("WEBGL_draw_buffers"):e=null),qa=e}return qa}let uc;function HA(){if(!uc){uc=Us.MEDIUM;const s=GA();if(s&&s.getShaderPrecisionFormat){const e=s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.HIGH_FLOAT);e&&(uc=e.precision?Us.HIGH:Us.MEDIUM)}}return uc}function Z0(s,e){const t=s.getShaderSource(e).split(`
|
`))}const s1={};let qa=s1;function GT(){if(qa===s1||qa!=null&&qa.isContextLost()){const s=We.ADAPTER.createCanvas();let e;We.PREFER_ENV>=Na.WEBGL2&&(e=s.getContext("webgl2",{})),e||(e=s.getContext("webgl",{})||s.getContext("experimental-webgl",{}),e?e.getExtension("WEBGL_draw_buffers"):e=null),qa=e}return qa}let uc;function HT(){if(!uc){uc=Us.MEDIUM;const s=GT();if(s&&s.getShaderPrecisionFormat){const e=s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.HIGH_FLOAT);e&&(uc=e.precision?Us.HIGH:Us.MEDIUM)}}return uc}function Z0(s,e){const t=s.getShaderSource(e).split(`
|
||||||
`).map((l,c)=>`${c}: ${l}`),i=s.getShaderInfoLog(e),n=i.split(`
|
`).map((l,c)=>`${c}: ${l}`),i=s.getShaderInfoLog(e),n=i.split(`
|
||||||
`),r={},a=n.map(l=>parseFloat(l.replace(/^ERROR\: 0\:([\d]+)\:.*$/,"$1"))).filter(l=>l&&!r[l]?(r[l]=!0,!0):!1),o=[""];a.forEach(l=>{t[l-1]=`%c${t[l-1]}%c`,o.push("background: #FF0000; color:#FFFFFF; font-size: 10px","font-size: 10px")});const h=t.join(`
|
`),r={},a=n.map(l=>parseFloat(l.replace(/^ERROR\: 0\:([\d]+)\:.*$/,"$1"))).filter(l=>l&&!r[l]?(r[l]=!0,!0):!1),o=[""];a.forEach(l=>{t[l-1]=`%c${t[l-1]}%c`,o.push("background: #FF0000; color:#FFFFFF; font-size: 10px","font-size: 10px")});const h=t.join(`
|
||||||
`);o[0]=h,console.error(i),console.groupCollapsed("click to view full shader code"),console.warn(...o),console.groupEnd()}function jA(s,e,t,i){s.getProgramParameter(e,s.LINK_STATUS)||(s.getShaderParameter(t,s.COMPILE_STATUS)||Z0(s,t),s.getShaderParameter(i,s.COMPILE_STATUS)||Z0(s,i),console.error("PixiJS Error: Could not initialize shader."),s.getProgramInfoLog(e)!==""&&console.warn("PixiJS Warning: gl.getProgramInfoLog()",s.getProgramInfoLog(e)))}const WA={float:1,vec2:2,vec3:3,vec4:4,int:1,ivec2:2,ivec3:3,ivec4:4,uint:1,uvec2:2,uvec3:3,uvec4:4,bool:1,bvec2:2,bvec3:3,bvec4:4,mat2:4,mat3:9,mat4:16,sampler2D:1};function nb(s){return WA[s]}let dc=null;const J0={FLOAT:"float",FLOAT_VEC2:"vec2",FLOAT_VEC3:"vec3",FLOAT_VEC4:"vec4",INT:"int",INT_VEC2:"ivec2",INT_VEC3:"ivec3",INT_VEC4:"ivec4",UNSIGNED_INT:"uint",UNSIGNED_INT_VEC2:"uvec2",UNSIGNED_INT_VEC3:"uvec3",UNSIGNED_INT_VEC4:"uvec4",BOOL:"bool",BOOL_VEC2:"bvec2",BOOL_VEC3:"bvec3",BOOL_VEC4:"bvec4",FLOAT_MAT2:"mat2",FLOAT_MAT3:"mat3",FLOAT_MAT4:"mat4",SAMPLER_2D:"sampler2D",INT_SAMPLER_2D:"sampler2D",UNSIGNED_INT_SAMPLER_2D:"sampler2D",SAMPLER_CUBE:"samplerCube",INT_SAMPLER_CUBE:"samplerCube",UNSIGNED_INT_SAMPLER_CUBE:"samplerCube",SAMPLER_2D_ARRAY:"sampler2DArray",INT_SAMPLER_2D_ARRAY:"sampler2DArray",UNSIGNED_INT_SAMPLER_2D_ARRAY:"sampler2DArray"};function rb(s,e){if(!dc){const t=Object.keys(J0);dc={};for(let i=0;i<t.length;++i){const n=t[i];dc[s[n]]=J0[n]}}return dc[e]}function Q0(s,e,t){if(s.substring(0,9)!=="precision"){let i=e;return e===Us.HIGH&&t!==Us.HIGH&&(i=Us.MEDIUM),`precision ${i} float;
|
`);o[0]=h,console.error(i),console.groupCollapsed("click to view full shader code"),console.warn(...o),console.groupEnd()}function jT(s,e,t,i){s.getProgramParameter(e,s.LINK_STATUS)||(s.getShaderParameter(t,s.COMPILE_STATUS)||Z0(s,t),s.getShaderParameter(i,s.COMPILE_STATUS)||Z0(s,i),console.error("PixiJS Error: Could not initialize shader."),s.getProgramInfoLog(e)!==""&&console.warn("PixiJS Warning: gl.getProgramInfoLog()",s.getProgramInfoLog(e)))}const WT={float:1,vec2:2,vec3:3,vec4:4,int:1,ivec2:2,ivec3:3,ivec4:4,uint:1,uvec2:2,uvec3:3,uvec4:4,bool:1,bvec2:2,bvec3:3,bvec4:4,mat2:4,mat3:9,mat4:16,sampler2D:1};function n1(s){return WT[s]}let dc=null;const J0={FLOAT:"float",FLOAT_VEC2:"vec2",FLOAT_VEC3:"vec3",FLOAT_VEC4:"vec4",INT:"int",INT_VEC2:"ivec2",INT_VEC3:"ivec3",INT_VEC4:"ivec4",UNSIGNED_INT:"uint",UNSIGNED_INT_VEC2:"uvec2",UNSIGNED_INT_VEC3:"uvec3",UNSIGNED_INT_VEC4:"uvec4",BOOL:"bool",BOOL_VEC2:"bvec2",BOOL_VEC3:"bvec3",BOOL_VEC4:"bvec4",FLOAT_MAT2:"mat2",FLOAT_MAT3:"mat3",FLOAT_MAT4:"mat4",SAMPLER_2D:"sampler2D",INT_SAMPLER_2D:"sampler2D",UNSIGNED_INT_SAMPLER_2D:"sampler2D",SAMPLER_CUBE:"samplerCube",INT_SAMPLER_CUBE:"samplerCube",UNSIGNED_INT_SAMPLER_CUBE:"samplerCube",SAMPLER_2D_ARRAY:"sampler2DArray",INT_SAMPLER_2D_ARRAY:"sampler2DArray",UNSIGNED_INT_SAMPLER_2D_ARRAY:"sampler2DArray"};function r1(s,e){if(!dc){const t=Object.keys(J0);dc={};for(let i=0;i<t.length;++i){const n=t[i];dc[s[n]]=J0[n]}}return dc[e]}function Q0(s,e,t){if(s.substring(0,9)!=="precision"){let i=e;return e===Us.HIGH&&t!==Us.HIGH&&(i=Us.MEDIUM),`precision ${i} float;
|
||||||
${s}`}else if(t!==Us.HIGH&&s.substring(0,15)==="precision highp")return s.replace("precision highp","precision mediump");return s}let Wo;function zA(){if(typeof Wo=="boolean")return Wo;try{Wo=new Function("param1","param2","param3","return param1[param2] === param3;")({a:"b"},"a","b")===!0}catch{Wo=!1}return Wo}let qA=0;const fc={},Jf=class no{constructor(e,t,i="pixi-shader",n={}){this.extra={},this.id=qA++,this.vertexSrc=e||no.defaultVertexSrc,this.fragmentSrc=t||no.defaultFragmentSrc,this.vertexSrc=this.vertexSrc.trim(),this.fragmentSrc=this.fragmentSrc.trim(),this.extra=n,this.vertexSrc.substring(0,8)!=="#version"&&(i=i.replace(/\s+/g,"-"),fc[i]?(fc[i]++,i+=`-${fc[i]}`):fc[i]=1,this.vertexSrc=`#define SHADER_NAME ${i}
|
${s}`}else if(t!==Us.HIGH&&s.substring(0,15)==="precision highp")return s.replace("precision highp","precision mediump");return s}let Wo;function zT(){if(typeof Wo=="boolean")return Wo;try{Wo=new Function("param1","param2","param3","return param1[param2] === param3;")({a:"b"},"a","b")===!0}catch{Wo=!1}return Wo}let qT=0;const fc={},Jf=class no{constructor(e,t,i="pixi-shader",n={}){this.extra={},this.id=qT++,this.vertexSrc=e||no.defaultVertexSrc,this.fragmentSrc=t||no.defaultFragmentSrc,this.vertexSrc=this.vertexSrc.trim(),this.fragmentSrc=this.fragmentSrc.trim(),this.extra=n,this.vertexSrc.substring(0,8)!=="#version"&&(i=i.replace(/\s+/g,"-"),fc[i]?(fc[i]++,i+=`-${fc[i]}`):fc[i]=1,this.vertexSrc=`#define SHADER_NAME ${i}
|
||||||
${this.vertexSrc}`,this.fragmentSrc=`#define SHADER_NAME ${i}
|
${this.vertexSrc}`,this.fragmentSrc=`#define SHADER_NAME ${i}
|
||||||
${this.fragmentSrc}`,this.vertexSrc=Q0(this.vertexSrc,no.defaultVertexPrecision,Us.HIGH),this.fragmentSrc=Q0(this.fragmentSrc,no.defaultFragmentPrecision,HA())),this.glPrograms={},this.syncUniforms=null}static get defaultVertexSrc(){return XA}static get defaultFragmentSrc(){return UA}static from(e,t,i){const n=e+t;let r=H0[n];return r||(H0[n]=r=new no(e,t,i)),r}};Jf.defaultVertexPrecision=Us.HIGH,Jf.defaultFragmentPrecision=ir.apple.device?Us.HIGH:Us.MEDIUM;let kn=Jf,KA=0;class ln{constructor(e,t,i){this.group=!0,this.syncUniforms={},this.dirtyId=0,this.id=KA++,this.static=!!t,this.ubo=!!i,e instanceof oi?(this.buffer=e,this.buffer.type=Bn.UNIFORM_BUFFER,this.autoManage=!1,this.ubo=!0):(this.uniforms=e,this.ubo&&(this.buffer=new oi(new Float32Array(1)),this.buffer.type=Bn.UNIFORM_BUFFER,this.autoManage=!0))}update(){this.dirtyId++,!this.autoManage&&this.buffer&&this.buffer.update()}add(e,t,i){if(!this.ubo)this.uniforms[e]=new ln(t,i);else throw new Error("[UniformGroup] uniform groups in ubo mode cannot be modified, or have uniform groups nested in them")}static from(e,t,i){return new ln(e,t,i)}static uboFrom(e,t){return new ln(e,t??!0,!0)}}class cn{constructor(e,t){this.uniformBindCount=0,this.program=e,t?t instanceof ln?this.uniformGroup=t:this.uniformGroup=new ln(t):this.uniformGroup=new ln({}),this.disposeRunner=new on("disposeShader")}checkUniformExists(e,t){if(t.uniforms[e])return!0;for(const i in t.uniforms){const n=t.uniforms[i];if(n.group===!0&&this.checkUniformExists(e,n))return!0}return!1}destroy(){this.uniformGroup=null,this.disposeRunner.emit(this),this.disposeRunner.destroy()}get uniforms(){return this.uniformGroup.uniforms}static from(e,t,i){const n=kn.from(e,t);return new cn(n,i)}}class ab{constructor(e,t){if(this.vertexSrc=e,this.fragTemplate=t,this.programCache={},this.defaultGroupCache={},!t.includes("%count%"))throw new Error('Fragment template must contain "%count%".');if(!t.includes("%forloop%"))throw new Error('Fragment template must contain "%forloop%".')}generateShader(e){if(!this.programCache[e]){const i=new Int32Array(e);for(let r=0;r<e;r++)i[r]=r;this.defaultGroupCache[e]=ln.from({uSamplers:i},!0);let n=this.fragTemplate;n=n.replace(/%count%/gi,`${e}`),n=n.replace(/%forloop%/gi,this.generateSampleSrc(e)),this.programCache[e]=new kn(this.vertexSrc,n)}const t={tint:new Float32Array([1,1,1,1]),translationMatrix:new li,default:this.defaultGroupCache[e]};return new cn(this.programCache[e],t)}generateSampleSrc(e){let t="";t+=`
|
${this.fragmentSrc}`,this.vertexSrc=Q0(this.vertexSrc,no.defaultVertexPrecision,Us.HIGH),this.fragmentSrc=Q0(this.fragmentSrc,no.defaultFragmentPrecision,HT())),this.glPrograms={},this.syncUniforms=null}static get defaultVertexSrc(){return XT}static get defaultFragmentSrc(){return UT}static from(e,t,i){const n=e+t;let r=H0[n];return r||(H0[n]=r=new no(e,t,i)),r}};Jf.defaultVertexPrecision=Us.HIGH,Jf.defaultFragmentPrecision=ir.apple.device?Us.HIGH:Us.MEDIUM;let kn=Jf,KT=0;class ln{constructor(e,t,i){this.group=!0,this.syncUniforms={},this.dirtyId=0,this.id=KT++,this.static=!!t,this.ubo=!!i,e instanceof oi?(this.buffer=e,this.buffer.type=Bn.UNIFORM_BUFFER,this.autoManage=!1,this.ubo=!0):(this.uniforms=e,this.ubo&&(this.buffer=new oi(new Float32Array(1)),this.buffer.type=Bn.UNIFORM_BUFFER,this.autoManage=!0))}update(){this.dirtyId++,!this.autoManage&&this.buffer&&this.buffer.update()}add(e,t,i){if(!this.ubo)this.uniforms[e]=new ln(t,i);else throw new Error("[UniformGroup] uniform groups in ubo mode cannot be modified, or have uniform groups nested in them")}static from(e,t,i){return new ln(e,t,i)}static uboFrom(e,t){return new ln(e,t??!0,!0)}}class cn{constructor(e,t){this.uniformBindCount=0,this.program=e,t?t instanceof ln?this.uniformGroup=t:this.uniformGroup=new ln(t):this.uniformGroup=new ln({}),this.disposeRunner=new on("disposeShader")}checkUniformExists(e,t){if(t.uniforms[e])return!0;for(const i in t.uniforms){const n=t.uniforms[i];if(n.group===!0&&this.checkUniformExists(e,n))return!0}return!1}destroy(){this.uniformGroup=null,this.disposeRunner.emit(this),this.disposeRunner.destroy()}get uniforms(){return this.uniformGroup.uniforms}static from(e,t,i){const n=kn.from(e,t);return new cn(n,i)}}class a1{constructor(e,t){if(this.vertexSrc=e,this.fragTemplate=t,this.programCache={},this.defaultGroupCache={},!t.includes("%count%"))throw new Error('Fragment template must contain "%count%".');if(!t.includes("%forloop%"))throw new Error('Fragment template must contain "%forloop%".')}generateShader(e){if(!this.programCache[e]){const i=new Int32Array(e);for(let r=0;r<e;r++)i[r]=r;this.defaultGroupCache[e]=ln.from({uSamplers:i},!0);let n=this.fragTemplate;n=n.replace(/%count%/gi,`${e}`),n=n.replace(/%forloop%/gi,this.generateSampleSrc(e)),this.programCache[e]=new kn(this.vertexSrc,n)}const t={tint:new Float32Array([1,1,1,1]),translationMatrix:new li,default:this.defaultGroupCache[e]};return new cn(this.programCache[e],t)}generateSampleSrc(e){let t="";t+=`
|
||||||
`,t+=`
|
`,t+=`
|
||||||
`;for(let i=0;i<e;i++)i>0&&(t+=`
|
`;for(let i=0;i<e;i++)i>0&&(t+=`
|
||||||
else `),i<e-1&&(t+=`if(vTextureId < ${i}.5)`),t+=`
|
else `),i<e-1&&(t+=`if(vTextureId < ${i}.5)`),t+=`
|
||||||
@@ -333,7 +333,7 @@ else `),i<e-1&&(t+=`if(vTextureId < ${i}.5)`),t+=`
|
|||||||
color = texture2D(uSamplers[${i}], vTextureCoord);`,t+=`
|
color = texture2D(uSamplers[${i}], vTextureCoord);`,t+=`
|
||||||
}`;return t+=`
|
}`;return t+=`
|
||||||
`,t+=`
|
`,t+=`
|
||||||
`,t}}class Qf{constructor(){this.elements=[],this.ids=[],this.count=0}clear(){for(let e=0;e<this.count;e++)this.elements[e]=null;this.count=0}}function ZA(){return!ir.apple.device}function JA(s){let e=!0;const t=We.ADAPTER.getNavigator();if(ir.tablet||ir.phone){if(ir.apple.device){const i=t.userAgent.match(/OS (\d+)_(\d+)?/);i&&parseInt(i[1],10)<11&&(e=!1)}if(ir.android.device){const i=t.userAgent.match(/Android\s([0-9.]*)/);i&&parseInt(i[1],10)<7&&(e=!1)}}return e?s:4}class Su{constructor(e){this.renderer=e}flush(){}destroy(){this.renderer=null}start(){}stop(){this.flush()}render(e){}}var QA=`varying vec2 vTextureCoord;
|
`,t}}class Qf{constructor(){this.elements=[],this.ids=[],this.count=0}clear(){for(let e=0;e<this.count;e++)this.elements[e]=null;this.count=0}}function ZT(){return!ir.apple.device}function JT(s){let e=!0;const t=We.ADAPTER.getNavigator();if(ir.tablet||ir.phone){if(ir.apple.device){const i=t.userAgent.match(/OS (\d+)_(\d+)?/);i&&parseInt(i[1],10)<11&&(e=!1)}if(ir.android.device){const i=t.userAgent.match(/Android\s([0-9.]*)/);i&&parseInt(i[1],10)<7&&(e=!1)}}return e?s:4}class Su{constructor(e){this.renderer=e}flush(){}destroy(){this.renderer=null}start(){}stop(){this.flush()}render(e){}}var QT=`varying vec2 vTextureCoord;
|
||||||
varying vec4 vColor;
|
varying vec4 vColor;
|
||||||
varying float vTextureId;
|
varying float vTextureId;
|
||||||
uniform sampler2D uSamplers[%count%];
|
uniform sampler2D uSamplers[%count%];
|
||||||
@@ -343,7 +343,7 @@ void main(void){
|
|||||||
%forloop%
|
%forloop%
|
||||||
gl_FragColor = color * vColor;
|
gl_FragColor = color * vColor;
|
||||||
}
|
}
|
||||||
`,eE=`precision highp float;
|
`,eA=`precision highp float;
|
||||||
attribute vec2 aVertexPosition;
|
attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
attribute vec4 aColor;
|
attribute vec4 aColor;
|
||||||
@@ -364,14 +364,14 @@ void main(void){
|
|||||||
vTextureId = aTextureId;
|
vTextureId = aTextureId;
|
||||||
vColor = aColor * tint;
|
vColor = aColor * tint;
|
||||||
}
|
}
|
||||||
`;const il=class Zs extends Su{constructor(e){super(e),this.setShaderGenerator(),this.geometryClass=eb,this.vertexSize=6,this.state=Un.for2d(),this.size=Zs.defaultBatchSize*4,this._vertexCount=0,this._indexCount=0,this._bufferedElements=[],this._bufferedTextures=[],this._bufferSize=0,this._shader=null,this._packedGeometries=[],this._packedGeometryPoolSize=2,this._flushId=0,this._aBuffers={},this._iBuffers={},this.maxTextures=1,this.renderer.on("prerender",this.onPrerender,this),e.runners.contextChange.add(this),this._dcIndex=0,this._aIndex=0,this._iIndex=0,this._attributeBuffer=null,this._indexBuffer=null,this._tempBoundTextures=[]}static get defaultMaxTextures(){return this._defaultMaxTextures=this._defaultMaxTextures??JA(32),this._defaultMaxTextures}static set defaultMaxTextures(e){this._defaultMaxTextures=e}static get canUploadSameBuffer(){return this._canUploadSameBuffer=this._canUploadSameBuffer??ZA(),this._canUploadSameBuffer}static set canUploadSameBuffer(e){this._canUploadSameBuffer=e}get MAX_TEXTURES(){return kt("7.1.0","BatchRenderer#MAX_TEXTURES renamed to BatchRenderer#maxTextures"),this.maxTextures}static get defaultVertexSrc(){return eE}static get defaultFragmentTemplate(){return QA}setShaderGenerator({vertex:e=Zs.defaultVertexSrc,fragment:t=Zs.defaultFragmentTemplate}={}){this.shaderGenerator=new ab(e,t)}contextChange(){const e=this.renderer.gl;We.PREFER_ENV===Na.WEBGL_LEGACY?this.maxTextures=1:(this.maxTextures=Math.min(e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS),Zs.defaultMaxTextures),this.maxTextures=IA(this.maxTextures,e)),this._shader=this.shaderGenerator.generateShader(this.maxTextures);for(let t=0;t<this._packedGeometryPoolSize;t++)this._packedGeometries[t]=new this.geometryClass;this.initFlushBuffers()}initFlushBuffers(){const{_drawCallPool:e,_textureArrayPool:t}=Zs,i=this.size/4,n=Math.floor(i/this.maxTextures)+1;for(;e.length<i;)e.push(new qf);for(;t.length<n;)t.push(new Qf);for(let r=0;r<this.maxTextures;r++)this._tempBoundTextures[r]=null}onPrerender(){this._flushId=0}render(e){e._texture.valid&&(this._vertexCount+e.vertexData.length/2>this.size&&this.flush(),this._vertexCount+=e.vertexData.length/2,this._indexCount+=e.indices.length,this._bufferedTextures[this._bufferSize]=e._texture.baseTexture,this._bufferedElements[this._bufferSize++]=e)}buildTexturesAndDrawCalls(){const{_bufferedTextures:e,maxTextures:t}=this,i=Zs._textureArrayPool,n=this.renderer.batch,r=this._tempBoundTextures,a=this.renderer.textureGC.count;let o=++yt._globalBatch,h=0,l=i[0],c=0;n.copyBoundTextures(r,t);for(let u=0;u<this._bufferSize;++u){const d=e[u];e[u]=null,d._batchEnabled!==o&&(l.count>=t&&(n.boundArray(l,r,o,t),this.buildDrawCalls(l,c,u),c=u,l=i[++h],++o),d._batchEnabled=o,d.touched=a,l.elements[l.count++]=d)}l.count>0&&(n.boundArray(l,r,o,t),this.buildDrawCalls(l,c,this._bufferSize),++h,++o);for(let u=0;u<r.length;u++)r[u]=null;yt._globalBatch=o}buildDrawCalls(e,t,i){const{_bufferedElements:n,_attributeBuffer:r,_indexBuffer:a,vertexSize:o}=this,h=Zs._drawCallPool;let l=this._dcIndex,c=this._aIndex,u=this._iIndex,d=h[l];d.start=this._iIndex,d.texArray=e;for(let f=t;f<i;++f){const p=n[f],m=p._texture.baseTexture,g=Kx[m.alphaMode?1:0][p.blendMode];n[f]=null,t<f&&d.blend!==g&&(d.size=u-d.start,t=f,d=h[++l],d.texArray=e,d.start=u),this.packInterleavedGeometry(p,r,a,c,u),c+=p.vertexData.length/2*o,u+=p.indices.length,d.blend=g}t<i&&(d.size=u-d.start,++l),this._dcIndex=l,this._aIndex=c,this._iIndex=u}bindAndClearTexArray(e){const t=this.renderer.texture;for(let i=0;i<e.count;i++)t.bind(e.elements[i],e.ids[i]),e.elements[i]=null;e.count=0}updateGeometry(){const{_packedGeometries:e,_attributeBuffer:t,_indexBuffer:i}=this;Zs.canUploadSameBuffer?(e[this._flushId]._buffer.update(t.rawBinaryData),e[this._flushId]._indexBuffer.update(i),this.renderer.geometry.updateBuffers()):(this._packedGeometryPoolSize<=this._flushId&&(this._packedGeometryPoolSize++,e[this._flushId]=new this.geometryClass),e[this._flushId]._buffer.update(t.rawBinaryData),e[this._flushId]._indexBuffer.update(i),this.renderer.geometry.bind(e[this._flushId]),this.renderer.geometry.updateBuffers(),this._flushId++)}drawBatches(){const e=this._dcIndex,{gl:t,state:i}=this.renderer,n=Zs._drawCallPool;let r=null;for(let a=0;a<e;a++){const{texArray:o,type:h,size:l,start:c,blend:u}=n[a];r!==o&&(r=o,this.bindAndClearTexArray(o)),this.state.blendMode=u,i.set(this.state),t.drawElements(h,l,t.UNSIGNED_SHORT,c*2)}}flush(){this._vertexCount!==0&&(this._attributeBuffer=this.getAttributeBuffer(this._vertexCount),this._indexBuffer=this.getIndexBuffer(this._indexCount),this._aIndex=0,this._iIndex=0,this._dcIndex=0,this.buildTexturesAndDrawCalls(),this.updateGeometry(),this.drawBatches(),this._bufferSize=0,this._vertexCount=0,this._indexCount=0)}start(){this.renderer.state.set(this.state),this.renderer.texture.ensureSamplerType(this.maxTextures),this.renderer.shader.bind(this._shader),Zs.canUploadSameBuffer&&this.renderer.geometry.bind(this._packedGeometries[this._f
Line truncated
|
`;const il=class Zs extends Su{constructor(e){super(e),this.setShaderGenerator(),this.geometryClass=e1,this.vertexSize=6,this.state=Un.for2d(),this.size=Zs.defaultBatchSize*4,this._vertexCount=0,this._indexCount=0,this._bufferedElements=[],this._bufferedTextures=[],this._bufferSize=0,this._shader=null,this._packedGeometries=[],this._packedGeometryPoolSize=2,this._flushId=0,this._aBuffers={},this._iBuffers={},this.maxTextures=1,this.renderer.on("prerender",this.onPrerender,this),e.runners.contextChange.add(this),this._dcIndex=0,this._aIndex=0,this._iIndex=0,this._attributeBuffer=null,this._indexBuffer=null,this._tempBoundTextures=[]}static get defaultMaxTextures(){return this._defaultMaxTextures=this._defaultMaxTextures??JT(32),this._defaultMaxTextures}static set defaultMaxTextures(e){this._defaultMaxTextures=e}static get canUploadSameBuffer(){return this._canUploadSameBuffer=this._canUploadSameBuffer??ZT(),this._canUploadSameBuffer}static set canUploadSameBuffer(e){this._canUploadSameBuffer=e}get MAX_TEXTURES(){return kt("7.1.0","BatchRenderer#MAX_TEXTURES renamed to BatchRenderer#maxTextures"),this.maxTextures}static get defaultVertexSrc(){return eA}static get defaultFragmentTemplate(){return QT}setShaderGenerator({vertex:e=Zs.defaultVertexSrc,fragment:t=Zs.defaultFragmentTemplate}={}){this.shaderGenerator=new a1(e,t)}contextChange(){const e=this.renderer.gl;We.PREFER_ENV===Na.WEBGL_LEGACY?this.maxTextures=1:(this.maxTextures=Math.min(e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS),Zs.defaultMaxTextures),this.maxTextures=IT(this.maxTextures,e)),this._shader=this.shaderGenerator.generateShader(this.maxTextures);for(let t=0;t<this._packedGeometryPoolSize;t++)this._packedGeometries[t]=new this.geometryClass;this.initFlushBuffers()}initFlushBuffers(){const{_drawCallPool:e,_textureArrayPool:t}=Zs,i=this.size/4,n=Math.floor(i/this.maxTextures)+1;for(;e.length<i;)e.push(new qf);for(;t.length<n;)t.push(new Qf);for(let r=0;r<this.maxTextures;r++)this._tempBoundTextures[r]=null}onPrerender(){this._flushId=0}render(e){e._texture.valid&&(this._vertexCount+e.vertexData.length/2>this.size&&this.flush(),this._vertexCount+=e.vertexData.length/2,this._indexCount+=e.indices.length,this._bufferedTextures[this._bufferSize]=e._texture.baseTexture,this._bufferedElements[this._bufferSize++]=e)}buildTexturesAndDrawCalls(){const{_bufferedTextures:e,maxTextures:t}=this,i=Zs._textureArrayPool,n=this.renderer.batch,r=this._tempBoundTextures,a=this.renderer.textureGC.count;let o=++yt._globalBatch,h=0,l=i[0],c=0;n.copyBoundTextures(r,t);for(let u=0;u<this._bufferSize;++u){const d=e[u];e[u]=null,d._batchEnabled!==o&&(l.count>=t&&(n.boundArray(l,r,o,t),this.buildDrawCalls(l,c,u),c=u,l=i[++h],++o),d._batchEnabled=o,d.touched=a,l.elements[l.count++]=d)}l.count>0&&(n.boundArray(l,r,o,t),this.buildDrawCalls(l,c,this._bufferSize),++h,++o);for(let u=0;u<r.length;u++)r[u]=null;yt._globalBatch=o}buildDrawCalls(e,t,i){const{_bufferedElements:n,_attributeBuffer:r,_indexBuffer:a,vertexSize:o}=this,h=Zs._drawCallPool;let l=this._dcIndex,c=this._aIndex,u=this._iIndex,d=h[l];d.start=this._iIndex,d.texArray=e;for(let f=t;f<i;++f){const p=n[f],m=p._texture.baseTexture,g=Kx[m.alphaMode?1:0][p.blendMode];n[f]=null,t<f&&d.blend!==g&&(d.size=u-d.start,t=f,d=h[++l],d.texArray=e,d.start=u),this.packInterleavedGeometry(p,r,a,c,u),c+=p.vertexData.length/2*o,u+=p.indices.length,d.blend=g}t<i&&(d.size=u-d.start,++l),this._dcIndex=l,this._aIndex=c,this._iIndex=u}bindAndClearTexArray(e){const t=this.renderer.texture;for(let i=0;i<e.count;i++)t.bind(e.elements[i],e.ids[i]),e.elements[i]=null;e.count=0}updateGeometry(){const{_packedGeometries:e,_attributeBuffer:t,_indexBuffer:i}=this;Zs.canUploadSameBuffer?(e[this._flushId]._buffer.update(t.rawBinaryData),e[this._flushId]._indexBuffer.update(i),this.renderer.geometry.updateBuffers()):(this._packedGeometryPoolSize<=this._flushId&&(this._packedGeometryPoolSize++,e[this._flushId]=new this.geometryClass),e[this._flushId]._buffer.update(t.rawBinaryData),e[this._flushId]._indexBuffer.update(i),this.renderer.geometry.bind(e[this._flushId]),this.renderer.geometry.updateBuffers(),this._flushId++)}drawBatches(){const e=this._dcIndex,{gl:t,state:i}=this.renderer,n=Zs._drawCallPool;let r=null;for(let a=0;a<e;a++){const{texArray:o,type:h,size:l,start:c,blend:u}=n[a];r!==o&&(r=o,this.bindAndClearTexArray(o)),this.state.blendMode=u,i.set(this.state),t.drawElements(h,l,t.UNSIGNED_SHORT,c*2)}}flush(){this._vertexCount!==0&&(this._attributeBuffer=this.getAttributeBuffer(this._vertexCount),this._indexBuffer=this.getIndexBuffer(this._indexCount),this._aIndex=0,this._iIndex=0,this._dcIndex=0,this.buildTexturesAndDrawCalls(),this.updateGeometry(),this.drawBatches(),this._bufferSize=0,this._vertexCount=0,this._indexCount=0)}start(){this.renderer.state.set(this.state),this.renderer.texture.ensureSamplerType(this.maxTextures),this.renderer.shader.bind(this._shader),Zs.canUploadSameBuffer&&this.renderer.geometry.bind(this._packedGeometries[this._f
Line truncated
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
|
|
||||||
void main(void){
|
void main(void){
|
||||||
gl_FragColor = texture2D(uSampler, vTextureCoord);
|
gl_FragColor = texture2D(uSampler, vTextureCoord);
|
||||||
}
|
}
|
||||||
`,iE=`attribute vec2 aVertexPosition;
|
`,iA=`attribute vec2 aVertexPosition;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
|
|
||||||
@@ -397,7 +397,7 @@ void main(void)
|
|||||||
gl_Position = filterVertexPosition();
|
gl_Position = filterVertexPosition();
|
||||||
vTextureCoord = filterTextureCoord();
|
vTextureCoord = filterTextureCoord();
|
||||||
}
|
}
|
||||||
`;const em=class sl extends cn{constructor(e,t,i){const n=kn.from(e||sl.defaultVertexSrc,t||sl.defaultFragmentSrc);super(n,i),this.padding=0,this.resolution=sl.defaultResolution,this.multisample=sl.defaultMultisample,this.enabled=!0,this.autoFit=!0,this.state=new Un}apply(e,t,i,n,r){e.applyFilter(this,t,i,n)}get blendMode(){return this.state.blendMode}set blendMode(e){this.state.blendMode=e}get resolution(){return this._resolution}set resolution(e){this._resolution=e}static get defaultVertexSrc(){return iE}static get defaultFragmentSrc(){return tE}};em.defaultResolution=1,em.defaultMultisample=Ei.NONE;let us=em;class yh{constructor(){this.clearBeforeRender=!0,this._backgroundColor=new jt(0),this.alpha=1}init(e){this.clearBeforeRender=e.clearBeforeRender;const{backgroundColor:t,background:i,backgroundAlpha:n}=e,r=i??t;r!==void 0&&(this.color=r),this.alpha=n}get color(){return this._backgroundColor.value}set color(e){this._backgroundColor.setValue(e)}get alpha(){return this._backgroundColor.alpha}set alpha(e){this._backgroundColor.setAlpha(e)}get backgroundColor(){return this._backgroundColor}destroy(){}}yh.defaultOptions={backgroundAlpha:1,backgroundColor:0,clearBeforeRender:!0},yh.extension={type:[He.RendererSystem,He.CanvasRendererSystem],name:"background"};tt.add(yh);class ob{constructor(e){this.renderer=e,this.emptyRenderer=new Su(e),this.currentRenderer=this.emptyRenderer}setObjectRenderer(e){this.currentRenderer!==e&&(this.currentRenderer.stop(),this.currentRenderer=e,this.currentRenderer.start())}flush(){this.setObjectRenderer(this.emptyRenderer)}reset(){this.setObjectRenderer(this.emptyRenderer)}copyBoundTextures(e,t){const{boundTextures:i}=this.renderer.texture;for(let n=t-1;n>=0;--n)e[n]=i[n]||null,e[n]&&(e[n]._batchLocation=n)}boundArray(e,t,i,n){const{elements:r,ids:a,count:o}=e;let h=0;for(let l=0;l<o;l++){const c=r[l],u=c._batchLocation;if(u>=0&&u<n&&t[u]===c){a[l]=u;continue}for(;h<n;){const d=t[h];if(d&&d._batchEnabled===i&&d._batchLocation===h){h++;continue}a[l]=h,c._batchLocation=h,t[h]=c;break}}}destroy(){this.renderer=null}}ob.extension={type:He.RendererSystem,name:"batch"};tt.add(ob);let ev=0;class xh{constructor(e){this.renderer=e,this.webGLVersion=1,this.extensions={},this.supports={uint32Indices:!1},this.handleContextLost=this.handleContextLost.bind(this),this.handleContextRestored=this.handleContextRestored.bind(this)}get isLost(){return!this.gl||this.gl.isContextLost()}contextChange(e){this.gl=e,this.renderer.gl=e,this.renderer.CONTEXT_UID=ev++}init(e){if(e.context)this.initFromContext(e.context);else{const t=this.renderer.background.alpha<1,i=e.premultipliedAlpha;this.preserveDrawingBuffer=e.preserveDrawingBuffer,this.useContextAlpha=e.useContextAlpha,this.powerPreference=e.powerPreference,this.initFromOptions({alpha:t,premultipliedAlpha:i,antialias:e.antialias,stencil:!0,preserveDrawingBuffer:e.preserveDrawingBuffer,powerPreference:e.powerPreference})}}initFromContext(e){this.gl=e,this.validateContext(e),this.renderer.gl=e,this.renderer.CONTEXT_UID=ev++,this.renderer.runners.contextChange.emit(e);const t=this.renderer.view;t.addEventListener!==void 0&&(t.addEventListener("webglcontextlost",this.handleContextLost,!1),t.addEventListener("webglcontextrestored",this.handleContextRestored,!1))}initFromOptions(e){const t=this.createContext(this.renderer.view,e);this.initFromContext(t)}createContext(e,t){let i;if(We.PREFER_ENV>=Na.WEBGL2&&(i=e.getContext("webgl2",t)),i)this.webGLVersion=2;else if(this.webGLVersion=1,i=e.getContext("webgl",t)||e.getContext("experimental-webgl",t),!i)throw new Error("This browser does not support WebGL. Try using the canvas renderer");return this.gl=i,this.getExtensions(),this.gl}getExtensions(){const{gl:e}=this,t={loseContext:e.getExtension("WEBGL_lose_context"),anisotropicFiltering:e.getExtension("EXT_texture_filter_anisotropic"),floatTextureLinear:e.getExtension("OES_texture_float_linear"),s3tc:e.getExtension("WEBGL_compressed_texture_s3tc"),s3tc_sRGB:e.getExtension("WEBGL_compressed_texture_s3tc_srgb"),etc:e.getExtension("WEBGL_compressed_texture_etc"),etc1:e.getExtension("WEBGL_compressed_texture_etc1"),pvrtc:e.getExtension("WEBGL_compressed_texture_pvrtc")||e.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"),atc:e.getExtension("WEBGL_compressed_texture_atc"),astc:e.getExtension("WEBGL_compressed_texture_astc"),bptc:e.getExtension("EXT_texture_compression_bptc")};this.webGLVersion===1?Object.assign(this.extensions,t,{drawBuffers:e.getExtension("WEBGL_draw_buffers"),depthTexture:e.getExtension("WEBGL_depth_texture"),vertexArrayObject:e.getExtension("OES_vertex_array_object")||e.getExtension("MOZ_OES_vertex_array_object")||e.getExtension("WEBKIT_OES_vertex_array_object"),uint32ElementIndex:e.getExtension("OES_element_index_uint"),floatTexture:e.getExtension("OES_texture_float"),floatTextureLinear:e.getExtension("OES_texture_float_linear"),textureHalfFloat:e.getExtension("OES_texture_half_float"),textureHalfFloatLinear:e.getExtension("OES_texture_ha
Line truncated
|
`;const em=class sl extends cn{constructor(e,t,i){const n=kn.from(e||sl.defaultVertexSrc,t||sl.defaultFragmentSrc);super(n,i),this.padding=0,this.resolution=sl.defaultResolution,this.multisample=sl.defaultMultisample,this.enabled=!0,this.autoFit=!0,this.state=new Un}apply(e,t,i,n,r){e.applyFilter(this,t,i,n)}get blendMode(){return this.state.blendMode}set blendMode(e){this.state.blendMode=e}get resolution(){return this._resolution}set resolution(e){this._resolution=e}static get defaultVertexSrc(){return iA}static get defaultFragmentSrc(){return tA}};em.defaultResolution=1,em.defaultMultisample=Ei.NONE;let us=em;class yh{constructor(){this.clearBeforeRender=!0,this._backgroundColor=new jt(0),this.alpha=1}init(e){this.clearBeforeRender=e.clearBeforeRender;const{backgroundColor:t,background:i,backgroundAlpha:n}=e,r=i??t;r!==void 0&&(this.color=r),this.alpha=n}get color(){return this._backgroundColor.value}set color(e){this._backgroundColor.setValue(e)}get alpha(){return this._backgroundColor.alpha}set alpha(e){this._backgroundColor.setAlpha(e)}get backgroundColor(){return this._backgroundColor}destroy(){}}yh.defaultOptions={backgroundAlpha:1,backgroundColor:0,clearBeforeRender:!0},yh.extension={type:[He.RendererSystem,He.CanvasRendererSystem],name:"background"};tt.add(yh);class o1{constructor(e){this.renderer=e,this.emptyRenderer=new Su(e),this.currentRenderer=this.emptyRenderer}setObjectRenderer(e){this.currentRenderer!==e&&(this.currentRenderer.stop(),this.currentRenderer=e,this.currentRenderer.start())}flush(){this.setObjectRenderer(this.emptyRenderer)}reset(){this.setObjectRenderer(this.emptyRenderer)}copyBoundTextures(e,t){const{boundTextures:i}=this.renderer.texture;for(let n=t-1;n>=0;--n)e[n]=i[n]||null,e[n]&&(e[n]._batchLocation=n)}boundArray(e,t,i,n){const{elements:r,ids:a,count:o}=e;let h=0;for(let l=0;l<o;l++){const c=r[l],u=c._batchLocation;if(u>=0&&u<n&&t[u]===c){a[l]=u;continue}for(;h<n;){const d=t[h];if(d&&d._batchEnabled===i&&d._batchLocation===h){h++;continue}a[l]=h,c._batchLocation=h,t[h]=c;break}}}destroy(){this.renderer=null}}o1.extension={type:He.RendererSystem,name:"batch"};tt.add(o1);let ev=0;class xh{constructor(e){this.renderer=e,this.webGLVersion=1,this.extensions={},this.supports={uint32Indices:!1},this.handleContextLost=this.handleContextLost.bind(this),this.handleContextRestored=this.handleContextRestored.bind(this)}get isLost(){return!this.gl||this.gl.isContextLost()}contextChange(e){this.gl=e,this.renderer.gl=e,this.renderer.CONTEXT_UID=ev++}init(e){if(e.context)this.initFromContext(e.context);else{const t=this.renderer.background.alpha<1,i=e.premultipliedAlpha;this.preserveDrawingBuffer=e.preserveDrawingBuffer,this.useContextAlpha=e.useContextAlpha,this.powerPreference=e.powerPreference,this.initFromOptions({alpha:t,premultipliedAlpha:i,antialias:e.antialias,stencil:!0,preserveDrawingBuffer:e.preserveDrawingBuffer,powerPreference:e.powerPreference})}}initFromContext(e){this.gl=e,this.validateContext(e),this.renderer.gl=e,this.renderer.CONTEXT_UID=ev++,this.renderer.runners.contextChange.emit(e);const t=this.renderer.view;t.addEventListener!==void 0&&(t.addEventListener("webglcontextlost",this.handleContextLost,!1),t.addEventListener("webglcontextrestored",this.handleContextRestored,!1))}initFromOptions(e){const t=this.createContext(this.renderer.view,e);this.initFromContext(t)}createContext(e,t){let i;if(We.PREFER_ENV>=Na.WEBGL2&&(i=e.getContext("webgl2",t)),i)this.webGLVersion=2;else if(this.webGLVersion=1,i=e.getContext("webgl",t)||e.getContext("experimental-webgl",t),!i)throw new Error("This browser does not support WebGL. Try using the canvas renderer");return this.gl=i,this.getExtensions(),this.gl}getExtensions(){const{gl:e}=this,t={loseContext:e.getExtension("WEBGL_lose_context"),anisotropicFiltering:e.getExtension("EXT_texture_filter_anisotropic"),floatTextureLinear:e.getExtension("OES_texture_float_linear"),s3tc:e.getExtension("WEBGL_compressed_texture_s3tc"),s3tc_sRGB:e.getExtension("WEBGL_compressed_texture_s3tc_srgb"),etc:e.getExtension("WEBGL_compressed_texture_etc"),etc1:e.getExtension("WEBGL_compressed_texture_etc1"),pvrtc:e.getExtension("WEBGL_compressed_texture_pvrtc")||e.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"),atc:e.getExtension("WEBGL_compressed_texture_atc"),astc:e.getExtension("WEBGL_compressed_texture_astc"),bptc:e.getExtension("EXT_texture_compression_bptc")};this.webGLVersion===1?Object.assign(this.extensions,t,{drawBuffers:e.getExtension("WEBGL_draw_buffers"),depthTexture:e.getExtension("WEBGL_depth_texture"),vertexArrayObject:e.getExtension("OES_vertex_array_object")||e.getExtension("MOZ_OES_vertex_array_object")||e.getExtension("WEBKIT_OES_vertex_array_object"),uint32ElementIndex:e.getExtension("OES_element_index_uint"),floatTexture:e.getExtension("OES_texture_float"),floatTextureLinear:e.getExtension("OES_texture_float_linear"),textureHalfFloat:e.getExtension("OES_texture_half_float"),textureHalfFloatLinear:e.getExtension("OES_texture_ha
Line truncated
|
||||||
varying vec2 vTextureCoord;
|
varying vec2 vTextureCoord;
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
@@ -422,7 +422,7 @@ void main(void)
|
|||||||
|
|
||||||
gl_FragColor = original;
|
gl_FragColor = original;
|
||||||
}
|
}
|
||||||
`,lE=`attribute vec2 aVertexPosition;
|
`,lA=`attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -438,7 +438,7 @@ void main(void)
|
|||||||
vTextureCoord = aTextureCoord;
|
vTextureCoord = aTextureCoord;
|
||||||
vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;
|
vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;
|
||||||
}
|
}
|
||||||
`;class cE extends us{constructor(e,t,i){let n=null;typeof e!="string"&&t===void 0&&i===void 0&&(n=e,e=void 0,t=void 0,i=void 0),super(e||lE,t||oE,i),this.maskSprite=n,this.maskMatrix=new li}get maskSprite(){return this._maskSprite}set maskSprite(e){this._maskSprite=e,this._maskSprite&&(this._maskSprite.renderable=!1)}apply(e,t,i,n){const r=this._maskSprite,a=r._texture;a.valid&&(a.uvMatrix||(a.uvMatrix=new gp(a,0)),a.uvMatrix.update(),this.uniforms.npmAlpha=a.baseTexture.alphaMode?0:1,this.uniforms.mask=a,this.uniforms.otherMatrix=e.calculateSpriteMatrix(this.maskMatrix,r).prepend(a.uvMatrix.mapCoord),this.uniforms.alpha=r.worldAlpha,this.uniforms.maskClamp=a.uvMatrix.uClampFrame,e.applyFilter(this,t,i,n))}}class hE{constructor(e=null){this.type=Ri.NONE,this.autoDetect=!0,this.maskObject=e||null,this.pooled=!1,this.isMaskData=!0,this.resolution=null,this.multisample=us.defaultMultisample,this.enabled=!0,this.colorMask=15,this._filters=null,this._stencilCounter=0,this._scissorCounter=0,this._scissorRect=null,this._scissorRectLocal=null,this._colorMask=15,this._target=null}get filter(){return this._filters?this._filters[0]:null}set filter(e){e?this._filters?this._filters[0]=e:this._filters=[e]:this._filters=null}reset(){this.pooled&&(this.maskObject=null,this.type=Ri.NONE,this.autoDetect=!0),this._target=null,this._scissorRectLocal=null}copyCountersOrReset(e){e?(this._stencilCounter=e._stencilCounter,this._scissorCounter=e._scissorCounter,this._scissorRect=e._scissorRect):(this._stencilCounter=0,this._scissorCounter=0,this._scissorRect=null)}}class pb{constructor(e){this.renderer=e,this.enableScissor=!0,this.alphaMaskPool=[],this.maskDataPool=[],this.maskStack=[],this.alphaMaskIndex=0}setMaskStack(e){this.maskStack=e,this.renderer.scissor.setMaskStack(e),this.renderer.stencil.setMaskStack(e)}push(e,t){let i=t;if(!i.isMaskData){const r=this.maskDataPool.pop()||new hE;r.pooled=!0,r.maskObject=t,i=r}const n=this.maskStack.length!==0?this.maskStack[this.maskStack.length-1]:null;if(i.copyCountersOrReset(n),i._colorMask=n?n._colorMask:15,i.autoDetect&&this.detect(i),i._target=e,i.type!==Ri.SPRITE&&this.maskStack.push(i),i.enabled)switch(i.type){case Ri.SCISSOR:this.renderer.scissor.push(i);break;case Ri.STENCIL:this.renderer.stencil.push(i);break;case Ri.SPRITE:i.copyCountersOrReset(null),this.pushSpriteMask(i);break;case Ri.COLOR:this.pushColorMask(i);break}i.type===Ri.SPRITE&&this.maskStack.push(i)}pop(e){const t=this.maskStack.pop();if(!(!t||t._target!==e)){if(t.enabled)switch(t.type){case Ri.SCISSOR:this.renderer.scissor.pop(t);break;case Ri.STENCIL:this.renderer.stencil.pop(t.maskObject);break;case Ri.SPRITE:this.popSpriteMask(t);break;case Ri.COLOR:this.popColorMask(t);break}if(t.reset(),t.pooled&&this.maskDataPool.push(t),this.maskStack.length!==0){const i=this.maskStack[this.maskStack.length-1];i.type===Ri.SPRITE&&i._filters&&(i._filters[0].maskSprite=i.maskObject)}}}detect(e){const t=e.maskObject;t?t.isSprite?e.type=Ri.SPRITE:this.enableScissor&&this.renderer.scissor.testScissor(e)?e.type=Ri.SCISSOR:e.type=Ri.STENCIL:e.type=Ri.COLOR}pushSpriteMask(e){const{maskObject:t}=e,i=e._target;let n=e._filters;n||(n=this.alphaMaskPool[this.alphaMaskIndex],n||(n=this.alphaMaskPool[this.alphaMaskIndex]=[new cE])),n[0].resolution=e.resolution,n[0].multisample=e.multisample,n[0].maskSprite=t;const r=i.filterArea;i.filterArea=t.getBounds(!0),this.renderer.filter.push(i,n),i.filterArea=r,e._filters||this.alphaMaskIndex++}popSpriteMask(e){this.renderer.filter.pop(),e._filters?e._filters[0].maskSprite=null:(this.alphaMaskIndex--,this.alphaMaskPool[this.alphaMaskIndex][0].maskSprite=null)}pushColorMask(e){const t=e._colorMask,i=e._colorMask=t&e.colorMask;i!==t&&this.renderer.gl.colorMask((i&1)!==0,(i&2)!==0,(i&4)!==0,(i&8)!==0)}popColorMask(e){const t=e._colorMask,i=this.maskStack.length>0?this.maskStack[this.maskStack.length-1]._colorMask:15;i!==t&&this.renderer.gl.colorMask((i&1)!==0,(i&2)!==0,(i&4)!==0,(i&8)!==0)}destroy(){this.renderer=null}}pb.extension={type:He.RendererSystem,name:"mask"};tt.add(pb);class gb{constructor(e){this.renderer=e,this.maskStack=[],this.glConst=0}getStackLength(){return this.maskStack.length}setMaskStack(e){const{gl:t}=this.renderer,i=this.getStackLength();this.maskStack=e;const n=this.getStackLength();n!==i&&(n===0?t.disable(this.glConst):(t.enable(this.glConst),this._useCurrent()))}_useCurrent(){}destroy(){this.renderer=null,this.maskStack=null}}const sv=new li,nv=[],vb=class $c extends gb{constructor(e){super(e),this.glConst=We.ADAPTER.getWebGLRenderingContext().SCISSOR_TEST}getStackLength(){const e=this.maskStack[this.maskStack.length-1];return e?e._scissorCounter:0}calcScissorRect(e){if(e._scissorRectLocal)return;const t=e._scissorRect,{maskObject:i}=e,{renderer:n}=this,r=n.renderTexture,a=i.getBounds(!0,nv.pop()??new Tt);this.roundFrameToPixels(a,r.current?r.current.resolution:n.resolution,r.sourceFrame,r.destinationFrame,n.projection.transform),t&&a.fit(t),e._scissorRectLocal=a}static
Line truncated
|
`;class cA extends us{constructor(e,t,i){let n=null;typeof e!="string"&&t===void 0&&i===void 0&&(n=e,e=void 0,t=void 0,i=void 0),super(e||lA,t||oA,i),this.maskSprite=n,this.maskMatrix=new li}get maskSprite(){return this._maskSprite}set maskSprite(e){this._maskSprite=e,this._maskSprite&&(this._maskSprite.renderable=!1)}apply(e,t,i,n){const r=this._maskSprite,a=r._texture;a.valid&&(a.uvMatrix||(a.uvMatrix=new gp(a,0)),a.uvMatrix.update(),this.uniforms.npmAlpha=a.baseTexture.alphaMode?0:1,this.uniforms.mask=a,this.uniforms.otherMatrix=e.calculateSpriteMatrix(this.maskMatrix,r).prepend(a.uvMatrix.mapCoord),this.uniforms.alpha=r.worldAlpha,this.uniforms.maskClamp=a.uvMatrix.uClampFrame,e.applyFilter(this,t,i,n))}}class hA{constructor(e=null){this.type=Ri.NONE,this.autoDetect=!0,this.maskObject=e||null,this.pooled=!1,this.isMaskData=!0,this.resolution=null,this.multisample=us.defaultMultisample,this.enabled=!0,this.colorMask=15,this._filters=null,this._stencilCounter=0,this._scissorCounter=0,this._scissorRect=null,this._scissorRectLocal=null,this._colorMask=15,this._target=null}get filter(){return this._filters?this._filters[0]:null}set filter(e){e?this._filters?this._filters[0]=e:this._filters=[e]:this._filters=null}reset(){this.pooled&&(this.maskObject=null,this.type=Ri.NONE,this.autoDetect=!0),this._target=null,this._scissorRectLocal=null}copyCountersOrReset(e){e?(this._stencilCounter=e._stencilCounter,this._scissorCounter=e._scissorCounter,this._scissorRect=e._scissorRect):(this._stencilCounter=0,this._scissorCounter=0,this._scissorRect=null)}}class p1{constructor(e){this.renderer=e,this.enableScissor=!0,this.alphaMaskPool=[],this.maskDataPool=[],this.maskStack=[],this.alphaMaskIndex=0}setMaskStack(e){this.maskStack=e,this.renderer.scissor.setMaskStack(e),this.renderer.stencil.setMaskStack(e)}push(e,t){let i=t;if(!i.isMaskData){const r=this.maskDataPool.pop()||new hA;r.pooled=!0,r.maskObject=t,i=r}const n=this.maskStack.length!==0?this.maskStack[this.maskStack.length-1]:null;if(i.copyCountersOrReset(n),i._colorMask=n?n._colorMask:15,i.autoDetect&&this.detect(i),i._target=e,i.type!==Ri.SPRITE&&this.maskStack.push(i),i.enabled)switch(i.type){case Ri.SCISSOR:this.renderer.scissor.push(i);break;case Ri.STENCIL:this.renderer.stencil.push(i);break;case Ri.SPRITE:i.copyCountersOrReset(null),this.pushSpriteMask(i);break;case Ri.COLOR:this.pushColorMask(i);break}i.type===Ri.SPRITE&&this.maskStack.push(i)}pop(e){const t=this.maskStack.pop();if(!(!t||t._target!==e)){if(t.enabled)switch(t.type){case Ri.SCISSOR:this.renderer.scissor.pop(t);break;case Ri.STENCIL:this.renderer.stencil.pop(t.maskObject);break;case Ri.SPRITE:this.popSpriteMask(t);break;case Ri.COLOR:this.popColorMask(t);break}if(t.reset(),t.pooled&&this.maskDataPool.push(t),this.maskStack.length!==0){const i=this.maskStack[this.maskStack.length-1];i.type===Ri.SPRITE&&i._filters&&(i._filters[0].maskSprite=i.maskObject)}}}detect(e){const t=e.maskObject;t?t.isSprite?e.type=Ri.SPRITE:this.enableScissor&&this.renderer.scissor.testScissor(e)?e.type=Ri.SCISSOR:e.type=Ri.STENCIL:e.type=Ri.COLOR}pushSpriteMask(e){const{maskObject:t}=e,i=e._target;let n=e._filters;n||(n=this.alphaMaskPool[this.alphaMaskIndex],n||(n=this.alphaMaskPool[this.alphaMaskIndex]=[new cA])),n[0].resolution=e.resolution,n[0].multisample=e.multisample,n[0].maskSprite=t;const r=i.filterArea;i.filterArea=t.getBounds(!0),this.renderer.filter.push(i,n),i.filterArea=r,e._filters||this.alphaMaskIndex++}popSpriteMask(e){this.renderer.filter.pop(),e._filters?e._filters[0].maskSprite=null:(this.alphaMaskIndex--,this.alphaMaskPool[this.alphaMaskIndex][0].maskSprite=null)}pushColorMask(e){const t=e._colorMask,i=e._colorMask=t&e.colorMask;i!==t&&this.renderer.gl.colorMask((i&1)!==0,(i&2)!==0,(i&4)!==0,(i&8)!==0)}popColorMask(e){const t=e._colorMask,i=this.maskStack.length>0?this.maskStack[this.maskStack.length-1]._colorMask:15;i!==t&&this.renderer.gl.colorMask((i&1)!==0,(i&2)!==0,(i&4)!==0,(i&8)!==0)}destroy(){this.renderer=null}}p1.extension={type:He.RendererSystem,name:"mask"};tt.add(p1);class g1{constructor(e){this.renderer=e,this.maskStack=[],this.glConst=0}getStackLength(){return this.maskStack.length}setMaskStack(e){const{gl:t}=this.renderer,i=this.getStackLength();this.maskStack=e;const n=this.getStackLength();n!==i&&(n===0?t.disable(this.glConst):(t.enable(this.glConst),this._useCurrent()))}_useCurrent(){}destroy(){this.renderer=null,this.maskStack=null}}const sv=new li,nv=[],v1=class $c extends g1{constructor(e){super(e),this.glConst=We.ADAPTER.getWebGLRenderingContext().SCISSOR_TEST}getStackLength(){const e=this.maskStack[this.maskStack.length-1];return e?e._scissorCounter:0}calcScissorRect(e){if(e._scissorRectLocal)return;const t=e._scissorRect,{maskObject:i}=e,{renderer:n}=this,r=n.renderTexture,a=i.getBounds(!0,nv.pop()??new Tt);this.roundFrameToPixels(a,r.current?r.current.resolution:n.resolution,r.sourceFrame,r.destinationFrame,n.projection.transform),t&&a.fit(t),e._scissorRectLocal=a}static
Line truncated
|
||||||
data[offset] = v;
|
data[offset] = v;
|
||||||
`,vec2:`
|
`,vec2:`
|
||||||
data[offset] = v[0];
|
data[offset] = v[0];
|
||||||
@@ -476,7 +476,7 @@ void main(void)
|
|||||||
{
|
{
|
||||||
data[offset + i] = v[i];
|
data[offset + i] = v[i];
|
||||||
}
|
}
|
||||||
`},Mb={float:4,vec2:8,vec3:12,vec4:16,int:4,ivec2:8,ivec3:12,ivec4:16,uint:4,uvec2:8,uvec3:12,uvec4:16,bool:4,bvec2:8,bvec3:12,bvec4:16,mat2:32,mat3:48,mat4:64};function xE(s){const e=s.map(r=>({data:r,offset:0,dataLen:0,dirty:0}));let t=0,i=0,n=0;for(let r=0;r<e.length;r++){const a=e[r];if(t=Mb[a.data.type],a.data.size>1&&(t=Math.max(t,16)*a.data.size),a.dataLen=t,i%t!==0&&i<16){const o=i%t%16;i+=o,n+=o}i+t>16?(n=Math.ceil(n/16)*16,a.offset=n,n+=t,i=t):(a.offset=n,i+=t,n+=t)}return n=Math.ceil(n/16)*16,{uboElements:e,size:n}}function bE(s,e){const t=[];for(const i in s)e[i]&&t.push(e[i]);return t.sort((i,n)=>i.index-n.index),t}function wE(s,e){if(!s.autoManage)return{size:0,syncFunc:vE};const t=bE(s.uniforms,e),{uboElements:i,size:n}=xE(t),r=[`
|
`},M1={float:4,vec2:8,vec3:12,vec4:16,int:4,ivec2:8,ivec3:12,ivec4:16,uint:4,uvec2:8,uvec3:12,uvec4:16,bool:4,bvec2:8,bvec3:12,bvec4:16,mat2:32,mat3:48,mat4:64};function xA(s){const e=s.map(r=>({data:r,offset:0,dataLen:0,dirty:0}));let t=0,i=0,n=0;for(let r=0;r<e.length;r++){const a=e[r];if(t=M1[a.data.type],a.data.size>1&&(t=Math.max(t,16)*a.data.size),a.dataLen=t,i%t!==0&&i<16){const o=i%t%16;i+=o,n+=o}i+t>16?(n=Math.ceil(n/16)*16,a.offset=n,n+=t,i=t):(a.offset=n,i+=t,n+=t)}return n=Math.ceil(n/16)*16,{uboElements:e,size:n}}function bA(s,e){const t=[];for(const i in s)e[i]&&t.push(e[i]);return t.sort((i,n)=>i.index-n.index),t}function wA(s,e){if(!s.autoManage)return{size:0,syncFunc:vA};const t=bA(s.uniforms,e),{uboElements:i,size:n}=xA(t),r=[`
|
||||||
var v = null;
|
var v = null;
|
||||||
var v2 = null;
|
var v2 = null;
|
||||||
var cv = null;
|
var cv = null;
|
||||||
@@ -484,7 +484,7 @@ void main(void)
|
|||||||
var gl = renderer.gl
|
var gl = renderer.gl
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var data = buffer.data;
|
var data = buffer.data;
|
||||||
`];for(let a=0;a<i.length;a++){const o=i[a],h=s.uniforms[o.data.name],l=o.data.name;let c=!1;for(let u=0;u<xo.length;u++){const d=xo[u];if(d.codeUbo&&d.test(o.data,h)){r.push(`offset = ${o.offset/4};`,xo[u].codeUbo(o.data.name,h)),c=!0;break}}if(!c)if(o.data.size>1){const u=nb(o.data.type),d=Math.max(Mb[o.data.type]/16,1),f=u/d,p=(4-f%4)%4;r.push(`
|
`];for(let a=0;a<i.length;a++){const o=i[a],h=s.uniforms[o.data.name],l=o.data.name;let c=!1;for(let u=0;u<xo.length;u++){const d=xo[u];if(d.codeUbo&&d.test(o.data,h)){r.push(`offset = ${o.offset/4};`,xo[u].codeUbo(o.data.name,h)),c=!0;break}}if(!c)if(o.data.size>1){const u=n1(o.data.type),d=Math.max(M1[o.data.type]/16,1),f=u/d,p=(4-f%4)%4;r.push(`
|
||||||
cv = ud.${l}.value;
|
cv = ud.${l}.value;
|
||||||
v = uv.${l};
|
v = uv.${l};
|
||||||
offset = ${o.offset/4};
|
offset = ${o.offset/4};
|
||||||
@@ -500,7 +500,7 @@ void main(void)
|
|||||||
offset += ${p};
|
offset += ${p};
|
||||||
}
|
}
|
||||||
|
|
||||||
`)}else{const u=yE[o.data.type];r.push(`
|
`)}else{const u=yA[o.data.type];r.push(`
|
||||||
cv = ud.${l}.value;
|
cv = ud.${l}.value;
|
||||||
v = uv.${l};
|
v = uv.${l};
|
||||||
offset = ${o.offset/4};
|
offset = ${o.offset/4};
|
||||||
@@ -508,7 +508,7 @@ void main(void)
|
|||||||
`)}}return r.push(`
|
`)}}return r.push(`
|
||||||
renderer.buffer.update(buffer);
|
renderer.buffer.update(buffer);
|
||||||
`),{size:n,syncFunc:new Function("ud","uv","renderer","syncData","buffer",r.join(`
|
`),{size:n,syncFunc:new Function("ud","uv","renderer","syncData","buffer",r.join(`
|
||||||
`))}}let SE=0;const gc={textureCount:0,uboCount:0};class _b{constructor(e){this.destroyed=!1,this.renderer=e,this.systemCheck(),this.gl=null,this.shader=null,this.program=null,this.cache={},this._uboCache={},this.id=SE++}systemCheck(){if(!zA())throw new Error("Current environment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.")}contextChange(e){this.gl=e,this.reset()}bind(e,t){e.disposeRunner.add(this),e.uniforms.globals=this.renderer.globalUniforms;const i=e.program,n=i.glPrograms[this.renderer.CONTEXT_UID]||this.generateProgram(e);return this.shader=e,this.program!==i&&(this.program=i,this.gl.useProgram(n.program)),t||(gc.textureCount=0,gc.uboCount=0,this.syncUniformGroup(e.uniformGroup,gc)),n}setUniforms(e){const t=this.shader.program,i=t.glPrograms[this.renderer.CONTEXT_UID];t.syncUniforms(i.uniformData,e,this.renderer)}syncUniformGroup(e,t){const i=this.getGlProgram();(!e.static||e.dirtyId!==i.uniformDirtyGroups[e.id])&&(i.uniformDirtyGroups[e.id]=e.dirtyId,this.syncUniforms(e,i,t))}syncUniforms(e,t,i){(e.syncUniforms[this.shader.program.id]||this.createSyncGroups(e))(t.uniformData,e.uniforms,this.renderer,i)}createSyncGroups(e){const t=this.getSignature(e,this.shader.program.uniformData,"u");return this.cache[t]||(this.cache[t]=$A(e,this.shader.program.uniformData)),e.syncUniforms[this.shader.program.id]=this.cache[t],e.syncUniforms[this.shader.program.id]}syncUniformBufferGroup(e,t){const i=this.getGlProgram();if(!e.static||e.dirtyId!==0||!i.uniformGroups[e.id]){e.dirtyId=0;const n=i.uniformGroups[e.id]||this.createSyncBufferGroup(e,i,t);e.buffer.update(),n(i.uniformData,e.uniforms,this.renderer,gc,e.buffer)}this.renderer.buffer.bindBufferBase(e.buffer,i.uniformBufferBindings[t])}createSyncBufferGroup(e,t,i){const{gl:n}=this.renderer;this.renderer.buffer.bind(e.buffer);const r=this.gl.getUniformBlockIndex(t.program,i);t.uniformBufferBindings[i]=this.shader.uniformBindCount,n.uniformBlockBinding(t.program,r,this.shader.uniformBindCount),this.shader.uniformBindCount++;const a=this.getSignature(e,this.shader.program.uniformData,"ubo");let o=this._uboCache[a];if(o||(o=this._uboCache[a]=wE(e,this.shader.program.uniformData)),e.autoManage){const h=new Float32Array(o.size/4);e.buffer.update(h)}return t.uniformGroups[e.id]=o.syncFunc,t.uniformGroups[e.id]}getSignature(e,t,i){const n=e.uniforms,r=[`${i}-`];for(const a in n)r.push(a),t[a]&&r.push(t[a].type);return r.join("-")}getGlProgram(){return this.shader?this.shader.program.glPrograms[this.renderer.CONTEXT_UID]:null}generateProgram(e){const t=this.gl,i=e.program,n=gE(t,i);return i.glPrograms[this.renderer.CONTEXT_UID]=n,n}reset(){this.program=null,this.shader=null}disposeShader(e){this.shader===e&&(this.shader=null)}destroy(){this.renderer=null,this.destroyed=!0}}_b.extension={type:He.RendererSystem,name:"shader"};tt.add(_b);class bh{constructor(e){this.renderer=e}run(e){const{renderer:t}=this;t.runners.init.emit(t.options),e.hello&&console.log(`PixiJS 7.4.3 - ${t.rendererLogId} - https://pixijs.com`),t.resize(t.screen.width,t.screen.height)}destroy(){}}bh.defaultOptions={hello:!1},bh.extension={type:[He.RendererSystem,He.CanvasRendererSystem],name:"startup"};tt.add(bh);function ME(s,e=[]){return e[dt.NORMAL]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.ADD]=[s.ONE,s.ONE],e[dt.MULTIPLY]=[s.DST_COLOR,s.ONE_MINUS_SRC_ALPHA,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SCREEN]=[s.ONE,s.ONE_MINUS_SRC_COLOR,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.OVERLAY]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.DARKEN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.LIGHTEN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR_DODGE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR_BURN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.HARD_LIGHT]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SOFT_LIGHT]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.DIFFERENCE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.EXCLUSION]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.HUE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SATURATION]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.LUMINOSITY]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.NONE]=[0,0],e[dt.NORMAL_NPM]=[s.SRC_ALPHA,s.ONE_MINUS_SRC_ALPHA,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.ADD_NPM]=[s.SRC_ALPHA,s.ONE,s.ONE,s.ONE],e[dt.SCREEN_NPM]=[s.SRC_ALPHA,s.ONE_MINUS_SRC_COLOR,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SRC_IN]=[s.DST_ALPHA,s.ZERO],e[dt.SRC_OUT]=[s.ONE_MINUS_DST_ALPHA,s.ZERO],e[dt.SRC_ATOP]=[s.DST_ALPHA,s.ONE_MINUS_SRC_ALPHA],e[dt.DST_OVER]=[s.ONE_MINUS_DST_ALPHA,s.ONE],e[dt.DST_IN]=[s.ZERO,s.SRC_ALPHA],e[dt.DST_OUT]=[s.ZERO,s.ONE_MINUS_SRC_ALPHA],e[dt.DST_ATOP]=[s.ONE_MINUS_DST_ALPHA,s.SRC_ALPHA],e[dt.XOR]=[s.ONE_MINUS_DST_ALPHA,s.ONE_MINUS_SRC_ALPHA],e[dt.SUBTRACT]=[s.ONE,s.ONE,s.ONE,s.ONE,s.FUNC_REVERSE_SUBTRACT,s.FUNC_ADD],e}const _E=0,TE=1,AE=2,EE=3,CE=4,IE=5,Tb=class im{constructor(){this.gl=null,this.stateId=0,this.polygonOffset=0,this.blendMode=dt.NONE,this._blendEq=!1,this.map=[],this.map[_E]=this.setBlend,this.map[TE]=this.setOffset,this.map[AE]=this.setCullFace,this.map[EE]=this.setDepthTest,this.ma
Line truncated
|
`))}}let SA=0;const gc={textureCount:0,uboCount:0};class _1{constructor(e){this.destroyed=!1,this.renderer=e,this.systemCheck(),this.gl=null,this.shader=null,this.program=null,this.cache={},this._uboCache={},this.id=SA++}systemCheck(){if(!zT())throw new Error("Current environment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.")}contextChange(e){this.gl=e,this.reset()}bind(e,t){e.disposeRunner.add(this),e.uniforms.globals=this.renderer.globalUniforms;const i=e.program,n=i.glPrograms[this.renderer.CONTEXT_UID]||this.generateProgram(e);return this.shader=e,this.program!==i&&(this.program=i,this.gl.useProgram(n.program)),t||(gc.textureCount=0,gc.uboCount=0,this.syncUniformGroup(e.uniformGroup,gc)),n}setUniforms(e){const t=this.shader.program,i=t.glPrograms[this.renderer.CONTEXT_UID];t.syncUniforms(i.uniformData,e,this.renderer)}syncUniformGroup(e,t){const i=this.getGlProgram();(!e.static||e.dirtyId!==i.uniformDirtyGroups[e.id])&&(i.uniformDirtyGroups[e.id]=e.dirtyId,this.syncUniforms(e,i,t))}syncUniforms(e,t,i){(e.syncUniforms[this.shader.program.id]||this.createSyncGroups(e))(t.uniformData,e.uniforms,this.renderer,i)}createSyncGroups(e){const t=this.getSignature(e,this.shader.program.uniformData,"u");return this.cache[t]||(this.cache[t]=$T(e,this.shader.program.uniformData)),e.syncUniforms[this.shader.program.id]=this.cache[t],e.syncUniforms[this.shader.program.id]}syncUniformBufferGroup(e,t){const i=this.getGlProgram();if(!e.static||e.dirtyId!==0||!i.uniformGroups[e.id]){e.dirtyId=0;const n=i.uniformGroups[e.id]||this.createSyncBufferGroup(e,i,t);e.buffer.update(),n(i.uniformData,e.uniforms,this.renderer,gc,e.buffer)}this.renderer.buffer.bindBufferBase(e.buffer,i.uniformBufferBindings[t])}createSyncBufferGroup(e,t,i){const{gl:n}=this.renderer;this.renderer.buffer.bind(e.buffer);const r=this.gl.getUniformBlockIndex(t.program,i);t.uniformBufferBindings[i]=this.shader.uniformBindCount,n.uniformBlockBinding(t.program,r,this.shader.uniformBindCount),this.shader.uniformBindCount++;const a=this.getSignature(e,this.shader.program.uniformData,"ubo");let o=this._uboCache[a];if(o||(o=this._uboCache[a]=wA(e,this.shader.program.uniformData)),e.autoManage){const h=new Float32Array(o.size/4);e.buffer.update(h)}return t.uniformGroups[e.id]=o.syncFunc,t.uniformGroups[e.id]}getSignature(e,t,i){const n=e.uniforms,r=[`${i}-`];for(const a in n)r.push(a),t[a]&&r.push(t[a].type);return r.join("-")}getGlProgram(){return this.shader?this.shader.program.glPrograms[this.renderer.CONTEXT_UID]:null}generateProgram(e){const t=this.gl,i=e.program,n=gA(t,i);return i.glPrograms[this.renderer.CONTEXT_UID]=n,n}reset(){this.program=null,this.shader=null}disposeShader(e){this.shader===e&&(this.shader=null)}destroy(){this.renderer=null,this.destroyed=!0}}_1.extension={type:He.RendererSystem,name:"shader"};tt.add(_1);class bh{constructor(e){this.renderer=e}run(e){const{renderer:t}=this;t.runners.init.emit(t.options),e.hello&&console.log(`PixiJS 7.4.3 - ${t.rendererLogId} - https://pixijs.com`),t.resize(t.screen.width,t.screen.height)}destroy(){}}bh.defaultOptions={hello:!1},bh.extension={type:[He.RendererSystem,He.CanvasRendererSystem],name:"startup"};tt.add(bh);function MA(s,e=[]){return e[dt.NORMAL]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.ADD]=[s.ONE,s.ONE],e[dt.MULTIPLY]=[s.DST_COLOR,s.ONE_MINUS_SRC_ALPHA,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SCREEN]=[s.ONE,s.ONE_MINUS_SRC_COLOR,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.OVERLAY]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.DARKEN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.LIGHTEN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR_DODGE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR_BURN]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.HARD_LIGHT]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SOFT_LIGHT]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.DIFFERENCE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.EXCLUSION]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.HUE]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SATURATION]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.COLOR]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.LUMINOSITY]=[s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.NONE]=[0,0],e[dt.NORMAL_NPM]=[s.SRC_ALPHA,s.ONE_MINUS_SRC_ALPHA,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.ADD_NPM]=[s.SRC_ALPHA,s.ONE,s.ONE,s.ONE],e[dt.SCREEN_NPM]=[s.SRC_ALPHA,s.ONE_MINUS_SRC_COLOR,s.ONE,s.ONE_MINUS_SRC_ALPHA],e[dt.SRC_IN]=[s.DST_ALPHA,s.ZERO],e[dt.SRC_OUT]=[s.ONE_MINUS_DST_ALPHA,s.ZERO],e[dt.SRC_ATOP]=[s.DST_ALPHA,s.ONE_MINUS_SRC_ALPHA],e[dt.DST_OVER]=[s.ONE_MINUS_DST_ALPHA,s.ONE],e[dt.DST_IN]=[s.ZERO,s.SRC_ALPHA],e[dt.DST_OUT]=[s.ZERO,s.ONE_MINUS_SRC_ALPHA],e[dt.DST_ATOP]=[s.ONE_MINUS_DST_ALPHA,s.SRC_ALPHA],e[dt.XOR]=[s.ONE_MINUS_DST_ALPHA,s.ONE_MINUS_SRC_ALPHA],e[dt.SUBTRACT]=[s.ONE,s.ONE,s.ONE,s.ONE,s.FUNC_REVERSE_SUBTRACT,s.FUNC_ADD],e}const _A=0,TA=1,AA=2,EA=3,CA=4,IA=5,T1=class im{constructor(){this.gl=null,this.stateId=0,this.polygonOffset=0,this.blendMode=dt.NONE,this._blendEq=!1,this.map=[],this.map[_A]=this.setBlend,this.map[TA]=this.setOffset,this.map[AA]=this.setCullFace,this.map[EA]=this.setDepthTest,this.ma
Line truncated
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -519,7 +519,7 @@ void main(void)
|
|||||||
{
|
{
|
||||||
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||||||
vTextureCoord = aTextureCoord;
|
vTextureCoord = aTextureCoord;
|
||||||
}`,LE=`attribute vec2 aVertexPosition;
|
}`,LA=`attribute vec2 aVertexPosition;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
|
|
||||||
@@ -545,7 +545,7 @@ void main(void)
|
|||||||
gl_Position = filterVertexPosition();
|
gl_Position = filterVertexPosition();
|
||||||
vTextureCoord = filterTextureCoord();
|
vTextureCoord = filterTextureCoord();
|
||||||
}
|
}
|
||||||
`;const DE=BE,Pb=LE;class kb{constructor(e){this.renderer=e}contextChange(e){let t;if(this.renderer.context.webGLVersion===1){const i=e.getParameter(e.FRAMEBUFFER_BINDING);e.bindFramebuffer(e.FRAMEBUFFER,null),t=e.getParameter(e.SAMPLES),e.bindFramebuffer(e.FRAMEBUFFER,i)}else{const i=e.getParameter(e.DRAW_FRAMEBUFFER_BINDING);e.bindFramebuffer(e.DRAW_FRAMEBUFFER,null),t=e.getParameter(e.SAMPLES),e.bindFramebuffer(e.DRAW_FRAMEBUFFER,i)}t>=Ei.HIGH?this.multisample=Ei.HIGH:t>=Ei.MEDIUM?this.multisample=Ei.MEDIUM:t>=Ei.LOW?this.multisample=Ei.LOW:this.multisample=Ei.NONE}destroy(){}}kb.extension={type:He.RendererSystem,name:"_multisample"};tt.add(kb);class NE{constructor(e){this.buffer=e||null,this.updateID=-1,this.byteLength=-1,this.refCount=0}}class Fb{constructor(e){this.renderer=e,this.managedBuffers={},this.boundBufferBases={}}destroy(){this.renderer=null}contextChange(){this.disposeAll(!0),this.gl=this.renderer.gl,this.CONTEXT_UID=this.renderer.CONTEXT_UID}bind(e){const{gl:t,CONTEXT_UID:i}=this,n=e._glBuffers[i]||this.createGLBuffer(e);t.bindBuffer(e.type,n.buffer)}unbind(e){const{gl:t}=this;t.bindBuffer(e,null)}bindBufferBase(e,t){const{gl:i,CONTEXT_UID:n}=this;if(this.boundBufferBases[t]!==e){const r=e._glBuffers[n]||this.createGLBuffer(e);this.boundBufferBases[t]=e,i.bindBufferBase(i.UNIFORM_BUFFER,t,r.buffer)}}bindBufferRange(e,t,i){const{gl:n,CONTEXT_UID:r}=this;i=i||0;const a=e._glBuffers[r]||this.createGLBuffer(e);n.bindBufferRange(n.UNIFORM_BUFFER,t||0,a.buffer,i*256,256)}update(e){const{gl:t,CONTEXT_UID:i}=this,n=e._glBuffers[i]||this.createGLBuffer(e);if(e._updateID!==n.updateID)if(n.updateID=e._updateID,t.bindBuffer(e.type,n.buffer),n.byteLength>=e.data.byteLength)t.bufferSubData(e.type,0,e.data);else{const r=e.static?t.STATIC_DRAW:t.DYNAMIC_DRAW;n.byteLength=e.data.byteLength,t.bufferData(e.type,e.data,r)}}dispose(e,t){if(!this.managedBuffers[e.id])return;delete this.managedBuffers[e.id];const i=e._glBuffers[this.CONTEXT_UID],n=this.gl;e.disposeRunner.remove(this),i&&(t||n.deleteBuffer(i.buffer),delete e._glBuffers[this.CONTEXT_UID])}disposeAll(e){const t=Object.keys(this.managedBuffers);for(let i=0;i<t.length;i++)this.dispose(this.managedBuffers[t[i]],e)}createGLBuffer(e){const{CONTEXT_UID:t,gl:i}=this;return e._glBuffers[t]=new NE(i.createBuffer()),this.managedBuffers[e.id]=e,e.disposeRunner.add(this),e._glBuffers[t]}}Fb.extension={type:He.RendererSystem,name:"buffer"};tt.add(Fb);class Ob{constructor(e){this.renderer=e}render(e,t){const i=this.renderer;let n,r,a,o;if(t&&(n=t.renderTexture,r=t.clear,a=t.transform,o=t.skipUpdateTransform),this.renderingToScreen=!n,i.runners.prerender.emit(),i.emit("prerender"),i.projection.transform=a,!i.context.isLost){if(n||(this.lastObjectRendered=e),!o){const h=e.enableTempParent();e.updateTransform(),e.disableTempParent(h)}i.renderTexture.bind(n),i.batch.currentRenderer.start(),(r??i.background.clearBeforeRender)&&i.renderTexture.clear(),e.render(i),i.batch.currentRenderer.flush(),n&&(t.blit&&i.framebuffer.blit(),n.baseTexture.update()),i.runners.postrender.emit(),i.projection.transform=null,i.emit("postrender")}}destroy(){this.renderer=null,this.lastObjectRendered=null}}Ob.extension={type:He.RendererSystem,name:"objectRenderer"};tt.add(Ob);const Hc=class sm extends PE{constructor(e){super(),this.type=Ox.WEBGL,e=Object.assign({},We.RENDER_OPTIONS,e),this.gl=null,this.CONTEXT_UID=0,this.globalUniforms=new ln({projectionMatrix:new li},!0);const t={runners:["init","destroy","contextChange","resolutionChange","reset","update","postrender","prerender","resize"],systems:sm.__systems,priority:["_view","textureGenerator","background","_plugin","startup","context","state","texture","buffer","geometry","framebuffer","transformFeedback","mask","scissor","stencil","projection","textureGC","filter","renderTexture","batch","objectRenderer","_multisample"]};this.setup(t),"useContextAlpha"in e&&(kt("7.0.0","options.useContextAlpha is deprecated, use options.premultipliedAlpha and options.backgroundAlpha instead"),e.premultipliedAlpha=e.useContextAlpha&&e.useContextAlpha!=="notMultiplied",e.backgroundAlpha=e.useContextAlpha===!1?1:e.backgroundAlpha),this._plugin.rendererPlugins=sm.__plugins,this.options=e,this.startup.run(this.options)}static test(e){return e!=null&&e.forceCanvas?!1:lA()}render(e,t){this.objectRenderer.render(e,t)}resize(e,t){this._view.resizeView(e,t)}reset(){return this.runners.reset.emit(),this}clear(){this.renderTexture.bind(),this.renderTexture.clear()}destroy(e=!1){this.runners.destroy.items.reverse(),this.emitWithCustomOptions(this.runners.destroy,{_view:e}),super.destroy()}get plugins(){return this._plugin.plugins}get multisample(){return this._multisample.multisample}get width(){return this._view.element.width}get height(){return this._view.element.height}get resolution(){return this._view.resolution}set resolution(e){this._view.resolution=e,this.runners.resolutionChange.emit(e)}get autoDensity(){return this._view.autoDensity}get view(){return
Line truncated
|
`;const DA=BA,P1=LA;class k1{constructor(e){this.renderer=e}contextChange(e){let t;if(this.renderer.context.webGLVersion===1){const i=e.getParameter(e.FRAMEBUFFER_BINDING);e.bindFramebuffer(e.FRAMEBUFFER,null),t=e.getParameter(e.SAMPLES),e.bindFramebuffer(e.FRAMEBUFFER,i)}else{const i=e.getParameter(e.DRAW_FRAMEBUFFER_BINDING);e.bindFramebuffer(e.DRAW_FRAMEBUFFER,null),t=e.getParameter(e.SAMPLES),e.bindFramebuffer(e.DRAW_FRAMEBUFFER,i)}t>=Ei.HIGH?this.multisample=Ei.HIGH:t>=Ei.MEDIUM?this.multisample=Ei.MEDIUM:t>=Ei.LOW?this.multisample=Ei.LOW:this.multisample=Ei.NONE}destroy(){}}k1.extension={type:He.RendererSystem,name:"_multisample"};tt.add(k1);class NA{constructor(e){this.buffer=e||null,this.updateID=-1,this.byteLength=-1,this.refCount=0}}class F1{constructor(e){this.renderer=e,this.managedBuffers={},this.boundBufferBases={}}destroy(){this.renderer=null}contextChange(){this.disposeAll(!0),this.gl=this.renderer.gl,this.CONTEXT_UID=this.renderer.CONTEXT_UID}bind(e){const{gl:t,CONTEXT_UID:i}=this,n=e._glBuffers[i]||this.createGLBuffer(e);t.bindBuffer(e.type,n.buffer)}unbind(e){const{gl:t}=this;t.bindBuffer(e,null)}bindBufferBase(e,t){const{gl:i,CONTEXT_UID:n}=this;if(this.boundBufferBases[t]!==e){const r=e._glBuffers[n]||this.createGLBuffer(e);this.boundBufferBases[t]=e,i.bindBufferBase(i.UNIFORM_BUFFER,t,r.buffer)}}bindBufferRange(e,t,i){const{gl:n,CONTEXT_UID:r}=this;i=i||0;const a=e._glBuffers[r]||this.createGLBuffer(e);n.bindBufferRange(n.UNIFORM_BUFFER,t||0,a.buffer,i*256,256)}update(e){const{gl:t,CONTEXT_UID:i}=this,n=e._glBuffers[i]||this.createGLBuffer(e);if(e._updateID!==n.updateID)if(n.updateID=e._updateID,t.bindBuffer(e.type,n.buffer),n.byteLength>=e.data.byteLength)t.bufferSubData(e.type,0,e.data);else{const r=e.static?t.STATIC_DRAW:t.DYNAMIC_DRAW;n.byteLength=e.data.byteLength,t.bufferData(e.type,e.data,r)}}dispose(e,t){if(!this.managedBuffers[e.id])return;delete this.managedBuffers[e.id];const i=e._glBuffers[this.CONTEXT_UID],n=this.gl;e.disposeRunner.remove(this),i&&(t||n.deleteBuffer(i.buffer),delete e._glBuffers[this.CONTEXT_UID])}disposeAll(e){const t=Object.keys(this.managedBuffers);for(let i=0;i<t.length;i++)this.dispose(this.managedBuffers[t[i]],e)}createGLBuffer(e){const{CONTEXT_UID:t,gl:i}=this;return e._glBuffers[t]=new NA(i.createBuffer()),this.managedBuffers[e.id]=e,e.disposeRunner.add(this),e._glBuffers[t]}}F1.extension={type:He.RendererSystem,name:"buffer"};tt.add(F1);class O1{constructor(e){this.renderer=e}render(e,t){const i=this.renderer;let n,r,a,o;if(t&&(n=t.renderTexture,r=t.clear,a=t.transform,o=t.skipUpdateTransform),this.renderingToScreen=!n,i.runners.prerender.emit(),i.emit("prerender"),i.projection.transform=a,!i.context.isLost){if(n||(this.lastObjectRendered=e),!o){const h=e.enableTempParent();e.updateTransform(),e.disableTempParent(h)}i.renderTexture.bind(n),i.batch.currentRenderer.start(),(r??i.background.clearBeforeRender)&&i.renderTexture.clear(),e.render(i),i.batch.currentRenderer.flush(),n&&(t.blit&&i.framebuffer.blit(),n.baseTexture.update()),i.runners.postrender.emit(),i.projection.transform=null,i.emit("postrender")}}destroy(){this.renderer=null,this.lastObjectRendered=null}}O1.extension={type:He.RendererSystem,name:"objectRenderer"};tt.add(O1);const Hc=class sm extends PA{constructor(e){super(),this.type=Ox.WEBGL,e=Object.assign({},We.RENDER_OPTIONS,e),this.gl=null,this.CONTEXT_UID=0,this.globalUniforms=new ln({projectionMatrix:new li},!0);const t={runners:["init","destroy","contextChange","resolutionChange","reset","update","postrender","prerender","resize"],systems:sm.__systems,priority:["_view","textureGenerator","background","_plugin","startup","context","state","texture","buffer","geometry","framebuffer","transformFeedback","mask","scissor","stencil","projection","textureGC","filter","renderTexture","batch","objectRenderer","_multisample"]};this.setup(t),"useContextAlpha"in e&&(kt("7.0.0","options.useContextAlpha is deprecated, use options.premultipliedAlpha and options.backgroundAlpha instead"),e.premultipliedAlpha=e.useContextAlpha&&e.useContextAlpha!=="notMultiplied",e.backgroundAlpha=e.useContextAlpha===!1?1:e.backgroundAlpha),this._plugin.rendererPlugins=sm.__plugins,this.options=e,this.startup.run(this.options)}static test(e){return e!=null&&e.forceCanvas?!1:lT()}render(e,t){this.objectRenderer.render(e,t)}resize(e,t){this._view.resizeView(e,t)}reset(){return this.runners.reset.emit(),this}clear(){this.renderTexture.bind(),this.renderTexture.clear()}destroy(e=!1){this.runners.destroy.items.reverse(),this.emitWithCustomOptions(this.runners.destroy,{_view:e}),super.destroy()}get plugins(){return this._plugin.plugins}get multisample(){return this._multisample.multisample}get width(){return this._view.element.width}get height(){return this._view.element.height}get resolution(){return this._view.resolution}set resolution(e){this._view.resolution=e,this.runners.resolutionChange.emit(e)}get autoDensity(){return this._view.autoDensity}get view(){return
Line truncated
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
uniform float uAlpha;
|
uniform float uAlpha;
|
||||||
@@ -554,9 +554,9 @@ void main(void)
|
|||||||
{
|
{
|
||||||
gl_FragColor = texture2D(uSampler, vTextureCoord) * uAlpha;
|
gl_FragColor = texture2D(uSampler, vTextureCoord) * uAlpha;
|
||||||
}
|
}
|
||||||
`;class qE extends us{constructor(e=1){super(DE,zE,{uAlpha:1}),this.alpha=e}get alpha(){return this.uniforms.uAlpha}set alpha(e){this.uniforms.uAlpha=e}}const KE={5:[.153388,.221461,.250301],7:[.071303,.131514,.189879,.214607],9:[.028532,.067234,.124009,.179044,.20236],11:[.0093,.028002,.065984,.121703,.175713,.198596],13:[.002406,.009255,.027867,.065666,.121117,.174868,.197641],15:[489e-6,.002403,.009246,.02784,.065602,.120999,.174697,.197448]},ZE=["varying vec2 vBlurTexCoords[%size%];","uniform sampler2D uSampler;","void main(void)","{"," gl_FragColor = vec4(0.0);"," %blur%","}"].join(`
|
`;class qA extends us{constructor(e=1){super(DA,zA,{uAlpha:1}),this.alpha=e}get alpha(){return this.uniforms.uAlpha}set alpha(e){this.uniforms.uAlpha=e}}const KA={5:[.153388,.221461,.250301],7:[.071303,.131514,.189879,.214607],9:[.028532,.067234,.124009,.179044,.20236],11:[.0093,.028002,.065984,.121703,.175713,.198596],13:[.002406,.009255,.027867,.065666,.121117,.174868,.197641],15:[489e-6,.002403,.009246,.02784,.065602,.120999,.174697,.197448]},ZA=["varying vec2 vBlurTexCoords[%size%];","uniform sampler2D uSampler;","void main(void)","{"," gl_FragColor = vec4(0.0);"," %blur%","}"].join(`
|
||||||
`);function JE(s){const e=KE[s],t=e.length;let i=ZE,n="";const r="gl_FragColor += texture2D(uSampler, vBlurTexCoords[%index%]) * %value%;";let a;for(let o=0;o<s;o++){let h=r.replace("%index%",o.toString());a=o,o>=t&&(a=s-o-1),h=h.replace("%value%",e[a].toString()),n+=h,n+=`
|
`);function JA(s){const e=KA[s],t=e.length;let i=ZA,n="";const r="gl_FragColor += texture2D(uSampler, vBlurTexCoords[%index%]) * %value%;";let a;for(let o=0;o<s;o++){let h=r.replace("%index%",o.toString());a=o,o>=t&&(a=s-o-1),h=h.replace("%value%",e[a].toString()),n+=h,n+=`
|
||||||
`}return i=i.replace("%blur%",n),i=i.replace("%size%",s.toString()),i}const QE=`
|
`}return i=i.replace("%blur%",n),i=i.replace("%size%",s.toString()),i}const QA=`
|
||||||
attribute vec2 aVertexPosition;
|
attribute vec2 aVertexPosition;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -586,8 +586,8 @@ void main(void)
|
|||||||
|
|
||||||
vec2 textureCoord = filterTextureCoord();
|
vec2 textureCoord = filterTextureCoord();
|
||||||
%blur%
|
%blur%
|
||||||
}`;function eC(s,e){const t=Math.ceil(s/2);let i=QE,n="",r;e?r="vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * strength, 0.0);":r="vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * strength);";for(let a=0;a<s;a++){let o=r.replace("%index%",a.toString());o=o.replace("%sampleIndex%",`${a-(t-1)}.0`),n+=o,n+=`
|
}`;function eE(s,e){const t=Math.ceil(s/2);let i=QA,n="",r;e?r="vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * strength, 0.0);":r="vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * strength);";for(let a=0;a<s;a++){let o=r.replace("%index%",a.toString());o=o.replace("%sampleIndex%",`${a-(t-1)}.0`),n+=o,n+=`
|
||||||
`}return i=i.replace("%blur%",n),i=i.replace("%size%",s.toString()),i}class cm extends us{constructor(e,t=8,i=4,n=us.defaultResolution,r=5){const a=eC(r,e),o=JE(r);super(a,o),this.horizontal=e,this.resolution=n,this._quality=0,this.quality=i,this.blur=t}apply(e,t,i,n){if(i?this.horizontal?this.uniforms.strength=1/i.width*(i.width/t.width):this.uniforms.strength=1/i.height*(i.height/t.height):this.horizontal?this.uniforms.strength=1/e.renderer.width*(e.renderer.width/t.width):this.uniforms.strength=1/e.renderer.height*(e.renderer.height/t.height),this.uniforms.strength*=this.strength,this.uniforms.strength/=this.passes,this.passes===1)e.applyFilter(this,t,i,n);else{const r=e.getFilterTexture(),a=e.renderer;let o=t,h=r;this.state.blend=!1,e.applyFilter(this,o,h,Cn.CLEAR);for(let l=1;l<this.passes-1;l++){e.bindAndClear(o,Cn.BLIT),this.uniforms.uSampler=h;const c=h;h=o,o=c,a.shader.bind(this),a.geometry.draw(5)}this.state.blend=!0,e.applyFilter(this,h,i,n),e.returnFilterTexture(r)}}get blur(){return this.strength}set blur(e){this.padding=1+Math.abs(e)*2,this.strength=e}get quality(){return this._quality}set quality(e){this._quality=e,this.passes=e}}class tC extends us{constructor(e=8,t=4,i=us.defaultResolution,n=5){super(),this._repeatEdgePixels=!1,this.blurXFilter=new cm(!0,e,t,i,n),this.blurYFilter=new cm(!1,e,t,i,n),this.resolution=i,this.quality=t,this.blur=e,this.repeatEdgePixels=!1}apply(e,t,i,n){const r=Math.abs(this.blurXFilter.strength),a=Math.abs(this.blurYFilter.strength);if(r&&a){const o=e.getFilterTexture();this.blurXFilter.apply(e,t,o,Cn.CLEAR),this.blurYFilter.apply(e,o,i,n),e.returnFilterTexture(o)}else a?this.blurYFilter.apply(e,t,i,n):this.blurXFilter.apply(e,t,i,n)}updatePadding(){this._repeatEdgePixels?this.padding=0:this.padding=Math.max(Math.abs(this.blurXFilter.strength),Math.abs(this.blurYFilter.strength))*2}get blur(){return this.blurXFilter.blur}set blur(e){this.blurXFilter.blur=this.blurYFilter.blur=e,this.updatePadding()}get quality(){return this.blurXFilter.quality}set quality(e){this.blurXFilter.quality=this.blurYFilter.quality=e}get blurX(){return this.blurXFilter.blur}set blurX(e){this.blurXFilter.blur=e,this.updatePadding()}get blurY(){return this.blurYFilter.blur}set blurY(e){this.blurYFilter.blur=e,this.updatePadding()}get blendMode(){return this.blurYFilter.blendMode}set blendMode(e){this.blurYFilter.blendMode=e}get repeatEdgePixels(){return this._repeatEdgePixels}set repeatEdgePixels(e){this._repeatEdgePixels=e,this.updatePadding()}}var iC=`varying vec2 vTextureCoord;
|
`}return i=i.replace("%blur%",n),i=i.replace("%size%",s.toString()),i}class cm extends us{constructor(e,t=8,i=4,n=us.defaultResolution,r=5){const a=eE(r,e),o=JA(r);super(a,o),this.horizontal=e,this.resolution=n,this._quality=0,this.quality=i,this.blur=t}apply(e,t,i,n){if(i?this.horizontal?this.uniforms.strength=1/i.width*(i.width/t.width):this.uniforms.strength=1/i.height*(i.height/t.height):this.horizontal?this.uniforms.strength=1/e.renderer.width*(e.renderer.width/t.width):this.uniforms.strength=1/e.renderer.height*(e.renderer.height/t.height),this.uniforms.strength*=this.strength,this.uniforms.strength/=this.passes,this.passes===1)e.applyFilter(this,t,i,n);else{const r=e.getFilterTexture(),a=e.renderer;let o=t,h=r;this.state.blend=!1,e.applyFilter(this,o,h,Cn.CLEAR);for(let l=1;l<this.passes-1;l++){e.bindAndClear(o,Cn.BLIT),this.uniforms.uSampler=h;const c=h;h=o,o=c,a.shader.bind(this),a.geometry.draw(5)}this.state.blend=!0,e.applyFilter(this,h,i,n),e.returnFilterTexture(r)}}get blur(){return this.strength}set blur(e){this.padding=1+Math.abs(e)*2,this.strength=e}get quality(){return this._quality}set quality(e){this._quality=e,this.passes=e}}class tE extends us{constructor(e=8,t=4,i=us.defaultResolution,n=5){super(),this._repeatEdgePixels=!1,this.blurXFilter=new cm(!0,e,t,i,n),this.blurYFilter=new cm(!1,e,t,i,n),this.resolution=i,this.quality=t,this.blur=e,this.repeatEdgePixels=!1}apply(e,t,i,n){const r=Math.abs(this.blurXFilter.strength),a=Math.abs(this.blurYFilter.strength);if(r&&a){const o=e.getFilterTexture();this.blurXFilter.apply(e,t,o,Cn.CLEAR),this.blurYFilter.apply(e,o,i,n),e.returnFilterTexture(o)}else a?this.blurYFilter.apply(e,t,i,n):this.blurXFilter.apply(e,t,i,n)}updatePadding(){this._repeatEdgePixels?this.padding=0:this.padding=Math.max(Math.abs(this.blurXFilter.strength),Math.abs(this.blurYFilter.strength))*2}get blur(){return this.blurXFilter.blur}set blur(e){this.blurXFilter.blur=this.blurYFilter.blur=e,this.updatePadding()}get quality(){return this.blurXFilter.quality}set quality(e){this.blurXFilter.quality=this.blurYFilter.quality=e}get blurX(){return this.blurXFilter.blur}set blurX(e){this.blurXFilter.blur=e,this.updatePadding()}get blurY(){return this.blurYFilter.blur}set blurY(e){this.blurYFilter.blur=e,this.updatePadding()}get blendMode(){return this.blurYFilter.blendMode}set blendMode(e){this.blurYFilter.blendMode=e}get repeatEdgePixels(){return this._repeatEdgePixels}set repeatEdgePixels(e){this._repeatEdgePixels=e,this.updatePadding()}}var iE=`varying vec2 vTextureCoord;
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
uniform float m[20];
|
uniform float m[20];
|
||||||
uniform float uAlpha;
|
uniform float uAlpha;
|
||||||
@@ -639,7 +639,7 @@ void main(void)
|
|||||||
|
|
||||||
gl_FragColor = vec4(rgb, result.a);
|
gl_FragColor = vec4(rgb, result.a);
|
||||||
}
|
}
|
||||||
`;class hm extends us{constructor(){const e={m:new Float32Array([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]),uAlpha:1};super(Pb,iC,e),this.alpha=1}_loadMatrix(e,t=!1){let i=e;t&&(this._multiply(i,this.uniforms.m,e),i=this._colorMatrix(i)),this.uniforms.m=i}_multiply(e,t,i){return e[0]=t[0]*i[0]+t[1]*i[5]+t[2]*i[10]+t[3]*i[15],e[1]=t[0]*i[1]+t[1]*i[6]+t[2]*i[11]+t[3]*i[16],e[2]=t[0]*i[2]+t[1]*i[7]+t[2]*i[12]+t[3]*i[17],e[3]=t[0]*i[3]+t[1]*i[8]+t[2]*i[13]+t[3]*i[18],e[4]=t[0]*i[4]+t[1]*i[9]+t[2]*i[14]+t[3]*i[19]+t[4],e[5]=t[5]*i[0]+t[6]*i[5]+t[7]*i[10]+t[8]*i[15],e[6]=t[5]*i[1]+t[6]*i[6]+t[7]*i[11]+t[8]*i[16],e[7]=t[5]*i[2]+t[6]*i[7]+t[7]*i[12]+t[8]*i[17],e[8]=t[5]*i[3]+t[6]*i[8]+t[7]*i[13]+t[8]*i[18],e[9]=t[5]*i[4]+t[6]*i[9]+t[7]*i[14]+t[8]*i[19]+t[9],e[10]=t[10]*i[0]+t[11]*i[5]+t[12]*i[10]+t[13]*i[15],e[11]=t[10]*i[1]+t[11]*i[6]+t[12]*i[11]+t[13]*i[16],e[12]=t[10]*i[2]+t[11]*i[7]+t[12]*i[12]+t[13]*i[17],e[13]=t[10]*i[3]+t[11]*i[8]+t[12]*i[13]+t[13]*i[18],e[14]=t[10]*i[4]+t[11]*i[9]+t[12]*i[14]+t[13]*i[19]+t[14],e[15]=t[15]*i[0]+t[16]*i[5]+t[17]*i[10]+t[18]*i[15],e[16]=t[15]*i[1]+t[16]*i[6]+t[17]*i[11]+t[18]*i[16],e[17]=t[15]*i[2]+t[16]*i[7]+t[17]*i[12]+t[18]*i[17],e[18]=t[15]*i[3]+t[16]*i[8]+t[17]*i[13]+t[18]*i[18],e[19]=t[15]*i[4]+t[16]*i[9]+t[17]*i[14]+t[18]*i[19]+t[19],e}_colorMatrix(e){const t=new Float32Array(e);return t[4]/=255,t[9]/=255,t[14]/=255,t[19]/=255,t}brightness(e,t){const i=[e,0,0,0,0,0,e,0,0,0,0,0,e,0,0,0,0,0,1,0];this._loadMatrix(i,t)}tint(e,t){const[i,n,r]=jt.shared.setValue(e).toArray(),a=[i,0,0,0,0,0,n,0,0,0,0,0,r,0,0,0,0,0,1,0];this._loadMatrix(a,t)}greyscale(e,t){const i=[e,e,e,0,0,e,e,e,0,0,e,e,e,0,0,0,0,0,1,0];this._loadMatrix(i,t)}blackAndWhite(e){const t=[.3,.6,.1,0,0,.3,.6,.1,0,0,.3,.6,.1,0,0,0,0,0,1,0];this._loadMatrix(t,e)}hue(e,t){e=(e||0)/180*Math.PI;const i=Math.cos(e),n=Math.sin(e),r=Math.sqrt,a=1/3,o=r(a),h=i+(1-i)*a,l=a*(1-i)-o*n,c=a*(1-i)+o*n,u=a*(1-i)+o*n,d=i+a*(1-i),f=a*(1-i)-o*n,p=a*(1-i)-o*n,m=a*(1-i)+o*n,g=i+a*(1-i),v=[h,l,c,0,0,u,d,f,0,0,p,m,g,0,0,0,0,0,1,0];this._loadMatrix(v,t)}contrast(e,t){const i=(e||0)+1,n=-.5*(i-1),r=[i,0,0,0,n,0,i,0,0,n,0,0,i,0,n,0,0,0,1,0];this._loadMatrix(r,t)}saturate(e=0,t){const i=e*2/3+1,n=(i-1)*-.5,r=[i,n,n,0,0,n,i,n,0,0,n,n,i,0,0,0,0,0,1,0];this._loadMatrix(r,t)}desaturate(){this.saturate(-1)}negative(e){const t=[-1,0,0,1,0,0,-1,0,1,0,0,0,-1,1,0,0,0,0,1,0];this._loadMatrix(t,e)}sepia(e){const t=[.393,.7689999,.18899999,0,0,.349,.6859999,.16799999,0,0,.272,.5339999,.13099999,0,0,0,0,0,1,0];this._loadMatrix(t,e)}technicolor(e){const t=[1.9125277891456083,-.8545344976951645,-.09155508482755585,0,11.793603434377337,-.3087833385928097,1.7658908555458428,-.10601743074722245,0,-70.35205161461398,-.231103377548616,-.7501899197440212,1.847597816108189,0,30.950940869491138,0,0,0,1,0];this._loadMatrix(t,e)}polaroid(e){const t=[1.438,-.062,-.062,0,0,-.122,1.378,-.122,0,0,-.016,-.016,1.483,0,0,0,0,0,1,0];this._loadMatrix(t,e)}toBGR(e){const t=[0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0];this._loadMatrix(t,e)}kodachrome(e){const t=[1.1285582396593525,-.3967382283601348,-.03992559172921793,0,63.72958762196502,-.16404339962244616,1.0835251566291304,-.05498805115633132,0,24.732407896706203,-.16786010706155763,-.5603416277695248,1.6014850761964943,0,35.62982807460946,0,0,0,1,0];this._loadMatrix(t,e)}browni(e){const t=[.5997023498159715,.34553243048391263,-.2708298674538042,0,47.43192855600873,-.037703249837783157,.8609577587992641,.15059552388459913,0,-36.96841498319127,.24113635128153335,-.07441037908422492,.44972182064877153,0,-7.562075277591283,0,0,0,1,0];this._loadMatrix(t,e)}vintage(e){const t=[.6279345635605994,.3202183420819367,-.03965408211312453,0,9.651285835294123,.02578397704808868,.6441188644374771,.03259127616149294,0,7.462829176470591,.0466055556782719,-.0851232987247891,.5241648018700465,0,5.159190588235296,0,0,0,1,0];this._loadMatrix(t,e)}colorTone(e,t,i,n,r){e=e||.2,t=t||.15,i=i||16770432,n=n||3375104;const a=jt.shared,[o,h,l]=a.setValue(i).toArray(),[c,u,d]=a.setValue(n).toArray(),f=[.3,.59,.11,0,0,o,h,l,e,0,c,u,d,t,0,o-c,h-u,l-d,0,0];this._loadMatrix(f,r)}night(e,t){e=e||.1;const i=[e*-2,-e,0,0,0,-e,0,e,0,0,0,e,e*2,0,0,0,0,0,1,0];this._loadMatrix(i,t)}predator(e,t){const i=[11.224130630493164*e,-4.794486999511719*e,-2.8746118545532227*e,0*e,.40342438220977783*e,-3.6330697536468506*e,9.193157196044922*e,-2.951810836791992*e,0*e,-1.316135048866272*e,-3.2184197902679443*e,-4.2375030517578125*e,7.476448059082031*e,0*e,.8044459223747253*e,0,0,0,1,0];this._loadMatrix(i,t)}lsd(e){const t=[2,-.4,.5,0,0,-.5,2,-.4,0,0,-.4,-.5,3,0,0,0,0,0,1,0];this._loadMatrix(t,e)}reset(){const e=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];this._loadMatrix(e,!1)}get matrix(){return this.uniforms.m}set matrix(e){this.uniforms.m=e}get alpha(){return this.uniforms.uAlpha}set alpha(e){this.uniforms.uAlpha=e}}hm.prototype.grayscale=hm.prototype.greyscale;var sC=`varying vec2 vFilterCoord;
|
`;class hm extends us{constructor(){const e={m:new Float32Array([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]),uAlpha:1};super(P1,iE,e),this.alpha=1}_loadMatrix(e,t=!1){let i=e;t&&(this._multiply(i,this.uniforms.m,e),i=this._colorMatrix(i)),this.uniforms.m=i}_multiply(e,t,i){return e[0]=t[0]*i[0]+t[1]*i[5]+t[2]*i[10]+t[3]*i[15],e[1]=t[0]*i[1]+t[1]*i[6]+t[2]*i[11]+t[3]*i[16],e[2]=t[0]*i[2]+t[1]*i[7]+t[2]*i[12]+t[3]*i[17],e[3]=t[0]*i[3]+t[1]*i[8]+t[2]*i[13]+t[3]*i[18],e[4]=t[0]*i[4]+t[1]*i[9]+t[2]*i[14]+t[3]*i[19]+t[4],e[5]=t[5]*i[0]+t[6]*i[5]+t[7]*i[10]+t[8]*i[15],e[6]=t[5]*i[1]+t[6]*i[6]+t[7]*i[11]+t[8]*i[16],e[7]=t[5]*i[2]+t[6]*i[7]+t[7]*i[12]+t[8]*i[17],e[8]=t[5]*i[3]+t[6]*i[8]+t[7]*i[13]+t[8]*i[18],e[9]=t[5]*i[4]+t[6]*i[9]+t[7]*i[14]+t[8]*i[19]+t[9],e[10]=t[10]*i[0]+t[11]*i[5]+t[12]*i[10]+t[13]*i[15],e[11]=t[10]*i[1]+t[11]*i[6]+t[12]*i[11]+t[13]*i[16],e[12]=t[10]*i[2]+t[11]*i[7]+t[12]*i[12]+t[13]*i[17],e[13]=t[10]*i[3]+t[11]*i[8]+t[12]*i[13]+t[13]*i[18],e[14]=t[10]*i[4]+t[11]*i[9]+t[12]*i[14]+t[13]*i[19]+t[14],e[15]=t[15]*i[0]+t[16]*i[5]+t[17]*i[10]+t[18]*i[15],e[16]=t[15]*i[1]+t[16]*i[6]+t[17]*i[11]+t[18]*i[16],e[17]=t[15]*i[2]+t[16]*i[7]+t[17]*i[12]+t[18]*i[17],e[18]=t[15]*i[3]+t[16]*i[8]+t[17]*i[13]+t[18]*i[18],e[19]=t[15]*i[4]+t[16]*i[9]+t[17]*i[14]+t[18]*i[19]+t[19],e}_colorMatrix(e){const t=new Float32Array(e);return t[4]/=255,t[9]/=255,t[14]/=255,t[19]/=255,t}brightness(e,t){const i=[e,0,0,0,0,0,e,0,0,0,0,0,e,0,0,0,0,0,1,0];this._loadMatrix(i,t)}tint(e,t){const[i,n,r]=jt.shared.setValue(e).toArray(),a=[i,0,0,0,0,0,n,0,0,0,0,0,r,0,0,0,0,0,1,0];this._loadMatrix(a,t)}greyscale(e,t){const i=[e,e,e,0,0,e,e,e,0,0,e,e,e,0,0,0,0,0,1,0];this._loadMatrix(i,t)}blackAndWhite(e){const t=[.3,.6,.1,0,0,.3,.6,.1,0,0,.3,.6,.1,0,0,0,0,0,1,0];this._loadMatrix(t,e)}hue(e,t){e=(e||0)/180*Math.PI;const i=Math.cos(e),n=Math.sin(e),r=Math.sqrt,a=1/3,o=r(a),h=i+(1-i)*a,l=a*(1-i)-o*n,c=a*(1-i)+o*n,u=a*(1-i)+o*n,d=i+a*(1-i),f=a*(1-i)-o*n,p=a*(1-i)-o*n,m=a*(1-i)+o*n,g=i+a*(1-i),v=[h,l,c,0,0,u,d,f,0,0,p,m,g,0,0,0,0,0,1,0];this._loadMatrix(v,t)}contrast(e,t){const i=(e||0)+1,n=-.5*(i-1),r=[i,0,0,0,n,0,i,0,0,n,0,0,i,0,n,0,0,0,1,0];this._loadMatrix(r,t)}saturate(e=0,t){const i=e*2/3+1,n=(i-1)*-.5,r=[i,n,n,0,0,n,i,n,0,0,n,n,i,0,0,0,0,0,1,0];this._loadMatrix(r,t)}desaturate(){this.saturate(-1)}negative(e){const t=[-1,0,0,1,0,0,-1,0,1,0,0,0,-1,1,0,0,0,0,1,0];this._loadMatrix(t,e)}sepia(e){const t=[.393,.7689999,.18899999,0,0,.349,.6859999,.16799999,0,0,.272,.5339999,.13099999,0,0,0,0,0,1,0];this._loadMatrix(t,e)}technicolor(e){const t=[1.9125277891456083,-.8545344976951645,-.09155508482755585,0,11.793603434377337,-.3087833385928097,1.7658908555458428,-.10601743074722245,0,-70.35205161461398,-.231103377548616,-.7501899197440212,1.847597816108189,0,30.950940869491138,0,0,0,1,0];this._loadMatrix(t,e)}polaroid(e){const t=[1.438,-.062,-.062,0,0,-.122,1.378,-.122,0,0,-.016,-.016,1.483,0,0,0,0,0,1,0];this._loadMatrix(t,e)}toBGR(e){const t=[0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0];this._loadMatrix(t,e)}kodachrome(e){const t=[1.1285582396593525,-.3967382283601348,-.03992559172921793,0,63.72958762196502,-.16404339962244616,1.0835251566291304,-.05498805115633132,0,24.732407896706203,-.16786010706155763,-.5603416277695248,1.6014850761964943,0,35.62982807460946,0,0,0,1,0];this._loadMatrix(t,e)}browni(e){const t=[.5997023498159715,.34553243048391263,-.2708298674538042,0,47.43192855600873,-.037703249837783157,.8609577587992641,.15059552388459913,0,-36.96841498319127,.24113635128153335,-.07441037908422492,.44972182064877153,0,-7.562075277591283,0,0,0,1,0];this._loadMatrix(t,e)}vintage(e){const t=[.6279345635605994,.3202183420819367,-.03965408211312453,0,9.651285835294123,.02578397704808868,.6441188644374771,.03259127616149294,0,7.462829176470591,.0466055556782719,-.0851232987247891,.5241648018700465,0,5.159190588235296,0,0,0,1,0];this._loadMatrix(t,e)}colorTone(e,t,i,n,r){e=e||.2,t=t||.15,i=i||16770432,n=n||3375104;const a=jt.shared,[o,h,l]=a.setValue(i).toArray(),[c,u,d]=a.setValue(n).toArray(),f=[.3,.59,.11,0,0,o,h,l,e,0,c,u,d,t,0,o-c,h-u,l-d,0,0];this._loadMatrix(f,r)}night(e,t){e=e||.1;const i=[e*-2,-e,0,0,0,-e,0,e,0,0,0,e,e*2,0,0,0,0,0,1,0];this._loadMatrix(i,t)}predator(e,t){const i=[11.224130630493164*e,-4.794486999511719*e,-2.8746118545532227*e,0*e,.40342438220977783*e,-3.6330697536468506*e,9.193157196044922*e,-2.951810836791992*e,0*e,-1.316135048866272*e,-3.2184197902679443*e,-4.2375030517578125*e,7.476448059082031*e,0*e,.8044459223747253*e,0,0,0,1,0];this._loadMatrix(i,t)}lsd(e){const t=[2,-.4,.5,0,0,-.5,2,-.4,0,0,-.4,-.5,3,0,0,0,0,0,1,0];this._loadMatrix(t,e)}reset(){const e=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];this._loadMatrix(e,!1)}get matrix(){return this.uniforms.m}set matrix(e){this.uniforms.m=e}get alpha(){return this.uniforms.uAlpha}set alpha(e){this.uniforms.uAlpha=e}}hm.prototype.grayscale=hm.prototype.greyscale;var sE=`varying vec2 vFilterCoord;
|
||||||
varying vec2 vTextureCoord;
|
varying vec2 vTextureCoord;
|
||||||
|
|
||||||
uniform vec2 scale;
|
uniform vec2 scale;
|
||||||
@@ -659,7 +659,7 @@ void main(void)
|
|||||||
|
|
||||||
gl_FragColor = texture2D(uSampler, clamp(vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y), inputClamp.xy, inputClamp.zw));
|
gl_FragColor = texture2D(uSampler, clamp(vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y), inputClamp.xy, inputClamp.zw));
|
||||||
}
|
}
|
||||||
`,nC=`attribute vec2 aVertexPosition;
|
`,nE=`attribute vec2 aVertexPosition;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
uniform mat3 filterMatrix;
|
uniform mat3 filterMatrix;
|
||||||
@@ -688,7 +688,7 @@ void main(void)
|
|||||||
vTextureCoord = filterTextureCoord();
|
vTextureCoord = filterTextureCoord();
|
||||||
vFilterCoord = ( filterMatrix * vec3( vTextureCoord, 1.0) ).xy;
|
vFilterCoord = ( filterMatrix * vec3( vTextureCoord, 1.0) ).xy;
|
||||||
}
|
}
|
||||||
`;class rC extends us{constructor(e,t){const i=new li;e.renderable=!1,super(nC,sC,{mapSampler:e._texture,filterMatrix:i,scale:{x:1,y:1},rotation:new Float32Array([1,0,0,1])}),this.maskSprite=e,this.maskMatrix=i,t==null&&(t=20),this.scale=new Ut(t,t)}apply(e,t,i,n){this.uniforms.filterMatrix=e.calculateSpriteMatrix(this.maskMatrix,this.maskSprite),this.uniforms.scale.x=this.scale.x,this.uniforms.scale.y=this.scale.y;const r=this.maskSprite.worldTransform,a=Math.sqrt(r.a*r.a+r.b*r.b),o=Math.sqrt(r.c*r.c+r.d*r.d);a!==0&&o!==0&&(this.uniforms.rotation[0]=r.a/a,this.uniforms.rotation[1]=r.b/a,this.uniforms.rotation[2]=r.c/o,this.uniforms.rotation[3]=r.d/o),e.applyFilter(this,t,i,n)}get map(){return this.uniforms.mapSampler}set map(e){this.uniforms.mapSampler=e}}var aC=`varying vec2 v_rgbNW;
|
`;class rE extends us{constructor(e,t){const i=new li;e.renderable=!1,super(nE,sE,{mapSampler:e._texture,filterMatrix:i,scale:{x:1,y:1},rotation:new Float32Array([1,0,0,1])}),this.maskSprite=e,this.maskMatrix=i,t==null&&(t=20),this.scale=new Ut(t,t)}apply(e,t,i,n){this.uniforms.filterMatrix=e.calculateSpriteMatrix(this.maskMatrix,this.maskSprite),this.uniforms.scale.x=this.scale.x,this.uniforms.scale.y=this.scale.y;const r=this.maskSprite.worldTransform,a=Math.sqrt(r.a*r.a+r.b*r.b),o=Math.sqrt(r.c*r.c+r.d*r.d);a!==0&&o!==0&&(this.uniforms.rotation[0]=r.a/a,this.uniforms.rotation[1]=r.b/a,this.uniforms.rotation[2]=r.c/o,this.uniforms.rotation[3]=r.d/o),e.applyFilter(this,t,i,n)}get map(){return this.uniforms.mapSampler}set map(e){this.uniforms.mapSampler=e}}var aE=`varying vec2 v_rgbNW;
|
||||||
varying vec2 v_rgbNE;
|
varying vec2 v_rgbNE;
|
||||||
varying vec2 v_rgbSW;
|
varying vec2 v_rgbSW;
|
||||||
varying vec2 v_rgbSE;
|
varying vec2 v_rgbSE;
|
||||||
@@ -809,7 +809,7 @@ void main() {
|
|||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
`,oC=`
|
`,oE=`
|
||||||
attribute vec2 aVertexPosition;
|
attribute vec2 aVertexPosition;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -851,7 +851,7 @@ void main(void) {
|
|||||||
|
|
||||||
texcoords(vFragCoord, inputSize.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);
|
texcoords(vFragCoord, inputSize.zw, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);
|
||||||
}
|
}
|
||||||
`;class lC extends us{constructor(){super(oC,aC)}}var cC=`precision highp float;
|
`;class lE extends us{constructor(){super(oE,aE)}}var cE=`precision highp float;
|
||||||
|
|
||||||
varying vec2 vTextureCoord;
|
varying vec2 vTextureCoord;
|
||||||
varying vec4 vColor;
|
varying vec4 vColor;
|
||||||
@@ -885,8 +885,8 @@ void main()
|
|||||||
|
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
}
|
}
|
||||||
`;class hC extends us{constructor(e=.5,t=Math.random()){super(Pb,cC,{uNoise:0,uSeed:0}),this.noise=e,this.seed=t}get noise(){return this.uniforms.uNoise}set noise(e){this.uniforms.uNoise=e}get seed(){return this.uniforms.uSeed}set seed(e){this.uniforms.uSeed=e}}const av={AlphaFilter:qE,BlurFilter:tC,BlurFilterPass:cm,ColorMatrixFilter:hm,DisplacementFilter:rC,FXAAFilter:lC,NoiseFilter:hC};Object.entries(av).forEach(([s,e])=>{Object.defineProperty(av,s,{get(){return kt("7.1.0",`filters.${s} has moved to ${s}`),e}})});class uC{constructor(){this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this.tickerAdded=!1,this._pauseUpdate=!0}init(e){this.removeTickerListener(),this.events=e,this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this.tickerAdded=!1,this._pauseUpdate=!0}get pauseUpdate(){return this._pauseUpdate}set pauseUpdate(e){this._pauseUpdate=e}addTickerListener(){this.tickerAdded||!this.domElement||(ds.system.add(this.tickerUpdate,this,Oa.INTERACTION),this.tickerAdded=!0)}removeTickerListener(){this.tickerAdded&&(ds.system.remove(this.tickerUpdate,this),this.tickerAdded=!1)}pointerMoved(){this._didMove=!0}update(){if(!this.domElement||this._pauseUpdate)return;if(this._didMove){this._didMove=!1;return}const e=this.events.rootPointerEvent;this.events.supportsTouchEvents&&e.pointerType==="touch"||globalThis.document.dispatchEvent(new PointerEvent("pointermove",{clientX:e.clientX,clientY:e.clientY}))}tickerUpdate(e){this._deltaTime+=e,!(this._deltaTime<this.interactionFrequency)&&(this._deltaTime=0,this.update())}}const Or=new uC;class Wl{constructor(e){this.bubbles=!0,this.cancelBubble=!0,this.cancelable=!1,this.composed=!1,this.defaultPrevented=!1,this.eventPhase=Wl.prototype.NONE,this.propagationStopped=!1,this.propagationImmediatelyStopped=!1,this.layer=new Ut,this.page=new Ut,this.NONE=0,this.CAPTURING_PHASE=1,this.AT_TARGET=2,this.BUBBLING_PHASE=3,this.manager=e}get layerX(){return this.layer.x}get layerY(){return this.layer.y}get pageX(){return this.page.x}get pageY(){return this.page.y}get data(){return this}composedPath(){return this.manager&&(!this.path||this.path[this.path.length-1]!==this.target)&&(this.path=this.target?this.manager.propagationPath(this.target):[]),this.path}initEvent(e,t,i){throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}initUIEvent(e,t,i,n,r){throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}preventDefault(){this.nativeEvent instanceof Event&&this.nativeEvent.cancelable&&this.nativeEvent.preventDefault(),this.defaultPrevented=!0}stopImmediatePropagation(){this.propagationImmediatelyStopped=!0}stopPropagation(){this.propagationStopped=!0}}class Sh extends Wl{constructor(){super(...arguments),this.client=new Ut,this.movement=new Ut,this.offset=new Ut,this.global=new Ut,this.screen=new Ut}get clientX(){return this.client.x}get clientY(){return this.client.y}get x(){return this.clientX}get y(){return this.clientY}get movementX(){return this.movement.x}get movementY(){return this.movement.y}get offsetX(){return this.offset.x}get offsetY(){return this.offset.y}get globalX(){return this.global.x}get globalY(){return this.global.y}get screenX(){return this.screen.x}get screenY(){return this.screen.y}getLocalPosition(e,t,i){return e.worldTransform.applyInverse(i||this.global,t)}getModifierState(e){return"getModifierState"in this.nativeEvent&&this.nativeEvent.getModifierState(e)}initMouseEvent(e,t,i,n,r,a,o,h,l,c,u,d,f,p,m){throw new Error("Method not implemented.")}}class Js extends Sh{constructor(){super(...arguments),this.width=0,this.height=0,this.isPrimary=!1}getCoalescedEvents(){return this.type==="pointermove"||this.type==="mousemove"||this.type==="touchmove"?[this]:[]}getPredictedEvents(){throw new Error("getPredictedEvents is not supported!")}}class bo extends Sh{constructor(){super(...arguments),this.DOM_DELTA_PIXEL=0,this.DOM_DELTA_LINE=1,this.DOM_DELTA_PAGE=2}}bo.DOM_DELTA_PIXEL=0,bo.DOM_DELTA_LINE=1,bo.DOM_DELTA_PAGE=2;const dC=2048,fC=new Ut,nf=new Ut;class mC{constructor(e){this.dispatch=new jl,this.moveOnAll=!1,this.enableGlobalMoveEvents=!0,this.mappingState={trackingData:{}},this.eventPool=new Map,this._allInteractiveElements=[],this._hitElements=[],this._isPointerMoveEvent=!1,this.rootTarget=e,this.hitPruneFn=this.hitPruneFn.bind(this),this.hitTestFn=this.hitTestFn.bind(this),this.mapPointerDown=this.mapPointerDown.bind(this),this.mapPointerMove=this.mapPointerMove.bind(this),this.mapPointerOut=this.mapPointerOut.bind(this),this.mapPointerOver=this.mapPointerOver.bind(this),this.mapPointerUp=this.mapPointerUp.bind(this),this.mapPointerUpOutside=this.mapPointerUpOutside.bind(this),this.mapWheel=this.mapWheel.bind(this),this.mappingTable={},this.addEventMapping("pointerdown",this.mapPointerDown),this.addEventMapping("pointermove",this.mapPointerMove),this.addEventMapping("pointerout",this.mapPointerOut),this.addEventMapping("pointerle
Line truncated
|
`;class hE extends us{constructor(e=.5,t=Math.random()){super(P1,cE,{uNoise:0,uSeed:0}),this.noise=e,this.seed=t}get noise(){return this.uniforms.uNoise}set noise(e){this.uniforms.uNoise=e}get seed(){return this.uniforms.uSeed}set seed(e){this.uniforms.uSeed=e}}const av={AlphaFilter:qA,BlurFilter:tE,BlurFilterPass:cm,ColorMatrixFilter:hm,DisplacementFilter:rE,FXAAFilter:lE,NoiseFilter:hE};Object.entries(av).forEach(([s,e])=>{Object.defineProperty(av,s,{get(){return kt("7.1.0",`filters.${s} has moved to ${s}`),e}})});class uE{constructor(){this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this.tickerAdded=!1,this._pauseUpdate=!0}init(e){this.removeTickerListener(),this.events=e,this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this.tickerAdded=!1,this._pauseUpdate=!0}get pauseUpdate(){return this._pauseUpdate}set pauseUpdate(e){this._pauseUpdate=e}addTickerListener(){this.tickerAdded||!this.domElement||(ds.system.add(this.tickerUpdate,this,Oa.INTERACTION),this.tickerAdded=!0)}removeTickerListener(){this.tickerAdded&&(ds.system.remove(this.tickerUpdate,this),this.tickerAdded=!1)}pointerMoved(){this._didMove=!0}update(){if(!this.domElement||this._pauseUpdate)return;if(this._didMove){this._didMove=!1;return}const e=this.events.rootPointerEvent;this.events.supportsTouchEvents&&e.pointerType==="touch"||globalThis.document.dispatchEvent(new PointerEvent("pointermove",{clientX:e.clientX,clientY:e.clientY}))}tickerUpdate(e){this._deltaTime+=e,!(this._deltaTime<this.interactionFrequency)&&(this._deltaTime=0,this.update())}}const Or=new uE;class Wl{constructor(e){this.bubbles=!0,this.cancelBubble=!0,this.cancelable=!1,this.composed=!1,this.defaultPrevented=!1,this.eventPhase=Wl.prototype.NONE,this.propagationStopped=!1,this.propagationImmediatelyStopped=!1,this.layer=new Ut,this.page=new Ut,this.NONE=0,this.CAPTURING_PHASE=1,this.AT_TARGET=2,this.BUBBLING_PHASE=3,this.manager=e}get layerX(){return this.layer.x}get layerY(){return this.layer.y}get pageX(){return this.page.x}get pageY(){return this.page.y}get data(){return this}composedPath(){return this.manager&&(!this.path||this.path[this.path.length-1]!==this.target)&&(this.path=this.target?this.manager.propagationPath(this.target):[]),this.path}initEvent(e,t,i){throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}initUIEvent(e,t,i,n,r){throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}preventDefault(){this.nativeEvent instanceof Event&&this.nativeEvent.cancelable&&this.nativeEvent.preventDefault(),this.defaultPrevented=!0}stopImmediatePropagation(){this.propagationImmediatelyStopped=!0}stopPropagation(){this.propagationStopped=!0}}class Sh extends Wl{constructor(){super(...arguments),this.client=new Ut,this.movement=new Ut,this.offset=new Ut,this.global=new Ut,this.screen=new Ut}get clientX(){return this.client.x}get clientY(){return this.client.y}get x(){return this.clientX}get y(){return this.clientY}get movementX(){return this.movement.x}get movementY(){return this.movement.y}get offsetX(){return this.offset.x}get offsetY(){return this.offset.y}get globalX(){return this.global.x}get globalY(){return this.global.y}get screenX(){return this.screen.x}get screenY(){return this.screen.y}getLocalPosition(e,t,i){return e.worldTransform.applyInverse(i||this.global,t)}getModifierState(e){return"getModifierState"in this.nativeEvent&&this.nativeEvent.getModifierState(e)}initMouseEvent(e,t,i,n,r,a,o,h,l,c,u,d,f,p,m){throw new Error("Method not implemented.")}}class Js extends Sh{constructor(){super(...arguments),this.width=0,this.height=0,this.isPrimary=!1}getCoalescedEvents(){return this.type==="pointermove"||this.type==="mousemove"||this.type==="touchmove"?[this]:[]}getPredictedEvents(){throw new Error("getPredictedEvents is not supported!")}}class bo extends Sh{constructor(){super(...arguments),this.DOM_DELTA_PIXEL=0,this.DOM_DELTA_LINE=1,this.DOM_DELTA_PAGE=2}}bo.DOM_DELTA_PIXEL=0,bo.DOM_DELTA_LINE=1,bo.DOM_DELTA_PAGE=2;const dE=2048,fE=new Ut,nf=new Ut;class mE{constructor(e){this.dispatch=new jl,this.moveOnAll=!1,this.enableGlobalMoveEvents=!0,this.mappingState={trackingData:{}},this.eventPool=new Map,this._allInteractiveElements=[],this._hitElements=[],this._isPointerMoveEvent=!1,this.rootTarget=e,this.hitPruneFn=this.hitPruneFn.bind(this),this.hitTestFn=this.hitTestFn.bind(this),this.mapPointerDown=this.mapPointerDown.bind(this),this.mapPointerMove=this.mapPointerMove.bind(this),this.mapPointerOut=this.mapPointerOut.bind(this),this.mapPointerOver=this.mapPointerOver.bind(this),this.mapPointerUp=this.mapPointerUp.bind(this),this.mapPointerUpOutside=this.mapPointerUpOutside.bind(this),this.mapWheel=this.mapWheel.bind(this),this.mappingTable={},this.addEventMapping("pointerdown",this.mapPointerDown),this.addEventMapping("pointermove",this.mapPointerMove),this.addEventMapping("pointerout",this.mapPointerOut),this.addEventMapping("pointerle
Line truncated
|
||||||
${u}`)}});return await Promise.all(h),r?n[a[0].src]:n}async unload(e){const t=en(e,i=>({alias:[i],src:i})).map(async i=>{var a,o;const n=wi.toAbsolute(i.src),r=this.promiseCache[n];if(r){const h=await r.promise;delete this.promiseCache[n],(o=(a=r.parser)==null?void 0:a.unload)==null||o.call(a,h,i,this)}});await Promise.all(t)}_validateParsers(){this._parsersValidated=!0,this._parserHash=this._parsers.filter(e=>e.name).reduce((e,t)=>(e[t.name]&&console.warn(`[Assets] loadParser name conflict "${t.name}"`),{...e,[t.name]:t}),{})}}var Is=(s=>(s[s.Low=0]="Low",s[s.Normal=1]="Normal",s[s.High=2]="High",s))(Is||{});const IC=".json",RC="application/json",PC={extension:{type:He.LoadParser,priority:Is.Low},name:"loadJson",test(s){return Do(s,RC)||bs(s,IC)},async load(s){return await(await We.ADAPTER.fetch(s)).json()}};tt.add(PC);const kC=".txt",FC="text/plain",OC={name:"loadTxt",extension:{type:He.LoadParser,priority:Is.Low},test(s){return Do(s,FC)||bs(s,kC)},async load(s){return await(await We.ADAPTER.fetch(s)).text()}};tt.add(OC);const BC=["normal","bold","100","200","300","400","500","600","700","800","900"],LC=[".ttf",".otf",".woff",".woff2"],DC=["font/ttf","font/otf","font/woff","font/woff2"],NC=/^(--|-?[A-Z_])[0-9A-Z_-]*$/i;function UC(s){const e=wi.extname(s),t=wi.basename(s,e).replace(/(-|_)/g," ").toLowerCase().split(" ").map(r=>r.charAt(0).toUpperCase()+r.slice(1));let i=t.length>0;for(const r of t)if(!r.match(NC)){i=!1;break}let n=t.join(" ");return i||(n=`"${n.replace(/[\\"]/g,"\\$&")}"`),n}const XC=/^[0-9A-Za-z%:/?#\[\]@!\$&'()\*\+,;=\-._~]*$/;function VC(s){return XC.test(s)?s:encodeURI(s)}const YC={extension:{type:He.LoadParser,priority:Is.Low},name:"loadWebFont",test(s){return Do(s,DC)||bs(s,LC)},async load(s,e){var i,n,r;const t=We.ADAPTER.getFontFaceSet();if(t){const a=[],o=((i=e.data)==null?void 0:i.family)??UC(s),h=((r=(n=e.data)==null?void 0:n.weights)==null?void 0:r.filter(c=>BC.includes(c)))??["normal"],l=e.data??{};for(let c=0;c<h.length;c++){const u=h[c],d=new FontFace(o,`url(${VC(s)})`,{...l,weight:u});await d.load(),t.add(d),a.push(d)}return a.length===1?a[0]:a}return console.warn("[loadWebFont] FontFace API is not supported. Skipping loading font"),null},unload(s){(Array.isArray(s)?s:[s]).forEach(e=>We.ADAPTER.getFontFaceSet().delete(e))}};tt.add(YC);const $C=`(function() {
|
${u}`)}});return await Promise.all(h),r?n[a[0].src]:n}async unload(e){const t=en(e,i=>({alias:[i],src:i})).map(async i=>{var a,o;const n=wi.toAbsolute(i.src),r=this.promiseCache[n];if(r){const h=await r.promise;delete this.promiseCache[n],(o=(a=r.parser)==null?void 0:a.unload)==null||o.call(a,h,i,this)}});await Promise.all(t)}_validateParsers(){this._parsersValidated=!0,this._parserHash=this._parsers.filter(e=>e.name).reduce((e,t)=>(e[t.name]&&console.warn(`[Assets] loadParser name conflict "${t.name}"`),{...e,[t.name]:t}),{})}}var Is=(s=>(s[s.Low=0]="Low",s[s.Normal=1]="Normal",s[s.High=2]="High",s))(Is||{});const IE=".json",RE="application/json",PE={extension:{type:He.LoadParser,priority:Is.Low},name:"loadJson",test(s){return Do(s,RE)||bs(s,IE)},async load(s){return await(await We.ADAPTER.fetch(s)).json()}};tt.add(PE);const kE=".txt",FE="text/plain",OE={name:"loadTxt",extension:{type:He.LoadParser,priority:Is.Low},test(s){return Do(s,FE)||bs(s,kE)},async load(s){return await(await We.ADAPTER.fetch(s)).text()}};tt.add(OE);const BE=["normal","bold","100","200","300","400","500","600","700","800","900"],LE=[".ttf",".otf",".woff",".woff2"],DE=["font/ttf","font/otf","font/woff","font/woff2"],NE=/^(--|-?[A-Z_])[0-9A-Z_-]*$/i;function UE(s){const e=wi.extname(s),t=wi.basename(s,e).replace(/(-|_)/g," ").toLowerCase().split(" ").map(r=>r.charAt(0).toUpperCase()+r.slice(1));let i=t.length>0;for(const r of t)if(!r.match(NE)){i=!1;break}let n=t.join(" ");return i||(n=`"${n.replace(/[\\"]/g,"\\$&")}"`),n}const XE=/^[0-9A-Za-z%:/?#\[\]@!\$&'()\*\+,;=\-._~]*$/;function VE(s){return XE.test(s)?s:encodeURI(s)}const YE={extension:{type:He.LoadParser,priority:Is.Low},name:"loadWebFont",test(s){return Do(s,DE)||bs(s,LE)},async load(s,e){var i,n,r;const t=We.ADAPTER.getFontFaceSet();if(t){const a=[],o=((i=e.data)==null?void 0:i.family)??UE(s),h=((r=(n=e.data)==null?void 0:n.weights)==null?void 0:r.filter(c=>BE.includes(c)))??["normal"],l=e.data??{};for(let c=0;c<h.length;c++){const u=h[c],d=new FontFace(o,`url(${VE(s)})`,{...l,weight:u});await d.load(),t.add(d),a.push(d)}return a.length===1?a[0]:a}return console.warn("[loadWebFont] FontFace API is not supported. Skipping loading font"),null},unload(s){(Array.isArray(s)?s:[s]).forEach(e=>We.ADAPTER.getFontFaceSet().delete(e))}};tt.add(YE);const $E=`(function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
const WHITE_PNG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
|
const WHITE_PNG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
|
||||||
async function checkImageBitmap() {
|
async function checkImageBitmap() {
|
||||||
@@ -903,7 +903,7 @@ ${u}`)}});return await Promise.all(h),r?n[a[0].src]:n}async unload(e){const t=en
|
|||||||
self.postMessage(result);
|
self.postMessage(result);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
`;let wo=null,pm=class{constructor(){wo||(wo=URL.createObjectURL(new Blob([$C],{type:"application/javascript"}))),this.worker=new Worker(wo)}};pm.revokeObjectURL=function(){wo&&(URL.revokeObjectURL(wo),wo=null)};const GC=`(function() {
|
`;let wo=null,pm=class{constructor(){wo||(wo=URL.createObjectURL(new Blob([$E],{type:"application/javascript"}))),this.worker=new Worker(wo)}};pm.revokeObjectURL=function(){wo&&(URL.revokeObjectURL(wo),wo=null)};const GE=`(function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
async function loadImageBitmap(url) {
|
async function loadImageBitmap(url) {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
@@ -929,8 +929,8 @@ ${u}`)}});return await Promise.all(h),r?n[a[0].src]:n}async unload(e){const t=en
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
`;let So=null;class Hb{constructor(){So||(So=URL.createObjectURL(new Blob([GC],{type:"application/javascript"}))),this.worker=new Worker(So)}}Hb.revokeObjectURL=function(){So&&(URL.revokeObjectURL(So),So=null)};let uv=0,rf;class HC{constructor(){this._initialized=!1,this._createdWorkers=0,this.workerPool=[],this.queue=[],this.resolveHash={}}isImageBitmapSupported(){return this._isImageBitmapSupported!==void 0?this._isImageBitmapSupported:(this._isImageBitmapSupported=new Promise(e=>{const{worker:t}=new pm;t.addEventListener("message",i=>{t.terminate(),pm.revokeObjectURL(),e(i.data)})}),this._isImageBitmapSupported)}loadImageBitmap(e){return this._run("loadImageBitmap",[e])}async _initWorkers(){this._initialized||(this._initialized=!0)}getWorker(){rf===void 0&&(rf=navigator.hardwareConcurrency||4);let e=this.workerPool.pop();return!e&&this._createdWorkers<rf&&(this._createdWorkers++,e=new Hb().worker,e.addEventListener("message",t=>{this.complete(t.data),this.returnWorker(t.target),this.next()})),e}returnWorker(e){this.workerPool.push(e)}complete(e){e.error!==void 0?this.resolveHash[e.uuid].reject(e.error):this.resolveHash[e.uuid].resolve(e.data),this.resolveHash[e.uuid]=null}async _run(e,t){await this._initWorkers();const i=new Promise((n,r)=>{this.queue.push({id:e,arguments:t,resolve:n,reject:r})});return this.next(),i}next(){if(!this.queue.length)return;const e=this.getWorker();if(!e)return;const t=this.queue.pop(),i=t.id;this.resolveHash[uv]={resolve:t.resolve,reject:t.reject},e.postMessage({data:t.arguments,uuid:uv++,id:i})}}const dv=new HC;function zl(s,e,t){s.resource.internal=!0;const i=new gt(s),n=()=>{delete e.promiseCache[t],va.has(t)&&va.remove(t)};return i.baseTexture.once("destroyed",()=>{t in e.promiseCache&&(console.warn("[Assets] A BaseTexture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the BaseTexture."),n())}),i.once("destroyed",()=>{s.destroyed||(console.warn("[Assets] A Texture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the Texture."),n())}),i}const jC=[".jpeg",".jpg",".png",".webp",".avif"],WC=["image/jpeg","image/png","image/webp","image/avif"];async function zC(s){const e=await We.ADAPTER.fetch(s);if(!e.ok)throw new Error(`[loadImageBitmap] Failed to fetch ${s}: ${e.status} ${e.statusText}`);const t=await e.blob();return await createImageBitmap(t)}const Mu={name:"loadTextures",extension:{type:He.LoadParser,priority:Is.High},config:{preferWorkers:!0,preferCreateImageBitmap:!0,crossOrigin:"anonymous"},test(s){return Do(s,WC)||bs(s,jC)},async load(s,e,t){var o;const i=globalThis.createImageBitmap&&this.config.preferCreateImageBitmap;let n;i?this.config.preferWorkers&&await dv.isImageBitmapSupported()?n=await dv.loadImageBitmap(s):n=await zC(s):n=await new Promise((h,l)=>{const c=new Image;c.crossOrigin=this.config.crossOrigin,c.src=s,c.complete?h(c):(c.onload=()=>h(c),c.onerror=u=>l(u))});const r={...e.data};r.resolution??(r.resolution=pr(s)),i&&((o=r.resourceOptions)==null?void 0:o.ownsImageBitmap)===void 0&&(r.resourceOptions={...r.resourceOptions},r.resourceOptions.ownsImageBitmap=!0);const a=new yt(n,r);return a.resource.src=s,zl(a,t,s)},unload(s){s.destroy(!0)}};tt.add(Mu);const qC=".svg",KC="image/svg+xml",ZC={extension:{type:He.LoadParser,priority:Is.High},name:"loadSVG",test(s){return Do(s,KC)||bs(s,qC)},async testParse(s){return rm.test(s)},async parse(s,e,t){var r;const i=new rm(s,(r=e==null?void 0:e.data)==null?void 0:r.resourceOptions);await i.load();const n=new yt(i,{resolution:pr(s),...e==null?void 0:e.data});return n.resource.src=e.src,zl(n,t,e.src)},async load(s,e){return(await We.ADAPTER.fetch(s)).text()},unload:Mu.unload};tt.add(ZC);const JC=[".mp4",".m4v",".webm",".ogv"],QC=["video/mp4","video/webm","video/ogg"],e2={name:"loadVideo",extension:{type:He.LoadParser,priority:Is.High},config:{defaultAutoPlay:!0,defaultUpdateFPS:0,defaultLoop:!1,defaultMuted:!1,defaultPlaysinline:!0},test(s){return Do(s,QC)||bs(s,JC)},async load(s,e,t){var a;let i;const n=await(await We.ADAPTER.fetch(s)).blob(),r=URL.createObjectURL(n);try{const o={autoPlay:this.config.defaultAutoPlay,updateFPS:this.config.defaultUpdateFPS,loop:this.config.defaultLoop,muted:this.config.defaultMuted,playsinline:this.config.defaultPlaysinline,...(a=e==null?void 0:e.data)==null?void 0:a.resourceOptions,autoLoad:!0},h=new Db(r,o);await h.load();const l=new yt(h,{alphaMode:await oA(),resolution:pr(s),...e==null?void 0:e.data});l.resource.src=s,i=zl(l,t,s),i.baseTexture.once("destroyed",()=>{URL.revokeObjectURL(r)})}catch(o){throw URL.revokeObjectURL(r),o}return i},unload(s){s.destroy(!0)}};tt.add(e2);class t2{constructor(){this._defaultBundleIdentifierOptions={connector:"-",createBundleAssetId:(e,t)=>`${e}${this._bundleIdConnector}${t}`,extractAssetIdFromBundle:(e,t)=>t.replace(`${e}${this._bundleIdConnector}`,"")},this._bundleIdConnector=this._defaultBundleIdentifierOptions.connector,this._createBundleAssetId=
Line truncated
|
`;let So=null;class H1{constructor(){So||(So=URL.createObjectURL(new Blob([GE],{type:"application/javascript"}))),this.worker=new Worker(So)}}H1.revokeObjectURL=function(){So&&(URL.revokeObjectURL(So),So=null)};let uv=0,rf;class HE{constructor(){this._initialized=!1,this._createdWorkers=0,this.workerPool=[],this.queue=[],this.resolveHash={}}isImageBitmapSupported(){return this._isImageBitmapSupported!==void 0?this._isImageBitmapSupported:(this._isImageBitmapSupported=new Promise(e=>{const{worker:t}=new pm;t.addEventListener("message",i=>{t.terminate(),pm.revokeObjectURL(),e(i.data)})}),this._isImageBitmapSupported)}loadImageBitmap(e){return this._run("loadImageBitmap",[e])}async _initWorkers(){this._initialized||(this._initialized=!0)}getWorker(){rf===void 0&&(rf=navigator.hardwareConcurrency||4);let e=this.workerPool.pop();return!e&&this._createdWorkers<rf&&(this._createdWorkers++,e=new H1().worker,e.addEventListener("message",t=>{this.complete(t.data),this.returnWorker(t.target),this.next()})),e}returnWorker(e){this.workerPool.push(e)}complete(e){e.error!==void 0?this.resolveHash[e.uuid].reject(e.error):this.resolveHash[e.uuid].resolve(e.data),this.resolveHash[e.uuid]=null}async _run(e,t){await this._initWorkers();const i=new Promise((n,r)=>{this.queue.push({id:e,arguments:t,resolve:n,reject:r})});return this.next(),i}next(){if(!this.queue.length)return;const e=this.getWorker();if(!e)return;const t=this.queue.pop(),i=t.id;this.resolveHash[uv]={resolve:t.resolve,reject:t.reject},e.postMessage({data:t.arguments,uuid:uv++,id:i})}}const dv=new HE;function zl(s,e,t){s.resource.internal=!0;const i=new gt(s),n=()=>{delete e.promiseCache[t],va.has(t)&&va.remove(t)};return i.baseTexture.once("destroyed",()=>{t in e.promiseCache&&(console.warn("[Assets] A BaseTexture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the BaseTexture."),n())}),i.once("destroyed",()=>{s.destroyed||(console.warn("[Assets] A Texture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the Texture."),n())}),i}const jE=[".jpeg",".jpg",".png",".webp",".avif"],WE=["image/jpeg","image/png","image/webp","image/avif"];async function zE(s){const e=await We.ADAPTER.fetch(s);if(!e.ok)throw new Error(`[loadImageBitmap] Failed to fetch ${s}: ${e.status} ${e.statusText}`);const t=await e.blob();return await createImageBitmap(t)}const Mu={name:"loadTextures",extension:{type:He.LoadParser,priority:Is.High},config:{preferWorkers:!0,preferCreateImageBitmap:!0,crossOrigin:"anonymous"},test(s){return Do(s,WE)||bs(s,jE)},async load(s,e,t){var o;const i=globalThis.createImageBitmap&&this.config.preferCreateImageBitmap;let n;i?this.config.preferWorkers&&await dv.isImageBitmapSupported()?n=await dv.loadImageBitmap(s):n=await zE(s):n=await new Promise((h,l)=>{const c=new Image;c.crossOrigin=this.config.crossOrigin,c.src=s,c.complete?h(c):(c.onload=()=>h(c),c.onerror=u=>l(u))});const r={...e.data};r.resolution??(r.resolution=pr(s)),i&&((o=r.resourceOptions)==null?void 0:o.ownsImageBitmap)===void 0&&(r.resourceOptions={...r.resourceOptions},r.resourceOptions.ownsImageBitmap=!0);const a=new yt(n,r);return a.resource.src=s,zl(a,t,s)},unload(s){s.destroy(!0)}};tt.add(Mu);const qE=".svg",KE="image/svg+xml",ZE={extension:{type:He.LoadParser,priority:Is.High},name:"loadSVG",test(s){return Do(s,KE)||bs(s,qE)},async testParse(s){return rm.test(s)},async parse(s,e,t){var r;const i=new rm(s,(r=e==null?void 0:e.data)==null?void 0:r.resourceOptions);await i.load();const n=new yt(i,{resolution:pr(s),...e==null?void 0:e.data});return n.resource.src=e.src,zl(n,t,e.src)},async load(s,e){return(await We.ADAPTER.fetch(s)).text()},unload:Mu.unload};tt.add(ZE);const JE=[".mp4",".m4v",".webm",".ogv"],QE=["video/mp4","video/webm","video/ogg"],eC={name:"loadVideo",extension:{type:He.LoadParser,priority:Is.High},config:{defaultAutoPlay:!0,defaultUpdateFPS:0,defaultLoop:!1,defaultMuted:!1,defaultPlaysinline:!0},test(s){return Do(s,QE)||bs(s,JE)},async load(s,e,t){var a;let i;const n=await(await We.ADAPTER.fetch(s)).blob(),r=URL.createObjectURL(n);try{const o={autoPlay:this.config.defaultAutoPlay,updateFPS:this.config.defaultUpdateFPS,loop:this.config.defaultLoop,muted:this.config.defaultMuted,playsinline:this.config.defaultPlaysinline,...(a=e==null?void 0:e.data)==null?void 0:a.resourceOptions,autoLoad:!0},h=new D1(r,o);await h.load();const l=new yt(h,{alphaMode:await oT(),resolution:pr(s),...e==null?void 0:e.data});l.resource.src=s,i=zl(l,t,s),i.baseTexture.once("destroyed",()=>{URL.revokeObjectURL(r)})}catch(o){throw URL.revokeObjectURL(r),o}return i},unload(s){s.destroy(!0)}};tt.add(eC);class tC{constructor(){this._defaultBundleIdentifierOptions={connector:"-",createBundleAssetId:(e,t)=>`${e}${this._bundleIdConnector}${t}`,extractAssetIdFromBundle:(e,t)=>t.replace(`${e}${this._bundleIdConnector}`,"")},this._bundleIdConnector=this._defaultBundleIdentifierOptions.connector,this._createBundleAssetId=
Line truncated
|
||||||
Please use Assets.add({ alias, src, data, format, loadParser }) instead.`),a.push({alias:e,src:t,data:i,format:n,loadParser:r})):Array.isArray(e)?a.push(...e):a.push(e);let o;o=h=>{this.hasKey(h)&&console.warn(`[Resolver] already has key: ${h} overwriting`)},en(a).forEach(h=>{const{src:l,srcs:c}=h;let{data:u,format:d,loadParser:f}=h;const p=en(l||c).map(v=>typeof v=="string"?AC(v):Array.isArray(v)?v:[v]),m=this.getAlias(h);Array.isArray(m)?m.forEach(o):o(m);const g=[];p.forEach(v=>{v.forEach(b=>{let y={};if(typeof b!="object"){y.src=b;for(let w=0;w<this._parsers.length;w++){const A=this._parsers[w];if(A.test(b)){y=A.parse(b);break}}}else u=b.data??u,d=b.format??d,f=b.loadParser??f,y={...y,...b};if(!m)throw new Error(`[Resolver] alias is undefined for this asset: ${y.src}`);y=this.buildResolvedAsset(y,{aliases:m,data:u,format:d,loadParser:f}),g.push(y)})}),m.forEach(v=>{this._assetMap[v]=g})})}resolveBundle(e){const t=_h(e);e=en(e);const i={};return e.forEach(n=>{const r=this._bundles[n];if(r){const a=this.resolve(r),o={};for(const h in a){const l=a[h];o[this._extractAssetIdFromBundle(n,h)]=l}i[n]=o}}),t?i[e[0]]:i}resolveUrl(e){const t=this.resolve(e);if(typeof e!="string"){const i={};for(const n in t)i[n]=t[n].src;return i}return t.src}resolve(e){const t=_h(e);e=en(e);const i={};return e.forEach(n=>{if(!this._resolverHash[n])if(this._assetMap[n]){let r=this._assetMap[n];const a=r[0],o=this._getPreferredOrder(r);o==null||o.priority.forEach(h=>{o.params[h].forEach(l=>{const c=r.filter(u=>u[h]?u[h]===l:!1);c.length&&(r=c)})}),this._resolverHash[n]=r[0]??a}else this._resolverHash[n]=this.buildResolvedAsset({alias:[n],src:n},{});i[n]=this._resolverHash[n]}),t?i[e[0]]:i}hasKey(e){return!!this._assetMap[e]}hasBundle(e){return!!this._bundles[e]}_getPreferredOrder(e){for(let t=0;t<e.length;t++){const i=e[0],n=this._preferredOrder.find(r=>r.params.format.includes(i.format));if(n)return n}return this._preferredOrder[0]}_appendDefaultSearchParams(e){if(!this._defaultSearchParams)return e;const t=/\?/.test(e)?"&":"?";return`${e}${t}${this._defaultSearchParams}`}buildResolvedAsset(e,t){const{aliases:i,data:n,loadParser:r,format:a}=t;return(this._basePath||this._rootPath)&&(e.src=wi.toAbsolute(e.src,this._basePath,this._rootPath)),e.alias=i??e.alias??[e.src],e.src=this._appendDefaultSearchParams(e.src),e.data={...n||{},...e.data},e.loadParser=r??e.loadParser,e.format=a??e.format??wi.extname(e.src).slice(1),e.srcs=e.src,e.name=e.alias,e}}class i2{constructor(){this._detections=[],this._initialized=!1,this.resolver=new t2,this.loader=new CC,this.cache=va,this._backgroundLoader=new TC(this.loader),this._backgroundLoader.active=!0,this.reset()}async init(e={}){var r,a;if(this._initialized){console.warn("[Assets]AssetManager already initialized, did you load before calling this Assets.init()?");return}if(this._initialized=!0,e.defaultSearchParams&&this.resolver.setDefaultSearchParams(e.defaultSearchParams),e.basePath&&(this.resolver.basePath=e.basePath),e.bundleIdentifier&&this.resolver.setBundleIdentifier(e.bundleIdentifier),e.manifest){let o=e.manifest;typeof o=="string"&&(o=await this.load(o)),this.resolver.addManifest(o)}const t=((r=e.texturePreference)==null?void 0:r.resolution)??1,i=typeof t=="number"?[t]:t,n=await this._detectFormats({preferredFormats:(a=e.texturePreference)==null?void 0:a.format,skipDetections:e.skipDetections,detections:this._detections});this.resolver.prefer({params:{format:n,resolution:i}}),e.preferences&&this.setPreferences(e.preferences)}add(e,t,i,n,r){this.resolver.add(e,t,i,n,r)}async load(e,t){this._initialized||await this.init();const i=_h(e),n=en(e).map(o=>{if(typeof o!="string"){const h=this.resolver.getAlias(o);return h.some(l=>!this.resolver.hasKey(l))&&this.add(o),Array.isArray(h)?h[0]:h}return this.resolver.hasKey(o)||this.add({alias:o,src:o}),o}),r=this.resolver.resolve(n),a=await this._mapLoadToResolve(r,t);return i?a[n[0]]:a}addBundle(e,t){this.resolver.addBundle(e,t)}async loadBundle(e,t){this._initialized||await this.init();let i=!1;typeof e=="string"&&(i=!0,e=[e]);const n=this.resolver.resolveBundle(e),r={},a=Object.keys(n);let o=0,h=0;const l=()=>{t==null||t(++o/h)},c=a.map(u=>{const d=n[u];return h+=Object.keys(d).length,this._mapLoadToResolve(d,l).then(f=>{r[u]=f})});return await Promise.all(c),i?r[e[0]]:r}async backgroundLoad(e){this._initialized||await this.init(),typeof e=="string"&&(e=[e]);const t=this.resolver.resolve(e);this._backgroundLoader.add(Object.values(t))}async backgroundLoadBundle(e){this._initialized||await this.init(),typeof e=="string"&&(e=[e]);const t=this.resolver.resolveBundle(e);Object.values(t).forEach(i=>{this._backgroundLoader.add(Object.values(i))})}reset(){this.resolver.reset(),this.loader.reset(),this.cache.reset(),this._initialized=!1}get(e){if(typeof e=="string")return va.get(e);const t={};for(let i=0;i<e.length;i++)t[i]=va.get(e[i]);return t}async _mapLoadToResolve(e,t){const i=Object.values(e),n=Object.keys(e);this._backgroundLoader.active=!1;co
Line truncated
|
Please use Assets.add({ alias, src, data, format, loadParser }) instead.`),a.push({alias:e,src:t,data:i,format:n,loadParser:r})):Array.isArray(e)?a.push(...e):a.push(e);let o;o=h=>{this.hasKey(h)&&console.warn(`[Resolver] already has key: ${h} overwriting`)},en(a).forEach(h=>{const{src:l,srcs:c}=h;let{data:u,format:d,loadParser:f}=h;const p=en(l||c).map(v=>typeof v=="string"?AE(v):Array.isArray(v)?v:[v]),m=this.getAlias(h);Array.isArray(m)?m.forEach(o):o(m);const g=[];p.forEach(v=>{v.forEach(b=>{let y={};if(typeof b!="object"){y.src=b;for(let w=0;w<this._parsers.length;w++){const A=this._parsers[w];if(A.test(b)){y=A.parse(b);break}}}else u=b.data??u,d=b.format??d,f=b.loadParser??f,y={...y,...b};if(!m)throw new Error(`[Resolver] alias is undefined for this asset: ${y.src}`);y=this.buildResolvedAsset(y,{aliases:m,data:u,format:d,loadParser:f}),g.push(y)})}),m.forEach(v=>{this._assetMap[v]=g})})}resolveBundle(e){const t=_h(e);e=en(e);const i={};return e.forEach(n=>{const r=this._bundles[n];if(r){const a=this.resolve(r),o={};for(const h in a){const l=a[h];o[this._extractAssetIdFromBundle(n,h)]=l}i[n]=o}}),t?i[e[0]]:i}resolveUrl(e){const t=this.resolve(e);if(typeof e!="string"){const i={};for(const n in t)i[n]=t[n].src;return i}return t.src}resolve(e){const t=_h(e);e=en(e);const i={};return e.forEach(n=>{if(!this._resolverHash[n])if(this._assetMap[n]){let r=this._assetMap[n];const a=r[0],o=this._getPreferredOrder(r);o==null||o.priority.forEach(h=>{o.params[h].forEach(l=>{const c=r.filter(u=>u[h]?u[h]===l:!1);c.length&&(r=c)})}),this._resolverHash[n]=r[0]??a}else this._resolverHash[n]=this.buildResolvedAsset({alias:[n],src:n},{});i[n]=this._resolverHash[n]}),t?i[e[0]]:i}hasKey(e){return!!this._assetMap[e]}hasBundle(e){return!!this._bundles[e]}_getPreferredOrder(e){for(let t=0;t<e.length;t++){const i=e[0],n=this._preferredOrder.find(r=>r.params.format.includes(i.format));if(n)return n}return this._preferredOrder[0]}_appendDefaultSearchParams(e){if(!this._defaultSearchParams)return e;const t=/\?/.test(e)?"&":"?";return`${e}${t}${this._defaultSearchParams}`}buildResolvedAsset(e,t){const{aliases:i,data:n,loadParser:r,format:a}=t;return(this._basePath||this._rootPath)&&(e.src=wi.toAbsolute(e.src,this._basePath,this._rootPath)),e.alias=i??e.alias??[e.src],e.src=this._appendDefaultSearchParams(e.src),e.data={...n||{},...e.data},e.loadParser=r??e.loadParser,e.format=a??e.format??wi.extname(e.src).slice(1),e.srcs=e.src,e.name=e.alias,e}}class iC{constructor(){this._detections=[],this._initialized=!1,this.resolver=new tC,this.loader=new CE,this.cache=va,this._backgroundLoader=new TE(this.loader),this._backgroundLoader.active=!0,this.reset()}async init(e={}){var r,a;if(this._initialized){console.warn("[Assets]AssetManager already initialized, did you load before calling this Assets.init()?");return}if(this._initialized=!0,e.defaultSearchParams&&this.resolver.setDefaultSearchParams(e.defaultSearchParams),e.basePath&&(this.resolver.basePath=e.basePath),e.bundleIdentifier&&this.resolver.setBundleIdentifier(e.bundleIdentifier),e.manifest){let o=e.manifest;typeof o=="string"&&(o=await this.load(o)),this.resolver.addManifest(o)}const t=((r=e.texturePreference)==null?void 0:r.resolution)??1,i=typeof t=="number"?[t]:t,n=await this._detectFormats({preferredFormats:(a=e.texturePreference)==null?void 0:a.format,skipDetections:e.skipDetections,detections:this._detections});this.resolver.prefer({params:{format:n,resolution:i}}),e.preferences&&this.setPreferences(e.preferences)}add(e,t,i,n,r){this.resolver.add(e,t,i,n,r)}async load(e,t){this._initialized||await this.init();const i=_h(e),n=en(e).map(o=>{if(typeof o!="string"){const h=this.resolver.getAlias(o);return h.some(l=>!this.resolver.hasKey(l))&&this.add(o),Array.isArray(h)?h[0]:h}return this.resolver.hasKey(o)||this.add({alias:o,src:o}),o}),r=this.resolver.resolve(n),a=await this._mapLoadToResolve(r,t);return i?a[n[0]]:a}addBundle(e,t){this.resolver.addBundle(e,t)}async loadBundle(e,t){this._initialized||await this.init();let i=!1;typeof e=="string"&&(i=!0,e=[e]);const n=this.resolver.resolveBundle(e),r={},a=Object.keys(n);let o=0,h=0;const l=()=>{t==null||t(++o/h)},c=a.map(u=>{const d=n[u];return h+=Object.keys(d).length,this._mapLoadToResolve(d,l).then(f=>{r[u]=f})});return await Promise.all(c),i?r[e[0]]:r}async backgroundLoad(e){this._initialized||await this.init(),typeof e=="string"&&(e=[e]);const t=this.resolver.resolve(e);this._backgroundLoader.add(Object.values(t))}async backgroundLoadBundle(e){this._initialized||await this.init(),typeof e=="string"&&(e=[e]);const t=this.resolver.resolveBundle(e);Object.values(t).forEach(i=>{this._backgroundLoader.add(Object.values(i))})}reset(){this.resolver.reset(),this.loader.reset(),this.cache.reset(),this._initialized=!1}get(e){if(typeof e=="string")return va.get(e);const t={};for(let i=0;i<e.length;i++)t[i]=va.get(e[i]);return t}async _mapLoadToResolve(e,t){const i=Object.values(e),n=Object.keys(e);this._backgroundLoader.active=!1;co
Line truncated
|
||||||
uniform vec4 uColor;
|
uniform vec4 uColor;
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
@@ -939,7 +939,7 @@ void main(void)
|
|||||||
{
|
{
|
||||||
gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor;
|
gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor;
|
||||||
}
|
}
|
||||||
`,J2=`attribute vec2 aVertexPosition;
|
`,JC=`attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
uniform mat3 projectionMatrix;
|
uniform mat3 projectionMatrix;
|
||||||
@@ -954,7 +954,7 @@ void main(void)
|
|||||||
|
|
||||||
vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;
|
vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;
|
||||||
}
|
}
|
||||||
`;class Ch extends cn{constructor(e,t){const i={uSampler:e,alpha:1,uTextureMatrix:li.IDENTITY,uColor:new Float32Array([1,1,1,1])};t=Object.assign({tint:16777215,alpha:1,pluginName:"batch"},t),t.uniforms&&Object.assign(i,t.uniforms),super(t.program||kn.from(J2,Z2),i),this._colorDirty=!1,this.uvMatrix=new gp(e),this.batchable=t.program===void 0,this.pluginName=t.pluginName,this._tintColor=new jt(t.tint),this._tintRGB=this._tintColor.toLittleEndianNumber(),this._colorDirty=!0,this.alpha=t.alpha}get texture(){return this.uniforms.uSampler}set texture(e){this.uniforms.uSampler!==e&&(!this.uniforms.uSampler.baseTexture.alphaMode!=!e.baseTexture.alphaMode&&(this._colorDirty=!0),this.uniforms.uSampler=e,this.uvMatrix.texture=e)}set alpha(e){e!==this._alpha&&(this._alpha=e,this._colorDirty=!0)}get alpha(){return this._alpha}set tint(e){e!==this.tint&&(this._tintColor.setValue(e),this._tintRGB=this._tintColor.toLittleEndianNumber(),this._colorDirty=!0)}get tint(){return this._tintColor.value}get tintValue(){return this._tintColor.toNumber()}update(){if(this._colorDirty){this._colorDirty=!1;const e=this.texture.baseTexture.alphaMode;jt.shared.setValue(this._tintColor).premultiply(this._alpha,e).toArray(this.uniforms.uColor)}this.uvMatrix.update()&&(this.uniforms.uTextureMatrix=this.uvMatrix.mapCoord)}}class Q2 extends Ll{constructor(e=gt.EMPTY,t,i,n,r){const a=new Sp(t,i,n);a.getBuffer("aVertexPosition").static=!1;const o=new Ch(e);super(a,o,null,r),this.autoUpdate=!0}get vertices(){return this.geometry.getBuffer("aVertexPosition").data}set vertices(e){this.geometry.getBuffer("aVertexPosition").data=e}_render(e){this.autoUpdate&&this.geometry.getBuffer("aVertexPosition").update(),super._render(e)}}class _v{constructor(e,t,i){this.geometry=new Nn,this.indexBuffer=null,this.size=i,this.dynamicProperties=[],this.staticProperties=[];for(let n=0;n<e.length;++n){let r=e[n];r={attributeName:r.attributeName,size:r.size,uploadFunction:r.uploadFunction,type:r.type||et.FLOAT,offset:r.offset},t[n]?this.dynamicProperties.push(r):this.staticProperties.push(r)}this.staticStride=0,this.staticBuffer=null,this.staticData=null,this.staticDataUint32=null,this.dynamicStride=0,this.dynamicBuffer=null,this.dynamicData=null,this.dynamicDataUint32=null,this._updateID=0,this.initBuffers()}initBuffers(){const e=this.geometry;let t=0;this.indexBuffer=new oi(wA(this.size),!0,!0),e.addIndex(this.indexBuffer),this.dynamicStride=0;for(let a=0;a<this.dynamicProperties.length;++a){const o=this.dynamicProperties[a];o.offset=t,t+=o.size,this.dynamicStride+=o.size}const i=new ArrayBuffer(this.size*this.dynamicStride*4*4);this.dynamicData=new Float32Array(i),this.dynamicDataUint32=new Uint32Array(i),this.dynamicBuffer=new oi(this.dynamicData,!1,!1);let n=0;this.staticStride=0;for(let a=0;a<this.staticProperties.length;++a){const o=this.staticProperties[a];o.offset=n,n+=o.size,this.staticStride+=o.size}const r=new ArrayBuffer(this.size*this.staticStride*4*4);this.staticData=new Float32Array(r),this.staticDataUint32=new Uint32Array(r),this.staticBuffer=new oi(this.staticData,!0,!1);for(let a=0;a<this.dynamicProperties.length;++a){const o=this.dynamicProperties[a];e.addAttribute(o.attributeName,this.dynamicBuffer,0,o.type===et.UNSIGNED_BYTE,o.type,this.dynamicStride*4,o.offset*4)}for(let a=0;a<this.staticProperties.length;++a){const o=this.staticProperties[a];e.addAttribute(o.attributeName,this.staticBuffer,0,o.type===et.UNSIGNED_BYTE,o.type,this.staticStride*4,o.offset*4)}}uploadDynamic(e,t,i){for(let n=0;n<this.dynamicProperties.length;n++){const r=this.dynamicProperties[n];r.uploadFunction(e,t,i,r.type===et.UNSIGNED_BYTE?this.dynamicDataUint32:this.dynamicData,this.dynamicStride,r.offset)}this.dynamicBuffer._updateID++}uploadStatic(e,t,i){for(let n=0;n<this.staticProperties.length;n++){const r=this.staticProperties[n];r.uploadFunction(e,t,i,r.type===et.UNSIGNED_BYTE?this.staticDataUint32:this.staticData,this.staticStride,r.offset)}this.staticBuffer._updateID++}destroy(){this.indexBuffer=null,this.dynamicProperties=null,this.dynamicBuffer=null,this.dynamicData=null,this.dynamicDataUint32=null,this.staticProperties=null,this.staticBuffer=null,this.staticData=null,this.staticDataUint32=null,this.geometry.destroy()}}var eI=`varying vec2 vTextureCoord;
|
`;class Ch extends cn{constructor(e,t){const i={uSampler:e,alpha:1,uTextureMatrix:li.IDENTITY,uColor:new Float32Array([1,1,1,1])};t=Object.assign({tint:16777215,alpha:1,pluginName:"batch"},t),t.uniforms&&Object.assign(i,t.uniforms),super(t.program||kn.from(JC,ZC),i),this._colorDirty=!1,this.uvMatrix=new gp(e),this.batchable=t.program===void 0,this.pluginName=t.pluginName,this._tintColor=new jt(t.tint),this._tintRGB=this._tintColor.toLittleEndianNumber(),this._colorDirty=!0,this.alpha=t.alpha}get texture(){return this.uniforms.uSampler}set texture(e){this.uniforms.uSampler!==e&&(!this.uniforms.uSampler.baseTexture.alphaMode!=!e.baseTexture.alphaMode&&(this._colorDirty=!0),this.uniforms.uSampler=e,this.uvMatrix.texture=e)}set alpha(e){e!==this._alpha&&(this._alpha=e,this._colorDirty=!0)}get alpha(){return this._alpha}set tint(e){e!==this.tint&&(this._tintColor.setValue(e),this._tintRGB=this._tintColor.toLittleEndianNumber(),this._colorDirty=!0)}get tint(){return this._tintColor.value}get tintValue(){return this._tintColor.toNumber()}update(){if(this._colorDirty){this._colorDirty=!1;const e=this.texture.baseTexture.alphaMode;jt.shared.setValue(this._tintColor).premultiply(this._alpha,e).toArray(this.uniforms.uColor)}this.uvMatrix.update()&&(this.uniforms.uTextureMatrix=this.uvMatrix.mapCoord)}}class QC extends Ll{constructor(e=gt.EMPTY,t,i,n,r){const a=new Sp(t,i,n);a.getBuffer("aVertexPosition").static=!1;const o=new Ch(e);super(a,o,null,r),this.autoUpdate=!0}get vertices(){return this.geometry.getBuffer("aVertexPosition").data}set vertices(e){this.geometry.getBuffer("aVertexPosition").data=e}_render(e){this.autoUpdate&&this.geometry.getBuffer("aVertexPosition").update(),super._render(e)}}class _v{constructor(e,t,i){this.geometry=new Nn,this.indexBuffer=null,this.size=i,this.dynamicProperties=[],this.staticProperties=[];for(let n=0;n<e.length;++n){let r=e[n];r={attributeName:r.attributeName,size:r.size,uploadFunction:r.uploadFunction,type:r.type||et.FLOAT,offset:r.offset},t[n]?this.dynamicProperties.push(r):this.staticProperties.push(r)}this.staticStride=0,this.staticBuffer=null,this.staticData=null,this.staticDataUint32=null,this.dynamicStride=0,this.dynamicBuffer=null,this.dynamicData=null,this.dynamicDataUint32=null,this._updateID=0,this.initBuffers()}initBuffers(){const e=this.geometry;let t=0;this.indexBuffer=new oi(wT(this.size),!0,!0),e.addIndex(this.indexBuffer),this.dynamicStride=0;for(let a=0;a<this.dynamicProperties.length;++a){const o=this.dynamicProperties[a];o.offset=t,t+=o.size,this.dynamicStride+=o.size}const i=new ArrayBuffer(this.size*this.dynamicStride*4*4);this.dynamicData=new Float32Array(i),this.dynamicDataUint32=new Uint32Array(i),this.dynamicBuffer=new oi(this.dynamicData,!1,!1);let n=0;this.staticStride=0;for(let a=0;a<this.staticProperties.length;++a){const o=this.staticProperties[a];o.offset=n,n+=o.size,this.staticStride+=o.size}const r=new ArrayBuffer(this.size*this.staticStride*4*4);this.staticData=new Float32Array(r),this.staticDataUint32=new Uint32Array(r),this.staticBuffer=new oi(this.staticData,!0,!1);for(let a=0;a<this.dynamicProperties.length;++a){const o=this.dynamicProperties[a];e.addAttribute(o.attributeName,this.dynamicBuffer,0,o.type===et.UNSIGNED_BYTE,o.type,this.dynamicStride*4,o.offset*4)}for(let a=0;a<this.staticProperties.length;++a){const o=this.staticProperties[a];e.addAttribute(o.attributeName,this.staticBuffer,0,o.type===et.UNSIGNED_BYTE,o.type,this.staticStride*4,o.offset*4)}}uploadDynamic(e,t,i){for(let n=0;n<this.dynamicProperties.length;n++){const r=this.dynamicProperties[n];r.uploadFunction(e,t,i,r.type===et.UNSIGNED_BYTE?this.dynamicDataUint32:this.dynamicData,this.dynamicStride,r.offset)}this.dynamicBuffer._updateID++}uploadStatic(e,t,i){for(let n=0;n<this.staticProperties.length;n++){const r=this.staticProperties[n];r.uploadFunction(e,t,i,r.type===et.UNSIGNED_BYTE?this.staticDataUint32:this.staticData,this.staticStride,r.offset)}this.staticBuffer._updateID++}destroy(){this.indexBuffer=null,this.dynamicProperties=null,this.dynamicBuffer=null,this.dynamicData=null,this.dynamicDataUint32=null,this.staticProperties=null,this.staticBuffer=null,this.staticData=null,this.staticDataUint32=null,this.geometry.destroy()}}var eI=`varying vec2 vTextureCoord;
|
||||||
varying vec4 vColor;
|
varying vec4 vColor;
|
||||||
|
|
||||||
uniform sampler2D uSampler;
|
uniform sampler2D uSampler;
|
||||||
@@ -987,8 +987,8 @@ void main(void){
|
|||||||
vTextureCoord = aTextureCoord;
|
vTextureCoord = aTextureCoord;
|
||||||
vColor = aColor * uColor;
|
vColor = aColor * uColor;
|
||||||
}
|
}
|
||||||
`;class Qb extends Su{constructor(e){super(e),this.shader=null,this.properties=null,this.tempMatrix=new li,this.properties=[{attributeName:"aVertexPosition",size:2,uploadFunction:this.uploadVertices,offset:0},{attributeName:"aPositionCoord",size:2,uploadFunction:this.uploadPosition,offset:0},{attributeName:"aRotation",size:1,uploadFunction:this.uploadRotation,offset:0},{attributeName:"aTextureCoord",size:2,uploadFunction:this.uploadUvs,offset:0},{attributeName:"aColor",size:1,type:et.UNSIGNED_BYTE,uploadFunction:this.uploadTint,offset:0}],this.shader=cn.from(tI,eI,{}),this.state=Un.for2d()}render(e){const t=e.children,i=e._maxSize,n=e._batchSize,r=this.renderer;let a=t.length;if(a===0)return;a>i&&!e.autoResize&&(a=i);let o=e._buffers;o||(o=e._buffers=this.generateBuffers(e));const h=t[0]._texture.baseTexture,l=h.alphaMode>0;this.state.blendMode=Zx(e.blendMode,l),r.state.set(this.state);const c=r.gl,u=e.worldTransform.copyTo(this.tempMatrix);u.prepend(r.globalUniforms.uniforms.projectionMatrix),this.shader.uniforms.translationMatrix=u.toArray(!0),this.shader.uniforms.uColor=jt.shared.setValue(e.tintRgb).premultiply(e.worldAlpha,l).toArray(this.shader.uniforms.uColor),this.shader.uniforms.uSampler=h,this.renderer.shader.bind(this.shader);let d=!1;for(let f=0,p=0;f<a;f+=n,p+=1){let m=a-f;m>n&&(m=n),p>=o.length&&o.push(this._generateOneMoreBuffer(e));const g=o[p];g.uploadDynamic(t,f,m);const v=e._bufferUpdateIDs[p]||0;d=d||g._updateID<v,d&&(g._updateID=e._updateID,g.uploadStatic(t,f,m)),r.geometry.bind(g.geometry),c.drawElements(c.TRIANGLES,m*6,c.UNSIGNED_SHORT,0)}}generateBuffers(e){const t=[],i=e._maxSize,n=e._batchSize,r=e._properties;for(let a=0;a<i;a+=n)t.push(new _v(this.properties,r,n));return t}_generateOneMoreBuffer(e){const t=e._batchSize,i=e._properties;return new _v(this.properties,i,t)}uploadVertices(e,t,i,n,r,a){let o=0,h=0,l=0,c=0;for(let u=0;u<i;++u){const d=e[t+u],f=d._texture,p=d.scale.x,m=d.scale.y,g=f.trim,v=f.orig;g?(h=g.x-d.anchor.x*v.width,o=h+g.width,c=g.y-d.anchor.y*v.height,l=c+g.height):(o=v.width*(1-d.anchor.x),h=v.width*-d.anchor.x,l=v.height*(1-d.anchor.y),c=v.height*-d.anchor.y),n[a]=h*p,n[a+1]=c*m,n[a+r]=o*p,n[a+r+1]=c*m,n[a+r*2]=o*p,n[a+r*2+1]=l*m,n[a+r*3]=h*p,n[a+r*3+1]=l*m,a+=r*4}}uploadPosition(e,t,i,n,r,a){for(let o=0;o<i;o++){const h=e[t+o].position;n[a]=h.x,n[a+1]=h.y,n[a+r]=h.x,n[a+r+1]=h.y,n[a+r*2]=h.x,n[a+r*2+1]=h.y,n[a+r*3]=h.x,n[a+r*3+1]=h.y,a+=r*4}}uploadRotation(e,t,i,n,r,a){for(let o=0;o<i;o++){const h=e[t+o].rotation;n[a]=h,n[a+r]=h,n[a+r*2]=h,n[a+r*3]=h,a+=r*4}}uploadUvs(e,t,i,n,r,a){for(let o=0;o<i;++o){const h=e[t+o]._texture._uvs;h?(n[a]=h.x0,n[a+1]=h.y0,n[a+r]=h.x1,n[a+r+1]=h.y1,n[a+r*2]=h.x2,n[a+r*2+1]=h.y2,n[a+r*3]=h.x3,n[a+r*3+1]=h.y3,a+=r*4):(n[a]=0,n[a+1]=0,n[a+r]=0,n[a+r+1]=0,n[a+r*2]=0,n[a+r*2+1]=0,n[a+r*3]=0,n[a+r*3+1]=0,a+=r*4)}}uploadTint(e,t,i,n,r,a){for(let o=0;o<i;++o){const h=e[t+o],l=jt.shared.setValue(h._tintRGB).toPremultiplied(h.alpha,h.texture.baseTexture.alphaMode>0);n[a]=l,n[a+r]=l,n[a+r*2]=l,n[a+r*3]=l,a+=r*4}}destroy(){super.destroy(),this.shader&&(this.shader.destroy(),this.shader=null),this.tempMatrix=null}}Qb.extension={name:"particle",type:He.RendererPlugin};tt.add(Qb);var Tu=(s=>(s[s.LINEAR_VERTICAL=0]="LINEAR_VERTICAL",s[s.LINEAR_HORIZONTAL=1]="LINEAR_HORIZONTAL",s))(Tu||{});const Mc={willReadFrequently:!0},vn=class ot{static get experimentalLetterSpacingSupported(){let e=ot._experimentalLetterSpacingSupported;if(e!==void 0){const t=We.ADAPTER.getCanvasRenderingContext2D().prototype;e=ot._experimentalLetterSpacingSupported="letterSpacing"in t||"textLetterSpacing"in t}return e}constructor(e,t,i,n,r,a,o,h,l){this.text=e,this.style=t,this.width=i,this.height=n,this.lines=r,this.lineWidths=a,this.lineHeight=o,this.maxLineWidth=h,this.fontProperties=l}static measureText(e,t,i,n=ot._canvas){i=i??t.wordWrap;const r=t.toFontString(),a=ot.measureFont(r);a.fontSize===0&&(a.fontSize=t.fontSize,a.ascent=t.fontSize);const o=n.getContext("2d",Mc);o.font=r;const h=(i?ot.wordWrap(e,t,n):e).split(/(?:\r\n|\r|\n)/),l=new Array(h.length);let c=0;for(let p=0;p<h.length;p++){const m=ot._measureText(h[p],t.letterSpacing,o);l[p]=m,c=Math.max(c,m)}let u=c+t.strokeThickness;t.dropShadow&&(u+=t.dropShadowDistance);const d=t.lineHeight||a.fontSize+t.strokeThickness;let f=Math.max(d,a.fontSize+t.strokeThickness*2)+t.leading+(h.length-1)*(d+t.leading);return t.dropShadow&&(f+=t.dropShadowDistance),new ot(e,t,u,f,h,l,d+t.leading,c,a)}static _measureText(e,t,i){let n=!1;ot.experimentalLetterSpacingSupported&&(ot.experimentalLetterSpacing?(i.letterSpacing=`${t}px`,i.textLetterSpacing=`${t}px`,n=!0):(i.letterSpacing="0px",i.textLetterSpacing="0px"));let r=i.measureText(e).width;return r>0&&(n?r-=t:r+=(ot.graphemeSegmenter(e).length-1)*t),r}static wordWrap(e,t,i=ot._canvas){const n=i.getContext("2d",Mc);let r=0,a="",o="";const h=Object.create(null),{letterSpacing:l,whiteSpace:c}=t,u=ot.collapseSpaces(c),d=ot.collapseNewlines(c);let f=!u;const p=t.wordWrapWidth+l,
Line truncated
|
`;class Q1 extends Su{constructor(e){super(e),this.shader=null,this.properties=null,this.tempMatrix=new li,this.properties=[{attributeName:"aVertexPosition",size:2,uploadFunction:this.uploadVertices,offset:0},{attributeName:"aPositionCoord",size:2,uploadFunction:this.uploadPosition,offset:0},{attributeName:"aRotation",size:1,uploadFunction:this.uploadRotation,offset:0},{attributeName:"aTextureCoord",size:2,uploadFunction:this.uploadUvs,offset:0},{attributeName:"aColor",size:1,type:et.UNSIGNED_BYTE,uploadFunction:this.uploadTint,offset:0}],this.shader=cn.from(tI,eI,{}),this.state=Un.for2d()}render(e){const t=e.children,i=e._maxSize,n=e._batchSize,r=this.renderer;let a=t.length;if(a===0)return;a>i&&!e.autoResize&&(a=i);let o=e._buffers;o||(o=e._buffers=this.generateBuffers(e));const h=t[0]._texture.baseTexture,l=h.alphaMode>0;this.state.blendMode=Zx(e.blendMode,l),r.state.set(this.state);const c=r.gl,u=e.worldTransform.copyTo(this.tempMatrix);u.prepend(r.globalUniforms.uniforms.projectionMatrix),this.shader.uniforms.translationMatrix=u.toArray(!0),this.shader.uniforms.uColor=jt.shared.setValue(e.tintRgb).premultiply(e.worldAlpha,l).toArray(this.shader.uniforms.uColor),this.shader.uniforms.uSampler=h,this.renderer.shader.bind(this.shader);let d=!1;for(let f=0,p=0;f<a;f+=n,p+=1){let m=a-f;m>n&&(m=n),p>=o.length&&o.push(this._generateOneMoreBuffer(e));const g=o[p];g.uploadDynamic(t,f,m);const v=e._bufferUpdateIDs[p]||0;d=d||g._updateID<v,d&&(g._updateID=e._updateID,g.uploadStatic(t,f,m)),r.geometry.bind(g.geometry),c.drawElements(c.TRIANGLES,m*6,c.UNSIGNED_SHORT,0)}}generateBuffers(e){const t=[],i=e._maxSize,n=e._batchSize,r=e._properties;for(let a=0;a<i;a+=n)t.push(new _v(this.properties,r,n));return t}_generateOneMoreBuffer(e){const t=e._batchSize,i=e._properties;return new _v(this.properties,i,t)}uploadVertices(e,t,i,n,r,a){let o=0,h=0,l=0,c=0;for(let u=0;u<i;++u){const d=e[t+u],f=d._texture,p=d.scale.x,m=d.scale.y,g=f.trim,v=f.orig;g?(h=g.x-d.anchor.x*v.width,o=h+g.width,c=g.y-d.anchor.y*v.height,l=c+g.height):(o=v.width*(1-d.anchor.x),h=v.width*-d.anchor.x,l=v.height*(1-d.anchor.y),c=v.height*-d.anchor.y),n[a]=h*p,n[a+1]=c*m,n[a+r]=o*p,n[a+r+1]=c*m,n[a+r*2]=o*p,n[a+r*2+1]=l*m,n[a+r*3]=h*p,n[a+r*3+1]=l*m,a+=r*4}}uploadPosition(e,t,i,n,r,a){for(let o=0;o<i;o++){const h=e[t+o].position;n[a]=h.x,n[a+1]=h.y,n[a+r]=h.x,n[a+r+1]=h.y,n[a+r*2]=h.x,n[a+r*2+1]=h.y,n[a+r*3]=h.x,n[a+r*3+1]=h.y,a+=r*4}}uploadRotation(e,t,i,n,r,a){for(let o=0;o<i;o++){const h=e[t+o].rotation;n[a]=h,n[a+r]=h,n[a+r*2]=h,n[a+r*3]=h,a+=r*4}}uploadUvs(e,t,i,n,r,a){for(let o=0;o<i;++o){const h=e[t+o]._texture._uvs;h?(n[a]=h.x0,n[a+1]=h.y0,n[a+r]=h.x1,n[a+r+1]=h.y1,n[a+r*2]=h.x2,n[a+r*2+1]=h.y2,n[a+r*3]=h.x3,n[a+r*3+1]=h.y3,a+=r*4):(n[a]=0,n[a+1]=0,n[a+r]=0,n[a+r+1]=0,n[a+r*2]=0,n[a+r*2+1]=0,n[a+r*3]=0,n[a+r*3+1]=0,a+=r*4)}}uploadTint(e,t,i,n,r,a){for(let o=0;o<i;++o){const h=e[t+o],l=jt.shared.setValue(h._tintRGB).toPremultiplied(h.alpha,h.texture.baseTexture.alphaMode>0);n[a]=l,n[a+r]=l,n[a+r*2]=l,n[a+r*3]=l,a+=r*4}}destroy(){super.destroy(),this.shader&&(this.shader.destroy(),this.shader=null),this.tempMatrix=null}}Q1.extension={name:"particle",type:He.RendererPlugin};tt.add(Q1);var Tu=(s=>(s[s.LINEAR_VERTICAL=0]="LINEAR_VERTICAL",s[s.LINEAR_HORIZONTAL=1]="LINEAR_HORIZONTAL",s))(Tu||{});const Mc={willReadFrequently:!0},vn=class ot{static get experimentalLetterSpacingSupported(){let e=ot._experimentalLetterSpacingSupported;if(e!==void 0){const t=We.ADAPTER.getCanvasRenderingContext2D().prototype;e=ot._experimentalLetterSpacingSupported="letterSpacing"in t||"textLetterSpacing"in t}return e}constructor(e,t,i,n,r,a,o,h,l){this.text=e,this.style=t,this.width=i,this.height=n,this.lines=r,this.lineWidths=a,this.lineHeight=o,this.maxLineWidth=h,this.fontProperties=l}static measureText(e,t,i,n=ot._canvas){i=i??t.wordWrap;const r=t.toFontString(),a=ot.measureFont(r);a.fontSize===0&&(a.fontSize=t.fontSize,a.ascent=t.fontSize);const o=n.getContext("2d",Mc);o.font=r;const h=(i?ot.wordWrap(e,t,n):e).split(/(?:\r\n|\r|\n)/),l=new Array(h.length);let c=0;for(let p=0;p<h.length;p++){const m=ot._measureText(h[p],t.letterSpacing,o);l[p]=m,c=Math.max(c,m)}let u=c+t.strokeThickness;t.dropShadow&&(u+=t.dropShadowDistance);const d=t.lineHeight||a.fontSize+t.strokeThickness;let f=Math.max(d,a.fontSize+t.strokeThickness*2)+t.leading+(h.length-1)*(d+t.leading);return t.dropShadow&&(f+=t.dropShadowDistance),new ot(e,t,u,f,h,l,d+t.leading,c,a)}static _measureText(e,t,i){let n=!1;ot.experimentalLetterSpacingSupported&&(ot.experimentalLetterSpacing?(i.letterSpacing=`${t}px`,i.textLetterSpacing=`${t}px`,n=!0):(i.letterSpacing="0px",i.textLetterSpacing="0px"));let r=i.measureText(e).width;return r>0&&(n?r-=t:r+=(ot.graphemeSegmenter(e).length-1)*t),r}static wordWrap(e,t,i=ot._canvas){const n=i.getContext("2d",Mc);let r=0,a="",o="";const h=Object.create(null),{letterSpacing:l,whiteSpace:c}=t,u=ot.collapseSpaces(c),d=ot.collapseNewlines(c);let f=!u;const p=t.wordWrapWidth+l,
Line truncated
|
||||||
`:e,e}static getFromCache(e,t,i,n){let r=i[e];return typeof r!="number"&&(r=ot._measureText(e,t,n)+t,i[e]=r),r}static collapseSpaces(e){return e==="normal"||e==="pre-line"}static collapseNewlines(e){return e==="normal"}static trimRight(e){if(typeof e!="string")return"";for(let t=e.length-1;t>=0;t--){const i=e[t];if(!ot.isBreakingSpace(i))break;e=e.slice(0,-1)}return e}static isNewline(e){return typeof e!="string"?!1:ot._newlines.includes(e.charCodeAt(0))}static isBreakingSpace(e,t){return typeof e!="string"?!1:ot._breakingSpaces.includes(e.charCodeAt(0))}static tokenize(e){const t=[];let i="";if(typeof e!="string")return t;for(let n=0;n<e.length;n++){const r=e[n],a=e[n+1];if(ot.isBreakingSpace(r,a)||ot.isNewline(r)){i!==""&&(t.push(i),i=""),t.push(r);continue}i+=r}return i!==""&&t.push(i),t}static canBreakWords(e,t){return t}static canBreakChars(e,t,i,n,r){return!0}static wordWrapSplit(e){return ot.graphemeSegmenter(e)}static measureFont(e){if(ot._fonts[e])return ot._fonts[e];const t={ascent:0,descent:0,fontSize:0},i=ot._canvas,n=ot._context;n.font=e;const r=ot.METRICS_STRING+ot.BASELINE_SYMBOL,a=Math.ceil(n.measureText(r).width);let o=Math.ceil(n.measureText(ot.BASELINE_SYMBOL).width);const h=Math.ceil(ot.HEIGHT_MULTIPLIER*o);if(o=o*ot.BASELINE_MULTIPLIER|0,a===0||h===0)return ot._fonts[e]=t,t;i.width=a,i.height=h,n.fillStyle="#f00",n.fillRect(0,0,a,h),n.font=e,n.textBaseline="alphabetic",n.fillStyle="#000",n.fillText(r,0,o);const l=n.getImageData(0,0,a,h).data,c=l.length,u=a*4;let d=0,f=0,p=!1;for(d=0;d<o;++d){for(let m=0;m<u;m+=4)if(l[f+m]!==255){p=!0;break}if(!p)f+=u;else break}for(t.ascent=o-d,f=c-u,p=!1,d=h;d>o;--d){for(let m=0;m<u;m+=4)if(l[f+m]!==255){p=!0;break}if(!p)f-=u;else break}return t.descent=d-o,t.fontSize=t.ascent+t.descent,ot._fonts[e]=t,t}static clearMetrics(e=""){e?delete ot._fonts[e]:ot._fonts={}}static get _canvas(){var e;if(!ot.__canvas){let t;try{const i=new OffscreenCanvas(0,0);if((e=i.getContext("2d",Mc))!=null&&e.measureText)return ot.__canvas=i,i;t=We.ADAPTER.createCanvas()}catch{t=We.ADAPTER.createCanvas()}t.width=t.height=10,ot.__canvas=t}return ot.__canvas}static get _context(){return ot.__context||(ot.__context=ot._canvas.getContext("2d",Mc)),ot.__context}};vn.METRICS_STRING="|ÉqÅ",vn.BASELINE_SYMBOL="M",vn.BASELINE_MULTIPLIER=1.4,vn.HEIGHT_MULTIPLIER=2,vn.graphemeSegmenter=(()=>{if(typeof(Intl==null?void 0:Intl.Segmenter)=="function"){const s=new Intl.Segmenter;return e=>[...s.segment(e)].map(t=>t.segment)}return s=>[...s]})(),vn.experimentalLetterSpacing=!1,vn._fonts={},vn._newlines=[10,13],vn._breakingSpaces=[9,32,8192,8193,8194,8195,8196,8197,8198,8200,8201,8202,8287,12288];let Ir=vn;const iI=["serif","sans-serif","monospace","cursive","fantasy","system-ui"],e1=class ol{constructor(e){this.styleID=0,this.reset(),uf(this,e,e)}clone(){const e={};return uf(e,this,ol.defaultStyle),new ol(e)}reset(){uf(this,ol.defaultStyle,ol.defaultStyle)}get align(){return this._align}set align(e){this._align!==e&&(this._align=e,this.styleID++)}get breakWords(){return this._breakWords}set breakWords(e){this._breakWords!==e&&(this._breakWords=e,this.styleID++)}get dropShadow(){return this._dropShadow}set dropShadow(e){this._dropShadow!==e&&(this._dropShadow=e,this.styleID++)}get dropShadowAlpha(){return this._dropShadowAlpha}set dropShadowAlpha(e){this._dropShadowAlpha!==e&&(this._dropShadowAlpha=e,this.styleID++)}get dropShadowAngle(){return this._dropShadowAngle}set dropShadowAngle(e){this._dropShadowAngle!==e&&(this._dropShadowAngle=e,this.styleID++)}get dropShadowBlur(){return this._dropShadowBlur}set dropShadowBlur(e){this._dropShadowBlur!==e&&(this._dropShadowBlur=e,this.styleID++)}get dropShadowColor(){return this._dropShadowColor}set dropShadowColor(e){const t=hf(e);this._dropShadowColor!==t&&(this._dropShadowColor=t,this.styleID++)}get dropShadowDistance(){return this._dropShadowDistance}set dropShadowDistance(e){this._dropShadowDistance!==e&&(this._dropShadowDistance=e,this.styleID++)}get fill(){return this._fill}set fill(e){const t=hf(e);this._fill!==t&&(this._fill=t,this.styleID++)}get fillGradientType(){return this._fillGradientType}set fillGradientType(e){this._fillGradientType!==e&&(this._fillGradientType=e,this.styleID++)}get fillGradientStops(){return this._fillGradientStops}set fillGradientStops(e){sI(this._fillGradientStops,e)||(this._fillGradientStops=e,this.styleID++)}get fontFamily(){return this._fontFamily}set fontFamily(e){this.fontFamily!==e&&(this._fontFamily=e,this.styleID++)}get fontSize(){return this._fontSize}set fontSize(e){this._fontSize!==e&&(this._fontSize=e,this.styleID++)}get fontStyle(){return this._fontStyle}set fontStyle(e){this._fontStyle!==e&&(this._fontStyle=e,this.styleID++)}get fontVariant(){return this._fontVariant}set fontVariant(e){this._fontVariant!==e&&(this._fontVariant=e,this.styleID++)}get fontWeight(){return this._fontWeight}set fontWeight(e){this._fontWeight!==e&&(this._fontWeight=e,this.styleID++)}get letterSpacing(){return this._lett
Line truncated
|
`:e,e}static getFromCache(e,t,i,n){let r=i[e];return typeof r!="number"&&(r=ot._measureText(e,t,n)+t,i[e]=r),r}static collapseSpaces(e){return e==="normal"||e==="pre-line"}static collapseNewlines(e){return e==="normal"}static trimRight(e){if(typeof e!="string")return"";for(let t=e.length-1;t>=0;t--){const i=e[t];if(!ot.isBreakingSpace(i))break;e=e.slice(0,-1)}return e}static isNewline(e){return typeof e!="string"?!1:ot._newlines.includes(e.charCodeAt(0))}static isBreakingSpace(e,t){return typeof e!="string"?!1:ot._breakingSpaces.includes(e.charCodeAt(0))}static tokenize(e){const t=[];let i="";if(typeof e!="string")return t;for(let n=0;n<e.length;n++){const r=e[n],a=e[n+1];if(ot.isBreakingSpace(r,a)||ot.isNewline(r)){i!==""&&(t.push(i),i=""),t.push(r);continue}i+=r}return i!==""&&t.push(i),t}static canBreakWords(e,t){return t}static canBreakChars(e,t,i,n,r){return!0}static wordWrapSplit(e){return ot.graphemeSegmenter(e)}static measureFont(e){if(ot._fonts[e])return ot._fonts[e];const t={ascent:0,descent:0,fontSize:0},i=ot._canvas,n=ot._context;n.font=e;const r=ot.METRICS_STRING+ot.BASELINE_SYMBOL,a=Math.ceil(n.measureText(r).width);let o=Math.ceil(n.measureText(ot.BASELINE_SYMBOL).width);const h=Math.ceil(ot.HEIGHT_MULTIPLIER*o);if(o=o*ot.BASELINE_MULTIPLIER|0,a===0||h===0)return ot._fonts[e]=t,t;i.width=a,i.height=h,n.fillStyle="#f00",n.fillRect(0,0,a,h),n.font=e,n.textBaseline="alphabetic",n.fillStyle="#000",n.fillText(r,0,o);const l=n.getImageData(0,0,a,h).data,c=l.length,u=a*4;let d=0,f=0,p=!1;for(d=0;d<o;++d){for(let m=0;m<u;m+=4)if(l[f+m]!==255){p=!0;break}if(!p)f+=u;else break}for(t.ascent=o-d,f=c-u,p=!1,d=h;d>o;--d){for(let m=0;m<u;m+=4)if(l[f+m]!==255){p=!0;break}if(!p)f-=u;else break}return t.descent=d-o,t.fontSize=t.ascent+t.descent,ot._fonts[e]=t,t}static clearMetrics(e=""){e?delete ot._fonts[e]:ot._fonts={}}static get _canvas(){var e;if(!ot.__canvas){let t;try{const i=new OffscreenCanvas(0,0);if((e=i.getContext("2d",Mc))!=null&&e.measureText)return ot.__canvas=i,i;t=We.ADAPTER.createCanvas()}catch{t=We.ADAPTER.createCanvas()}t.width=t.height=10,ot.__canvas=t}return ot.__canvas}static get _context(){return ot.__context||(ot.__context=ot._canvas.getContext("2d",Mc)),ot.__context}};vn.METRICS_STRING="|ÉqÅ",vn.BASELINE_SYMBOL="M",vn.BASELINE_MULTIPLIER=1.4,vn.HEIGHT_MULTIPLIER=2,vn.graphemeSegmenter=(()=>{if(typeof(Intl==null?void 0:Intl.Segmenter)=="function"){const s=new Intl.Segmenter;return e=>[...s.segment(e)].map(t=>t.segment)}return s=>[...s]})(),vn.experimentalLetterSpacing=!1,vn._fonts={},vn._newlines=[10,13],vn._breakingSpaces=[9,32,8192,8193,8194,8195,8196,8197,8198,8200,8201,8202,8287,12288];let Ir=vn;const iI=["serif","sans-serif","monospace","cursive","fantasy","system-ui"],eb=class ol{constructor(e){this.styleID=0,this.reset(),uf(this,e,e)}clone(){const e={};return uf(e,this,ol.defaultStyle),new ol(e)}reset(){uf(this,ol.defaultStyle,ol.defaultStyle)}get align(){return this._align}set align(e){this._align!==e&&(this._align=e,this.styleID++)}get breakWords(){return this._breakWords}set breakWords(e){this._breakWords!==e&&(this._breakWords=e,this.styleID++)}get dropShadow(){return this._dropShadow}set dropShadow(e){this._dropShadow!==e&&(this._dropShadow=e,this.styleID++)}get dropShadowAlpha(){return this._dropShadowAlpha}set dropShadowAlpha(e){this._dropShadowAlpha!==e&&(this._dropShadowAlpha=e,this.styleID++)}get dropShadowAngle(){return this._dropShadowAngle}set dropShadowAngle(e){this._dropShadowAngle!==e&&(this._dropShadowAngle=e,this.styleID++)}get dropShadowBlur(){return this._dropShadowBlur}set dropShadowBlur(e){this._dropShadowBlur!==e&&(this._dropShadowBlur=e,this.styleID++)}get dropShadowColor(){return this._dropShadowColor}set dropShadowColor(e){const t=hf(e);this._dropShadowColor!==t&&(this._dropShadowColor=t,this.styleID++)}get dropShadowDistance(){return this._dropShadowDistance}set dropShadowDistance(e){this._dropShadowDistance!==e&&(this._dropShadowDistance=e,this.styleID++)}get fill(){return this._fill}set fill(e){const t=hf(e);this._fill!==t&&(this._fill=t,this.styleID++)}get fillGradientType(){return this._fillGradientType}set fillGradientType(e){this._fillGradientType!==e&&(this._fillGradientType=e,this.styleID++)}get fillGradientStops(){return this._fillGradientStops}set fillGradientStops(e){sI(this._fillGradientStops,e)||(this._fillGradientStops=e,this.styleID++)}get fontFamily(){return this._fontFamily}set fontFamily(e){this.fontFamily!==e&&(this._fontFamily=e,this.styleID++)}get fontSize(){return this._fontSize}set fontSize(e){this._fontSize!==e&&(this._fontSize=e,this.styleID++)}get fontStyle(){return this._fontStyle}set fontStyle(e){this._fontStyle!==e&&(this._fontStyle=e,this.styleID++)}get fontVariant(){return this._fontVariant}set fontVariant(e){this._fontVariant!==e&&(this._fontVariant=e,this.styleID++)}get fontWeight(){return this._fontWeight}set fontWeight(e){this._fontWeight!==e&&(this._fontWeight=e,this.styleID++)}get letterSpacing(){return this._lett
Line truncated
|
||||||
#define SHADER_NAME Tiling-Sprite-100
|
#define SHADER_NAME Tiling-Sprite-100
|
||||||
|
|
||||||
precision lowp float;
|
precision lowp float;
|
||||||
@@ -1102,7 +1102,7 @@ void main(void)
|
|||||||
vec4 texSample = texture2D(uSampler, vTextureCoord);
|
vec4 texSample = texture2D(uSampler, vTextureCoord);
|
||||||
gl_FragColor = texSample * uColor;
|
gl_FragColor = texSample * uColor;
|
||||||
}
|
}
|
||||||
`;const _c=new li;class a1 extends Su{constructor(e){super(e),e.runners.contextChange.add(this),this.quad=new ub,this.state=Un.for2d()}contextChange(){const e=this.renderer,t={globals:e.globalUniforms};this.simpleShader=cn.from(Tv,yI,t),this.shader=e.context.webGLVersion>1?cn.from(gI,pI,t):cn.from(Tv,vI,t)}render(e){const t=this.renderer,i=this.quad;let n=i.vertices;n[0]=n[6]=e._width*-e.anchor.x,n[1]=n[3]=e._height*-e.anchor.y,n[2]=n[4]=e._width*(1-e.anchor.x),n[5]=n[7]=e._height*(1-e.anchor.y);const r=e.uvRespectAnchor?e.anchor.x:0,a=e.uvRespectAnchor?e.anchor.y:0;n=i.uvs,n[0]=n[6]=-r,n[1]=n[3]=-a,n[2]=n[4]=1-r,n[5]=n[7]=1-a,i.invalidate();const o=e._texture,h=o.baseTexture,l=h.alphaMode>0,c=e.tileTransform.localTransform,u=e.uvMatrix;let d=h.isPowerOfTwo&&o.frame.width===h.width&&o.frame.height===h.height;d&&(h._glTextures[t.CONTEXT_UID]?d=h.wrapMode!==an.CLAMP:h.wrapMode===an.CLAMP&&(h.wrapMode=an.REPEAT));const f=d?this.simpleShader:this.shader,p=o.width,m=o.height,g=e._width,v=e._height;_c.set(c.a*p/g,c.b*p/v,c.c*m/g,c.d*m/v,c.tx/g,c.ty/v),_c.invert(),d?_c.prepend(u.mapCoord):(f.uniforms.uMapCoord=u.mapCoord.toArray(!0),f.uniforms.uClampFrame=u.uClampFrame,f.uniforms.uClampOffset=u.uClampOffset),f.uniforms.uTransform=_c.toArray(!0),f.uniforms.uColor=jt.shared.setValue(e.tint).premultiply(e.worldAlpha,l).toArray(f.uniforms.uColor),f.uniforms.translationMatrix=e.transform.worldTransform.toArray(!0),f.uniforms.uSampler=o,t.shader.bind(f),t.geometry.bind(i),this.state.blendMode=Zx(e.blendMode,l),t.state.set(this.state),t.geometry.draw(this.renderer.gl.TRIANGLES,6,0)}}a1.extension={name:"tilingSprite",type:He.RendererPlugin};tt.add(a1);const o1=class ll{constructor(e,t,i){this.linkedSheets=[],(e instanceof yt||e instanceof gt)&&(e={texture:e,data:t,resolutionFilename:i});const{texture:n,data:r,resolutionFilename:a=null,cachePrefix:o=""}=e;this.cachePrefix=o,this._texture=n instanceof gt?n:null,this.baseTexture=n instanceof yt?n:this._texture.baseTexture,this.textures={},this.animations={},this.data=r;const h=this.baseTexture.resource;this.resolution=this._updateResolution(a||(h?h.url:null)),this._frames=this.data.frames,this._frameKeys=Object.keys(this._frames),this._batchIndex=0,this._callback=null}_updateResolution(e=null){const{scale:t}=this.data.meta;let i=pr(e,null);return i===null&&(i=typeof t=="number"?t:parseFloat(t??"1")),i!==1&&this.baseTexture.setResolution(i),i}parse(){return new Promise(e=>{this._callback=e,this._batchIndex=0,this._frameKeys.length<=ll.BATCH_SIZE?(this._processFrames(0),this._processAnimations(),this._parseComplete()):this._nextBatch()})}_processFrames(e){let t=e;const i=ll.BATCH_SIZE;for(;t-e<i&&t<this._frameKeys.length;){const n=this._frameKeys[t],r=this._frames[n],a=r.frame;if(a){let o=null,h=null;const l=r.trimmed!==!1&&r.sourceSize?r.sourceSize:r.frame,c=new Tt(0,0,Math.floor(l.w)/this.resolution,Math.floor(l.h)/this.resolution);r.rotated?o=new Tt(Math.floor(a.x)/this.resolution,Math.floor(a.y)/this.resolution,Math.floor(a.h)/this.resolution,Math.floor(a.w)/this.resolution):o=new Tt(Math.floor(a.x)/this.resolution,Math.floor(a.y)/this.resolution,Math.floor(a.w)/this.resolution,Math.floor(a.h)/this.resolution),r.trimmed!==!1&&r.spriteSourceSize&&(h=new Tt(Math.floor(r.spriteSourceSize.x)/this.resolution,Math.floor(r.spriteSourceSize.y)/this.resolution,Math.floor(a.w)/this.resolution,Math.floor(a.h)/this.resolution)),this.textures[n]=new gt(this.baseTexture,o,c,h,r.rotated?2:0,r.anchor,r.borders),gt.addToCache(this.textures[n],this.cachePrefix+n.toString())}t++}}_processAnimations(){const e=this.data.animations||{};for(const t in e){this.animations[t]=[];for(let i=0;i<e[t].length;i++){const n=e[t][i];this.animations[t].push(this.textures[n])}}}_parseComplete(){const e=this._callback;this._callback=null,this._batchIndex=0,e.call(this,this.textures)}_nextBatch(){this._processFrames(this._batchIndex*ll.BATCH_SIZE),this._batchIndex++,setTimeout(()=>{this._batchIndex*ll.BATCH_SIZE<this._frameKeys.length?this._nextBatch():(this._processAnimations(),this._parseComplete())},0)}destroy(e=!1){var t;for(const i in this.textures)this.textures[i].destroy();this._frames=null,this._frameKeys=null,this.data=null,this.textures=null,e&&((t=this._texture)==null||t.destroy(),this.baseTexture.destroy()),this._texture=null,this.baseTexture=null,this.linkedSheets=[]}};o1.BATCH_SIZE=1e3;let Av=o1;const xI=["jpg","png","jpeg","avif","webp","s3tc","s3tc_sRGB","etc","etc1","pvrtc","atc","astc","bptc"];function l1(s,e,t){const i={};if(s.forEach(n=>{i[n]=e}),Object.keys(e.textures).forEach(n=>{i[`${e.cachePrefix}${n}`]=e.textures[n]}),!t){const n=wi.dirname(s[0]);e.linkedSheets.forEach((r,a)=>{Object.assign(i,l1([`${n}/${e.data.meta.related_multi_packs[a]}`],r,!0))})}return i}const bI={extension:He.Asset,cache:{test:s=>s instanceof Av,getCacheableAssets:(s,e)=>l1(s,e,!1)},resolver:{test:s=>{const e=s.split("?")[0].split("."),t=e.pop(),i=e.pop();return t==="json"&&xI.includes(i)},parse:s=>{var t;const
Line truncated
|
`;const _c=new li;class ab extends Su{constructor(e){super(e),e.runners.contextChange.add(this),this.quad=new u1,this.state=Un.for2d()}contextChange(){const e=this.renderer,t={globals:e.globalUniforms};this.simpleShader=cn.from(Tv,yI,t),this.shader=e.context.webGLVersion>1?cn.from(gI,pI,t):cn.from(Tv,vI,t)}render(e){const t=this.renderer,i=this.quad;let n=i.vertices;n[0]=n[6]=e._width*-e.anchor.x,n[1]=n[3]=e._height*-e.anchor.y,n[2]=n[4]=e._width*(1-e.anchor.x),n[5]=n[7]=e._height*(1-e.anchor.y);const r=e.uvRespectAnchor?e.anchor.x:0,a=e.uvRespectAnchor?e.anchor.y:0;n=i.uvs,n[0]=n[6]=-r,n[1]=n[3]=-a,n[2]=n[4]=1-r,n[5]=n[7]=1-a,i.invalidate();const o=e._texture,h=o.baseTexture,l=h.alphaMode>0,c=e.tileTransform.localTransform,u=e.uvMatrix;let d=h.isPowerOfTwo&&o.frame.width===h.width&&o.frame.height===h.height;d&&(h._glTextures[t.CONTEXT_UID]?d=h.wrapMode!==an.CLAMP:h.wrapMode===an.CLAMP&&(h.wrapMode=an.REPEAT));const f=d?this.simpleShader:this.shader,p=o.width,m=o.height,g=e._width,v=e._height;_c.set(c.a*p/g,c.b*p/v,c.c*m/g,c.d*m/v,c.tx/g,c.ty/v),_c.invert(),d?_c.prepend(u.mapCoord):(f.uniforms.uMapCoord=u.mapCoord.toArray(!0),f.uniforms.uClampFrame=u.uClampFrame,f.uniforms.uClampOffset=u.uClampOffset),f.uniforms.uTransform=_c.toArray(!0),f.uniforms.uColor=jt.shared.setValue(e.tint).premultiply(e.worldAlpha,l).toArray(f.uniforms.uColor),f.uniforms.translationMatrix=e.transform.worldTransform.toArray(!0),f.uniforms.uSampler=o,t.shader.bind(f),t.geometry.bind(i),this.state.blendMode=Zx(e.blendMode,l),t.state.set(this.state),t.geometry.draw(this.renderer.gl.TRIANGLES,6,0)}}ab.extension={name:"tilingSprite",type:He.RendererPlugin};tt.add(ab);const ob=class ll{constructor(e,t,i){this.linkedSheets=[],(e instanceof yt||e instanceof gt)&&(e={texture:e,data:t,resolutionFilename:i});const{texture:n,data:r,resolutionFilename:a=null,cachePrefix:o=""}=e;this.cachePrefix=o,this._texture=n instanceof gt?n:null,this.baseTexture=n instanceof yt?n:this._texture.baseTexture,this.textures={},this.animations={},this.data=r;const h=this.baseTexture.resource;this.resolution=this._updateResolution(a||(h?h.url:null)),this._frames=this.data.frames,this._frameKeys=Object.keys(this._frames),this._batchIndex=0,this._callback=null}_updateResolution(e=null){const{scale:t}=this.data.meta;let i=pr(e,null);return i===null&&(i=typeof t=="number"?t:parseFloat(t??"1")),i!==1&&this.baseTexture.setResolution(i),i}parse(){return new Promise(e=>{this._callback=e,this._batchIndex=0,this._frameKeys.length<=ll.BATCH_SIZE?(this._processFrames(0),this._processAnimations(),this._parseComplete()):this._nextBatch()})}_processFrames(e){let t=e;const i=ll.BATCH_SIZE;for(;t-e<i&&t<this._frameKeys.length;){const n=this._frameKeys[t],r=this._frames[n],a=r.frame;if(a){let o=null,h=null;const l=r.trimmed!==!1&&r.sourceSize?r.sourceSize:r.frame,c=new Tt(0,0,Math.floor(l.w)/this.resolution,Math.floor(l.h)/this.resolution);r.rotated?o=new Tt(Math.floor(a.x)/this.resolution,Math.floor(a.y)/this.resolution,Math.floor(a.h)/this.resolution,Math.floor(a.w)/this.resolution):o=new Tt(Math.floor(a.x)/this.resolution,Math.floor(a.y)/this.resolution,Math.floor(a.w)/this.resolution,Math.floor(a.h)/this.resolution),r.trimmed!==!1&&r.spriteSourceSize&&(h=new Tt(Math.floor(r.spriteSourceSize.x)/this.resolution,Math.floor(r.spriteSourceSize.y)/this.resolution,Math.floor(a.w)/this.resolution,Math.floor(a.h)/this.resolution)),this.textures[n]=new gt(this.baseTexture,o,c,h,r.rotated?2:0,r.anchor,r.borders),gt.addToCache(this.textures[n],this.cachePrefix+n.toString())}t++}}_processAnimations(){const e=this.data.animations||{};for(const t in e){this.animations[t]=[];for(let i=0;i<e[t].length;i++){const n=e[t][i];this.animations[t].push(this.textures[n])}}}_parseComplete(){const e=this._callback;this._callback=null,this._batchIndex=0,e.call(this,this.textures)}_nextBatch(){this._processFrames(this._batchIndex*ll.BATCH_SIZE),this._batchIndex++,setTimeout(()=>{this._batchIndex*ll.BATCH_SIZE<this._frameKeys.length?this._nextBatch():(this._processAnimations(),this._parseComplete())},0)}destroy(e=!1){var t;for(const i in this.textures)this.textures[i].destroy();this._frames=null,this._frameKeys=null,this.data=null,this.textures=null,e&&((t=this._texture)==null||t.destroy(),this.baseTexture.destroy()),this._texture=null,this.baseTexture=null,this.linkedSheets=[]}};ob.BATCH_SIZE=1e3;let Av=ob;const xI=["jpg","png","jpeg","avif","webp","s3tc","s3tc_sRGB","etc","etc1","pvrtc","atc","astc","bptc"];function lb(s,e,t){const i={};if(s.forEach(n=>{i[n]=e}),Object.keys(e.textures).forEach(n=>{i[`${e.cachePrefix}${n}`]=e.textures[n]}),!t){const n=wi.dirname(s[0]);e.linkedSheets.forEach((r,a)=>{Object.assign(i,lb([`${n}/${e.data.meta.related_multi_packs[a]}`],r,!0))})}return i}const bI={extension:He.Asset,cache:{test:s=>s instanceof Av,getCacheableAssets:(s,e)=>lb(s,e,!1)},resolver:{test:s=>{const e=s.split("?")[0].split("."),t=e.pop(),i=e.pop();return t==="json"&&xI.includes(i)},parse:s=>{var t;const
Line truncated
|
||||||
varying vec2 vTextureCoord;\r
|
varying vec2 vTextureCoord;\r
|
||||||
uniform sampler2D uSampler;\r
|
uniform sampler2D uSampler;\r
|
||||||
\r
|
\r
|
||||||
@@ -1156,8 +1156,8 @@ void main(void)\r
|
|||||||
\r
|
\r
|
||||||
vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;\r
|
vTextureCoord = (uTextureMatrix * vec3(aTextureCoord, 1.0)).xy;\r
|
||||||
}\r
|
}\r
|
||||||
`;const Ev=[],Cv=[],Iv=[],EI=class h1 extends ji{constructor(e,t={}){super();const{align:i,tint:n,maxWidth:r,letterSpacing:a,fontName:o,fontSize:h}=Object.assign({},h1.styleDefaults,t);if(!Ar.available[o])throw new Error(`Missing BitmapFont "${o}"`);this._activePagesMeshData=[],this._textWidth=0,this._textHeight=0,this._align=i,this._tintColor=new jt(n),this._font=void 0,this._fontName=o,this._fontSize=h,this.text=e,this._maxWidth=r,this._maxLineHeight=0,this._letterSpacing=a,this._anchor=new sr(()=>{this.dirty=!0},this,0,0),this._roundPixels=We.ROUND_PIXELS,this.dirty=!0,this._resolution=We.RESOLUTION,this._autoResolution=!0,this._textureCache={}}updateText(){var I;const e=Ar.available[this._fontName],t=this.fontSize,i=t/e.size,n=new Ut,r=[],a=[],o=[],h=this._text.replace(/(?:\r\n|\r)/g,`
|
`;const Ev=[],Cv=[],Iv=[],EI=class hb extends ji{constructor(e,t={}){super();const{align:i,tint:n,maxWidth:r,letterSpacing:a,fontName:o,fontSize:h}=Object.assign({},hb.styleDefaults,t);if(!Ar.available[o])throw new Error(`Missing BitmapFont "${o}"`);this._activePagesMeshData=[],this._textWidth=0,this._textHeight=0,this._align=i,this._tintColor=new jt(n),this._font=void 0,this._fontName=o,this._fontSize=h,this.text=e,this._maxWidth=r,this._maxLineHeight=0,this._letterSpacing=a,this._anchor=new sr(()=>{this.dirty=!0},this,0,0),this._roundPixels=We.ROUND_PIXELS,this.dirty=!0,this._resolution=We.RESOLUTION,this._autoResolution=!0,this._textureCache={}}updateText(){var I;const e=Ar.available[this._fontName],t=this.fontSize,i=t/e.size,n=new Ut,r=[],a=[],o=[],h=this._text.replace(/(?:\r\n|\r)/g,`
|
||||||
`)||" ",l=c1(h),c=this._maxWidth*e.size/t,u=e.distanceFieldType==="none"?Ev:Cv;let d=null,f=0,p=0,m=0,g=-1,v=0,b=0,y=0,w=0;for(let k=0;k<l.length;k++){const B=l[k],D=qc(B);if(/(?:\s)/.test(B)&&(g=k,v=f,w++),B==="\r"||B===`
|
`)||" ",l=cb(h),c=this._maxWidth*e.size/t,u=e.distanceFieldType==="none"?Ev:Cv;let d=null,f=0,p=0,m=0,g=-1,v=0,b=0,y=0,w=0;for(let k=0;k<l.length;k++){const B=l[k],D=qc(B);if(/(?:\s)/.test(B)&&(g=k,v=f,w++),B==="\r"||B===`
|
||||||
`){a.push(f),o.push(-1),p=Math.max(p,f),++m,++b,n.x=0,n.y+=e.lineHeight,d=null,w=0;continue}const U=e.chars[D];if(!U)continue;d&&U.kerning[d]&&(n.x+=U.kerning[d]);const L=Iv.pop()||{texture:gt.EMPTY,line:0,charCode:0,prevSpaces:0,position:new Ut};L.texture=U.texture,L.line=m,L.charCode=D,L.position.x=Math.round(n.x+U.xOffset+this._letterSpacing/2),L.position.y=Math.round(n.y+U.yOffset),L.prevSpaces=w,r.push(L),f=L.position.x+Math.max(U.xAdvance-U.xOffset,U.texture.orig.width),n.x+=U.xAdvance+this._letterSpacing,y=Math.max(y,U.yOffset+U.texture.height),d=D,g!==-1&&c>0&&n.x>c&&(++b,yo(r,1+g-b,1+k-g),k=g,g=-1,a.push(v),o.push(r.length>0?r[r.length-1].prevSpaces:0),p=Math.max(p,v),m++,n.x=0,n.y+=e.lineHeight,d=null,w=0)}const A=l[l.length-1];A!=="\r"&&A!==`
|
`){a.push(f),o.push(-1),p=Math.max(p,f),++m,++b,n.x=0,n.y+=e.lineHeight,d=null,w=0;continue}const U=e.chars[D];if(!U)continue;d&&U.kerning[d]&&(n.x+=U.kerning[d]);const L=Iv.pop()||{texture:gt.EMPTY,line:0,charCode:0,prevSpaces:0,position:new Ut};L.texture=U.texture,L.line=m,L.charCode=D,L.position.x=Math.round(n.x+U.xOffset+this._letterSpacing/2),L.position.y=Math.round(n.y+U.yOffset),L.prevSpaces=w,r.push(L),f=L.position.x+Math.max(U.xAdvance-U.xOffset,U.texture.orig.width),n.x+=U.xAdvance+this._letterSpacing,y=Math.max(y,U.yOffset+U.texture.height),d=D,g!==-1&&c>0&&n.x>c&&(++b,yo(r,1+g-b,1+k-g),k=g,g=-1,a.push(v),o.push(r.length>0?r[r.length-1].prevSpaces:0),p=Math.max(p,v),m++,n.x=0,n.y+=e.lineHeight,d=null,w=0)}const A=l[l.length-1];A!=="\r"&&A!==`
|
||||||
`&&(/(?:\s)/.test(A)&&(f=v),a.push(f),p=Math.max(p,f),o.push(-1));const T=[];for(let k=0;k<=m;k++){let B=0;this._align==="right"?B=p-a[k]:this._align==="center"?B=(p-a[k])/2:this._align==="justify"&&(B=o[k]<0?0:(p-a[k])/o[k]),T.push(B)}const _=r.length,R={},P=[],F=this._activePagesMeshData;u.push(...F);for(let k=0;k<_;k++){const B=r[k].texture,D=B.baseTexture.uid;if(!R[D]){let U=u.pop();if(!U){const N=new Sp;let O,X;e.distanceFieldType==="none"?(O=new Ch(gt.EMPTY),X=dt.NORMAL):(O=new Ch(gt.EMPTY,{program:kn.from(AI,TI),uniforms:{uFWidth:0}}),X=dt.NORMAL_NPM);const E=new Ll(N,O);E.blendMode=X,U={index:0,indexCount:0,vertexCount:0,uvsCount:0,total:0,mesh:E,vertices:null,uvs:null,indices:null}}U.index=0,U.indexCount=0,U.vertexCount=0,U.uvsCount=0,U.total=0;const{_textureCache:L}=this;L[D]=L[D]||new gt(B.baseTexture),U.mesh.texture=L[D],U.mesh.tint=this._tintColor.value,P.push(U),R[D]=U}R[D].total++}for(let k=0;k<F.length;k++)P.includes(F[k])||this.removeChild(F[k].mesh);for(let k=0;k<P.length;k++)P[k].mesh.parent!==this&&this.addChild(P[k].mesh);this._activePagesMeshData=P;for(const k in R){const B=R[k],D=B.total;if(!(((I=B.indices)==null?void 0:I.length)>6*D)||B.vertices.length<Ll.BATCHABLE_SIZE*2)B.vertices=new Float32Array(8*D),B.uvs=new Float32Array(8*D),B.indices=new Uint16Array(6*D);else{const U=B.total,L=B.vertices;for(let N=U*4*2;N<L.length;N++)L[N]=0}B.mesh.size=6*D}for(let k=0;k<_;k++){const B=r[k];let D=B.position.x+T[B.line]*(this._align==="justify"?B.prevSpaces:1);this._roundPixels&&(D=Math.round(D));const U=D*i,L=B.position.y*i,N=B.texture,O=R[N.baseTexture.uid],X=N.frame,E=N._uvs,V=O.index++;O.indices[V*6+0]=0+V*4,O.indices[V*6+1]=1+V*4,O.indices[V*6+2]=2+V*4,O.indices[V*6+3]=0+V*4,O.indices[V*6+4]=2+V*4,O.indices[V*6+5]=3+V*4,O.vertices[V*8+0]=U,O.vertices[V*8+1]=L,O.vertices[V*8+2]=U+X.width*i,O.vertices[V*8+3]=L,O.vertices[V*8+4]=U+X.width*i,O.vertices[V*8+5]=L+X.height*i,O.vertices[V*8+6]=U,O.vertices[V*8+7]=L+X.height*i,O.uvs[V*8+0]=E.x0,O.uvs[V*8+1]=E.y0,O.uvs[V*8+2]=E.x1,O.uvs[V*8+3]=E.y1,O.uvs[V*8+4]=E.x2,O.uvs[V*8+5]=E.y2,O.uvs[V*8+6]=E.x3,O.uvs[V*8+7]=E.y3}this._textWidth=p*i,this._textHeight=(n.y+e.lineHeight)*i;for(const k in R){const B=R[k];if(this.anchor.x!==0||this.anchor.y!==0){let N=0;const O=this._textWidth*this.anchor.x,X=this._textHeight*this.anchor.y;for(let E=0;E<B.total;E++)B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X}this._maxLineHeight=y*i;const D=B.mesh.geometry.getBuffer("aVertexPosition"),U=B.mesh.geometry.getBuffer("aTextureCoord"),L=B.mesh.geometry.getIndex();D.data=B.vertices,U.data=B.uvs,L.data=B.indices,D.update(),U.update(),L.update()}for(let k=0;k<r.length;k++)Iv.push(r[k]);this._font=e,this.dirty=!1}updateTransform(){this.validate(),this.containerUpdateTransform()}_render(e){this._autoResolution&&this._resolution!==e.resolution&&(this._resolution=e.resolution,this.dirty=!0);const{distanceFieldRange:t,distanceFieldType:i,size:n}=Ar.available[this._fontName];if(i!=="none"){const{a:r,b:a,c:o,d:h}=this.worldTransform,l=Math.sqrt(r*r+a*a),c=Math.sqrt(o*o+h*h),u=(Math.abs(l)+Math.abs(c))/2,d=this.fontSize/n,f=e._view.resolution;for(const p of this._activePagesMeshData)p.mesh.shader.uniforms.uFWidth=u*t*d*f}super._render(e)}getLocalBounds(){return this.validate(),super.getLocalBounds()}validate(){const e=Ar.available[this._fontName];if(!e)throw new Error(`Missing BitmapFont "${this._fontName}"`);this._font!==e&&(this.dirty=!0),this.dirty&&this.updateText()}get tint(){return this._tintColor.value}set tint(e){if(this.tint!==e){this._tintColor.setValue(e);for(let t=0;t<this._activePagesMeshData.length;t++)this._activePagesMeshData[t].mesh.tint=e}}get align(){return this._align}set align(e){this._align!==e&&(this._align=e,this.dirty=!0)}get fontName(){return this._fontName}set fontName(e){if(!Ar.available[e])throw new Error(`Missing BitmapFont "${e}"`);this._fontName!==e&&(this._fontName=e,this.dirty=!0)}get fontSize(){return this._fontSize??Ar.available[this._fontName].size}set fontSize(e){this._fontSize!==e&&(this._fontSize=e,this.dirty=!0)}get anchor(){return this._anchor}set anchor(e){typeof e=="number"?this._anchor.set(e):this._anchor.copyFrom(e)}get text(){return this._text}set text(e){e=String(e??""),this._text!==e&&(this._text=e,this.dirty=!0)}get maxWidth(){return this._maxWidth}set maxWidth(e){this._maxWidth!==e&&(this._maxWidth=e,this.dirty=!0)}get maxLineHeight(){return this.validate(),this._maxLineHeight}get textWidth(){return this.validate(),this._textWidth}get letterSpacing(){return this._letterSpacing}set letterSpacing(e){this._letterSpacing!==e&&(this._letterSpacing=e,this.dirty=!0)}get roundPixels(){return this._roundPixels}set roundPixels(e){e!==this._roundPixels&&(this._roundPixels=e,this.dirty=!0)}get textHeight(){return this.validate(),this._textHeight}get resolution(){return this._resolution}set resolution(e){this._autoResolution=!1,this._res
Line truncated
|
`&&(/(?:\s)/.test(A)&&(f=v),a.push(f),p=Math.max(p,f),o.push(-1));const T=[];for(let k=0;k<=m;k++){let B=0;this._align==="right"?B=p-a[k]:this._align==="center"?B=(p-a[k])/2:this._align==="justify"&&(B=o[k]<0?0:(p-a[k])/o[k]),T.push(B)}const _=r.length,R={},P=[],F=this._activePagesMeshData;u.push(...F);for(let k=0;k<_;k++){const B=r[k].texture,D=B.baseTexture.uid;if(!R[D]){let U=u.pop();if(!U){const N=new Sp;let O,X;e.distanceFieldType==="none"?(O=new Ch(gt.EMPTY),X=dt.NORMAL):(O=new Ch(gt.EMPTY,{program:kn.from(AI,TI),uniforms:{uFWidth:0}}),X=dt.NORMAL_NPM);const E=new Ll(N,O);E.blendMode=X,U={index:0,indexCount:0,vertexCount:0,uvsCount:0,total:0,mesh:E,vertices:null,uvs:null,indices:null}}U.index=0,U.indexCount=0,U.vertexCount=0,U.uvsCount=0,U.total=0;const{_textureCache:L}=this;L[D]=L[D]||new gt(B.baseTexture),U.mesh.texture=L[D],U.mesh.tint=this._tintColor.value,P.push(U),R[D]=U}R[D].total++}for(let k=0;k<F.length;k++)P.includes(F[k])||this.removeChild(F[k].mesh);for(let k=0;k<P.length;k++)P[k].mesh.parent!==this&&this.addChild(P[k].mesh);this._activePagesMeshData=P;for(const k in R){const B=R[k],D=B.total;if(!(((I=B.indices)==null?void 0:I.length)>6*D)||B.vertices.length<Ll.BATCHABLE_SIZE*2)B.vertices=new Float32Array(8*D),B.uvs=new Float32Array(8*D),B.indices=new Uint16Array(6*D);else{const U=B.total,L=B.vertices;for(let N=U*4*2;N<L.length;N++)L[N]=0}B.mesh.size=6*D}for(let k=0;k<_;k++){const B=r[k];let D=B.position.x+T[B.line]*(this._align==="justify"?B.prevSpaces:1);this._roundPixels&&(D=Math.round(D));const U=D*i,L=B.position.y*i,N=B.texture,O=R[N.baseTexture.uid],X=N.frame,E=N._uvs,V=O.index++;O.indices[V*6+0]=0+V*4,O.indices[V*6+1]=1+V*4,O.indices[V*6+2]=2+V*4,O.indices[V*6+3]=0+V*4,O.indices[V*6+4]=2+V*4,O.indices[V*6+5]=3+V*4,O.vertices[V*8+0]=U,O.vertices[V*8+1]=L,O.vertices[V*8+2]=U+X.width*i,O.vertices[V*8+3]=L,O.vertices[V*8+4]=U+X.width*i,O.vertices[V*8+5]=L+X.height*i,O.vertices[V*8+6]=U,O.vertices[V*8+7]=L+X.height*i,O.uvs[V*8+0]=E.x0,O.uvs[V*8+1]=E.y0,O.uvs[V*8+2]=E.x1,O.uvs[V*8+3]=E.y1,O.uvs[V*8+4]=E.x2,O.uvs[V*8+5]=E.y2,O.uvs[V*8+6]=E.x3,O.uvs[V*8+7]=E.y3}this._textWidth=p*i,this._textHeight=(n.y+e.lineHeight)*i;for(const k in R){const B=R[k];if(this.anchor.x!==0||this.anchor.y!==0){let N=0;const O=this._textWidth*this.anchor.x,X=this._textHeight*this.anchor.y;for(let E=0;E<B.total;E++)B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X,B.vertices[N++]-=O,B.vertices[N++]-=X}this._maxLineHeight=y*i;const D=B.mesh.geometry.getBuffer("aVertexPosition"),U=B.mesh.geometry.getBuffer("aTextureCoord"),L=B.mesh.geometry.getIndex();D.data=B.vertices,U.data=B.uvs,L.data=B.indices,D.update(),U.update(),L.update()}for(let k=0;k<r.length;k++)Iv.push(r[k]);this._font=e,this.dirty=!1}updateTransform(){this.validate(),this.containerUpdateTransform()}_render(e){this._autoResolution&&this._resolution!==e.resolution&&(this._resolution=e.resolution,this.dirty=!0);const{distanceFieldRange:t,distanceFieldType:i,size:n}=Ar.available[this._fontName];if(i!=="none"){const{a:r,b:a,c:o,d:h}=this.worldTransform,l=Math.sqrt(r*r+a*a),c=Math.sqrt(o*o+h*h),u=(Math.abs(l)+Math.abs(c))/2,d=this.fontSize/n,f=e._view.resolution;for(const p of this._activePagesMeshData)p.mesh.shader.uniforms.uFWidth=u*t*d*f}super._render(e)}getLocalBounds(){return this.validate(),super.getLocalBounds()}validate(){const e=Ar.available[this._fontName];if(!e)throw new Error(`Missing BitmapFont "${this._fontName}"`);this._font!==e&&(this.dirty=!0),this.dirty&&this.updateText()}get tint(){return this._tintColor.value}set tint(e){if(this.tint!==e){this._tintColor.setValue(e);for(let t=0;t<this._activePagesMeshData.length;t++)this._activePagesMeshData[t].mesh.tint=e}}get align(){return this._align}set align(e){this._align!==e&&(this._align=e,this.dirty=!0)}get fontName(){return this._fontName}set fontName(e){if(!Ar.available[e])throw new Error(`Missing BitmapFont "${e}"`);this._fontName!==e&&(this._fontName=e,this.dirty=!0)}get fontSize(){return this._fontSize??Ar.available[this._fontName].size}set fontSize(e){this._fontSize!==e&&(this._fontSize=e,this.dirty=!0)}get anchor(){return this._anchor}set anchor(e){typeof e=="number"?this._anchor.set(e):this._anchor.copyFrom(e)}get text(){return this._text}set text(e){e=String(e??""),this._text!==e&&(this._text=e,this.dirty=!0)}get maxWidth(){return this._maxWidth}set maxWidth(e){this._maxWidth!==e&&(this._maxWidth=e,this.dirty=!0)}get maxLineHeight(){return this.validate(),this._maxLineHeight}get textWidth(){return this.validate(),this._textWidth}get letterSpacing(){return this._letterSpacing}set letterSpacing(e){this._letterSpacing!==e&&(this._letterSpacing=e,this.dirty=!0)}get roundPixels(){return this._roundPixels}set roundPixels(e){e!==this._roundPixels&&(this._roundPixels=e,this.dirty=!0)}get textHeight(){return this.validate(),this._textHeight}get resolution(){return this._resolution}set resolution(e){this._autoResolution=!1,this._res
Line truncated
|
||||||
@font-face {
|
@font-face {
|
||||||
@@ -1166,7 +1166,7 @@ void main(void)\r
|
|||||||
font-weight: ${t.weight};
|
font-weight: ${t.weight};
|
||||||
font-style: ${t.style};
|
font-style: ${t.style};
|
||||||
font-display: ${t.display};
|
font-display: ${t.display};
|
||||||
}`,this._stylesheet)}get stylesheet(){return this._stylesheet}set stylesheet(e){this._stylesheet!==e&&(this._stylesheet=e,this.styleID++)}normalizeColor(e){return Array.isArray(e)&&(e=xA(e)),typeof e=="number"?yA(e):e}dropShadowToCSS(){let e=this.normalizeColor(this.dropShadowColor);const t=this.dropShadowAlpha,i=Math.round(Math.cos(this.dropShadowAngle)*this.dropShadowDistance),n=Math.round(Math.sin(this.dropShadowAngle)*this.dropShadowDistance);e.startsWith("#")&&t<1&&(e+=(t*255|0).toString(16).padStart(2,"0"));const r=`${i}px ${n}px`;return this.dropShadowBlur>0?`text-shadow: ${r} ${this.dropShadowBlur}px ${e}`:`text-shadow: ${r} ${e}`}reset(){Object.assign(this,ao.defaultOptions)}onBeforeDraw(){const{fontsDirty:e}=this;return this.fontsDirty=!1,this.isSafari&&this._fonts.length>0&&e?new Promise(t=>setTimeout(t,100)):Promise.resolve()}get isSafari(){const{userAgent:e}=We.ADAPTER.getNavigator();return/^((?!chrome|android).)*safari/i.test(e)}set fillGradientStops(e){console.warn("[HTMLTextStyle] fillGradientStops is not supported by HTMLText")}get fillGradientStops(){return super.fillGradientStops}set fillGradientType(e){console.warn("[HTMLTextStyle] fillGradientType is not supported by HTMLText")}get fillGradientType(){return super.fillGradientType}set miterLimit(e){console.warn("[HTMLTextStyle] miterLimit is not supported by HTMLText")}get miterLimit(){return super.miterLimit}set trim(e){console.warn("[HTMLTextStyle] trim is not supported by HTMLText")}get trim(){return super.trim}set textBaseline(e){console.warn("[HTMLTextStyle] textBaseline is not supported by HTMLText")}get textBaseline(){return super.textBaseline}set leading(e){console.warn("[HTMLTextStyle] leading is not supported by HTMLText")}get leading(){return super.leading}set lineJoin(e){console.warn("[HTMLTextStyle] lineJoin is not supported by HTMLText")}get lineJoin(){return super.lineJoin}};Sm.availableFonts={},Sm.defaultOptions={align:"left",breakWords:!1,dropShadow:!1,dropShadowAlpha:1,dropShadowAngle:Math.PI/6,dropShadowBlur:0,dropShadowColor:"black",dropShadowDistance:5,fill:"black",fontFamily:"Arial",fontSize:26,fontStyle:"normal",fontVariant:"normal",fontWeight:"normal",letterSpacing:0,lineHeight:0,padding:0,stroke:"black",strokeThickness:0,whiteSpace:"normal",wordWrap:!1,wordWrapWidth:100};let ff=Sm;const Tc=class oo extends Zr{constructor(e="",t={}){super(gt.EMPTY),this._text=null,this._style=null,this._autoResolution=!0,this.localStyleID=-1,this.dirty=!1,this._updateID=0,this.ownsStyle=!1;const i=new Image,n=gt.from(i,{scaleMode:We.SCALE_MODE,resourceOptions:{autoLoad:!1}});n.orig=new Tt,n.trim=new Tt,this.texture=n;const r="http://www.w3.org/2000/svg",a="http://www.w3.org/1999/xhtml",o=document.createElementNS(r,"svg"),h=document.createElementNS(r,"foreignObject"),l=document.createElementNS(a,"div"),c=document.createElementNS(a,"style");h.setAttribute("width","10000"),h.setAttribute("height","10000"),h.style.overflow="hidden",o.appendChild(h),this.maxWidth=oo.defaultMaxWidth,this.maxHeight=oo.defaultMaxHeight,this._domElement=l,this._styleElement=c,this._svgRoot=o,this._foreignObject=h,this._foreignObject.appendChild(c),this._foreignObject.appendChild(l),this._image=i,this._loadImage=new Image,this._autoResolution=oo.defaultAutoResolution,this._resolution=oo.defaultResolution??We.RESOLUTION,this.text=e,this.style=t}measureText(e){var c,u;const{text:t,style:i,resolution:n}=Object.assign({text:this._text,style:this._style,resolution:this._resolution},e);Object.assign(this._domElement,{innerHTML:t,style:i.toCSS(n)}),this._styleElement.textContent=i.toGlobalCSS(),document.body.appendChild(this._svgRoot);const r=this._domElement.getBoundingClientRect();this._svgRoot.remove();const{width:a,height:o}=r;(a>this.maxWidth||o>this.maxHeight)&&console.warn("[HTMLText] Large expanse of text, increase HTMLText.maxWidth or HTMLText.maxHeight property.");const h=Math.min(this.maxWidth,Math.ceil(a)),l=Math.min(this.maxHeight,Math.ceil(o));return this._svgRoot.setAttribute("width",h.toString()),this._svgRoot.setAttribute("height",l.toString()),t!==this._text&&(this._domElement.innerHTML=this._text),i!==this._style&&(Object.assign(this._domElement,{style:(c=this._style)==null?void 0:c.toCSS(n)}),this._styleElement.textContent=(u=this._style)==null?void 0:u.toGlobalCSS()),{width:h+i.padding*2,height:l+i.padding*2}}async updateText(e=!0){const{style:t,_image:i,_loadImage:n}=this;if(this.localStyleID!==t.styleID&&(this.dirty=!0,this.localStyleID=t.styleID),!this.dirty&&e)return;const{width:r,height:a}=this.measureText();i.width=n.width=Math.ceil(Math.max(1,r)),i.height=n.height=Math.ceil(Math.max(1,a)),this._updateID++;const o=this._updateID;await new Promise(h=>{n.onload=async()=>{if(o<this._updateID){h();return}await t.onBeforeDraw(),i.src=n.src,n.onload=null,n.src="",this.updateTexture(),h()};const l=new XMLSerializer().serializeToString(this._svgRoot);n.src=`data:image/svg+xml;charset=utf8,${encodeURIComponent(l)}`})}get source(){ret
Line truncated
|
}`,this._stylesheet)}get stylesheet(){return this._stylesheet}set stylesheet(e){this._stylesheet!==e&&(this._stylesheet=e,this.styleID++)}normalizeColor(e){return Array.isArray(e)&&(e=xT(e)),typeof e=="number"?yT(e):e}dropShadowToCSS(){let e=this.normalizeColor(this.dropShadowColor);const t=this.dropShadowAlpha,i=Math.round(Math.cos(this.dropShadowAngle)*this.dropShadowDistance),n=Math.round(Math.sin(this.dropShadowAngle)*this.dropShadowDistance);e.startsWith("#")&&t<1&&(e+=(t*255|0).toString(16).padStart(2,"0"));const r=`${i}px ${n}px`;return this.dropShadowBlur>0?`text-shadow: ${r} ${this.dropShadowBlur}px ${e}`:`text-shadow: ${r} ${e}`}reset(){Object.assign(this,ao.defaultOptions)}onBeforeDraw(){const{fontsDirty:e}=this;return this.fontsDirty=!1,this.isSafari&&this._fonts.length>0&&e?new Promise(t=>setTimeout(t,100)):Promise.resolve()}get isSafari(){const{userAgent:e}=We.ADAPTER.getNavigator();return/^((?!chrome|android).)*safari/i.test(e)}set fillGradientStops(e){console.warn("[HTMLTextStyle] fillGradientStops is not supported by HTMLText")}get fillGradientStops(){return super.fillGradientStops}set fillGradientType(e){console.warn("[HTMLTextStyle] fillGradientType is not supported by HTMLText")}get fillGradientType(){return super.fillGradientType}set miterLimit(e){console.warn("[HTMLTextStyle] miterLimit is not supported by HTMLText")}get miterLimit(){return super.miterLimit}set trim(e){console.warn("[HTMLTextStyle] trim is not supported by HTMLText")}get trim(){return super.trim}set textBaseline(e){console.warn("[HTMLTextStyle] textBaseline is not supported by HTMLText")}get textBaseline(){return super.textBaseline}set leading(e){console.warn("[HTMLTextStyle] leading is not supported by HTMLText")}get leading(){return super.leading}set lineJoin(e){console.warn("[HTMLTextStyle] lineJoin is not supported by HTMLText")}get lineJoin(){return super.lineJoin}};Sm.availableFonts={},Sm.defaultOptions={align:"left",breakWords:!1,dropShadow:!1,dropShadowAlpha:1,dropShadowAngle:Math.PI/6,dropShadowBlur:0,dropShadowColor:"black",dropShadowDistance:5,fill:"black",fontFamily:"Arial",fontSize:26,fontStyle:"normal",fontVariant:"normal",fontWeight:"normal",letterSpacing:0,lineHeight:0,padding:0,stroke:"black",strokeThickness:0,whiteSpace:"normal",wordWrap:!1,wordWrapWidth:100};let ff=Sm;const Tc=class oo extends Zr{constructor(e="",t={}){super(gt.EMPTY),this._text=null,this._style=null,this._autoResolution=!0,this.localStyleID=-1,this.dirty=!1,this._updateID=0,this.ownsStyle=!1;const i=new Image,n=gt.from(i,{scaleMode:We.SCALE_MODE,resourceOptions:{autoLoad:!1}});n.orig=new Tt,n.trim=new Tt,this.texture=n;const r="http://www.w3.org/2000/svg",a="http://www.w3.org/1999/xhtml",o=document.createElementNS(r,"svg"),h=document.createElementNS(r,"foreignObject"),l=document.createElementNS(a,"div"),c=document.createElementNS(a,"style");h.setAttribute("width","10000"),h.setAttribute("height","10000"),h.style.overflow="hidden",o.appendChild(h),this.maxWidth=oo.defaultMaxWidth,this.maxHeight=oo.defaultMaxHeight,this._domElement=l,this._styleElement=c,this._svgRoot=o,this._foreignObject=h,this._foreignObject.appendChild(c),this._foreignObject.appendChild(l),this._image=i,this._loadImage=new Image,this._autoResolution=oo.defaultAutoResolution,this._resolution=oo.defaultResolution??We.RESOLUTION,this.text=e,this.style=t}measureText(e){var c,u;const{text:t,style:i,resolution:n}=Object.assign({text:this._text,style:this._style,resolution:this._resolution},e);Object.assign(this._domElement,{innerHTML:t,style:i.toCSS(n)}),this._styleElement.textContent=i.toGlobalCSS(),document.body.appendChild(this._svgRoot);const r=this._domElement.getBoundingClientRect();this._svgRoot.remove();const{width:a,height:o}=r;(a>this.maxWidth||o>this.maxHeight)&&console.warn("[HTMLText] Large expanse of text, increase HTMLText.maxWidth or HTMLText.maxHeight property.");const h=Math.min(this.maxWidth,Math.ceil(a)),l=Math.min(this.maxHeight,Math.ceil(o));return this._svgRoot.setAttribute("width",h.toString()),this._svgRoot.setAttribute("height",l.toString()),t!==this._text&&(this._domElement.innerHTML=this._text),i!==this._style&&(Object.assign(this._domElement,{style:(c=this._style)==null?void 0:c.toCSS(n)}),this._styleElement.textContent=(u=this._style)==null?void 0:u.toGlobalCSS()),{width:h+i.padding*2,height:l+i.padding*2}}async updateText(e=!0){const{style:t,_image:i,_loadImage:n}=this;if(this.localStyleID!==t.styleID&&(this.dirty=!0,this.localStyleID=t.styleID),!this.dirty&&e)return;const{width:r,height:a}=this.measureText();i.width=n.width=Math.ceil(Math.max(1,r)),i.height=n.height=Math.ceil(Math.max(1,a)),this._updateID++;const o=this._updateID;await new Promise(h=>{n.onload=async()=>{if(o<this._updateID){h();return}await t.onBeforeDraw(),i.src=n.src,n.onload=null,n.src="",this.updateTexture(),h()};const l=new XMLSerializer().serializeToString(this._svgRoot);n.src=`data:image/svg+xml;charset=utf8,${encodeURIComponent(l)}`})}get source(){ret
Line truncated
|
||||||
attribute vec2 aVertexPosition;
|
attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
@@ -1236,4 +1236,4 @@ void main(void){
|
|||||||
gl_FragColor.a = color.a * vColor.a;
|
gl_FragColor.a = color.a * vColor.a;
|
||||||
gl_FragColor.rgb = ((color.a - 1.0) * vDarkColor.a + 1.0 - color.rgb) * vDarkColor.rgb + color.rgb * vColor.rgb;
|
gl_FragColor.rgb = ((color.a - 1.0) * vDarkColor.a + 1.0 - color.rgb) * vDarkColor.rgb + color.rgb * vColor.rgb;
|
||||||
}
|
}
|
||||||
`;class pw extends Cr{constructor(e){super(e),this.shaderGenerator=new ab(hP,uP),this.geometryClass=cP,this.vertexSize=7}packInterleavedGeometry(e,t,i,n,r){const{uint32View:a,float32View:o}=t,h=n/this.vertexSize,l=e.uvs,c=e.indices,u=e.vertexData,d=e._texture.baseTexture._batchLocation,f=Math.min(e.worldAlpha,1),p=jt.shared.setValue(e._tintRGB).toPremultiplied(f,!0),m=jt.shared.setValue(e._darkTintRGB).premultiply(f,!0).toPremultiplied(1,!1);for(let g=0;g<u.length;g+=2)o[n++]=u[g],o[n++]=u[g+1],o[n++]=l[g],o[n++]=l[g+1],a[n++]=p,a[n++]=m,o[n++]=d;for(let g=0;g<c.length;g++)i[r++]=h+c[g]}}x(pw,"extension",{name:"darkTintBatch",type:He.RendererPlugin});tt.add(pw);function dP(s){return s.replace(/\\/g,"/").split("/").pop()||s}function Ip(s,e,t){for(const i of s.pages){const n=e.get(dP(i.name).toLowerCase());if(!n)throw new Error(`Atlas 引用的纹理未选择:${i.name}`);n.baseTexture.alphaMode=i.pma?Ni.PMA:Ni.UNPACK,i.setTexture(t.from(n.baseTexture))}}function gw(s,e){const t=new iw(s);return Ip(t,e,La),new Am(t)}function fP(s,e,t){return{skeletonData:new Dh(gw(e,t)).readSkeletonData(s),SpineClass:Nh}}function mP(s,e,t){return{skeletonData:new Cm(gw(e,t)).readSkeletonData(s),SpineClass:Nh}}const pP="modulepreload",gP=function(s){return"/"+s},Gv={},Hv=function(e,t,i){let n=Promise.resolve();if(t&&t.length>0){let a=function(l){return Promise.all(l.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),h=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));n=a(t.map(l=>{if(l=gP(l),l in Gv)return;Gv[l]=!0;const c=l.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${u}`))return;const d=document.createElement("link");if(d.rel=c?"stylesheet":pP,c||(d.as="script"),d.crossOrigin="",d.href=l,h&&d.setAttribute("nonce",h),document.head.appendChild(d),c)return new Promise((f,p)=>{d.addEventListener("load",f),d.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${l}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return n.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})};async function vw(){const[s,e]=await Promise.all([Hv(()=>import("./index-BUbc0qUx.js"),[]),Hv(()=>import("./index-B1oGFzi8.js"),__vite__mapDeps([0,1]))]);return{core:s,pixi:e}}async function vP(s,e,t){const{core:i,pixi:n}=await vw(),r=new i.TextureAtlas(e);return Ip(r,t,n.SpineTexture),{skeletonData:new i.SkeletonJson(new i.AtlasAttachmentLoader(r)).readSkeletonData(s),SpineClass:n.Spine}}async function yP(s,e,t){const{core:i,pixi:n}=await vw(),r=new i.TextureAtlas(e);return Ip(r,t,n.SpineTexture),{skeletonData:new i.SkeletonBinary(new i.AtlasAttachmentLoader(r)).readSkeletonData(s),SpineClass:n.Spine}}function Lr(s,e,t){const i=Number(s[e]);return Number.isFinite(i)?i:t}function xs(s,e,t,i){s[e]==null&&(s[e]=Lr(s,t,i))}function Rp(s,e,t){if(typeof s.curve!="number")return;const i=Lr(s,"time",0),r=Lr(e,"time",0)-i,a=s.curve,o=Lr(s,"c2",0),h=Lr(s,"c3",1),l=Lr(s,"c4",1);s.curve=t.flatMap(([c,u])=>[i+r*a,c+(u-c)*o,i+r*h,c+(u-c)*l]),delete s.c2,delete s.c3,delete s.c4}function Yr(s,e){if(Array.isArray(s))for(let t=0;t<s.length-1;t++){const i=s[t],n=s[t+1];Rp(i,n,e.map(({key:r,fallback:a})=>[Lr(i,r,a),Lr(n,r,a)]))}}function xP(s){if(Array.isArray(s))for(let e=0;e<s.length-1;e++)Rp(s[e],s[e+1],[[0,1]])}function Qa(s,e){const t=typeof s=="string"?s.replace(/^#/,""):"",i=t.length===6?`${t}ff`:t.padEnd(8,"f").slice(0,8),n=[0,2,4,6].map(r=>Number.parseInt(i.slice(r,r+2),16)/255);return e?n:n.slice(0,3)}function jv(s,e){if(Array.isArray(s))for(let t=0;t<s.length-1;t++){const i=s[t],n=s[t+1],r=e?[...Qa(i.light,!0),...Qa(i.dark,!1)]:Qa(i.color,!0),a=e?[...Qa(n.light,!0),...Qa(n.dark,!1)]:Qa(n.color,!0);Rp(i,n,r.map((o,h)=>[o,a[h]]))}}function bP(s){for(const e of s.bones||[])e.inherit==null&&e.transform!=null&&(e.inherit=e.transform);for(const e of s.transform||[])xs(e,"mixRotate","rotateMix",1),xs(e,"mixX","translateMix",1),e.mixY==null&&(e.mixY=e.mixX),xs(e,"mixScaleX","scaleMix",1),e.mixScaleY==null&&(e.mixScaleY=e.mixScaleX),xs(e,"mixShearY","shearMix",1);for(const e of s.path||[])xs(e,"mixRotate","rotateMix",1),xs(e,"mixX","translateMix",1),e.mixY==null&&(e.mixY=e.mixX)}function wP(s){for(const e of s.skins||[])for(const t of Object.values(e.attachments||{}))for(const i of Object.values(t||{}))i.parent!=null&&i.timelines==null&&i.deform!=null&&(i.timelines=i.deform)}function SP(s){for(const e of Object.values(s.slots||{}))e.color&&!e.rgba&&(e.rgba=e.color,delete e.color),e.twoColor&&!e.rgba2&&(e.rgba2=e.twoColor,delete e.twoColor),jv(e.rgba,!1),jv(e.rgba2,!0)}function MP(s){for(const e of Object.values(s.bones||{})){for(const t of e.rotate||[])xs(t,"value","angle",0);Yr(e.rotate,[{key:"value",fallback:0}]),Yr(e.tra
Line truncated
|
`;class pw extends Cr{constructor(e){super(e),this.shaderGenerator=new a1(hP,uP),this.geometryClass=cP,this.vertexSize=7}packInterleavedGeometry(e,t,i,n,r){const{uint32View:a,float32View:o}=t,h=n/this.vertexSize,l=e.uvs,c=e.indices,u=e.vertexData,d=e._texture.baseTexture._batchLocation,f=Math.min(e.worldAlpha,1),p=jt.shared.setValue(e._tintRGB).toPremultiplied(f,!0),m=jt.shared.setValue(e._darkTintRGB).premultiply(f,!0).toPremultiplied(1,!1);for(let g=0;g<u.length;g+=2)o[n++]=u[g],o[n++]=u[g+1],o[n++]=l[g],o[n++]=l[g+1],a[n++]=p,a[n++]=m,o[n++]=d;for(let g=0;g<c.length;g++)i[r++]=h+c[g]}}x(pw,"extension",{name:"darkTintBatch",type:He.RendererPlugin});tt.add(pw);function dP(s){return s.replace(/\\/g,"/").split("/").pop()||s}function Ip(s,e,t){for(const i of s.pages){const n=e.get(dP(i.name).toLowerCase());if(!n)throw new Error(`Atlas 引用的纹理未选择:${i.name}`);n.baseTexture.alphaMode=i.pma?Ni.PMA:Ni.UNPACK,i.setTexture(t.from(n.baseTexture))}}function gw(s,e){const t=new iw(s);return Ip(t,e,La),new Am(t)}function fP(s,e,t){return{skeletonData:new Dh(gw(e,t)).readSkeletonData(s),SpineClass:Nh}}function mP(s,e,t){return{skeletonData:new Cm(gw(e,t)).readSkeletonData(s),SpineClass:Nh}}const pP="modulepreload",gP=function(s){return"/"+s},Gv={},Hv=function(e,t,i){let n=Promise.resolve();if(t&&t.length>0){let a=function(l){return Promise.all(l.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),h=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));n=a(t.map(l=>{if(l=gP(l),l in Gv)return;Gv[l]=!0;const c=l.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${u}`))return;const d=document.createElement("link");if(d.rel=c?"stylesheet":pP,c||(d.as="script"),d.crossOrigin="",d.href=l,h&&d.setAttribute("nonce",h),document.head.appendChild(d),c)return new Promise((f,p)=>{d.addEventListener("load",f),d.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${l}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return n.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})};async function vw(){const[s,e]=await Promise.all([Hv(()=>import("./index-Cr68vmVa.js"),[]),Hv(()=>import("./index-hYCzV0U1.js"),__vite__mapDeps([0,1]))]);return{core:s,pixi:e}}async function vP(s,e,t){const{core:i,pixi:n}=await vw(),r=new i.TextureAtlas(e);return Ip(r,t,n.SpineTexture),{skeletonData:new i.SkeletonJson(new i.AtlasAttachmentLoader(r)).readSkeletonData(s),SpineClass:n.Spine}}async function yP(s,e,t){const{core:i,pixi:n}=await vw(),r=new i.TextureAtlas(e);return Ip(r,t,n.SpineTexture),{skeletonData:new i.SkeletonBinary(new i.AtlasAttachmentLoader(r)).readSkeletonData(s),SpineClass:n.Spine}}function Lr(s,e,t){const i=Number(s[e]);return Number.isFinite(i)?i:t}function xs(s,e,t,i){s[e]==null&&(s[e]=Lr(s,t,i))}function Rp(s,e,t){if(typeof s.curve!="number")return;const i=Lr(s,"time",0),r=Lr(e,"time",0)-i,a=s.curve,o=Lr(s,"c2",0),h=Lr(s,"c3",1),l=Lr(s,"c4",1);s.curve=t.flatMap(([c,u])=>[i+r*a,c+(u-c)*o,i+r*h,c+(u-c)*l]),delete s.c2,delete s.c3,delete s.c4}function Yr(s,e){if(Array.isArray(s))for(let t=0;t<s.length-1;t++){const i=s[t],n=s[t+1];Rp(i,n,e.map(({key:r,fallback:a})=>[Lr(i,r,a),Lr(n,r,a)]))}}function xP(s){if(Array.isArray(s))for(let e=0;e<s.length-1;e++)Rp(s[e],s[e+1],[[0,1]])}function Qa(s,e){const t=typeof s=="string"?s.replace(/^#/,""):"",i=t.length===6?`${t}ff`:t.padEnd(8,"f").slice(0,8),n=[0,2,4,6].map(r=>Number.parseInt(i.slice(r,r+2),16)/255);return e?n:n.slice(0,3)}function jv(s,e){if(Array.isArray(s))for(let t=0;t<s.length-1;t++){const i=s[t],n=s[t+1],r=e?[...Qa(i.light,!0),...Qa(i.dark,!1)]:Qa(i.color,!0),a=e?[...Qa(n.light,!0),...Qa(n.dark,!1)]:Qa(n.color,!0);Rp(i,n,r.map((o,h)=>[o,a[h]]))}}function bP(s){for(const e of s.bones||[])e.inherit==null&&e.transform!=null&&(e.inherit=e.transform);for(const e of s.transform||[])xs(e,"mixRotate","rotateMix",1),xs(e,"mixX","translateMix",1),e.mixY==null&&(e.mixY=e.mixX),xs(e,"mixScaleX","scaleMix",1),e.mixScaleY==null&&(e.mixScaleY=e.mixScaleX),xs(e,"mixShearY","shearMix",1);for(const e of s.path||[])xs(e,"mixRotate","rotateMix",1),xs(e,"mixX","translateMix",1),e.mixY==null&&(e.mixY=e.mixX)}function wP(s){for(const e of s.skins||[])for(const t of Object.values(e.attachments||{}))for(const i of Object.values(t||{}))i.parent!=null&&i.timelines==null&&i.deform!=null&&(i.timelines=i.deform)}function SP(s){for(const e of Object.values(s.slots||{}))e.color&&!e.rgba&&(e.rgba=e.color,delete e.color),e.twoColor&&!e.rgba2&&(e.rgba2=e.twoColor,delete e.twoColor),jv(e.rgba,!1),jv(e.rgba2,!0)}function MP(s){for(const e of Object.values(s.bones||{})){for(const t of e.rotate||[])xs(t,"value","angle",0);Yr(e.rotate,[{key:"value",fallback:0}]),Yr(e.tra
Line truncated
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
var Ae=Object.defineProperty;var Be=(m,o,e)=>o in m?Ae(m,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):m[o]=e;var a=(m,o,e)=>Be(m,typeof o!="symbol"?o+"":o,e);import{T as Fe,a as he,b as F,S as ue,M as pe,c as oe,W as ne,B as H,d as z,L as Te,E as O,e as ce,p as q,f as Ee,g as fe,A as Q,h as me,i as De,j as V,s as re,G as we,k as $,l as Y,m as Le,P as Ie,n as Re,o as Ne,C as X,q as be,r as _e,t as Ue,u as Ye,v as Xe,w as ee,x as j,y as ke,z as Ve,D as J,F as ge,V as Ge,H as ae,I as We,J as $e,U as xe,K as E,R as ve,N as ye,O as Pe,Q as Oe,X as He,Y as ze,Z as qe,_ as Ke}from"./index-D08n0lXm.js";import{$ as bt,a0 as kt,a1 as vt,a2 as yt,a3 as Pt,a4 as St,a5 as Mt,a6 as At,a7 as Bt,a8 as Ft,a9 as Et,aa as Dt,ab as Lt,ac as It,ad as Rt,ae as Nt,af as _t,ag as Ut,ah as Yt,ai as Xt,aj as Vt,ak as Gt,al as Wt,am as $t,an as Ot,ao as Ht,ap as zt,aq as qt,ar as Kt,as as Zt,at as Jt,au as Qt,av as jt,aw as es,ax as ts,ay as ss,az as os,aA as ns,aB as as,aC as rs,aD as is,aE as ls,aF as hs,aG as cs,aH as ds,aI as us,aJ as ps,aK as fs,aL as ms,aM as gs,aN as xs,aO as Cs,aP as Ts,aQ as ws,aR as bs,aS as ks,aT as vs,aU as ys,aV as Ps,aW as Ss,aX as Ms,aY as As,aZ as Bs,a_ as Fs,a$ as Es,b0 as Ds,b1 as Ls,b2 as Is,b3 as Rs,b4 as Ns,b5 as _s,b6 as Us,b7 as Ys,b8 as Xs,b9 as Vs,ba as Gs,bb as Ws,bc as $s,bd as Os,be as Hs,bf as zs,bg as qs,bh as Ks,bi as Zs,bj as Js,bk as Qs,bl as js,bm as eo,bn as to,bo as so,bp as oo,bq as no,br as ao,bs as ro,bt as io,bu as lo,bv as ho,bw as co,bx as uo,by as po,bz as fo,bA as mo,bB as go,bC as xo,bD as Co,bE as To,bF as wo,bG as bo,bH as ko,bI as vo,bJ as yo,bK as Po,bL as So,bM as Mo,bN as Ao,bO as Bo,bP as Fo,bQ as Eo,bR as Do,bS as Lo,bT as Io,bU as Ro,bV as No,bW as _o,bX as Uo,bY as Yo,bZ as Xo,b_ as Vo,b$ as Go,c0 as Wo,c1 as $o,c2 as Oo,c3 as Ho}from"./index-D08n0lXm.js";import{SkeletonClipping as de,SkeletonPhysicsMovement as Ze,AnimationStateData as Je,AtlasAttachmentLoader as Qe,SkeletonBounds as je}from"./index-BUbc0qUx.js";import{AssetCache as qo,AssetManagerBase as Ko,Downloader as Zo,SkeletonRendererCore as Jo,Triangulator as Qo}from"./index-BUbc0qUx.js";if(typeof window<"u"&&window.PIXI){const m=window.require;window.require=o=>{if(m)return m(o);if(o.startsWith("@pixi/"))return window.PIXI}}const _=class _ extends Fe{constructor(e){super(e.resource.source);a(this,"texture");this.texture=he.from(e)}static from(e){return _.textureMap.has(e)?_.textureMap.get(e):new _(e)}setFilters(e,t){this.texture.baseTexture.scaleMode=_.toPixiTextureFilter(e),this.texture.baseTexture.mipmap=_.toPixiMipMap(e)}setWraps(e,t){this.texture.baseTexture.wrapMode=_.toPixiTextureWrap(e)}dispose(){this.texture.destroy()}static toPixiTextureFilter(e){switch(e){case F.Nearest:case F.MipMapNearestLinear:case F.MipMapNearestNearest:return ue.NEAREST;case F.Linear:case F.MipMapLinearLinear:case F.MipMapLinearNearest:return ue.LINEAR;default:throw new Error(`Unknown texture filter: ${String(e)}`)}}static toPixiMipMap(e){switch(e){case F.Nearest:case F.Linear:return pe.OFF;case F.MipMapNearestLinear:case F.MipMapNearestNearest:case F.MipMapLinearLinear:case F.MipMapLinearNearest:return pe.ON;default:throw new Error(`Unknown texture filter: ${String(e)}`)}}static toPixiTextureWrap(e){switch(e){case oe.ClampToEdge:return ne.CLAMP;case oe.MirroredRepeat:return ne.MIRRORED_REPEAT;case oe.Repeat:return ne.REPEAT;default:throw new Error(`Unknown texture wrap: ${String(e)}`)}}static toPixiBlending(e){switch(e){case H.Normal:return z.NORMAL;case H.Additive:return z.ADD;case H.Multiply:return z.MULTIPLY;case H.Screen:return z.SCREEN;default:throw new Error(`Unknown blendMode: ${String(e)}`)}}};a(_,"textureMap",new Map);let G=_;const K="spineTextureAtlasLoader",et={extension:O.Asset,resolver:{test:m=>V(m,".atlas"),parse:m=>{var e,t;const o=m.split(".");return{resolution:parseFloat(((t=(e=re.RETINA_PREFIX)==null?void 0:e.exec(m))==null?void 0:t[1])??"1"),format:o[o.length-2],src:m}}},loader:{name:K,extension:{type:O.LoadParser,priority:Te.Normal,name:K},test(m){return V(m,".atlas")},async load(m){const o=await re.ADAPTER.fetch(m);if(!o.ok)throw new Error(`[${K}] Failed to fetch ${m}: ${o.status} ${o.statusText}`);return await o.text()},testParse(m,o){const e=V(o.src,".atlas"),t=typeof m=="string",s=o.loadParser===K;return Promise.resolve((e||s)&&t)},unload(m){m.dispose()},async parse(m,o,e){var p;const t=o.data||{};let s=q.dirname(o.src);s&&s.lastIndexOf("/")!==s.length-1&&(s+="/");const n=new Ee(m);if(t.images instanceof fe||typeof t.images=="string"){const h=t.images;t.images={},t.images[n.pages[0].name]=h}const r=[];let l=!0;for(const h of e.parsers)if(h.name==="loadTextures"){l=(p=h.config)==null?void 0:p.preferCreateImageBitmap;break}Q.setPreferences({preferCreateImageBitmap:!1});for(const h of n.pages){const u=h.name,c=t!=null&&t.images?t.images[u]:void 0;if(c instanceof fe)h.setTexture(G.from(c));else{const i=c??q.normalize([...s.split(q.sep),u].join(q.sep)),g={src:De(i,o.src),data:{...t.imageMetadLine truncated
|
var Ae=Object.defineProperty;var Be=(m,o,e)=>o in m?Ae(m,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):m[o]=e;var a=(m,o,e)=>Be(m,typeof o!="symbol"?o+"":o,e);import{T as Fe,a as he,b as F,S as ue,M as pe,c as oe,W as ne,B as H,d as z,L as Te,E as O,e as ce,p as q,f as Ee,g as fe,A as Q,h as me,i as De,j as V,s as re,G as we,k as $,l as Y,m as Le,P as Ie,n as Re,o as Ne,C as X,q as be,r as _e,t as Ue,u as Ye,v as Xe,w as ee,x as j,y as ke,z as Ve,D as J,F as ge,V as Ge,H as ae,I as We,J as $e,U as xe,K as E,R as ve,N as ye,O as Pe,Q as Oe,X as He,Y as ze,Z as qe,_ as Ke}from"./index-CxN-rEmg.js";import{$ as bt,a0 as kt,a1 as vt,a2 as yt,a3 as Pt,a4 as St,a5 as Mt,a6 as At,a7 as Bt,a8 as Ft,a9 as Et,aa as Dt,ab as Lt,ac as It,ad as Rt,ae as Nt,af as _t,ag as Ut,ah as Yt,ai as Xt,aj as Vt,ak as Gt,al as Wt,am as $t,an as Ot,ao as Ht,ap as zt,aq as qt,ar as Kt,as as Zt,at as Jt,au as Qt,av as jt,aw as es,ax as ts,ay as ss,az as os,aA as ns,aB as as,aC as rs,aD as is,aE as ls,aF as hs,aG as cs,aH as ds,aI as us,aJ as ps,aK as fs,aL as ms,aM as gs,aN as xs,aO as Cs,aP as Ts,aQ as ws,aR as bs,aS as ks,aT as vs,aU as ys,aV as Ps,aW as Ss,aX as Ms,aY as As,aZ as Bs,a_ as Fs,a$ as Es,b0 as Ds,b1 as Ls,b2 as Is,b3 as Rs,b4 as Ns,b5 as _s,b6 as Us,b7 as Ys,b8 as Xs,b9 as Vs,ba as Gs,bb as Ws,bc as $s,bd as Os,be as Hs,bf as zs,bg as qs,bh as Ks,bi as Zs,bj as Js,bk as Qs,bl as js,bm as eo,bn as to,bo as so,bp as oo,bq as no,br as ao,bs as ro,bt as io,bu as lo,bv as ho,bw as co,bx as uo,by as po,bz as fo,bA as mo,bB as go,bC as xo,bD as Co,bE as To,bF as wo,bG as bo,bH as ko,bI as vo,bJ as yo,bK as Po,bL as So,bM as Mo,bN as Ao,bO as Bo,bP as Fo,bQ as Eo,bR as Do,bS as Lo,bT as Io,bU as Ro,bV as No,bW as _o,bX as Uo,bY as Yo,bZ as Xo,b_ as Vo,b$ as Go,c0 as Wo,c1 as $o,c2 as Oo,c3 as Ho}from"./index-CxN-rEmg.js";import{SkeletonClipping as de,SkeletonPhysicsMovement as Ze,AnimationStateData as Je,AtlasAttachmentLoader as Qe,SkeletonBounds as je}from"./index-Cr68vmVa.js";import{AssetCache as qo,AssetManagerBase as Ko,Downloader as Zo,SkeletonRendererCore as Jo,Triangulator as Qo}from"./index-Cr68vmVa.js";if(typeof window<"u"&&window.PIXI){const m=window.require;window.require=o=>{if(m)return m(o);if(o.startsWith("@pixi/"))return window.PIXI}}const _=class _ extends Fe{constructor(e){super(e.resource.source);a(this,"texture");this.texture=he.from(e)}static from(e){return _.textureMap.has(e)?_.textureMap.get(e):new _(e)}setFilters(e,t){this.texture.baseTexture.scaleMode=_.toPixiTextureFilter(e),this.texture.baseTexture.mipmap=_.toPixiMipMap(e)}setWraps(e,t){this.texture.baseTexture.wrapMode=_.toPixiTextureWrap(e)}dispose(){this.texture.destroy()}static toPixiTextureFilter(e){switch(e){case F.Nearest:case F.MipMapNearestLinear:case F.MipMapNearestNearest:return ue.NEAREST;case F.Linear:case F.MipMapLinearLinear:case F.MipMapLinearNearest:return ue.LINEAR;default:throw new Error(`Unknown texture filter: ${String(e)}`)}}static toPixiMipMap(e){switch(e){case F.Nearest:case F.Linear:return pe.OFF;case F.MipMapNearestLinear:case F.MipMapNearestNearest:case F.MipMapLinearLinear:case F.MipMapLinearNearest:return pe.ON;default:throw new Error(`Unknown texture filter: ${String(e)}`)}}static toPixiTextureWrap(e){switch(e){case oe.ClampToEdge:return ne.CLAMP;case oe.MirroredRepeat:return ne.MIRRORED_REPEAT;case oe.Repeat:return ne.REPEAT;default:throw new Error(`Unknown texture wrap: ${String(e)}`)}}static toPixiBlending(e){switch(e){case H.Normal:return z.NORMAL;case H.Additive:return z.ADD;case H.Multiply:return z.MULTIPLY;case H.Screen:return z.SCREEN;default:throw new Error(`Unknown blendMode: ${String(e)}`)}}};a(_,"textureMap",new Map);let G=_;const K="spineTextureAtlasLoader",et={extension:O.Asset,resolver:{test:m=>V(m,".atlas"),parse:m=>{var e,t;const o=m.split(".");return{resolution:parseFloat(((t=(e=re.RETINA_PREFIX)==null?void 0:e.exec(m))==null?void 0:t[1])??"1"),format:o[o.length-2],src:m}}},loader:{name:K,extension:{type:O.LoadParser,priority:Te.Normal,name:K},test(m){return V(m,".atlas")},async load(m){const o=await re.ADAPTER.fetch(m);if(!o.ok)throw new Error(`[${K}] Failed to fetch ${m}: ${o.status} ${o.statusText}`);return await o.text()},testParse(m,o){const e=V(o.src,".atlas"),t=typeof m=="string",s=o.loadParser===K;return Promise.resolve((e||s)&&t)},unload(m){m.dispose()},async parse(m,o,e){var p;const t=o.data||{};let s=q.dirname(o.src);s&&s.lastIndexOf("/")!==s.length-1&&(s+="/");const n=new Ee(m);if(t.images instanceof fe||typeof t.images=="string"){const h=t.images;t.images={},t.images[n.pages[0].name]=h}const r=[];let l=!0;for(const h of e.parsers)if(h.name==="loadTextures"){l=(p=h.config)==null?void 0:p.preferCreateImageBitmap;break}Q.setPreferences({preferCreateImageBitmap:!1});for(const h of n.pages){const u=h.name,c=t!=null&&t.images?t.images[u]:void 0;if(c instanceof fe)h.setTexture(G.from(c));else{const i=c??q.normalize([...s.split(q.sep),u].join(q.sep)),g={src:De(i,o.src),data:{...t.imageMetadLine truncated
|
||||||
attribute vec2 aVertexPosition;
|
attribute vec2 aVertexPosition;
|
||||||
attribute vec2 aTextureCoord;
|
attribute vec2 aTextureCoord;
|
||||||
|
|
||||||
Vendored
+14
-14
@@ -1,17 +1,17 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Spine粒子编辑器</title>
|
<title>Spine粒子编辑器</title>
|
||||||
<style>
|
<style>
|
||||||
html, body { margin: 0; padding: 0; background: #111; color: #eee; font-family: system-ui, -apple-system, sans-serif; }
|
html, body { margin: 0; padding: 0; background: #111; color: #eee; font-family: system-ui, -apple-system, sans-serif; }
|
||||||
#app { display: flex; flex-direction: column; height: 100vh; }
|
#app { display: flex; flex-direction: column; height: 100vh; }
|
||||||
</style>
|
</style>
|
||||||
<script type="module" crossorigin src="/assets/index-D08n0lXm.js"></script>
|
<script type="module" crossorigin src="/assets/index-CxN-rEmg.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Acs2Q5qJ.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-ClCjv-tT.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>␍
|
<div id="app"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
Binary file not shown.
@@ -58,10 +58,10 @@ async function buildWindows() {
|
|||||||
const compiler = process.env.CXX || 'cl.exe'
|
const compiler = process.env.CXX || 'cl.exe'
|
||||||
if (/cl(?:\.exe)?$/i.test(compiler)) {
|
if (/cl(?:\.exe)?$/i.test(compiler)) {
|
||||||
run(compiler, ['/nologo', '/std:c++17', '/O2', '/EHsc', '/utf-8', '/W4', ...commonSources, join(native, 'app_windows.cpp'),
|
run(compiler, ['/nologo', '/std:c++17', '/O2', '/EHsc', '/utf-8', '/W4', ...commonSources, join(native, 'app_windows.cpp'),
|
||||||
'/link', '/SUBSYSTEM:WINDOWS', `'/OUT:${executable}'`.replaceAll("'", ''), 'ws2_32.lib', 'comdlg32.lib', 'user32.lib', 'gdi32.lib'])
|
'/link', '/SUBSYSTEM:WINDOWS', `'/OUT:${executable}'`.replaceAll("'", ''), 'ws2_32.lib', 'iphlpapi.lib', 'comdlg32.lib', 'user32.lib', 'gdi32.lib'])
|
||||||
} else {
|
} else {
|
||||||
run(compiler, ['-std=c++17', '-O2', '-Wall', '-Wextra', '-Werror', ...commonSources, join(native, 'app_windows.cpp'),
|
run(compiler, ['-std=c++17', '-O2', '-Wall', '-Wextra', '-Werror', ...commonSources, join(native, 'app_windows.cpp'),
|
||||||
'-municode', '-mwindows', '-lws2_32', '-lcomdlg32', '-o', executable])
|
'-municode', '-mwindows', '-lws2_32', '-liphlpapi', '-lcomdlg32', '-o', executable])
|
||||||
}
|
}
|
||||||
await cp(join(root, 'connector', 'DISTRIBUTION.md'), join(packageDir, '使用说明.md'))
|
await cp(join(root, 'connector', 'DISTRIBUTION.md'), join(packageDir, '使用说明.md'))
|
||||||
console.log(`已生成 Windows 应用:${executable}`)
|
console.log(`已生成 Windows 应用:${executable}`)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pushd "%SRC_DIR%"
|
|||||||
cl /nologo /std:c++17 /utf-8 /O1 /GL /EHsc /MT /W3 /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0601 ^
|
cl /nologo /std:c++17 /utf-8 /O1 /GL /EHsc /MT /W3 /D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0601 ^
|
||||||
app_windows.cpp config.cpp connector_service.cpp encoding.cpp http_server.cpp json.cpp platform_common.cpp process_windows.cpp workspace.cpp zip.cpp ^
|
app_windows.cpp config.cpp connector_service.cpp encoding.cpp http_server.cpp json.cpp platform_common.cpp process_windows.cpp workspace.cpp zip.cpp ^
|
||||||
/link /SUBSYSTEM:WINDOWS /LTCG /OPT:REF /OPT:ICF ^
|
/link /SUBSYSTEM:WINDOWS /LTCG /OPT:REF /OPT:ICF ^
|
||||||
user32.lib gdi32.lib comdlg32.lib ws2_32.lib shell32.lib ole32.lib ^
|
user32.lib gdi32.lib comdlg32.lib ws2_32.lib iphlpapi.lib shell32.lib ole32.lib ^
|
||||||
/OUT:"%OUT_DIR%\SpineParticleConnector.exe"
|
/OUT:"%OUT_DIR%\SpineParticleConnector.exe"
|
||||||
set "RC=%ERRORLEVEL%"
|
set "RC=%ERRORLEVEL%"
|
||||||
del /q "%SRC_DIR%\*.obj" >nul 2>&1
|
del /q "%SRC_DIR%\*.obj" >nul 2>&1
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
import { Texture } from 'pixi.js'
|
||||||
|
import type { EmitMode, EmitShape, Blend, AttributeMode, RotationMode, ScaleAxisMode, CurvePoint, ColorGradientStop, ColliderShape, CollisionResponse, SceneCollider, ParticleImageResource, ParticleSequenceFrame, TrailImageResource, EmitterConfig, ParticleState, TrailBoneState, TrailState } from './particleTypes'
|
||||||
|
import { BLEND_MAP } from './particleConstants'
|
||||||
|
|
||||||
|
export function defaultConfig(): EmitterConfig {
|
||||||
|
return {
|
||||||
|
texture: null as unknown as Texture,
|
||||||
|
imageResources: [{
|
||||||
|
id: 1,
|
||||||
|
texture: null,
|
||||||
|
textureName: 'star',
|
||||||
|
textureFolder: '',
|
||||||
|
previewUrl: '/images/star.png',
|
||||||
|
imageMode: 'fixed',
|
||||||
|
sequencePlayback: 'loop-forward',
|
||||||
|
sequenceFps: 30,
|
||||||
|
sequenceFrames: [],
|
||||||
|
weight: 100,
|
||||||
|
locked: false,
|
||||||
|
scale: 1,
|
||||||
|
anchorX: 0.5,
|
||||||
|
anchorY: 0.5,
|
||||||
|
colorMode: 'fixed',
|
||||||
|
colorStart: '#ffffff',
|
||||||
|
colorEnd: '#ff4d4d',
|
||||||
|
colorGradient: [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ff4d4d' }],
|
||||||
|
blend: 'normal',
|
||||||
|
independentAlpha: 'off',
|
||||||
|
alpha: 1,
|
||||||
|
alphaCurveMode: '跟随导出',
|
||||||
|
alphaCurve: [{ x: 0, y: 0 }, { x: 0.1, y: 1 }, { x: 0.9, y: 1 }, { x: 1, y: 0 }],
|
||||||
|
}],
|
||||||
|
imageWeightVersion: 1,
|
||||||
|
name: 'ParticleSystem1',
|
||||||
|
mode: 'stream',
|
||||||
|
rate: 20,
|
||||||
|
streamBehavior: 'normal',
|
||||||
|
loopDurationFrames: 30,
|
||||||
|
generateLoopStartAnimation: false,
|
||||||
|
loopStartUseCustomDuration: false,
|
||||||
|
loopStartDurationFrames: 30,
|
||||||
|
generateLoopEndAnimation: false,
|
||||||
|
loopEndUseCustomDuration: false,
|
||||||
|
loopEndDurationFrames: 30,
|
||||||
|
burstCount: 50,
|
||||||
|
burstLoop: false, // 一次性爆发(持续爆发开关已移除)
|
||||||
|
delay: 0,
|
||||||
|
duration: 1, // 持续发射时长;默认 1 秒(30f)。爆发模式运行时按 0 处理。
|
||||||
|
shape: 'point',
|
||||||
|
shapeRangeVersion: 1,
|
||||||
|
innerRadius: 0,
|
||||||
|
outerRadius: 30,
|
||||||
|
innerRectW: 0,
|
||||||
|
innerRectH: 0,
|
||||||
|
outerRectW: 60,
|
||||||
|
outerRectH: 40,
|
||||||
|
spawnPathId: 0,
|
||||||
|
spawnPathDistribution: 'fixed',
|
||||||
|
radius: 30,
|
||||||
|
rectW: 60,
|
||||||
|
rectH: 40,
|
||||||
|
centerX: 0,
|
||||||
|
centerY: 0,
|
||||||
|
rootRotation: 0,
|
||||||
|
rootScaleX: 1,
|
||||||
|
rootScaleY: 1,
|
||||||
|
emissionAngleMode: 'random',
|
||||||
|
direction: 0,
|
||||||
|
spread: 360,
|
||||||
|
coneDirection: 90,
|
||||||
|
coneAngle: 60,
|
||||||
|
lifeMin: 0.6,
|
||||||
|
lifeMax: 1.8,
|
||||||
|
lifeMode: 'random',
|
||||||
|
lifeCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
speedMin: 30,
|
||||||
|
speedMax: 140,
|
||||||
|
speedMode: 'random',
|
||||||
|
speedCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
scaleMin: 0.3,
|
||||||
|
scaleMax: 0.8,
|
||||||
|
scaleMode: 'random',
|
||||||
|
scaleAxisMode: 'uniform',
|
||||||
|
scaleYMin: 0.3,
|
||||||
|
scaleYMax: 0.8,
|
||||||
|
scaleCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
scaleYCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
scaleEndRatio: 0.3,
|
||||||
|
initialRotationMode: 'random',
|
||||||
|
initialRotationMin: 0,
|
||||||
|
initialRotationMax: 360,
|
||||||
|
initialRotationCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
alpha: 0.9,
|
||||||
|
alphaEnd: 0,
|
||||||
|
rotationSpeedMin: -180,
|
||||||
|
rotationSpeedMax: 180,
|
||||||
|
speedOverLifeEnabled: false,
|
||||||
|
speedOverLifeMode: 'fixed',
|
||||||
|
speedOverLifeMin: 1,
|
||||||
|
speedOverLifeMax: 1,
|
||||||
|
speedOverLifeCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
scaleOverLifeEnabled: false,
|
||||||
|
scaleOverLifeMode: 'curve',
|
||||||
|
scaleOverLifeAxisMode: 'uniform',
|
||||||
|
scaleOverLifeMin: 0,
|
||||||
|
scaleOverLifeMax: 1,
|
||||||
|
scaleYOverLifeMin: 0,
|
||||||
|
scaleYOverLifeMax: 1,
|
||||||
|
scaleOverLifeCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
scaleYOverLifeCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
rotationOverLifeEnabled: false,
|
||||||
|
rotationOverLifeMode: 'random',
|
||||||
|
rotationOverLifeCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
gravity: 980,
|
||||||
|
gravityMode: 'fixed',
|
||||||
|
gravityMin: 980,
|
||||||
|
gravityMax: 980,
|
||||||
|
gravityDirection: -90,
|
||||||
|
gravityCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
damping: 0,
|
||||||
|
seed: 0,
|
||||||
|
colorStart: '#ffffff',
|
||||||
|
colorEnd: '#ff4d4d',
|
||||||
|
colorOverLife: false,
|
||||||
|
blend: 'add',
|
||||||
|
alphaMode: 'curve',
|
||||||
|
curveMode: '线性',
|
||||||
|
alphaCurve: [{ x: 0, y: 0 }, { x: 0.2, y: 1 }, { x: 0.8, y: 1 }, { x: 1, y: 0 }],
|
||||||
|
textureName: 'star',
|
||||||
|
textureFolder: '',
|
||||||
|
alphaScale: 1,
|
||||||
|
anchorX: 0.5,
|
||||||
|
anchorY: 0.5,
|
||||||
|
independentAlpha: 'off',
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
noise: false,
|
||||||
|
noiseAmt: 30,
|
||||||
|
noiseRange: 100,
|
||||||
|
noiseSpeed: 1,
|
||||||
|
useGravity: false,
|
||||||
|
wind: false,
|
||||||
|
windX: 0,
|
||||||
|
windY: 0,
|
||||||
|
windMode: 'fixed',
|
||||||
|
windMin: 100,
|
||||||
|
windMax: 100,
|
||||||
|
windDirection: 0,
|
||||||
|
windCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
forceField: false,
|
||||||
|
forceStrength: 10,
|
||||||
|
forceRadius: 100,
|
||||||
|
forceCenterX: 0,
|
||||||
|
forceCenterY: 0,
|
||||||
|
forceFalloff: 'linear',
|
||||||
|
attraction: false,
|
||||||
|
attractionCenterX: 200,
|
||||||
|
attractionCenterY: 0,
|
||||||
|
attractionRadius: 20,
|
||||||
|
attractionDuration: 0.5,
|
||||||
|
attractionDelay: 0.1,
|
||||||
|
attractionDelayMode: 'particle',
|
||||||
|
attractionPath: 'curve',
|
||||||
|
trail: false,
|
||||||
|
trailConfigVersion: 3,
|
||||||
|
trailResources: [{
|
||||||
|
id: 1,
|
||||||
|
texture: null,
|
||||||
|
textureName: 'trail',
|
||||||
|
previewUrl: '/images/trail.png',
|
||||||
|
weight: 100,
|
||||||
|
locked: false,
|
||||||
|
rotation: 0,
|
||||||
|
colorMode: 'particle',
|
||||||
|
color: '#ffffff',
|
||||||
|
colorGradient: [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ffffff' }],
|
||||||
|
blend: 'normal',
|
||||||
|
alphaMode: 'particle',
|
||||||
|
alpha: 1,
|
||||||
|
alphaCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
boneCount: 3,
|
||||||
|
trailRatio: 100,
|
||||||
|
lengthMode: 'fixed',
|
||||||
|
length: 100,
|
||||||
|
lengthMin: 100,
|
||||||
|
lengthMax: 100,
|
||||||
|
widthSizeMode: 'fixed',
|
||||||
|
width: 100,
|
||||||
|
widthMin: 100,
|
||||||
|
widthMax: 100,
|
||||||
|
widthMode: 'particle',
|
||||||
|
lifeLengthEnabled: false,
|
||||||
|
lifeLengthCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
shapeEnabled: false,
|
||||||
|
shapeCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
}],
|
||||||
|
trailResourceVersion: 6,
|
||||||
|
trailTexture: null,
|
||||||
|
trailTextureName: 'trail',
|
||||||
|
trailPreviewUrl: '/images/trail.png',
|
||||||
|
trailColorMode: 'particle',
|
||||||
|
trailColor: '#ffffff',
|
||||||
|
trailColorGradient: [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ffffff' }],
|
||||||
|
trailAlphaMode: 'particle',
|
||||||
|
trailAlpha: 1,
|
||||||
|
trailAlphaCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
trailBoneCount: 3,
|
||||||
|
trailLength: 100,
|
||||||
|
trailWidth: 100,
|
||||||
|
trailWidthMode: 'particle',
|
||||||
|
trailLifeLengthEnabled: false,
|
||||||
|
trailLifeLengthCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
trailShapeEnabled: false,
|
||||||
|
trailShapeCurve: [{ x: 0, y: 1 }, { x: 1, y: 1 }],
|
||||||
|
trailGridRows: 4,
|
||||||
|
trailGridCols: 4,
|
||||||
|
collision: false,
|
||||||
|
collisionColliderIds: [],
|
||||||
|
pathFollow: false,
|
||||||
|
emitterFollow: false,
|
||||||
|
emitterFollowMode: 'none',
|
||||||
|
emitterFollowPathId: 0,
|
||||||
|
emitterFollowSpineId: 0,
|
||||||
|
emitterFollowBoneName: '',
|
||||||
|
emitterFollowBoneOffsetX: 0,
|
||||||
|
emitterFollowBoneOffsetY: 0,
|
||||||
|
emitterFollowBoneRotation: 0,
|
||||||
|
emitterFollowBoneScaleX: 1,
|
||||||
|
emitterFollowBoneScaleY: 1,
|
||||||
|
emitterAngleMode: 'fixed',
|
||||||
|
emitterFollowCurve: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
|
||||||
|
emitterFollowDuration: 2,
|
||||||
|
emitterFollowSpace: 'world',
|
||||||
|
emitterFollowDirection: 'forward',
|
||||||
|
emitterFollowOffset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 为热更新或旧版配置补齐后来新增的字段,避免 undefined 进入粒子计算。 */
|
||||||
|
export function ensureEmitterConfig(config: EmitterConfig): EmitterConfig {
|
||||||
|
const defaults = defaultConfig() as unknown as Record<string, unknown>
|
||||||
|
const target = config as unknown as Record<string, unknown>
|
||||||
|
const needsShapeRangeMigration = target.shapeRangeVersion !== 1
|
||||||
|
const legacyRadius = Number(target.radius)
|
||||||
|
const legacyRectW = Number(target.rectW)
|
||||||
|
const legacyRectH = Number(target.rectH)
|
||||||
|
const legacyDirection = Number(target.direction)
|
||||||
|
const legacySpread = Number(target.spread)
|
||||||
|
const legacyShape = target.shape
|
||||||
|
const needsImageWeightMigration = target.imageWeightVersion !== 1
|
||||||
|
const needsLoopDurationMigration = target.loopDurationFrames == null
|
||||||
|
const needsGravityMigration = target.gravityMode == null
|
||||||
|
const legacyGravity = Number(target.gravity)
|
||||||
|
const needsWindMigration = target.windMode == null
|
||||||
|
const legacyWindX = Number(target.windX)
|
||||||
|
const legacyWindY = Number(target.windY)
|
||||||
|
const needsForceFieldMigration = target.forceFalloff == null
|
||||||
|
const legacyForceStrength = Number(target.forceStrength)
|
||||||
|
const needsTrailCurveMigration = target.trailConfigVersion !== 3
|
||||||
|
const needsTrailResourceMigration = !Array.isArray(target.trailResources)
|
||||||
|
const needsTrailResourceStyleMigration = target.trailResourceVersion !== 6
|
||||||
|
const legacyTrailTexture = target.trailTexture as Texture<any> | null | undefined
|
||||||
|
const legacyTrailTextureName = String(target.trailTextureName || 'trail')
|
||||||
|
const legacyTrailPreviewUrl = String(target.trailPreviewUrl || '/images/trail.png').replace('/trails/trail.png', '/images/trail.png')
|
||||||
|
for (const [key, value] of Object.entries(defaults)) {
|
||||||
|
if (target[key] !== undefined && target[key] !== null) continue
|
||||||
|
target[key] = Array.isArray(value)
|
||||||
|
? value.map((item) => typeof item === 'object' && item !== null ? { ...item } : item)
|
||||||
|
: value
|
||||||
|
}
|
||||||
|
if (needsShapeRangeMigration) {
|
||||||
|
config.innerRadius = 0
|
||||||
|
config.outerRadius = Math.max(0, Number.isFinite(legacyRadius) ? legacyRadius : defaults.outerRadius as number)
|
||||||
|
config.innerRectW = 0
|
||||||
|
config.innerRectH = 0
|
||||||
|
config.outerRectW = Math.max(0, Number.isFinite(legacyRectW) ? legacyRectW : defaults.outerRectW as number)
|
||||||
|
config.outerRectH = Math.max(0, Number.isFinite(legacyRectH) ? legacyRectH : defaults.outerRectH as number)
|
||||||
|
config.coneDirection = Number.isFinite(legacyDirection) ? legacyDirection : 90
|
||||||
|
config.emissionAngleMode = Number.isFinite(legacySpread) && legacySpread > 0 ? 'random' : 'fixed'
|
||||||
|
if (legacyShape === 'cone') config.spread = Math.max(0, Number(config.coneAngle) || 0)
|
||||||
|
config.shapeRangeVersion = 1
|
||||||
|
}
|
||||||
|
if (config.shape === 'orbit') config.shape = 'path'
|
||||||
|
if (!['fixed', 'random', 'radial', 'path-normal', 'path-tangent'].includes(config.emissionAngleMode)) config.emissionAngleMode = 'fixed'
|
||||||
|
if (!['circle', 'rect', 'cone'].includes(config.shape) && config.emissionAngleMode === 'radial') config.emissionAngleMode = 'fixed'
|
||||||
|
if (config.shape !== 'path' && (config.emissionAngleMode === 'path-normal' || config.emissionAngleMode === 'path-tangent')) config.emissionAngleMode = 'fixed'
|
||||||
|
config.spawnPathId = Math.max(0, Math.round(Number(config.spawnPathId) || 0))
|
||||||
|
if (config.spawnPathDistribution !== 'fixed' && config.spawnPathDistribution !== 'random') config.spawnPathDistribution = 'fixed'
|
||||||
|
config.innerRadius = Math.max(0, Number(config.innerRadius) || 0)
|
||||||
|
config.outerRadius = Math.max(config.innerRadius, Number(config.outerRadius) || 0)
|
||||||
|
config.innerRectW = Math.max(0, Number(config.innerRectW) || 0)
|
||||||
|
config.innerRectH = Math.max(0, Number(config.innerRectH) || 0)
|
||||||
|
config.outerRectW = Math.max(config.innerRectW, Number(config.outerRectW) || 0)
|
||||||
|
config.outerRectH = Math.max(config.innerRectH, Number(config.outerRectH) || 0)
|
||||||
|
const normalizedCollisionColliderIds = Array.from(new Set(
|
||||||
|
(Array.isArray(config.collisionColliderIds) ? config.collisionColliderIds : [])
|
||||||
|
.map((id) => Math.round(Number(id)))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0),
|
||||||
|
))
|
||||||
|
if (normalizedCollisionColliderIds.length !== config.collisionColliderIds.length
|
||||||
|
|| normalizedCollisionColliderIds.some((id, index) => id !== config.collisionColliderIds[index])) {
|
||||||
|
config.collisionColliderIds = normalizedCollisionColliderIds
|
||||||
|
}
|
||||||
|
if (config.streamBehavior !== 'normal' && config.streamBehavior !== 'loop') config.streamBehavior = 'normal'
|
||||||
|
if (needsLoopDurationMigration || !Number.isFinite(config.loopDurationFrames)) config.loopDurationFrames = Math.ceil(Math.max(1 / 30, Number(config.duration) || 1) * 30)
|
||||||
|
config.loopDurationFrames = Math.min(1800, Math.max(1, Math.ceil(config.loopDurationFrames)))
|
||||||
|
if (typeof config.generateLoopStartAnimation !== 'boolean') config.generateLoopStartAnimation = false
|
||||||
|
if (typeof config.generateLoopEndAnimation !== 'boolean') config.generateLoopEndAnimation = false
|
||||||
|
if (typeof config.loopStartUseCustomDuration !== 'boolean') config.loopStartUseCustomDuration = false
|
||||||
|
if (typeof config.loopEndUseCustomDuration !== 'boolean') config.loopEndUseCustomDuration = false
|
||||||
|
config.loopStartDurationFrames = Math.min(1800, Math.max(1, Math.ceil(Number(config.loopStartDurationFrames) || 30)))
|
||||||
|
config.loopEndDurationFrames = Math.min(1800, Math.max(1, Math.ceil(Number(config.loopEndDurationFrames) || 30)))
|
||||||
|
if (!Array.isArray(config.imageResources) || config.imageResources.length === 0) {
|
||||||
|
config.imageResources = defaultConfig().imageResources
|
||||||
|
}
|
||||||
|
for (const resource of config.imageResources) {
|
||||||
|
if (!resource.texture && config.texture) resource.texture = config.texture
|
||||||
|
if (!resource.textureName) resource.textureName = 'star'
|
||||||
|
if (resource.previewUrl === '/particles/star.png') resource.previewUrl = '/images/star.png'
|
||||||
|
if (!resource.previewUrl) resource.previewUrl = resource.textureName === 'star' ? '/images/star.png' : ''
|
||||||
|
if (resource.imageMode !== 'fixed' && resource.imageMode !== 'sequence') resource.imageMode = 'fixed'
|
||||||
|
if ((resource.sequencePlayback as string) === 'once') resource.sequencePlayback = 'once-hold'
|
||||||
|
else if ((resource.sequencePlayback as string) === 'loop') resource.sequencePlayback = 'loop-forward'
|
||||||
|
else if (!['once-hide', 'once-hold', 'loop-forward', 'loop-reverse'].includes(resource.sequencePlayback)) {
|
||||||
|
resource.sequencePlayback = 'loop-forward'
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(resource.sequenceFps)) resource.sequenceFps = 30
|
||||||
|
resource.sequenceFps = Math.min(120, Math.max(1, Number(resource.sequenceFps) || 30))
|
||||||
|
if (!Array.isArray(resource.sequenceFrames)) resource.sequenceFrames = []
|
||||||
|
for (const frame of resource.sequenceFrames) {
|
||||||
|
if (typeof frame.name !== 'string') frame.name = ''
|
||||||
|
if (typeof frame.previewUrl !== 'string') frame.previewUrl = ''
|
||||||
|
if (frame.texture === undefined) frame.texture = null
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(resource.weight)) resource.weight = 100
|
||||||
|
if (typeof resource.locked !== 'boolean') resource.locked = false
|
||||||
|
if (!Number.isFinite(resource.scale)) resource.scale = 1
|
||||||
|
if (!Number.isFinite(resource.anchorX)) resource.anchorX = 0.5
|
||||||
|
if (!Number.isFinite(resource.anchorY)) resource.anchorY = 0.5
|
||||||
|
if (resource.colorMode !== 'fixed' && resource.colorMode !== 'random' && resource.colorMode !== 'lifetime') {
|
||||||
|
resource.colorMode = config.colorOverLife ? 'lifetime' : 'fixed'
|
||||||
|
}
|
||||||
|
if (!resource.colorStart) resource.colorStart = config.colorStart || '#ffffff'
|
||||||
|
if (!resource.colorEnd) resource.colorEnd = config.colorEnd || resource.colorStart
|
||||||
|
if (!Array.isArray(resource.colorGradient) || resource.colorGradient.length < 2) {
|
||||||
|
resource.colorGradient = [
|
||||||
|
{ position: 0, color: resource.colorStart },
|
||||||
|
{ position: 1, color: resource.colorEnd },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(BLEND_MAP, resource.blend)) resource.blend = config.blend || 'normal'
|
||||||
|
if (!['off', 'fixed', 'curve'].includes(resource.independentAlpha)) resource.independentAlpha = config.independentAlpha || 'off'
|
||||||
|
if (!Number.isFinite(resource.alpha)) resource.alpha = Number.isFinite(config.alphaScale) ? config.alphaScale : 1
|
||||||
|
if (!['跟随导出', '线性', '贝塞尔'].includes(resource.alphaCurveMode)) resource.alphaCurveMode = config.curveMode || '跟随导出'
|
||||||
|
if (!Array.isArray(resource.alphaCurve) || resource.alphaCurve.length < 2) {
|
||||||
|
resource.alphaCurve = (config.alphaCurve || [{ x: 0, y: 1 }, { x: 1, y: 1 }]).map((point) => ({ ...point }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.imageResources.length === 1) {
|
||||||
|
config.imageResources[0].weight = 100
|
||||||
|
config.imageResources[0].locked = false
|
||||||
|
} else if (needsImageWeightMigration) {
|
||||||
|
const equalWeight = 100 / config.imageResources.length
|
||||||
|
config.imageResources.forEach((resource, index) => {
|
||||||
|
resource.weight = index === config.imageResources.length - 1
|
||||||
|
? 100 - equalWeight * (config.imageResources.length - 1)
|
||||||
|
: equalWeight
|
||||||
|
resource.locked = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
config.imageWeightVersion = 1
|
||||||
|
if (needsGravityMigration) {
|
||||||
|
const migratedStrength = Number.isFinite(legacyGravity) && legacyGravity !== 0 ? legacyGravity : 980
|
||||||
|
config.gravity = migratedStrength
|
||||||
|
config.gravityMin = migratedStrength
|
||||||
|
config.gravityMax = migratedStrength
|
||||||
|
}
|
||||||
|
if (needsWindMigration) {
|
||||||
|
const hasLegacyWind = Number.isFinite(legacyWindX) && Number.isFinite(legacyWindY) && (legacyWindX !== 0 || legacyWindY !== 0)
|
||||||
|
config.windMin = hasLegacyWind ? Math.hypot(legacyWindX, legacyWindY) : 100
|
||||||
|
config.windMax = config.windMin
|
||||||
|
config.windDirection = hasLegacyWind ? Math.atan2(-legacyWindY, legacyWindX) * 180 / Math.PI : 0
|
||||||
|
}
|
||||||
|
if (needsForceFieldMigration) {
|
||||||
|
config.forceStrength = Number.isFinite(legacyForceStrength) && legacyForceStrength !== 0 ? legacyForceStrength : 10
|
||||||
|
}
|
||||||
|
if (needsTrailCurveMigration) {
|
||||||
|
config.trailConfigVersion = 3
|
||||||
|
config.trailAlphaCurve = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
config.trailLifeLengthCurve = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
config.trailShapeCurve = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
config.trailGridRows = 4
|
||||||
|
config.trailGridCols = 4
|
||||||
|
}
|
||||||
|
if (needsTrailResourceMigration || !config.trailResources.length) {
|
||||||
|
config.trailResources = [{
|
||||||
|
id: 1,
|
||||||
|
texture: legacyTrailTexture || null,
|
||||||
|
textureName: legacyTrailTextureName,
|
||||||
|
previewUrl: legacyTrailPreviewUrl,
|
||||||
|
weight: 100,
|
||||||
|
locked: false,
|
||||||
|
rotation: 0,
|
||||||
|
colorMode: config.trailColorMode,
|
||||||
|
color: config.trailColor,
|
||||||
|
colorGradient: config.trailColorGradient.map((stop) => ({ ...stop })),
|
||||||
|
blend: 'normal',
|
||||||
|
alphaMode: config.trailAlphaMode,
|
||||||
|
alpha: config.trailAlpha,
|
||||||
|
alphaCurve: config.trailAlphaCurve.map((point) => ({ ...point })),
|
||||||
|
boneCount: config.trailBoneCount,
|
||||||
|
trailRatio: 100,
|
||||||
|
lengthMode: 'fixed',
|
||||||
|
length: config.trailLength,
|
||||||
|
lengthMin: config.trailLength,
|
||||||
|
lengthMax: config.trailLength,
|
||||||
|
widthSizeMode: 'fixed',
|
||||||
|
width: config.trailWidth,
|
||||||
|
widthMin: config.trailWidth,
|
||||||
|
widthMax: config.trailWidth,
|
||||||
|
widthMode: config.trailWidthMode,
|
||||||
|
lifeLengthEnabled: config.trailLifeLengthEnabled,
|
||||||
|
lifeLengthCurve: config.trailLifeLengthCurve.map((point) => ({ ...point })),
|
||||||
|
shapeEnabled: config.trailShapeEnabled,
|
||||||
|
shapeCurve: config.trailShapeCurve.map((point) => ({ ...point })),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
for (const resource of config.trailResources) {
|
||||||
|
if (!resource.textureName) resource.textureName = 'trail'
|
||||||
|
if (resource.previewUrl === '/trails/trail.png') resource.previewUrl = '/images/trail.png'
|
||||||
|
if (!resource.previewUrl) resource.previewUrl = '/images/trail.png'
|
||||||
|
if (!Number.isFinite(resource.weight)) resource.weight = 100
|
||||||
|
if (typeof resource.locked !== 'boolean') resource.locked = false
|
||||||
|
if (!Number.isFinite(resource.rotation)) resource.rotation = 0
|
||||||
|
if (!['particle', 'fixed', 'curve'].includes(resource.colorMode)) resource.colorMode = config.trailColorMode || 'particle'
|
||||||
|
if (!resource.color) resource.color = config.trailColor || '#ffffff'
|
||||||
|
if (!Array.isArray(resource.colorGradient) || resource.colorGradient.length < 2) {
|
||||||
|
resource.colorGradient = (config.trailColorGradient || [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ffffff' }]).map((stop) => ({ ...stop }))
|
||||||
|
}
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(BLEND_MAP, resource.blend)) resource.blend = 'normal'
|
||||||
|
if (!['particle', 'fixed', 'curve'].includes(resource.alphaMode)) resource.alphaMode = config.trailAlphaMode || 'particle'
|
||||||
|
if (!Number.isFinite(resource.alpha)) resource.alpha = Number.isFinite(config.trailAlpha) ? config.trailAlpha : 1
|
||||||
|
if (!Array.isArray(resource.alphaCurve) || resource.alphaCurve.length < 2) {
|
||||||
|
resource.alphaCurve = (config.trailAlphaCurve || [{ x: 0, y: 1 }, { x: 1, y: 1 }]).map((point) => ({ ...point }))
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(resource.boneCount)) resource.boneCount = Number.isFinite(config.trailBoneCount) ? config.trailBoneCount : 3
|
||||||
|
if (!Number.isFinite(resource.trailRatio)) resource.trailRatio = 100
|
||||||
|
resource.trailRatio = Math.min(100, Math.max(0, resource.trailRatio))
|
||||||
|
if (!['fixed', 'random'].includes(resource.lengthMode)) resource.lengthMode = 'fixed'
|
||||||
|
if (!Number.isFinite(resource.length)) resource.length = Number.isFinite(config.trailLength) ? config.trailLength : 100
|
||||||
|
if (!Number.isFinite(resource.lengthMin)) resource.lengthMin = resource.length
|
||||||
|
if (!Number.isFinite(resource.lengthMax)) resource.lengthMax = resource.length
|
||||||
|
if (!['fixed', 'random'].includes(resource.widthSizeMode)) resource.widthSizeMode = 'fixed'
|
||||||
|
if (!Number.isFinite(resource.width)) resource.width = Number.isFinite(config.trailWidth) ? config.trailWidth : 100
|
||||||
|
if (!Number.isFinite(resource.widthMin)) resource.widthMin = resource.width
|
||||||
|
if (!Number.isFinite(resource.widthMax)) resource.widthMax = resource.width
|
||||||
|
if (!['particle', 'fixed'].includes(resource.widthMode)) resource.widthMode = config.trailWidthMode || 'particle'
|
||||||
|
if (typeof resource.lifeLengthEnabled !== 'boolean') resource.lifeLengthEnabled = Boolean(config.trailLifeLengthEnabled)
|
||||||
|
if (!Array.isArray(resource.lifeLengthCurve) || resource.lifeLengthCurve.length < 2) {
|
||||||
|
resource.lifeLengthCurve = (config.trailLifeLengthCurve || [{ x: 0, y: 1 }, { x: 1, y: 1 }]).map((point) => ({ ...point }))
|
||||||
|
}
|
||||||
|
if (typeof resource.shapeEnabled !== 'boolean') resource.shapeEnabled = Boolean(config.trailShapeEnabled)
|
||||||
|
if (!Array.isArray(resource.shapeCurve) || resource.shapeCurve.length < 2) {
|
||||||
|
resource.shapeCurve = (config.trailShapeCurve || [{ x: 0, y: 1 }, { x: 1, y: 1 }]).map((point) => ({ ...point }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.trailResources.length === 1) {
|
||||||
|
config.trailResources[0].weight = 100
|
||||||
|
config.trailResources[0].locked = false
|
||||||
|
}
|
||||||
|
if (needsTrailResourceStyleMigration) {
|
||||||
|
for (const resource of config.trailResources) {
|
||||||
|
resource.alphaCurve = resource.alphaCurve?.length >= 2 ? resource.alphaCurve : [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.trailResourceVersion = 6
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import type { Blend } from './particleTypes'
|
||||||
|
|
||||||
|
export const BLEND_MAP: Record<Blend, number> = { normal: 0, add: 1, multiply: 2, screen: 3 }
|
||||||
|
export const MAX_PARTICLES = 400
|
||||||
+7
-1095
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,225 @@
|
|||||||
|
import type { EmitMode, EmitShape, Blend, AttributeMode, RotationMode, ScaleAxisMode, CurvePoint, ColorGradientStop, ColliderShape, CollisionResponse, SceneCollider, ParticleImageResource, ParticleSequenceFrame, TrailImageResource, EmitterConfig, ParticleState, TrailBoneState, TrailState } from './particleTypes'
|
||||||
|
|
||||||
|
export function lerp(a: number, b: number, t: number) { return a + (b - a) * t }
|
||||||
|
export function finite(value: number, fallback: number) { return Number.isFinite(value) ? value : fallback }
|
||||||
|
export type SimulationTransform = { centerX: number; centerY: number; rotation: number; scaleX?: number; scaleY?: number } | null
|
||||||
|
export function localToWorldMath(cfg: EmitterConfig, localX: number, localY: number, transform: SimulationTransform = null) {
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(transform?.scaleX ?? cfg.rootScaleX))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(transform?.scaleY ?? cfg.rootScaleY))
|
||||||
|
const angle = -(transform?.rotation ?? cfg.rootRotation) * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const centerX = transform?.centerX ?? cfg.centerX
|
||||||
|
const centerY = transform?.centerY ?? cfg.centerY
|
||||||
|
const screenX = centerX + cos * scaleX * localX - sin * scaleY * localY
|
||||||
|
const screenY = -centerY + sin * scaleX * localX + cos * scaleY * localY
|
||||||
|
return { x: screenX, y: -screenY }
|
||||||
|
}
|
||||||
|
export function worldMathToLocal(cfg: EmitterConfig, worldX: number, worldY: number, transform: SimulationTransform = null) {
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(transform?.scaleX ?? cfg.rootScaleX))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(transform?.scaleY ?? cfg.rootScaleY))
|
||||||
|
const angle = -(transform?.rotation ?? cfg.rootRotation) * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const screenX = worldX - (transform?.centerX ?? cfg.centerX)
|
||||||
|
const screenY = -worldY + (transform?.centerY ?? cfg.centerY)
|
||||||
|
return {
|
||||||
|
x: (cos * screenX + sin * screenY) / scaleX,
|
||||||
|
y: (-sin * screenX + cos * screenY) / scaleY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function localVelocityToWorldMath(cfg: EmitterConfig, vx: number, vy: number, transform: SimulationTransform = null) {
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(transform?.scaleX ?? cfg.rootScaleX))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(transform?.scaleY ?? cfg.rootScaleY))
|
||||||
|
const angle = -(transform?.rotation ?? cfg.rootRotation) * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const screenX = cos * scaleX * vx - sin * scaleY * vy
|
||||||
|
const screenY = sin * scaleX * vx + cos * scaleY * vy
|
||||||
|
return { x: screenX, y: -screenY }
|
||||||
|
}
|
||||||
|
export function worldVelocityToLocalMath(cfg: EmitterConfig, vx: number, vy: number, transform: SimulationTransform = null) {
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(transform?.scaleX ?? cfg.rootScaleX))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(transform?.scaleY ?? cfg.rootScaleY))
|
||||||
|
const angle = -(transform?.rotation ?? cfg.rootRotation) * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const screenY = -vy
|
||||||
|
return {
|
||||||
|
x: (cos * vx + sin * screenY) / scaleX,
|
||||||
|
y: (-sin * vx + cos * screenY) / scaleY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ColliderHit = { x: number; y: number; nx: number; ny: number }
|
||||||
|
export function resolveColliderPoint(collider: SceneCollider, worldX: number, worldY: number): ColliderHit | null {
|
||||||
|
const angle = collider.rotation * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(collider.scaleX ?? 1))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(collider.scaleY ?? 1))
|
||||||
|
const dx = worldX - collider.x, dy = worldY - collider.y
|
||||||
|
const localX = (cos * dx + sin * dy) / scaleX
|
||||||
|
const localY = (-sin * dx + cos * dy) / scaleY
|
||||||
|
const toWorld = (x: number, y: number, nx: number, ny: number): ColliderHit => {
|
||||||
|
// 非等比缩放下,法线使用逆转置缩放后再旋转,保证反弹方向正确。
|
||||||
|
const scaledNormalX = nx / scaleX
|
||||||
|
const scaledNormalY = ny / scaleY
|
||||||
|
const normalLength = Math.max(0.0001, Math.hypot(scaledNormalX, scaledNormalY))
|
||||||
|
const localNormalX = scaledNormalX / normalLength
|
||||||
|
const localNormalY = scaledNormalY / normalLength
|
||||||
|
const worldNormalX = cos * localNormalX - sin * localNormalY
|
||||||
|
const worldNormalY = sin * localNormalX + cos * localNormalY
|
||||||
|
return {
|
||||||
|
x: collider.x + cos * x * scaleX - sin * y * scaleY + worldNormalX * 0.05,
|
||||||
|
y: collider.y + sin * x * scaleX + cos * y * scaleY + worldNormalY * 0.05,
|
||||||
|
nx: worldNormalX,
|
||||||
|
ny: worldNormalY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (collider.shape === 'circle') {
|
||||||
|
const radius = Math.max(0.001, collider.radius)
|
||||||
|
const distance = Math.hypot(localX, localY)
|
||||||
|
if (distance >= radius) return null
|
||||||
|
const nx = distance > 0.0001 ? localX / distance : 1
|
||||||
|
const ny = distance > 0.0001 ? localY / distance : 0
|
||||||
|
return toWorld(nx * radius, ny * radius, nx, ny)
|
||||||
|
}
|
||||||
|
if (collider.shape === 'ellipse') {
|
||||||
|
const radiusX = Math.max(0.001, collider.radiusX)
|
||||||
|
const radiusY = Math.max(0.001, collider.radiusY)
|
||||||
|
const normalized = Math.hypot(localX / radiusX, localY / radiusY)
|
||||||
|
if (normalized >= 1) return null
|
||||||
|
const scale = normalized > 0.0001 ? 1 / normalized : 1
|
||||||
|
const x = normalized > 0.0001 ? localX * scale : radiusX
|
||||||
|
const y = normalized > 0.0001 ? localY * scale : 0
|
||||||
|
const gradientLength = Math.max(0.0001, Math.hypot(x / (radiusX * radiusX), y / (radiusY * radiusY)))
|
||||||
|
return toWorld(x, y, x / (radiusX * radiusX) / gradientLength, y / (radiusY * radiusY) / gradientLength)
|
||||||
|
}
|
||||||
|
if (collider.shape === 'rect') {
|
||||||
|
const halfWidth = Math.max(0.001, collider.width * 0.5)
|
||||||
|
const halfHeight = Math.max(0.001, collider.height * 0.5)
|
||||||
|
if (Math.abs(localX) >= halfWidth || Math.abs(localY) >= halfHeight) return null
|
||||||
|
const penetrationX = halfWidth - Math.abs(localX)
|
||||||
|
const penetrationY = halfHeight - Math.abs(localY)
|
||||||
|
if (penetrationX < penetrationY) {
|
||||||
|
const nx = localX >= 0 ? 1 : -1
|
||||||
|
return toWorld(nx * halfWidth, localY, nx, 0)
|
||||||
|
}
|
||||||
|
const ny = localY >= 0 ? 1 : -1
|
||||||
|
return toWorld(localX, ny * halfHeight, 0, ny)
|
||||||
|
}
|
||||||
|
const sides = Math.max(3, Math.min(16, Math.round(collider.sides || (collider.shape === 'custom' ? 5 : 6))))
|
||||||
|
const radiusX = Math.max(0.001, collider.width * 0.5)
|
||||||
|
const radiusY = Math.max(0.001, collider.height * 0.5)
|
||||||
|
const points = Array.from({ length: sides }, (_, index) => {
|
||||||
|
const theta = Math.PI * 0.5 + index / sides * Math.PI * 2
|
||||||
|
return { x: Math.cos(theta) * radiusX, y: Math.sin(theta) * radiusY }
|
||||||
|
})
|
||||||
|
let inside = false
|
||||||
|
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
|
||||||
|
const a = points[i], b = points[j]
|
||||||
|
if ((a.y > localY) !== (b.y > localY) && localX < (b.x - a.x) * (localY - a.y) / (b.y - a.y) + a.x) inside = !inside
|
||||||
|
}
|
||||||
|
if (!inside) return null
|
||||||
|
let nearestX = points[0].x, nearestY = points[0].y, nearestDistance = Infinity
|
||||||
|
for (let i = 0; i < points.length; i++) {
|
||||||
|
const a = points[i], b = points[(i + 1) % points.length]
|
||||||
|
const edgeX = b.x - a.x, edgeY = b.y - a.y
|
||||||
|
const edgeLengthSq = Math.max(0.0001, edgeX * edgeX + edgeY * edgeY)
|
||||||
|
const amount = Math.min(1, Math.max(0, ((localX - a.x) * edgeX + (localY - a.y) * edgeY) / edgeLengthSq))
|
||||||
|
const x = a.x + edgeX * amount, y = a.y + edgeY * amount
|
||||||
|
const distance = Math.hypot(x - localX, y - localY)
|
||||||
|
if (distance < nearestDistance) { nearestDistance = distance; nearestX = x; nearestY = y }
|
||||||
|
}
|
||||||
|
const normalLength = Math.max(0.0001, Math.hypot(nearestX - localX, nearestY - localY))
|
||||||
|
return toWorld(nearestX, nearestY, (nearestX - localX) / normalLength, (nearestY - localY) / normalLength)
|
||||||
|
}
|
||||||
|
export function tFromLife(particle: { life: number; maxLife: number }) {
|
||||||
|
return Math.min(1, Math.max(0, 1 - particle.life / Math.max(0.0001, particle.maxLife)))
|
||||||
|
}
|
||||||
|
export function seededUnit(seed: number, index: number, salt: number) {
|
||||||
|
let value = ((seed >>> 0) ^ Math.imul(index + 1, 0x9e3779b1) ^ salt) >>> 0
|
||||||
|
value ^= value >>> 16
|
||||||
|
value = Math.imul(value, 0x7feb352d)
|
||||||
|
value ^= value >>> 15
|
||||||
|
value = Math.imul(value, 0x846ca68b)
|
||||||
|
value ^= value >>> 16
|
||||||
|
return (value >>> 0) / 4294967296
|
||||||
|
}
|
||||||
|
export function initialValue(mode: AttributeMode, min: number, max: number, curve: CurvePoint[], random: number) {
|
||||||
|
if (mode === 'fixed') return min
|
||||||
|
if (mode === 'curve') return lerp(min, max, curveAt(curve, random))
|
||||||
|
return lerp(min, max, random)
|
||||||
|
}
|
||||||
|
export function overLifeValue(mode: AttributeMode, min: number, max: number, curve: CurvePoint[], age: number, sampled: number) {
|
||||||
|
if (mode === 'curve') return lerp(min, max, curveAt(curve, age))
|
||||||
|
return mode === 'random' ? sampled : min
|
||||||
|
}
|
||||||
|
/** 在透明度曲线关键帧上按 x(生命周期时刻 0~1)求 y(透明度 0~1)。保证有序、端点外取端点 */
|
||||||
|
export function cubicValue(a: number, b: number, c: number, d: number, t: number) {
|
||||||
|
const inverse = 1 - t
|
||||||
|
return inverse * inverse * inverse * a + 3 * inverse * inverse * t * b + 3 * inverse * t * t * c + t * t * t * d
|
||||||
|
}
|
||||||
|
|
||||||
|
export function curveAt(pts: CurvePoint[] | undefined, x: number, mode: '线性' | '贝塞尔' = '线性'): number {
|
||||||
|
if (!Array.isArray(pts) || pts.length === 0) return 1
|
||||||
|
const p = [...pts].sort((a, b) => a.x - b.x)
|
||||||
|
if (x <= p[0].x) return p[0].y
|
||||||
|
if (x >= p[p.length - 1].x) return p[p.length - 1].y
|
||||||
|
for (let i = 0; i < p.length - 1; i++) {
|
||||||
|
if (x >= p[i].x && x <= p[i + 1].x) {
|
||||||
|
const start = p[i], end = p[i + 1]
|
||||||
|
const spanX = end.x - start.x || 1
|
||||||
|
if (mode !== '贝塞尔') return lerp(start.y, end.y, (x - start.x) / spanX)
|
||||||
|
const control1X = start.x + (start.outX ?? spanX / 3)
|
||||||
|
const control1Y = start.y + (start.outY ?? (end.y - start.y) / 3)
|
||||||
|
const control2X = end.x + (end.inX ?? -spanX / 3)
|
||||||
|
const control2Y = end.y + (end.inY ?? -(end.y - start.y) / 3)
|
||||||
|
let low = 0, high = 1
|
||||||
|
for (let iteration = 0; iteration < 14; iteration++) {
|
||||||
|
const amount = (low + high) / 2
|
||||||
|
if (cubicValue(start.x, control1X, control2X, end.x, amount) < x) low = amount
|
||||||
|
else high = amount
|
||||||
|
}
|
||||||
|
return cubicValue(start.y, control1Y, control2Y, end.y, (low + high) / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p[p.length - 1].y
|
||||||
|
}
|
||||||
|
/** 确定性伪随机(mulberry32),种子相同则序列相同 */
|
||||||
|
export function mulberry32(seed: number): () => number {
|
||||||
|
let a = seed >>> 0
|
||||||
|
return function () {
|
||||||
|
a |= 0; a = (a + 0x6D2B79F5) | 0
|
||||||
|
let t = Math.imul(a ^ (a >>> 15), 1 | a)
|
||||||
|
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
|
||||||
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function hexToNumber(hex: string): number {
|
||||||
|
let h = hex.replace('#', '')
|
||||||
|
if (h.length === 3) h = h.split('').map((c) => c + c).join('')
|
||||||
|
return parseInt(h, 16)
|
||||||
|
}
|
||||||
|
export function blendRgb(start: string, end: string, t: number): number {
|
||||||
|
const s = hexToRgb(start), e = hexToRgb(end)
|
||||||
|
const r = Math.round(s[0] + (e[0] - s[0]) * t)
|
||||||
|
const g = Math.round(s[1] + (e[1] - s[1]) * t)
|
||||||
|
const b = Math.round(s[2] + (e[2] - s[2]) * t)
|
||||||
|
return (r << 16) | (g << 8) | b
|
||||||
|
}
|
||||||
|
export function gradientColorAt(stops: ColorGradientStop[] | undefined, t: number): number {
|
||||||
|
if (!Array.isArray(stops) || stops.length === 0) return 0xffffff
|
||||||
|
const sorted = [...stops].sort((a, b) => a.position - b.position)
|
||||||
|
if (t <= sorted[0].position) return hexToNumber(sorted[0].color)
|
||||||
|
if (t >= sorted[sorted.length - 1].position) return hexToNumber(sorted[sorted.length - 1].color)
|
||||||
|
for (let index = 0; index < sorted.length - 1; index++) {
|
||||||
|
const start = sorted[index], end = sorted[index + 1]
|
||||||
|
if (t < start.position || t > end.position) continue
|
||||||
|
const amount = (t - start.position) / Math.max(0.0001, end.position - start.position)
|
||||||
|
return blendRgb(start.color, end.color, amount)
|
||||||
|
}
|
||||||
|
return hexToNumber(sorted[sorted.length - 1].color)
|
||||||
|
}
|
||||||
|
export function hexToRgb(hex: string): [number, number, number] {
|
||||||
|
let h = hex.replace('#', '')
|
||||||
|
if (h.length === 3) h = h.split('').map((c) => c + c).join('')
|
||||||
|
const n = parseInt(h, 16)
|
||||||
|
return [(n >> 16) & 255, (n >> 8) & 255, n & 255]
|
||||||
|
}
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
import type { Texture } from 'pixi.js'
|
||||||
|
import type { EmissionAngleMode } from './emitterShape'
|
||||||
|
|
||||||
|
/** 发射模式 */
|
||||||
|
export type EmitMode = 'stream' | 'burst'
|
||||||
|
/** 发射形状 */
|
||||||
|
export type EmitShape = 'point' | 'circle' | 'rect' | 'cone' | 'path' | 'orbit'
|
||||||
|
/** 混合模式 */
|
||||||
|
export type Blend = 'normal' | 'add' | 'multiply' | 'screen'
|
||||||
|
/** 粒子属性取值方式 */
|
||||||
|
export type AttributeMode = 'fixed' | 'random' | 'curve'
|
||||||
|
/** 初始旋转额外支持跟随发射方向 */
|
||||||
|
export type RotationMode = AttributeMode | 'direction'
|
||||||
|
export type ScaleAxisMode = 'uniform' | 'separate'
|
||||||
|
export interface CurvePoint { x: number; y: number; inX?: number; inY?: number; outX?: number; outY?: number }
|
||||||
|
export interface ColorGradientStop { position: number; color: string }
|
||||||
|
|
||||||
|
export type ColliderShape = 'circle' | 'ellipse' | 'rect' | 'polygon' | 'custom'
|
||||||
|
export type CollisionResponse = 'physics' | 'pause' | 'destroy'
|
||||||
|
export interface SceneCollider {
|
||||||
|
id: number
|
||||||
|
shape: ColliderShape
|
||||||
|
enabled: boolean
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
rotation: number
|
||||||
|
scaleX: number
|
||||||
|
scaleY: number
|
||||||
|
radius: number
|
||||||
|
radiusX: number
|
||||||
|
radiusY: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
sides: number
|
||||||
|
response: CollisionResponse
|
||||||
|
elasticity: number
|
||||||
|
friction: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParticleImageResource {
|
||||||
|
id: number
|
||||||
|
texture: Texture<any> | null
|
||||||
|
textureName: string
|
||||||
|
textureFolder: string
|
||||||
|
previewUrl: string
|
||||||
|
/** 图片模式:固定图片或按粒子出生时间播放的序列帧。 */
|
||||||
|
imageMode: 'fixed' | 'sequence'
|
||||||
|
/** 序列帧播放方式:单次隐藏、单次定格、正向循环或反向循环。 */
|
||||||
|
sequencePlayback: 'once-hide' | 'once-hold' | 'loop-forward' | 'loop-reverse'
|
||||||
|
/** 序列帧每秒播放帧数。 */
|
||||||
|
sequenceFps: number
|
||||||
|
sequenceFrames: ParticleSequenceFrame[]
|
||||||
|
/** 发射权重,多个资源按权重随机选择 */
|
||||||
|
weight: number
|
||||||
|
/** 锁定后,调整其他资源时保持当前占比 */
|
||||||
|
locked: boolean
|
||||||
|
scale: number
|
||||||
|
anchorX: number
|
||||||
|
anchorY: number
|
||||||
|
/** 每张贴图独立的颜色、混合与透明度设置 */
|
||||||
|
colorMode: 'fixed' | 'random' | 'lifetime'
|
||||||
|
colorStart: string
|
||||||
|
colorEnd: string
|
||||||
|
colorGradient: ColorGradientStop[]
|
||||||
|
blend: Blend
|
||||||
|
independentAlpha: 'off' | 'fixed' | 'curve'
|
||||||
|
alpha: number
|
||||||
|
alphaCurveMode: '跟随导出' | '线性' | '贝塞尔'
|
||||||
|
alphaCurve: CurvePoint[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParticleSequenceFrame {
|
||||||
|
name: string
|
||||||
|
previewUrl: string
|
||||||
|
texture: Texture<any> | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrailImageResource {
|
||||||
|
id: number
|
||||||
|
texture: Texture<any> | null
|
||||||
|
textureName: string
|
||||||
|
previewUrl: string
|
||||||
|
weight: number
|
||||||
|
locked: boolean
|
||||||
|
/** 图片默认方向,修改后重新生成 UV 并绑定蒙皮 */
|
||||||
|
rotation: number
|
||||||
|
colorMode: 'particle' | 'fixed' | 'curve'
|
||||||
|
color: string
|
||||||
|
colorGradient: ColorGradientStop[]
|
||||||
|
blend: Blend
|
||||||
|
alphaMode: 'particle' | 'fixed' | 'curve'
|
||||||
|
alpha: number
|
||||||
|
alphaCurve: CurvePoint[]
|
||||||
|
boneCount: number
|
||||||
|
/** 选中该资源的粒子实际生成拖尾的比例(0-100)。 */
|
||||||
|
trailRatio: number
|
||||||
|
lengthMode: 'fixed' | 'random'
|
||||||
|
length: number
|
||||||
|
lengthMin: number
|
||||||
|
lengthMax: number
|
||||||
|
widthSizeMode: 'fixed' | 'random'
|
||||||
|
width: number
|
||||||
|
widthMin: number
|
||||||
|
widthMax: number
|
||||||
|
widthMode: 'particle' | 'fixed'
|
||||||
|
lifeLengthEnabled: boolean
|
||||||
|
lifeLengthCurve: CurvePoint[]
|
||||||
|
shapeEnabled: boolean
|
||||||
|
shapeCurve: CurvePoint[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 粒子配置(引用保持,面板改字段即实时生效) */
|
||||||
|
export interface EmitterConfig {
|
||||||
|
texture: Texture<any>
|
||||||
|
imageResources: ParticleImageResource[]
|
||||||
|
imageWeightVersion: number
|
||||||
|
name: string
|
||||||
|
// 发射
|
||||||
|
mode: EmitMode
|
||||||
|
rate: number // 持续:每秒发射数
|
||||||
|
/** 持续发射表现:常规持续或可无缝衔接的循环段。 */
|
||||||
|
streamBehavior: 'normal' | 'loop'
|
||||||
|
/** 循环持续的独立时长(帧),不覆盖常规持续的 duration 秒数。 */
|
||||||
|
loopDurationFrames: number
|
||||||
|
/** 循环持续模式下是否生成循环开始动画。 */
|
||||||
|
generateLoopStartAnimation: boolean
|
||||||
|
/** 是否手动指定进入循环稳定状态的时长;关闭时按最长粒子生命周期计算。 */
|
||||||
|
loopStartUseCustomDuration: boolean
|
||||||
|
/** 进入循环稳定状态的时长(帧)。 */
|
||||||
|
loopStartDurationFrames: number
|
||||||
|
/** 循环持续模式下是否生成循环结束动画。 */
|
||||||
|
generateLoopEndAnimation: boolean
|
||||||
|
/** 是否手动指定从循环稳定状态消亡的时长;关闭时按最长粒子生命周期计算。 */
|
||||||
|
loopEndUseCustomDuration: boolean
|
||||||
|
/** 从循环稳定状态消亡的时长(帧)。 */
|
||||||
|
loopEndDurationFrames: number
|
||||||
|
burstCount: number // 爆发:单次数量
|
||||||
|
burstLoop: boolean // 爆发是否每秒重复
|
||||||
|
/** 延迟发射(秒)= 粒子条左端位置 */
|
||||||
|
delay: number
|
||||||
|
/** 持续发射时长(秒)= 时间轴绿色段;爆发模式不使用该时长 */
|
||||||
|
duration: number
|
||||||
|
shape: EmitShape // 发射形状
|
||||||
|
/** 发射范围结构版本,用于旧配置迁移。 */
|
||||||
|
shapeRangeVersion: number
|
||||||
|
/** 圆形和锥形发射范围的内外半径。 */
|
||||||
|
innerRadius: number
|
||||||
|
outerRadius: number
|
||||||
|
/** 矩形发射范围:外矩形减去内矩形。 */
|
||||||
|
innerRectW: number
|
||||||
|
innerRectH: number
|
||||||
|
outerRectW: number
|
||||||
|
outerRectH: number
|
||||||
|
/** 粒子出生路径;与修改器中的发射器整体跟随路径完全独立。 */
|
||||||
|
spawnPathId: number
|
||||||
|
spawnPathDistribution: 'fixed' | 'random'
|
||||||
|
/** 旧版形状尺寸字段,保留用于配置迁移。 */
|
||||||
|
radius: number
|
||||||
|
rectW: number
|
||||||
|
rectH: number
|
||||||
|
/** 发射器中心 X/Y */
|
||||||
|
centerX: number
|
||||||
|
centerY: number
|
||||||
|
// 根节点(root)变换:作用于整个粒子系统(旋转/缩放),与场景对象参数联动
|
||||||
|
rootRotation: number
|
||||||
|
rootScaleX: number
|
||||||
|
rootScaleY: number
|
||||||
|
// 方向
|
||||||
|
emissionAngleMode: EmissionAngleMode
|
||||||
|
direction: number
|
||||||
|
spread: number
|
||||||
|
/** 锥形区域朝向;与粒子发射角度独立。 */
|
||||||
|
coneDirection: number
|
||||||
|
/** 锥形开口角度(发电器形状=锥体时,粒子在锥形扇区内均匀散开) */
|
||||||
|
coneAngle: number
|
||||||
|
// 属性
|
||||||
|
lifeMin: number
|
||||||
|
lifeMax: number
|
||||||
|
lifeMode: AttributeMode
|
||||||
|
lifeCurve: CurvePoint[]
|
||||||
|
speedMin: number
|
||||||
|
speedMax: number
|
||||||
|
speedMode: AttributeMode
|
||||||
|
speedCurve: CurvePoint[]
|
||||||
|
scaleMin: number
|
||||||
|
scaleMax: number
|
||||||
|
scaleMode: AttributeMode
|
||||||
|
scaleAxisMode: ScaleAxisMode
|
||||||
|
scaleYMin: number
|
||||||
|
scaleYMax: number
|
||||||
|
scaleCurve: CurvePoint[]
|
||||||
|
scaleYCurve: CurvePoint[]
|
||||||
|
scaleEndRatio: number
|
||||||
|
initialRotationMode: RotationMode
|
||||||
|
initialRotationMin: number
|
||||||
|
initialRotationMax: number
|
||||||
|
initialRotationCurve: CurvePoint[]
|
||||||
|
alpha: number
|
||||||
|
alphaEnd: number
|
||||||
|
rotationSpeedMin: number
|
||||||
|
rotationSpeedMax: number
|
||||||
|
// 生命周期内属性
|
||||||
|
speedOverLifeEnabled: boolean
|
||||||
|
speedOverLifeMode: AttributeMode
|
||||||
|
speedOverLifeMin: number
|
||||||
|
speedOverLifeMax: number
|
||||||
|
speedOverLifeCurve: CurvePoint[]
|
||||||
|
scaleOverLifeEnabled: boolean
|
||||||
|
scaleOverLifeMode: AttributeMode
|
||||||
|
scaleOverLifeAxisMode: ScaleAxisMode
|
||||||
|
scaleOverLifeMin: number
|
||||||
|
scaleOverLifeMax: number
|
||||||
|
scaleYOverLifeMin: number
|
||||||
|
scaleYOverLifeMax: number
|
||||||
|
scaleOverLifeCurve: CurvePoint[]
|
||||||
|
scaleYOverLifeCurve: CurvePoint[]
|
||||||
|
rotationOverLifeEnabled: boolean
|
||||||
|
rotationOverLifeMode: AttributeMode
|
||||||
|
rotationOverLifeCurve: CurvePoint[]
|
||||||
|
// 物理
|
||||||
|
/** 旧版重力值,保留用于配置迁移 */
|
||||||
|
gravity: number
|
||||||
|
gravityMode: 'fixed' | 'random'
|
||||||
|
gravityMin: number
|
||||||
|
gravityMax: number
|
||||||
|
/** 数学角度:0 向右,-90 向屏幕下方 */
|
||||||
|
gravityDirection: number
|
||||||
|
/** 生命周期内重力倍率,纵轴范围 0~1 */
|
||||||
|
gravityCurve: CurvePoint[]
|
||||||
|
/** 阻尼(速度衰减系数,0=无) */
|
||||||
|
damping: number
|
||||||
|
/** 全局随机种子;包括 0 在内的任意整数都可确定性复现 */
|
||||||
|
seed: number
|
||||||
|
// 颜色
|
||||||
|
colorStart: string
|
||||||
|
colorEnd: string
|
||||||
|
colorOverLife: boolean
|
||||||
|
// 混合
|
||||||
|
blend: Blend
|
||||||
|
/** 透明度模式:fixed(固定)/ curve(曲线) */
|
||||||
|
alphaMode: 'fixed' | 'curve'
|
||||||
|
/** 曲线模式(透明度插值方式) */
|
||||||
|
curveMode: '跟随导出' | '线性' | '贝塞尔'
|
||||||
|
/** 透明度曲线关键帧:x=生命周期时刻(0~1),y=透明度(0~1,1=完全不透明) */
|
||||||
|
alphaCurve: { x: number; y: number }[]
|
||||||
|
/** 图片名称(贴图资源名,默认 dot) */
|
||||||
|
textureName: string
|
||||||
|
/** 子文件夹(图片资源路径,占位) */
|
||||||
|
textureFolder: string
|
||||||
|
/** 占比(0~1) */
|
||||||
|
alphaScale: number
|
||||||
|
/** 贴图锚点/轴心 x,y(0~1) */
|
||||||
|
anchorX: number
|
||||||
|
anchorY: number
|
||||||
|
/** 独立透明度:off / fixed / curve */
|
||||||
|
independentAlpha: 'off' | 'fixed' | 'curve'
|
||||||
|
// 发射点偏移
|
||||||
|
offsetX: number
|
||||||
|
offsetY: number
|
||||||
|
// ===== 修改器 (Modifiers) =====
|
||||||
|
/** 开启移动噪声 */
|
||||||
|
noise: boolean
|
||||||
|
/** 噪声影响比例(0~100%) */
|
||||||
|
noiseAmt: number
|
||||||
|
/** 粒子偏离原轨迹的最大距离(px) */
|
||||||
|
noiseRange: number
|
||||||
|
/** 噪声每秒变化速度 */
|
||||||
|
noiseSpeed: number
|
||||||
|
/** 开启重力(由 gravity 控制) */
|
||||||
|
useGravity: boolean
|
||||||
|
/** 开启风力 */
|
||||||
|
wind: boolean
|
||||||
|
/** 旧版二维风力,保留用于配置迁移 */
|
||||||
|
windX: number
|
||||||
|
windY: number
|
||||||
|
windMode: 'fixed' | 'random'
|
||||||
|
windMin: number
|
||||||
|
windMax: number
|
||||||
|
/** 数学角度:0 向右,-90 向屏幕下方 */
|
||||||
|
windDirection: number
|
||||||
|
/** 生命周期内风力倍率,纵轴范围 0~1 */
|
||||||
|
windCurve: CurvePoint[]
|
||||||
|
/** 开启力场(吸力/斥力) */
|
||||||
|
forceField: boolean
|
||||||
|
forceStrength: number
|
||||||
|
forceRadius: number
|
||||||
|
/** 力场中心使用场景全局数学坐标 */
|
||||||
|
forceCenterX: number
|
||||||
|
forceCenterY: number
|
||||||
|
forceFalloff: 'constant' | 'linear' | 'inverse'
|
||||||
|
/** 开启吸附:粒子在延迟后从当前位置移动到全局吸附点,到达范围后死亡 */
|
||||||
|
attraction: boolean
|
||||||
|
attractionCenterX: number
|
||||||
|
attractionCenterY: number
|
||||||
|
attractionRadius: number
|
||||||
|
attractionDuration: number
|
||||||
|
attractionDelay: number
|
||||||
|
attractionDelayMode: 'global' | 'particle'
|
||||||
|
attractionPath: 'linear' | 'curve'
|
||||||
|
/** 开启拖尾 */
|
||||||
|
trail: boolean
|
||||||
|
trailConfigVersion: number
|
||||||
|
trailResources: TrailImageResource[]
|
||||||
|
trailResourceVersion: number
|
||||||
|
/** 拖尾使用独立图片和独立网格蒙皮 */
|
||||||
|
/** @deprecated 旧版单拖尾资源字段,仅用于配置迁移 */
|
||||||
|
trailTexture: Texture<any> | null
|
||||||
|
trailTextureName: string
|
||||||
|
trailPreviewUrl: string
|
||||||
|
trailColorMode: 'particle' | 'fixed' | 'curve'
|
||||||
|
trailColor: string
|
||||||
|
trailColorGradient: ColorGradientStop[]
|
||||||
|
trailAlphaMode: 'particle' | 'fixed' | 'curve'
|
||||||
|
trailAlpha: number
|
||||||
|
trailAlphaCurve: CurvePoint[]
|
||||||
|
/** 每个粒子的同级拖尾骨骼数量 */
|
||||||
|
trailBoneCount: number
|
||||||
|
trailLength: number
|
||||||
|
trailWidth: number
|
||||||
|
trailWidthMode: 'particle' | 'fixed'
|
||||||
|
trailLifeLengthEnabled: boolean
|
||||||
|
trailLifeLengthCurve: CurvePoint[]
|
||||||
|
trailShapeEnabled: boolean
|
||||||
|
trailShapeCurve: CurvePoint[]
|
||||||
|
/** 蒙皮网格拓扑尺寸 */
|
||||||
|
trailGridRows: number
|
||||||
|
trailGridCols: number
|
||||||
|
/** 开启碰撞;仅 collisionColliderIds 中显式选中的场景碰撞体参与计算。 */
|
||||||
|
collision: boolean
|
||||||
|
collisionColliderIds: number[]
|
||||||
|
/** 开启路径跟随(预留) */
|
||||||
|
pathFollow: boolean
|
||||||
|
emitterFollow: boolean
|
||||||
|
emitterFollowMode: 'none' | 'path' | 'bone'
|
||||||
|
emitterFollowPathId: number
|
||||||
|
emitterFollowSpineId: number
|
||||||
|
emitterFollowBoneName: string
|
||||||
|
emitterFollowBoneOffsetX: number
|
||||||
|
emitterFollowBoneOffsetY: number
|
||||||
|
emitterFollowBoneRotation: number
|
||||||
|
emitterFollowBoneScaleX: number
|
||||||
|
emitterFollowBoneScaleY: number
|
||||||
|
emitterAngleMode: 'fixed' | 'direction'
|
||||||
|
emitterFollowCurve: CurvePoint[]
|
||||||
|
emitterFollowDuration: number
|
||||||
|
emitterFollowSpace: 'local' | 'world'
|
||||||
|
emitterFollowDirection: 'forward' | 'reverse'
|
||||||
|
emitterFollowOffset: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 粒子瞬间状态(供骨骼同步 / 导出) */
|
||||||
|
export interface ParticleState {
|
||||||
|
/** 对应骨骼在 root 下的命名 p_<i> */
|
||||||
|
boneName: string
|
||||||
|
/** 每次出生唯一且可复现的编号;关闭导出骨骼池时用于区分同一池槽的多次复用。 */
|
||||||
|
spawnId?: number
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
rotation: number
|
||||||
|
scaleX: number
|
||||||
|
scaleY: number
|
||||||
|
/** 已烘焙的数学世界坐标;导出时不再依赖路径、碰撞体或 Spine。 */
|
||||||
|
worldX?: number
|
||||||
|
worldY?: number
|
||||||
|
worldRotation?: number
|
||||||
|
worldScaleX?: number
|
||||||
|
worldScaleY?: number
|
||||||
|
alpha: number
|
||||||
|
colorHex: number
|
||||||
|
resourceId?: number
|
||||||
|
/** 当前图片资源的序列帧索引,固定图片为 0。 */
|
||||||
|
imageFrameIndex?: number
|
||||||
|
/** 单次播放结束后可只隐藏图片,粒子和拖尾仍继续存在。 */
|
||||||
|
imageVisible?: boolean
|
||||||
|
/** 拖尾拥有一组同级骨骼和蒙皮状态,录制/回放时一并保存。 */
|
||||||
|
trailState?: TrailState
|
||||||
|
active: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrailBoneState {
|
||||||
|
boneName: string
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
rotation: number
|
||||||
|
worldX?: number
|
||||||
|
worldY?: number
|
||||||
|
worldRotation?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrailState {
|
||||||
|
bones: TrailBoneState[]
|
||||||
|
/** 兼容旧配置;当前拖尾整组骨骼同时出生和死亡,存在时始终等于骨骼总数。 */
|
||||||
|
activeBoneCount?: number
|
||||||
|
width: number
|
||||||
|
alpha: number
|
||||||
|
colorHex: number
|
||||||
|
resourceId?: number
|
||||||
|
}
|
||||||
|
|
||||||
+19
-2359
File diff suppressed because it is too large.
Load diff
+17
-609
@@ -129,6 +129,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { type BonePreview, type SceneTransformTarget } from './stage/stageTypes'
|
||||||
|
import { pathLocalToWorld, worldToPathLocal, type FollowCurvePoint, followCurveValue, cubicPoint } from './stage/stageGeometry'
|
||||||
|
import { niceStep, drawAxis } from './stage/axisGuides'
|
||||||
|
import { drawOrigin, drawShapeRange, drawForceField, drawAttraction } from './stage/emitterGuides'
|
||||||
|
import { drawBoneFollowTargets, drawGizmo, colorNumber, drawCollisionBodies, drawPaths } from './stage/sceneGuides'
|
||||||
|
import { appendStateBones, drawDirectionalAxis, drawBones } from './stage/boneGuides'
|
||||||
|
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { Application, Container, Graphics, Sprite, Text, Texture } from 'pixi.js'
|
import { Application, Container, Graphics, Sprite, Text, Texture } from 'pixi.js'
|
||||||
import { ParticleEmitter, ensureEmitterConfig, type ParticleState } from '../core/particleEmitter'
|
import { ParticleEmitter, ensureEmitterConfig, type ParticleState } from '../core/particleEmitter'
|
||||||
@@ -335,100 +342,6 @@ function clearBackground(resetControls = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自适应刻度步进(仿时间轴):返回 1/2/5 序列里的最合适步长,保证屏幕间距合理
|
|
||||||
function niceStep(target: number, minStep = 0): number {
|
|
||||||
if (target <= 0) return 1
|
|
||||||
const pow = Math.pow(10, Math.floor(Math.log10(target)))
|
|
||||||
const candidates = [1, 2, 5, 10, 20, 50, 100]
|
|
||||||
let best = 1
|
|
||||||
for (const c of candidates) {
|
|
||||||
if (c * pow >= target) { best = c * pow; break }
|
|
||||||
}
|
|
||||||
return Math.max(best, minStep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 画无限长 x/y 轴 + 自适应整数刻度 + 数值(0,0 在画布中心)
|
|
||||||
function drawAxis() {
|
|
||||||
if (!axisGfx) return
|
|
||||||
const g = axisGfx
|
|
||||||
g.clear()
|
|
||||||
const S = store.settings
|
|
||||||
if (!S.tickEnabled) { // 未启用刻度:隐藏整个坐标轴(轴+刻度+数值)
|
|
||||||
for (const t of axisLabels) t.visible = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const cx = 0, cy = 0 // 世界原点(中心)
|
|
||||||
const halfW = app!.screen.width / store.editor.viewScale
|
|
||||||
const halfH = app!.screen.height / store.editor.viewScale
|
|
||||||
const axisColor = Number('0x' + S.axisColor.replace('#', ''))
|
|
||||||
// 轴线(贯穿到视口边缘外)
|
|
||||||
g.lineStyle(1.5, axisColor, 0.35)
|
|
||||||
g.moveTo(-halfW, cy); g.lineTo(halfW, cy) // x 轴
|
|
||||||
g.moveTo(cx, -halfH); g.lineTo(cx, halfH) // y 轴
|
|
||||||
// 刻度步进:目标 ~50px 一个刻度(世界单位 = px/viewScale)
|
|
||||||
const sc = store.editor.viewScale
|
|
||||||
const step = niceStep(50 / sc)
|
|
||||||
// 数值显示范围:只在中心 ±range 内标数值(避免远端堆积/干扰);范围随视口半宽
|
|
||||||
const rangeW = halfW
|
|
||||||
const rangeH = halfH
|
|
||||||
// 刻度线(贯穿整个视口;仅启用刻度时画)
|
|
||||||
const tickW = Math.max(1, S.tickWidth)
|
|
||||||
if (S.tickEnabled) {
|
|
||||||
for (let x = 0; x <= halfW; x += step) {
|
|
||||||
const v = Math.round(x / step) * step
|
|
||||||
if (v === 0) continue
|
|
||||||
g.lineStyle(tickW, axisColor, 0.25)
|
|
||||||
g.moveTo(v, cy - 4); g.lineTo(v, cy + 4)
|
|
||||||
g.moveTo(-v, cy - 4); g.lineTo(-v, cy + 4)
|
|
||||||
}
|
|
||||||
for (let y = 0; y <= halfH; y += step) {
|
|
||||||
const v = Math.round(y / step) * step
|
|
||||||
if (v === 0) continue
|
|
||||||
g.lineStyle(tickW, axisColor, 0.25)
|
|
||||||
g.moveTo(cx - 4, y); g.lineTo(cx + 4, y)
|
|
||||||
g.moveTo(cx - 4, -y); g.lineTo(cx + 4, -y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 数值标签(文字放 axisLabelLayer,不随 world 缩放,尺寸/清晰度恒定)
|
|
||||||
// 只在中心 ±range 内标注数值,超出不显示,避免看不清/干扰
|
|
||||||
const cxS = app!.screen.width / 2 + store.editor.viewX
|
|
||||||
const cyS = app!.screen.height * 0.5 + store.editor.viewY
|
|
||||||
const fs = S.axisFontSize // 字号由系统设置手动控制(默认 12px)
|
|
||||||
const labelColor = Number('0x' + S.axisColor.replace('#', ''))
|
|
||||||
while (axisLabels.length < 64) {
|
|
||||||
const t = new Text('', { fontFamily: 'monospace', fontSize: fs, fill: labelColor })
|
|
||||||
t.anchor.set(0.5, 0.5)
|
|
||||||
axisLabelLayer!.addChild(t)
|
|
||||||
axisLabels.push(t)
|
|
||||||
}
|
|
||||||
let li = 0
|
|
||||||
const showLbl = S.showAxisLabels && S.tickEnabled
|
|
||||||
// x 轴正/负数值(放在轴下方一点)
|
|
||||||
for (let x = 0; x <= halfW; x += step) {
|
|
||||||
const v = Math.round(x / step) * step
|
|
||||||
if (v === 0 || !showLbl || v > rangeW) continue
|
|
||||||
const lbl = axisLabels[li++]; if (!lbl) break
|
|
||||||
lbl.style.fontSize = fs
|
|
||||||
lbl.text = String(v); lbl.position.set(cxS + v * sc, cyS + 9); lbl.visible = true
|
|
||||||
const lvb = axisLabels[li++]; if (!lvb) break
|
|
||||||
lvb.style.fontSize = fs
|
|
||||||
lvb.text = String(-v); lvb.position.set(cxS - v * sc, cyS + 9); lvb.visible = true
|
|
||||||
}
|
|
||||||
// y 轴(数值放在轴右侧)
|
|
||||||
for (let y = 0; y <= halfH; y += step) {
|
|
||||||
const v = Math.round(y / step) * step
|
|
||||||
if (v === 0 || !showLbl || v > rangeH) continue
|
|
||||||
const lbl = axisLabels[li++]; if (!lbl) break
|
|
||||||
lbl.style.fontSize = fs
|
|
||||||
lbl.text = String(v); lbl.position.set(cxS + 8, cyS - y * sc); lbl.visible = true
|
|
||||||
const lvb = axisLabels[li++]; if (!lvb) break
|
|
||||||
lvb.style.fontSize = fs
|
|
||||||
lvb.text = String(-v); lvb.position.set(cxS + 8, cyS + y * sc); lvb.visible = true
|
|
||||||
}
|
|
||||||
// 隐藏多余标签
|
|
||||||
while (li < axisLabels.length) { const t = axisLabels[li++]; if (t) t.visible = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== 视图控制:滚轮缩放 / 左键拖拽平移 ====
|
// ==== 视图控制:滚轮缩放 / 左键拖拽平移 ====
|
||||||
|
|
||||||
// 滚轮缩放画布(viewScale),基于鼠标所在位置缩放更平滑
|
// 滚轮缩放画布(viewScale),基于鼠标所在位置缩放更平滑
|
||||||
@@ -491,23 +404,6 @@ function pointerToWorldMath(clientX: number, clientY: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathLocalToWorld(path: ScenePath, x: number, y: number) {
|
|
||||||
const angle = path.rotation * Math.PI / 180
|
|
||||||
const cos = Math.cos(angle), sin = Math.sin(angle)
|
|
||||||
const scaleX = Math.max(0.0001, Math.abs(path.scaleX ?? 1))
|
|
||||||
const scaleY = Math.max(0.0001, Math.abs(path.scaleY ?? 1))
|
|
||||||
return { x: path.x + cos * x * scaleX - sin * y * scaleY, y: path.y + sin * x * scaleX + cos * y * scaleY }
|
|
||||||
}
|
|
||||||
|
|
||||||
function worldToPathLocal(path: ScenePath, x: number, y: number) {
|
|
||||||
const angle = path.rotation * Math.PI / 180
|
|
||||||
const cos = Math.cos(angle), sin = Math.sin(angle)
|
|
||||||
const scaleX = Math.max(0.0001, Math.abs(path.scaleX ?? 1))
|
|
||||||
const scaleY = Math.max(0.0001, Math.abs(path.scaleY ?? 1))
|
|
||||||
const dx = x - path.x, dy = y - path.y
|
|
||||||
return { x: (cos * dx + sin * dy) / scaleX, y: (-sin * dx + cos * dy) / scaleY }
|
|
||||||
}
|
|
||||||
|
|
||||||
function activePath() {
|
function activePath() {
|
||||||
if (store.activeObjectType !== 'path') return null
|
if (store.activeObjectType !== 'path') return null
|
||||||
const path = store.paths.find((item) => item.id === store.activeObjectId) || null
|
const path = store.paths.find((item) => item.id === store.activeObjectId) || null
|
||||||
@@ -740,17 +636,6 @@ function onAttractionDown(e: PointerEvent) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type SceneTransformTarget = {
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
rotation: number
|
|
||||||
scaleX: number
|
|
||||||
scaleY: number
|
|
||||||
setPosition: (x: number, y: number) => void
|
|
||||||
setRotation: (rotation: number) => void
|
|
||||||
setScale: (scaleX: number, scaleY: number) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectedTransformTarget(): SceneTransformTarget | null {
|
function selectedTransformTarget(): SceneTransformTarget | null {
|
||||||
const id = store.activeObjectId
|
const id = store.activeObjectId
|
||||||
if (store.activeObjectType === 'particle') {
|
if (store.activeObjectType === 'particle') {
|
||||||
@@ -866,483 +751,6 @@ function syncEmitters() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawOrigin() {
|
|
||||||
if (!originGfx) return
|
|
||||||
const g = originGfx
|
|
||||||
g.clear()
|
|
||||||
if (!store.settings.showEmitter) return
|
|
||||||
// 每个系统对应一个发射点(坐标点);没有系统时不画
|
|
||||||
if (!store.systems.length) return
|
|
||||||
for (const sys of store.systems) {
|
|
||||||
if (sys.visible === false) continue
|
|
||||||
const preview = emitterPreviewTransform.get(sys.id)
|
|
||||||
const cx = preview?.x ?? sys.config.centerX ?? 0
|
|
||||||
const cy = -(preview?.y ?? sys.config.centerY ?? 0)
|
|
||||||
const active = store.activeObjectType === 'particle' && sys.id === store.activeId
|
|
||||||
const col = active ? 0xffcc33 : 0x8a94b8
|
|
||||||
const opacity = active ? 0.4 : 0.2
|
|
||||||
g.lineStyle(1, col, opacity)
|
|
||||||
g.drawCircle(cx, cy, 10)
|
|
||||||
g.beginFill(col, active ? 0.8 : 0.4)
|
|
||||||
g.drawCircle(cx, cy, 3)
|
|
||||||
g.endFill()
|
|
||||||
g.lineStyle(1, col, active ? 0.3 : 0.15)
|
|
||||||
g.moveTo(cx - 16, cy); g.lineTo(cx + 16, cy)
|
|
||||||
g.moveTo(cx, cy - 16); g.lineTo(cx, cy + 16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 标出层级树当前选中的骨骼;发射器骨骼跟随复用同一目标点。 */
|
|
||||||
function drawBoneFollowTargets() {
|
|
||||||
if (!boneFollowTargetGfx) return
|
|
||||||
const g = boneFollowTargetGfx
|
|
||||||
g.parent?.addChild(g)
|
|
||||||
g.clear()
|
|
||||||
const targets = new Map<string, { spineId: number; boneName: string }>()
|
|
||||||
for (const spine of store.spines) {
|
|
||||||
if (spine.visible === false || !spine.selectedBone || spine.status !== 'ready') continue
|
|
||||||
targets.set(`${spine.id}:${spine.selectedBone}`, { spineId: spine.id, boneName: spine.selectedBone })
|
|
||||||
}
|
|
||||||
for (const system of store.systems) {
|
|
||||||
if (system.visible === false) continue
|
|
||||||
const config = system.config
|
|
||||||
if (!config.emitterFollow || config.emitterFollowMode !== 'bone') continue
|
|
||||||
if (!config.emitterFollowSpineId || !config.emitterFollowBoneName) continue
|
|
||||||
const spine = store.spines.find((item) => item.id === config.emitterFollowSpineId)
|
|
||||||
if (!spine || spine.visible === false) continue
|
|
||||||
targets.set(`${config.emitterFollowSpineId}:${config.emitterFollowBoneName}`, {
|
|
||||||
spineId: config.emitterFollowSpineId,
|
|
||||||
boneName: config.emitterFollowBoneName,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for (const item of targets.values()) {
|
|
||||||
const target = spineRuntime?.getBoneWorldTransform(item.spineId, item.boneName)
|
|
||||||
if (!target) continue
|
|
||||||
const x = target.x
|
|
||||||
const y = -target.y
|
|
||||||
g.lineStyle(2, 0xc48cff, 0.95)
|
|
||||||
g.beginFill(0xc48cff, 0.2)
|
|
||||||
g.drawCircle(x, y, 8)
|
|
||||||
g.endFill()
|
|
||||||
g.beginFill(0xf2e7ff, 1)
|
|
||||||
g.drawCircle(x, y, 3.5)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制当前选中系统的发射器形状范围(点/路径不额外绘制),随参数实时响应。
|
|
||||||
function drawShapeRange() {
|
|
||||||
if (!shapeGfx) return
|
|
||||||
const g = shapeGfx
|
|
||||||
g.clear()
|
|
||||||
if (!store.settings.showEmitter) return
|
|
||||||
const sys = store.systems.find((s) => s.id === store.activeId)
|
|
||||||
if (!sys || sys.visible === false) return
|
|
||||||
const c = sys.config
|
|
||||||
const preview = emitterPreviewTransform.get(sys.id)
|
|
||||||
// root 中心(发射器容器 position = centerX/-centerY),数学坐标
|
|
||||||
const cx = preview?.x ?? c.centerX ?? 0
|
|
||||||
const cy = -(preview?.y ?? c.centerY ?? 0)
|
|
||||||
const shape = c.shape
|
|
||||||
if (shape === 'point' || shape === 'path') return
|
|
||||||
// 相对 root 中心的局部点(lx,ly,数学坐标)先加偏移(发射源 = root 中心 + offset),再应用 root 缩放+旋转,加 root 中心 → 世界坐标
|
|
||||||
const sxr = c.rootScaleX ?? 1, syr = c.rootScaleY ?? 1
|
|
||||||
const rot = -(preview?.rotation ?? c.rootRotation ?? 0) * Math.PI / 180 // 数学上正 → 屏幕:逆时针为正
|
|
||||||
const cs = Math.cos(rot), sn = Math.sin(rot)
|
|
||||||
const oxo = c.offsetX ?? 0, oyo = c.offsetY ?? 0
|
|
||||||
const pt = (lx: number, ly: number): [number, number] => {
|
|
||||||
// 对齐粒子:局部 y 为数学上正 → 屏幕取负;发射源偏移 offsetY(数学上正)→ 屏幕也取负
|
|
||||||
const X = lx + oxo, Y = -ly - oyo
|
|
||||||
const x = X * sxr, y = Y * syr
|
|
||||||
return [cx + x * cs - y * sn, cy + (x * sn + y * cs)]
|
|
||||||
}
|
|
||||||
if (shape === 'circle') {
|
|
||||||
const range = normalizedRadialRange(c.innerRadius, c.outerRadius)
|
|
||||||
g.lineStyle(2, 0x3ce86a, 0.7)
|
|
||||||
// 圆环:以 root 变换后的椭圆近似(逐点)
|
|
||||||
const steps = 48
|
|
||||||
for (const radius of [range.outer, range.inner]) {
|
|
||||||
if (radius <= 0) continue
|
|
||||||
for (let i = 0; i <= steps; i++) {
|
|
||||||
const a = i / steps * Math.PI * 2
|
|
||||||
const [px, py] = pt(Math.cos(a) * radius, Math.sin(a) * radius)
|
|
||||||
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (shape === 'rect') {
|
|
||||||
const range = normalizedRectRange(c)
|
|
||||||
g.lineStyle(2, 0x4a8cff, 0.7)
|
|
||||||
for (const [width, height] of [[range.outerW, range.outerH], [range.innerW, range.innerH]]) {
|
|
||||||
if (width <= 0 || height <= 0) continue
|
|
||||||
const hw = width / 2, hh = height / 2
|
|
||||||
const p1 = pt(-hw, -hh), p2 = pt(hw, -hh), p3 = pt(hw, hh), p4 = pt(-hw, hh)
|
|
||||||
g.moveTo(p1[0], p1[1]); g.lineTo(p2[0], p2[1]); g.lineTo(p3[0], p3[1]); g.lineTo(p4[0], p4[1]); g.lineTo(p1[0], p1[1])
|
|
||||||
}
|
|
||||||
} else if (shape === 'cone') {
|
|
||||||
const range = normalizedRadialRange(c.innerRadius, c.outerRadius)
|
|
||||||
const half = (c.coneAngle / 2) * Math.PI / 180
|
|
||||||
const base = c.coneDirection * Math.PI / 180
|
|
||||||
// 锥形环带的两条边、内弧和外弧。
|
|
||||||
const innerEdge1 = pt(Math.cos(base - half) * range.inner, Math.sin(base - half) * range.inner)
|
|
||||||
const innerEdge2 = pt(Math.cos(base + half) * range.inner, Math.sin(base + half) * range.inner)
|
|
||||||
const outerEdge1 = pt(Math.cos(base - half) * range.outer, Math.sin(base - half) * range.outer)
|
|
||||||
const outerEdge2 = pt(Math.cos(base + half) * range.outer, Math.sin(base + half) * range.outer)
|
|
||||||
g.lineStyle(2, 0x3ce86a, 0.7)
|
|
||||||
g.moveTo(innerEdge1[0], innerEdge1[1]); g.lineTo(outerEdge1[0], outerEdge1[1])
|
|
||||||
g.moveTo(innerEdge2[0], innerEdge2[1]); g.lineTo(outerEdge2[0], outerEdge2[1])
|
|
||||||
g.lineStyle(1, 0x3ce86a, 0.5)
|
|
||||||
const steps = 24
|
|
||||||
for (const radius of [range.outer, range.inner]) {
|
|
||||||
if (radius <= 0) continue
|
|
||||||
for (let i = 0; i <= steps; i++) {
|
|
||||||
const a = base - half + (2 * half * i / steps)
|
|
||||||
const [px, py] = pt(Math.cos(a) * radius, Math.sin(a) * radius)
|
|
||||||
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 绘制当前选中系统的全局力场;不应用粒子系统 root 变换。 */
|
|
||||||
function drawForceField() {
|
|
||||||
if (!forceFieldGfx) return
|
|
||||||
const g = forceFieldGfx
|
|
||||||
g.clear()
|
|
||||||
const sys = store.systems.find((item) => item.id === store.activeId)
|
|
||||||
if (sys?.visible === false) return
|
|
||||||
const cfg = sys?.config
|
|
||||||
if (!cfg?.forceField || cfg.forceRadius <= 0) return
|
|
||||||
const x = cfg.forceCenterX
|
|
||||||
const y = -cfg.forceCenterY
|
|
||||||
const color = cfg.forceStrength >= 0 ? 0x58c7ff : 0xff6b7a
|
|
||||||
g.lineStyle(2, color, 0.85)
|
|
||||||
g.beginFill(color, 0.08)
|
|
||||||
g.drawCircle(x, y, cfg.forceRadius)
|
|
||||||
g.endFill()
|
|
||||||
g.lineStyle(1, color, 0.7)
|
|
||||||
g.moveTo(x - 9, y); g.lineTo(x + 9, y)
|
|
||||||
g.moveTo(x, y - 9); g.lineTo(x, y + 9)
|
|
||||||
g.beginFill(color, 0.95)
|
|
||||||
g.drawCircle(x, y, 4)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 绘制全局吸附点与吸附完成半径;半径为 0 时显示为红色点。 */
|
|
||||||
function drawAttraction() {
|
|
||||||
if (!attractionGfx) return
|
|
||||||
const g = attractionGfx
|
|
||||||
g.clear()
|
|
||||||
const sys = store.systems.find((item) => item.id === store.activeId)
|
|
||||||
if (sys?.visible === false) return
|
|
||||||
const cfg = sys?.config
|
|
||||||
if (!cfg?.attraction) return
|
|
||||||
const x = cfg.attractionCenterX
|
|
||||||
const y = -cfg.attractionCenterY
|
|
||||||
const radius = Math.max(0, cfg.attractionRadius)
|
|
||||||
const color = 0xff4d5f
|
|
||||||
if (radius > 0) {
|
|
||||||
g.lineStyle(2, color, 0.95)
|
|
||||||
g.beginFill(color, 0.09)
|
|
||||||
g.drawCircle(x, y, radius)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
g.lineStyle(1, color, 0.85)
|
|
||||||
g.moveTo(x - 10, y); g.lineTo(x + 10, y)
|
|
||||||
g.moveTo(x, y - 10); g.lineTo(x, y + 10)
|
|
||||||
g.beginFill(color, 1)
|
|
||||||
g.drawCircle(x, y, radius > 0 ? 4 : 6)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前场景对象的变换手柄:跟随其位置、旋转和缩放。
|
|
||||||
function drawGizmo() {
|
|
||||||
if (!gizmoGfx) return
|
|
||||||
const g = gizmoGfx
|
|
||||||
g.clear()
|
|
||||||
if (!activeTool.value) return
|
|
||||||
const target = selectedTransformTarget()
|
|
||||||
if (!target) return
|
|
||||||
const cx = target.x
|
|
||||||
const cy = -target.y
|
|
||||||
const sxr = target.scaleX, syr = target.scaleY
|
|
||||||
const rot = -target.rotation * Math.PI / 180
|
|
||||||
const cs = Math.cos(rot), sn = Math.sin(rot)
|
|
||||||
// 局部点(数学坐标,ly 上正) → 屏幕点(应用 root 缩放+旋转+平移到 root 中心)
|
|
||||||
const pt = (lx: number, ly: number): [number, number] => {
|
|
||||||
const x = lx * sxr, y = (-ly) * syr
|
|
||||||
return [cx + x * cs - y * sn, cy + (x * sn + y * cs)]
|
|
||||||
}
|
|
||||||
const line = (ax: number, ay: number, bx: number, by: number) => {
|
|
||||||
const a = pt(ax, ay), b = pt(bx, by)
|
|
||||||
g.moveTo(a[0], a[1]); g.lineTo(b[0], b[1])
|
|
||||||
}
|
|
||||||
const R = 26
|
|
||||||
// 中心点
|
|
||||||
g.lineStyle(2, 0x35d0ff, 0.9)
|
|
||||||
g.beginFill(0x35d0ff, 0.25)
|
|
||||||
g.drawCircle(cx, cy, 6)
|
|
||||||
g.endFill()
|
|
||||||
if (activeTool.value === 'translate') {
|
|
||||||
// 十字箭头
|
|
||||||
g.lineStyle(2, 0x35d0ff, 0.9)
|
|
||||||
line(-R, 0, R, 0); line(0, -R, 0, R)
|
|
||||||
line(R - 6, -4, R, 0); line(R, 0, R - 6, 4) // 右箭头
|
|
||||||
line(-(R - 6), -4, -R, 0); line(-R, 0, -(R - 6), 4) // 左箭头
|
|
||||||
line(-4, R - 6, 0, R); line(0, R, 4, R - 6) // 上箭头
|
|
||||||
line(-4, -(R - 6), 0, -R); line(0, -R, 4, -(R - 6)) // 下箭头
|
|
||||||
} else if (activeTool.value === 'rotate') {
|
|
||||||
// 旋转外圈(经缩放/旋转后可能是椭圆)
|
|
||||||
g.lineStyle(2, 0x35d0ff, 0.8)
|
|
||||||
const steps = 48
|
|
||||||
for (let i = 0; i <= steps; i++) {
|
|
||||||
const a = i / steps * Math.PI * 2
|
|
||||||
const [px, py] = pt(Math.cos(a) * R, Math.sin(a) * R)
|
|
||||||
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
|
||||||
}
|
|
||||||
// 指向点:固定于局部顶部(0° 刻度在 12 点方向),经 pt(含 root 旋转)后随 rootRotation 旋转。
|
|
||||||
// 默认(rootRotation=0)时指向点位于顶部;旋转时它随 root 一起转,反映当前旋转角。
|
|
||||||
const [tx, ty] = pt(0, R)
|
|
||||||
g.beginFill(0x35d0ff, 0.95)
|
|
||||||
g.drawCircle(tx, ty, 4)
|
|
||||||
g.endFill()
|
|
||||||
} else if (activeTool.value === 'scale') {
|
|
||||||
// 四角方点(经缩放/旋转后为旋转矩形)
|
|
||||||
g.lineStyle(2, 0x35d0ff, 0.9)
|
|
||||||
const corners: [number, number][] = [[-R, -R], [R, -R], [R, R], [-R, R]]
|
|
||||||
const pts = corners.map(([ox, oy]) => pt(ox, oy))
|
|
||||||
g.moveTo(pts[0][0], pts[0][1]); g.lineTo(pts[1][0], pts[1][1]); g.lineTo(pts[2][0], pts[2][1]); g.lineTo(pts[3][0], pts[3][1]); g.lineTo(pts[0][0], pts[0][1])
|
|
||||||
g.beginFill(0x35d0ff, 0.9)
|
|
||||||
for (const p of pts) g.drawRect(p[0] - 3, p[1] - 3, 6, 6)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 粒子→骨骼映射:每个活动粒子 = 一根挂在 root 下的骨骼
|
|
||||||
type BonePreview = { boneName: string; x: number; y: number; rotation: number; parentX?: number; parentY?: number; trail?: boolean }
|
|
||||||
|
|
||||||
function appendStateBones(target: BonePreview[], state: ParticleState, parentX: number, parentY: number) {
|
|
||||||
// 粒子快照同时保存局部坐标和烘焙世界坐标。骨骼调试图层挂在 world 下,必须使用
|
|
||||||
// 世界坐标,才能与发生位移、旋转、缩放或跟随后的粒子保持逐点重合。
|
|
||||||
const hasWorldPosition = Number.isFinite(state.worldX) && Number.isFinite(state.worldY)
|
|
||||||
const hasWorldRotation = Number.isFinite(state.worldRotation)
|
|
||||||
target.push({
|
|
||||||
boneName: state.boneName,
|
|
||||||
x: hasWorldPosition ? state.worldX! : state.x,
|
|
||||||
y: hasWorldPosition ? -state.worldY! : state.y,
|
|
||||||
rotation: hasWorldRotation ? -state.worldRotation! : -state.rotation,
|
|
||||||
parentX,
|
|
||||||
parentY,
|
|
||||||
})
|
|
||||||
if (!state.trailState?.bones.length) return
|
|
||||||
state.trailState.bones.forEach((bone) => {
|
|
||||||
// 导出结构中拖尾骨骼全部直接挂在粒子系统骨骼下,画布连线也保持同级语义。
|
|
||||||
const hasBoneWorldPosition = Number.isFinite(bone.worldX) && Number.isFinite(bone.worldY)
|
|
||||||
const hasBoneWorldRotation = Number.isFinite(bone.worldRotation)
|
|
||||||
target.push({
|
|
||||||
boneName: bone.boneName,
|
|
||||||
x: hasBoneWorldPosition ? bone.worldX! : bone.x,
|
|
||||||
y: hasBoneWorldPosition ? -bone.worldY! : bone.y,
|
|
||||||
rotation: hasBoneWorldRotation ? -bone.worldRotation! : bone.rotation,
|
|
||||||
parentX,
|
|
||||||
parentY,
|
|
||||||
trail: true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawDirectionalAxis(
|
|
||||||
g: Graphics,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
directionX: number,
|
|
||||||
directionY: number,
|
|
||||||
length: number,
|
|
||||||
width: number,
|
|
||||||
color: number,
|
|
||||||
) {
|
|
||||||
const negativeLength = length * 0.62
|
|
||||||
const tipX = x + directionX * length
|
|
||||||
const tipY = y + directionY * length
|
|
||||||
const headLength = Math.max(2.5, length * 0.32)
|
|
||||||
const headWidth = Math.max(1.7, length * 0.22)
|
|
||||||
const baseX = tipX - directionX * headLength
|
|
||||||
const baseY = tipY - directionY * headLength
|
|
||||||
const perpendicularX = -directionY
|
|
||||||
const perpendicularY = directionX
|
|
||||||
g.lineStyle(width, color, 0.95)
|
|
||||||
g.moveTo(x - directionX * negativeLength, y - directionY * negativeLength)
|
|
||||||
g.lineTo(baseX, baseY)
|
|
||||||
g.lineStyle(0)
|
|
||||||
g.beginFill(color, 0.95)
|
|
||||||
g.drawPolygon([
|
|
||||||
tipX, tipY,
|
|
||||||
baseX + perpendicularX * headWidth, baseY + perpendicularY * headWidth,
|
|
||||||
baseX - perpendicularX * headWidth, baseY - perpendicularY * headWidth,
|
|
||||||
])
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBones(bones: BonePreview[]) {
|
|
||||||
if (!boneGfx) return
|
|
||||||
const g = boneGfx
|
|
||||||
g.clear()
|
|
||||||
if (!store.settings.showBones) return
|
|
||||||
const cx = 0, cy = 0
|
|
||||||
const viewScale = Math.max(0.25, Math.abs(store.editor.viewScale) || 1)
|
|
||||||
const pointSize = Math.min(10, Math.max(0.1, Number(store.settings.bonePointDrawSize) || 1))
|
|
||||||
const axisSize = Math.min(10, Math.max(0.1, Number(store.settings.boneAxisDrawSize) || 2))
|
|
||||||
// 所有尺寸先定义为屏幕像素,再除以画布缩放,保证缩放画布时视觉大小不变。
|
|
||||||
const pointRadius = 2.5 * pointSize / viewScale
|
|
||||||
const forwardLength = 4.5 * axisSize / viewScale
|
|
||||||
const forwardWidth = Math.max(0.65, axisSize * 0.625) / viewScale
|
|
||||||
// root → 每颗粒子骨骼 的连线
|
|
||||||
for (const b of bones) {
|
|
||||||
const color = b.trail ? 0xff4d8d : 0xff8c5a
|
|
||||||
g.lineStyle(1, color, b.trail ? 0.65 : 0.35)
|
|
||||||
g.moveTo(b.parentX ?? cx, b.parentY ?? cy)
|
|
||||||
g.lineTo(b.x, b.y)
|
|
||||||
// 局部坐标轴为两根交叉轴线,仅在 +X、+Y 端绘制箭头。
|
|
||||||
const angle = b.rotation * Math.PI / 180
|
|
||||||
const cos = Math.cos(angle), sin = Math.sin(angle)
|
|
||||||
drawDirectionalAxis(g, b.x, b.y, cos, sin, forwardLength, forwardWidth, 0xef5350)
|
|
||||||
drawDirectionalAxis(g, b.x, b.y, sin, -cos, forwardLength, forwardWidth, 0x55c96b)
|
|
||||||
// 骨骼点
|
|
||||||
g.beginFill(color, 0.9)
|
|
||||||
g.drawCircle(b.x, b.y, pointRadius)
|
|
||||||
g.endFill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function colorNumber(value: string, fallback = 0xffffff) {
|
|
||||||
const parsed = Number.parseInt(value.replace('#', ''), 16)
|
|
||||||
return Number.isFinite(parsed) ? parsed : fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawCollisionBodies() {
|
|
||||||
if (!collisionGfx) return
|
|
||||||
const g = collisionGfx
|
|
||||||
g.clear()
|
|
||||||
for (const collider of previewColliders) {
|
|
||||||
if (collider.visible === false) continue
|
|
||||||
const selected = store.activeObjectType === 'collision' && store.activeObjectId === collider.id
|
|
||||||
const color = collider.enabled ? (selected ? 0xff5e72 : 0xd4475b) : 0x586174
|
|
||||||
const angle = collider.rotation * Math.PI / 180
|
|
||||||
const cos = Math.cos(angle), sin = Math.sin(angle)
|
|
||||||
const scaleX = Math.max(0.0001, Math.abs(collider.scaleX ?? 1))
|
|
||||||
const scaleY = Math.max(0.0001, Math.abs(collider.scaleY ?? 1))
|
|
||||||
const point = (x: number, y: number) => ({
|
|
||||||
x: collider.x + cos * x * scaleX - sin * y * scaleY,
|
|
||||||
y: -(collider.y + sin * x * scaleX + cos * y * scaleY),
|
|
||||||
})
|
|
||||||
let points: Array<{ x: number; y: number }> = []
|
|
||||||
if (collider.shape === 'circle' || collider.shape === 'ellipse') {
|
|
||||||
const radiusX = collider.shape === 'circle' ? collider.radius : collider.radiusX
|
|
||||||
const radiusY = collider.shape === 'circle' ? collider.radius : collider.radiusY
|
|
||||||
points = Array.from({ length: 49 }, (_, index) => {
|
|
||||||
const theta = index / 48 * Math.PI * 2
|
|
||||||
return point(Math.cos(theta) * radiusX, Math.sin(theta) * radiusY)
|
|
||||||
})
|
|
||||||
} else if (collider.shape === 'rect') {
|
|
||||||
const hw = collider.width * 0.5, hh = collider.height * 0.5
|
|
||||||
points = [point(-hw, -hh), point(hw, -hh), point(hw, hh), point(-hw, hh), point(-hw, -hh)]
|
|
||||||
} else {
|
|
||||||
const sides = Math.max(3, Math.min(16, Math.round(collider.sides)))
|
|
||||||
points = Array.from({ length: sides + 1 }, (_, index) => {
|
|
||||||
const theta = Math.PI * 0.5 + (index % sides) / sides * Math.PI * 2
|
|
||||||
return point(Math.cos(theta) * collider.width * 0.5, Math.sin(theta) * collider.height * 0.5)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!points.length) continue
|
|
||||||
g.lineStyle(selected ? 2.5 : 1.5, color, collider.enabled ? 0.95 : 0.45)
|
|
||||||
g.beginFill(color, selected ? 0.12 : 0.055)
|
|
||||||
g.moveTo(points[0].x, points[0].y)
|
|
||||||
for (let i = 1; i < points.length; i++) g.lineTo(points[i].x, points[i].y)
|
|
||||||
g.endFill()
|
|
||||||
const center = point(0, 0)
|
|
||||||
g.lineStyle(1, color, 0.8)
|
|
||||||
g.moveTo(center.x - 7, center.y); g.lineTo(center.x + 7, center.y)
|
|
||||||
g.moveTo(center.x, center.y - 7); g.lineTo(center.x, center.y + 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPaths() {
|
|
||||||
if (!pathGfx) return
|
|
||||||
const g = pathGfx
|
|
||||||
g.clear()
|
|
||||||
for (const path of store.paths) {
|
|
||||||
if (path.visible === false || !path.enabled || !path.points.length) continue
|
|
||||||
const selected = store.activeObjectType === 'path' && store.activeObjectId === path.id
|
|
||||||
const color = colorNumber(path.color, 0xff933e)
|
|
||||||
const screenPoint = (x: number, y: number) => {
|
|
||||||
const worldPoint = pathLocalToWorld(path, x, y)
|
|
||||||
return { x: worldPoint.x, y: -worldPoint.y }
|
|
||||||
}
|
|
||||||
const first = path.points[0]
|
|
||||||
const start = screenPoint(first.x, first.y)
|
|
||||||
g.lineStyle(selected ? 2.5 : 1.8, color, selected ? 1 : 0.75)
|
|
||||||
g.moveTo(start.x, start.y)
|
|
||||||
const segmentCount = path.closed ? path.points.length : path.points.length - 1
|
|
||||||
for (let i = 0; i < segmentCount; i++) {
|
|
||||||
const a = path.points[i]
|
|
||||||
const b = path.points[(i + 1) % path.points.length]
|
|
||||||
const controlA = screenPoint(a.x + a.outX, a.y + a.outY)
|
|
||||||
const controlB = screenPoint(b.x + b.inX, b.y + b.inY)
|
|
||||||
const end = screenPoint(b.x, b.y)
|
|
||||||
g.bezierCurveTo(controlA.x, controlA.y, controlB.x, controlB.y, end.x, end.y)
|
|
||||||
}
|
|
||||||
if (!selected) continue
|
|
||||||
path.points.forEach((anchor, index) => {
|
|
||||||
const point = screenPoint(anchor.x, anchor.y)
|
|
||||||
const input = screenPoint(anchor.x + anchor.inX, anchor.y + anchor.inY)
|
|
||||||
const output = screenPoint(anchor.x + anchor.outX, anchor.y + anchor.outY)
|
|
||||||
const hasInput = Math.hypot(anchor.inX, anchor.inY) > 0.01
|
|
||||||
const hasOutput = Math.hypot(anchor.outX, anchor.outY) > 0.01
|
|
||||||
g.lineStyle(1, 0xa9b4cb, 0.65)
|
|
||||||
if (hasInput) { g.moveTo(point.x, point.y); g.lineTo(input.x, input.y) }
|
|
||||||
if (hasOutput) { g.moveTo(point.x, point.y); g.lineTo(output.x, output.y) }
|
|
||||||
if (hasInput) { g.beginFill(0xd5dcec, 0.95); g.drawCircle(input.x, input.y, 3); g.endFill() }
|
|
||||||
if (hasOutput) { g.beginFill(0xd5dcec, 0.95); g.drawCircle(output.x, output.y, 3); g.endFill() }
|
|
||||||
g.beginFill(selectedPathNodes.has(index) ? 0xffffff : color, 1)
|
|
||||||
g.lineStyle(1.5, selectedPathNodes.has(index) ? 0x5d7cff : 0xffffff, 0.95)
|
|
||||||
g.drawCircle(point.x, point.y, selectedPathNodes.has(index) ? 5 : 4)
|
|
||||||
g.endFill()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type FollowCurvePoint = { x: number; y: number; inX?: number; inY?: number; outX?: number; outY?: number }
|
|
||||||
|
|
||||||
function followCurveValue(points: FollowCurvePoint[], t: number, mode: 'linear' | 'bezier' = 'linear') {
|
|
||||||
if (!points.length) return t
|
|
||||||
const sorted = [...points].sort((a, b) => a.x - b.x)
|
|
||||||
if (t <= sorted[0].x) return sorted[0].y
|
|
||||||
for (let index = 1; index < sorted.length; index++) {
|
|
||||||
const a = sorted[index - 1], b = sorted[index]
|
|
||||||
if (t <= b.x) {
|
|
||||||
let amount = (t - a.x) / Math.max(0.0001, b.x - a.x)
|
|
||||||
if (mode === 'bezier') {
|
|
||||||
const x1 = a.x + (a.outX ?? (b.x - a.x) / 3)
|
|
||||||
const x2 = b.x + (b.inX ?? -(b.x - a.x) / 3)
|
|
||||||
let low = 0, high = 1
|
|
||||||
for (let step = 0; step < 18; step++) {
|
|
||||||
const middle = (low + high) / 2
|
|
||||||
if (cubicPoint(a.x, x1, x2, b.x, middle) < t) low = middle
|
|
||||||
else high = middle
|
|
||||||
}
|
|
||||||
amount = (low + high) / 2
|
|
||||||
return cubicPoint(a.y, a.y + (a.outY ?? (b.y - a.y) / 3), b.y + (b.inY ?? -(b.y - a.y) / 3), b.y, amount)
|
|
||||||
}
|
|
||||||
return a.y + (b.y - a.y) * amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sorted[sorted.length - 1].y
|
|
||||||
}
|
|
||||||
|
|
||||||
function cubicPoint(a: number, b: number, c: number, d: number, t: number) {
|
|
||||||
const u = 1 - t
|
|
||||||
return u * u * u * a + 3 * u * u * t * b + 3 * u * t * t * c + t * t * t * d
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildPreviewColliders(timeSeconds: number) {
|
function buildPreviewColliders(timeSeconds: number) {
|
||||||
previewColliders = store.colliders.map((collider) => {
|
previewColliders = store.colliders.map((collider) => {
|
||||||
if (!collider.followPath || !collider.followPathId) return collider
|
if (!collider.followPath || !collider.followPathId) return collider
|
||||||
@@ -1410,7 +818,7 @@ function loop() {
|
|||||||
backgroundSprite.alpha = Math.min(1, Math.max(0, Number(store.settings.backgroundOpacity) || 0))
|
backgroundSprite.alpha = Math.min(1, Math.max(0, Number(store.settings.backgroundOpacity) || 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
drawAxis()
|
drawAxis(store, app, axisGfx, axisLabelLayer, axisLabels)
|
||||||
syncEmitters()
|
syncEmitters()
|
||||||
// 根节点(root)变换:位置(centerX/centerY)=位移,rotation/scale 绕 root 中心,与场景对象参数联动
|
// 根节点(root)变换:位置(centerX/centerY)=位移,rotation/scale 绕 root 中心,与场景对象参数联动
|
||||||
const followFps = Math.max(1, store.timeline.fps)
|
const followFps = Math.max(1, store.timeline.fps)
|
||||||
@@ -1638,15 +1046,15 @@ function loop() {
|
|||||||
timelineDisplayFrame = displayFrame
|
timelineDisplayFrame = displayFrame
|
||||||
spineRuntime?.sync(store.spines, displayFrame, fps)
|
spineRuntime?.sync(store.spines, displayFrame, fps)
|
||||||
for (const [, emitter] of emitterMap) emitter.drawTrailMeshDebug(store.settings.showSkinMesh)
|
for (const [, emitter] of emitterMap) emitter.drawTrailMeshDebug(store.settings.showSkinMesh)
|
||||||
drawCollisionBodies()
|
drawCollisionBodies(store, previewColliders, collisionGfx)
|
||||||
drawPaths()
|
drawPaths(store, selectedPathNodes, pathGfx)
|
||||||
drawBoneFollowTargets()
|
drawBoneFollowTargets(store, spineRuntime, boneFollowTargetGfx)
|
||||||
drawOrigin()
|
drawOrigin(store, emitterPreviewTransform, originGfx)
|
||||||
drawShapeRange()
|
drawShapeRange(store, emitterPreviewTransform, shapeGfx)
|
||||||
drawForceField()
|
drawForceField(store, forceFieldGfx)
|
||||||
drawAttraction()
|
drawAttraction(store, attractionGfx)
|
||||||
drawGizmo()
|
drawGizmo(activeTool, selectedTransformTarget, gizmoGfx)
|
||||||
drawBones(collected)
|
drawBones(collected, store, boneGfx)
|
||||||
if (info.value) { info.value.systems = store.systems.length; info.value.particles = total }
|
if (info.value) { info.value.systems = store.systems.length; info.value.particles = total }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 2. 发射模式 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('emitMode')">
|
||||||
|
<span class="caret">{{ collapsed.emitMode ? '▸' : '▾' }}</span><span>发射模式</span>
|
||||||
|
<button class="group-reset" title="重置发射模式参数" @click.stop="resetModule('emitMode')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body" v-show="!collapsed.emitMode">
|
||||||
|
<div class="segs">
|
||||||
|
<button class="seg" :class="{ on: sys.config.mode === 'stream' }" @click="setEmitMode('stream')">持续</button>
|
||||||
|
<button class="seg" :class="{ on: sys.config.mode === 'burst' }" @click="setEmitMode('burst')">爆发</button>
|
||||||
|
</div>
|
||||||
|
<template v-if="sys.config.mode === 'stream'">
|
||||||
|
<div class="segs stream-behavior-segs">
|
||||||
|
<button class="seg" :class="{ on: sys.config.streamBehavior === 'normal' }" @click="setStreamBehavior('normal')">常规持续</button>
|
||||||
|
<button class="seg" :class="{ on: sys.config.streamBehavior === 'loop' }" @click="setStreamBehavior('loop')">循环持续</button>
|
||||||
|
</div>
|
||||||
|
<NumSlider label="发射速率" :min="1" :max="300" :step="1" v-model="sys.config.rate" />
|
||||||
|
<NumSlider v-if="sys.config.streamBehavior === 'normal'" label="发射时长" :min="1 / 30" :max="60" :step="1 / 30" v-model="sys.config.duration" />
|
||||||
|
<NumSlider v-else label="发射时长 (f)" :min="1" :max="1800" :step="1" :model-value="loopDurationFrames" @update:model-value="setLoopDurationFrames" />
|
||||||
|
<div v-if="sys.config.streamBehavior === 'loop'" class="stream-loop-options">
|
||||||
|
<div class="loop-section-row">
|
||||||
|
<label class="switch-row">
|
||||||
|
<input type="checkbox" :checked="sys.config.generateLoopStartAnimation" @change="setLoopSection('start', $event)" />
|
||||||
|
<span class="switch-ui"></span><span>生成开始循环动画</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.generateLoopStartAnimation" class="loop-duration-control">
|
||||||
|
<label class="switch-row loop-duration-toggle">
|
||||||
|
<input type="checkbox" :checked="sys.config.loopStartUseCustomDuration" @change="setLoopCustomDuration('start', $event)" />
|
||||||
|
<span class="switch-ui"></span><span>指定时长</span>
|
||||||
|
</label>
|
||||||
|
<label class="loop-section-duration">
|
||||||
|
<input v-if="sys.config.loopStartUseCustomDuration" :value="sys.config.loopStartDurationFrames" type="number" min="1" max="1800" step="1" aria-label="开始循环段时长" @change="setLoopSectionDuration('start', $event)" /><span v-if="sys.config.loopStartUseCustomDuration">f</span>
|
||||||
|
<span v-else class="loop-auto-duration">自动 {{ automaticLoopDuration }}f</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="loop-section-row">
|
||||||
|
<label class="switch-row">
|
||||||
|
<input type="checkbox" :checked="sys.config.generateLoopEndAnimation" @change="setLoopSection('end', $event)" />
|
||||||
|
<span class="switch-ui"></span><span>生成结束循环动画</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.generateLoopEndAnimation" class="loop-duration-control">
|
||||||
|
<label class="switch-row loop-duration-toggle">
|
||||||
|
<input type="checkbox" :checked="sys.config.loopEndUseCustomDuration" @change="setLoopCustomDuration('end', $event)" />
|
||||||
|
<span class="switch-ui"></span><span>指定时长</span>
|
||||||
|
</label>
|
||||||
|
<label class="loop-section-duration">
|
||||||
|
<input v-if="sys.config.loopEndUseCustomDuration" :value="sys.config.loopEndDurationFrames" type="number" min="1" max="1800" step="1" aria-label="结束循环段时长" @change="setLoopSectionDuration('end', $event)" /><span v-if="sys.config.loopEndUseCustomDuration">f</span>
|
||||||
|
<span v-else class="loop-auto-duration">自动 {{ automaticLoopDuration }}f</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="sys.config.streamBehavior === 'loop'" class="modifier-hint stream-loop-hint">循环持续模式不支持骨骼跟随和路径跟随;进入或退出时会将粒子跟随设置恢复默认值。</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NumSlider label="单次数量" :min="1" :max="1000" :step="1" v-model="sys.config.burstCount" />
|
||||||
|
</template>
|
||||||
|
<NumSlider label="延迟时间" :min="0" :max="5" :step="0.1" v-model="sys.config.delay" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 3. 发射器形状 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('shape')">
|
||||||
|
<span class="caret">{{ collapsed.shape ? '▸' : '▾' }}</span><span>发射器形状</span>
|
||||||
|
<button class="group-reset" title="重置发射器形状参数" @click.stop="resetModule('shape')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body" v-show="!collapsed.shape">
|
||||||
|
<label class="row"><span>形状</span>
|
||||||
|
<select v-model="sys.config.shape" class="inp" @change="onShapeChange">
|
||||||
|
<option value="point">点</option>
|
||||||
|
<option value="circle">圆形</option>
|
||||||
|
<option value="rect">矩形</option>
|
||||||
|
<option value="cone">锥形</option>
|
||||||
|
<option value="path">路径</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<template v-if="sys.config.shape === 'circle' || sys.config.shape === 'cone'">
|
||||||
|
<NumSlider :label="sys.config.shape === 'cone' ? '内径' : '内半径'" :min="0" :max="sys.config.outerRadius" :step="1" v-model="sys.config.innerRadius" />
|
||||||
|
<NumSlider :label="sys.config.shape === 'cone' ? '外径' : '外半径'" :min="sys.config.innerRadius" :max="300" :step="1" v-model="sys.config.outerRadius" />
|
||||||
|
</template>
|
||||||
|
<template v-if="sys.config.shape === 'cone'">
|
||||||
|
<NumSlider label="锥形朝向" :min="-360" :max="360" :step="1" v-model="sys.config.coneDirection" />
|
||||||
|
<NumSlider label="锥形开口角度" :min="1" :max="360" :step="1" v-model="sys.config.coneAngle" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="sys.config.shape === 'rect'">
|
||||||
|
<NumSlider label="内宽" :min="0" :max="sys.config.outerRectW" :step="1" v-model="sys.config.innerRectW" />
|
||||||
|
<NumSlider label="内高" :min="0" :max="sys.config.outerRectH" :step="1" v-model="sys.config.innerRectH" />
|
||||||
|
<NumSlider label="外宽" :min="sys.config.innerRectW" :max="400" :step="1" v-model="sys.config.outerRectW" />
|
||||||
|
<NumSlider label="外高" :min="sys.config.innerRectH" :max="400" :step="1" v-model="sys.config.outerRectH" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="sys.config.shape === 'path'">
|
||||||
|
<label class="row"><span>选择路径</span>
|
||||||
|
<select v-model.number="sys.config.spawnPathId" class="inp" @change="onSpawnPathChange">
|
||||||
|
<option :value="0">无路径(点发射)</option>
|
||||||
|
<option v-for="path in store.paths" :key="path.id" :value="path.id" :disabled="!path.enabled">{{ path.name }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label v-if="sys.config.spawnPathId > 0" class="row"><span>出生分布</span>
|
||||||
|
<select v-model="sys.config.spawnPathDistribution" class="inp">
|
||||||
|
<option value="fixed">等间距固定分布</option>
|
||||||
|
<option value="random">随机分布</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="row"><span>发射角度</span>
|
||||||
|
<select v-model="sys.config.emissionAngleMode" class="inp">
|
||||||
|
<option value="fixed">固定</option>
|
||||||
|
<option value="random">随机</option>
|
||||||
|
<option v-if="sys.config.shape === 'circle' || sys.config.shape === 'rect' || sys.config.shape === 'cone'" value="radial">径向(中心向外)</option>
|
||||||
|
<option v-if="sys.config.shape === 'path' && sys.config.spawnPathId > 0" value="path-normal">垂直于路径</option>
|
||||||
|
<option v-if="sys.config.shape === 'path' && sys.config.spawnPathId > 0" value="path-tangent">沿路径方向</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<NumSlider v-if="sys.config.emissionAngleMode === 'fixed'" label="固定角度" :min="-360" :max="360" :step="1" v-model="sys.config.direction" />
|
||||||
|
<template v-else-if="sys.config.emissionAngleMode === 'random'">
|
||||||
|
<NumSlider label="基准角度" :min="-360" :max="360" :step="1" v-model="sys.config.direction" />
|
||||||
|
<NumSlider label="随机范围" :min="0" :max="360" :step="1" v-model="sys.config.spread" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { store, sys, automaticLoopDuration, loopDurationFrames, collapsed, toggle, setEmitMode, setLoopDurationFrames, setStreamBehavior, setLoopSection, setLoopSectionDuration, setLoopCustomDuration, resetModule, select, onShapeChange, onSpawnPathChange } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-reset {
|
||||||
|
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
|
||||||
|
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.divider { height: 1px; background: #2a2a3c; margin: 8px 0; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.inp { flex: 1; min-width: 0; padding: 3px 5px; background: #1a1a28; border: 1px solid #2e2e44; border-radius: 5px; color: #dde; font-size: 12px; }
|
||||||
|
.segs { display: flex; gap: 6px; margin: 6px 0; }
|
||||||
|
.stream-behavior-segs { margin-top: 10px; }
|
||||||
|
.stream-loop-options { display: grid; gap: 8px; margin: 8px 0 4px; padding: 9px 10px; border: 1px solid #32405a; border-radius: 6px; background: #171f2e; }
|
||||||
|
.loop-section-row { display: flex; min-height: 27px; align-items: center; justify-content: space-between; gap: 8px; }
|
||||||
|
.loop-duration-control { display: flex; flex: 0 0 auto; align-items: center; gap: 7px; }
|
||||||
|
.loop-duration-toggle { gap: 5px; font-size: 10px; white-space: nowrap; }
|
||||||
|
.loop-duration-toggle .switch-ui { width: 26px; height: 14px; }
|
||||||
|
.loop-duration-toggle .switch-ui::after { width: 10px; height: 10px; }
|
||||||
|
.loop-duration-toggle input:checked + .switch-ui::after { transform: translateX(12px); }
|
||||||
|
.loop-section-duration { display: flex; flex: 0 0 auto; align-items: center; gap: 4px; color: #7f8ba4; font-size: 11px; }
|
||||||
|
.loop-section-duration input { position: static; width: 54px; height: 25px; padding: 0 6px; border: 1px solid #3a465f; border-radius: 5px; outline: none; background: #101725; color: #e4e8f2; text-align: right; opacity: 1; pointer-events: auto; }
|
||||||
|
.loop-section-duration input:focus { border-color: #716eff; }
|
||||||
|
.loop-auto-duration { min-width: 54px; color: #77839a; text-align: right; white-space: nowrap; }
|
||||||
|
.seg { flex: 1; padding: 5px; background: #1e1e2e; border: 1px solid #2e2e44; border-radius: 6px; color: #aab; font-size: 12px; cursor: pointer; }
|
||||||
|
.seg.on { background: #3a5a8c; color: #fff; border-color: #5a7ab8; }
|
||||||
|
.seg { display: flex; gap: 6px; margin: 6px 0; }
|
||||||
|
.inp.tiny { width: 46px; flex: 0 0 46px; text-align: center; }
|
||||||
|
.switch-row { display: flex; align-items: center; gap: 9px; color: #b8c1d5; font-size: 12px; cursor: pointer; }
|
||||||
|
.switch-row input { position: absolute; opacity: 0; pointer-events: none; }
|
||||||
|
.switch-ui { position: relative; width: 32px; height: 18px; border-radius: 10px; background: #354258; transition: background .16s ease; }
|
||||||
|
.switch-ui::after { content: ''; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: transform .16s ease; }
|
||||||
|
.switch-row input:checked + .switch-ui { background: #625ff1; }
|
||||||
|
.switch-row input:checked + .switch-ui::after { transform: translateX(14px); }
|
||||||
|
.modifier-hint { margin: 7px 0 0 2px; color: #647086; font-size: 9px; line-height: 1.5; }
|
||||||
|
.stream-loop-hint { margin: 9px 2px 3px; padding: 7px 8px; border: 1px solid #4a436e; border-radius: 5px; background: #211f35; color: #aaa4d7; }
|
||||||
|
.switch-row input:disabled + .switch-ui { opacity: 0.45; cursor: not-allowed; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 7. 导出设置:当前阶段只维护参数,后续接入 JSON 生成。 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('export')">
|
||||||
|
<span class="caret">{{ collapsed.export ? '▸' : '▾' }}</span><span>导出设置</span>
|
||||||
|
<button class="group-reset" title="重置导出设置" @click.stop="resetModule('export')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body export-settings" v-show="!collapsed.export">
|
||||||
|
<label class="export-field">
|
||||||
|
<span>图片路径 (Spine Images Path)</span>
|
||||||
|
<input v-model.trim="exportSettings.imagesPath" class="inp export-input" type="text" placeholder="./images/" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="export-field">
|
||||||
|
<span>Spine 版本</span>
|
||||||
|
<select v-model="exportSettings.spineVersion" class="inp export-input">
|
||||||
|
<option value="3.8">Spine 3.8</option>
|
||||||
|
<option value="4.0">Spine 4.0</option>
|
||||||
|
<option value="4.1">Spine 4.1</option>
|
||||||
|
<option value="4.2">Spine 4.2</option>
|
||||||
|
<option value="4.3">Spine 4.3</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="export-field">
|
||||||
|
<span>关键帧插值曲线</span>
|
||||||
|
<select v-model="exportSettings.keyframeCurve" class="inp export-input">
|
||||||
|
<option value="linear">线性 (Linear)</option>
|
||||||
|
<option value="bezier">平滑贝塞尔 (Smooth Bézier)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class="export-help">平滑贝塞尔会在误差允许范围内拟合连续运动;出生、死亡、附件切换和突变仍使用阶跃关键帧。</div>
|
||||||
|
<div v-if="exportSettings.spineVersion === '3.8'" class="export-help">Spine 3.8 不支持 4.x 的逐通道数组贝塞尔,导出时会转换为经过容差精简的分段线性关键帧。</div>
|
||||||
|
|
||||||
|
<div class="export-fps-row">
|
||||||
|
<label class="export-field export-fps-field">
|
||||||
|
<span>导出 FPS</span>
|
||||||
|
<input v-model.number="exportSettings.fps" class="inp export-input" type="number" min="1" max="240" step="1" @change="normalizeExportFps" />
|
||||||
|
</label>
|
||||||
|
<label class="export-checkbox"><input v-model="exportSettings.omitFps" type="checkbox" />不写 fps</label>
|
||||||
|
</div>
|
||||||
|
<div class="export-help">修改 FPS 时保持动画实际秒数不变;“不写 fps”只省略 Spine 的 fps 元数据。</div>
|
||||||
|
|
||||||
|
<div class="export-switches">
|
||||||
|
<label class="switch-row export-switch">
|
||||||
|
<input v-model="exportSettings.integerFrameAlignment" type="checkbox" />
|
||||||
|
<span class="switch-ui"></span><span>整数帧对齐</span>
|
||||||
|
</label>
|
||||||
|
<label class="switch-row export-switch">
|
||||||
|
<input v-model="exportSettings.bonePool" type="checkbox" />
|
||||||
|
<span class="switch-ui"></span><span>骨骼对象池</span>
|
||||||
|
</label>
|
||||||
|
<label class="switch-row export-switch export-switch-last">
|
||||||
|
<input v-model="exportSettings.excludeEmptyAnimations" type="checkbox" />
|
||||||
|
<span class="switch-ui"></span><span>排除无内容动画</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { exportSettings, collapsed, toggle, resetModule, normalizeExportFps, select } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-reset {
|
||||||
|
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
|
||||||
|
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.inp { flex: 1; min-width: 0; padding: 3px 5px; background: #1a1a28; border: 1px solid #2e2e44; border-radius: 5px; color: #dde; font-size: 12px; }
|
||||||
|
.placeholder { color: #667; font-size: 11px; padding: 6px 0; }
|
||||||
|
.export-settings { padding-top: 3px; }
|
||||||
|
.export-field { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 13px; color: #9ca8bf; font-size: 11px; font-weight: 600; }
|
||||||
|
.export-input { width: 100%; min-height: 34px; box-sizing: border-box; padding: 6px 9px; color: #e3e7f2; background: #141a29; border-color: #35445f; }
|
||||||
|
.export-fps-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: end; }
|
||||||
|
.export-fps-field { margin-bottom: 5px; }
|
||||||
|
.export-checkbox { display: flex; align-items: center; gap: 7px; min-height: 34px; margin-bottom: 5px; color: #aeb8cc; font-size: 11px; white-space: nowrap; }
|
||||||
|
.export-checkbox input { width: 17px; height: 17px; margin: 0; accent-color: #625ff1; }
|
||||||
|
.export-help { margin: -6px 0 14px; color: #66758e; font-size: 10px; line-height: 1.5; }
|
||||||
|
.export-switches { display: grid; gap: 13px; margin: 4px 0 7px; padding-top: 2px; }
|
||||||
|
.export-switch { font-weight: 600; }
|
||||||
|
.export-switch-last { margin-top: 13px; }
|
||||||
|
.inp.tiny { width: 46px; flex: 0 0 46px; text-align: center; }
|
||||||
|
.switch-row { display: flex; align-items: center; gap: 9px; color: #b8c1d5; font-size: 12px; cursor: pointer; }
|
||||||
|
.switch-row input { position: absolute; opacity: 0; pointer-events: none; }
|
||||||
|
.switch-ui { position: relative; width: 32px; height: 18px; border-radius: 10px; background: #354258; transition: background .16s ease; }
|
||||||
|
.switch-ui::after { content: ''; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: transform .16s ease; }
|
||||||
|
.switch-row input:checked + .switch-ui { background: #625ff1; }
|
||||||
|
.switch-row input:checked + .switch-ui::after { transform: translateX(14px); }
|
||||||
|
.switch-row input:disabled + .switch-ui { opacity: 0.45; cursor: not-allowed; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 5. 外观与资源 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('look')">
|
||||||
|
<span class="caret">{{ collapsed.look ? '▸' : '▾' }}</span><span>外观与资源</span>
|
||||||
|
<button class="group-reset" title="重置外观与资源参数" @click.stop="resetModule('look')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body" v-show="!collapsed.look">
|
||||||
|
<!-- 透明度 -->
|
||||||
|
<div class="look-title">透明度</div>
|
||||||
|
<div class="seg">
|
||||||
|
<button class="seg-btn" :class="{ active: sys.config.alphaMode === 'curve' }" @click="sys.config.alphaMode = 'curve'">曲线</button>
|
||||||
|
<button class="seg-btn" :class="{ active: sys.config.alphaMode === 'fixed' }" @click="sys.config.alphaMode = 'fixed'">固定</button>
|
||||||
|
</div>
|
||||||
|
<template v-if="sys.config.alphaMode === 'fixed'">
|
||||||
|
<NumSlider label="透明度" :min="0" :max="1" :step="0.01" v-model="sys.config.alpha" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<label class="row"><span>曲线模式</span>
|
||||||
|
<select v-model="sys.config.curveMode" class="inp">
|
||||||
|
<option>跟随导出</option><option>线性</option><option>贝塞尔</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class="curve-wrap">
|
||||||
|
<CurveEditor v-model="sys.config.alphaCurve" :default-value="ALPHA_CURVE" :mode="resolvedCurveEditorMode(sys.config.curveMode)" />
|
||||||
|
<div class="curve-labels"><span>0(透明)</span><span>生命周期 →</span><span>1(不透明)</span></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 图片资源 -->
|
||||||
|
<div class="look-title image-title">
|
||||||
|
<span>图片资源</span>
|
||||||
|
<button class="image-add" title="新增图片资源" @click="addImageResource">+</button>
|
||||||
|
</div>
|
||||||
|
<ImageResourceCard
|
||||||
|
v-for="resource in sys.config.imageResources"
|
||||||
|
:key="resource.id"
|
||||||
|
:resource="resource"
|
||||||
|
:resource-count="sys.config.imageResources.length"
|
||||||
|
supports-sequence
|
||||||
|
default-file="star.png"
|
||||||
|
@pick="openTexturePicker"
|
||||||
|
@pick-sequence="openSequencePicker"
|
||||||
|
@reset="resetTexture"
|
||||||
|
@remove="removeImageResource"
|
||||||
|
@update-weight="setResourceWeight"
|
||||||
|
@toggle-lock="toggleResourceLock"
|
||||||
|
>
|
||||||
|
<NumSlider label="缩放" :min="0.1" :max="3" :step="0.05" v-model="resource.scale" />
|
||||||
|
<label class="row"><span>轴心 x,y</span><input type="number" step="0.05" class="inp tiny" v-model.number="resource.anchorX" /><input type="number" step="0.05" class="inp tiny" v-model.number="resource.anchorY" /></label>
|
||||||
|
|
||||||
|
<div class="resource-divider"></div>
|
||||||
|
<label class="row resource-color-row"><span>颜色</span>
|
||||||
|
<select v-model="resource.colorMode" class="inp">
|
||||||
|
<option value="fixed">固定色</option>
|
||||||
|
<option value="random">随机颜色</option>
|
||||||
|
<option value="lifetime">生命周期</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="resource.blend" class="inp blend-select">
|
||||||
|
<option value="normal">正常</option>
|
||||||
|
<option value="add">相加</option>
|
||||||
|
<option value="multiply">相乘</option>
|
||||||
|
<option value="screen">滤色</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<template v-if="resource.colorMode === 'fixed'">
|
||||||
|
<label class="row resource-color-picker"><span>固定色</span><input type="color" v-model="resource.colorStart" class="colr" /><input type="text" class="hex-input" :value="resource.colorStart" spellcheck="false" aria-label="固定颜色值" @change="setResourceFixedColor(resource, $event)" @keydown.enter="commitResourceFixedColor(resource, $event)" /></label>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ColorGradientEditor v-model="resource.colorGradient" />
|
||||||
|
<div v-if="resource.colorMode === 'random'" class="random-color-hint">每个粒子出生时从色带随机取色,生命周期内保持不变</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="resource-alpha-head">
|
||||||
|
<span>独立透明度</span>
|
||||||
|
<div class="alpha-modes">
|
||||||
|
<button :class="{ on: resource.independentAlpha === 'off' }" @click="resource.independentAlpha = 'off'">关闭</button>
|
||||||
|
<button :class="{ on: resource.independentAlpha === 'fixed' }" @click="resource.independentAlpha = 'fixed'">固定</button>
|
||||||
|
<button :class="{ on: resource.independentAlpha === 'curve' }" @click="resource.independentAlpha = 'curve'">曲线</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="resource.independentAlpha === 'fixed'" label="透明度" :min="0" :max="1" :step="0.01" v-model="resource.alpha" />
|
||||||
|
<template v-else-if="resource.independentAlpha === 'curve'">
|
||||||
|
<label class="row"><span>曲线模式</span>
|
||||||
|
<select v-model="resource.alphaCurveMode" class="inp">
|
||||||
|
<option>跟随导出</option><option>线性</option><option>贝塞尔</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class="resource-alpha-curve">
|
||||||
|
<CurveEditor v-model="resource.alphaCurve" :default-value="RESOURCE_ALPHA_CURVE" :mode="resolvedCurveEditorMode(resource.alphaCurveMode)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ImageResourceCard>
|
||||||
|
<input ref="textureFileInput" class="file-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif" @change="onTextureFile" />
|
||||||
|
<input ref="sequenceFileInput" class="file-input" type="file" multiple accept="image/png,image/jpeg,image/webp,image/gif" @change="onSequenceFiles" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { resolvedCurveEditorMode, sys, textureFileInput, sequenceFileInput, addImageResource, openTexturePicker, openSequencePicker, onTextureFile, onSequenceFiles, resetTexture, setResourceFixedColor, commitResourceFixedColor, removeImageResource, setResourceWeight, toggleResourceLock, collapsed, toggle, resetModule, select, ALPHA_CURVE, RESOURCE_ALPHA_CURVE } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-reset {
|
||||||
|
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
|
||||||
|
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.divider { height: 1px; background: #2a2a3c; margin: 8px 0; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.inp { flex: 1; min-width: 0; padding: 3px 5px; background: #1a1a28; border: 1px solid #2e2e44; border-radius: 5px; color: #dde; font-size: 12px; }
|
||||||
|
.colr { width: 28px; height: 22px; border: none; background: none; cursor: pointer; }
|
||||||
|
.seg { flex: 1; padding: 5px; background: #1e1e2e; border: 1px solid #2e2e44; border-radius: 6px; color: #aab; font-size: 12px; cursor: pointer; }
|
||||||
|
.seg.on { background: #3a5a8c; color: #fff; border-color: #5a7ab8; }
|
||||||
|
.look-title { font-size: 12px; font-weight: 600; color: #ccd; margin: 10px 0 6px; padding-bottom: 4px; border-bottom: 1px solid #2a2a3c; }
|
||||||
|
.look-title:first-child { margin-top: 2px; }
|
||||||
|
.seg { display: flex; gap: 6px; margin: 6px 0; }
|
||||||
|
.seg-btn { flex: 1; padding: 5px; background: #1e1e2e; border: 1px solid #2e2e44; border-radius: 6px; color: #aab; font-size: 12px; cursor: pointer; }
|
||||||
|
.seg-btn.active { background: #3a5a8c; color: #fff; border-color: #5a7ab8; }
|
||||||
|
.curve-wrap { margin: 6px 0; }
|
||||||
|
.curve-labels { display: flex; justify-content: space-between; color: #566; font-size: 10px; margin-top: 2px; }
|
||||||
|
.image-title { display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.image-add { width: 26px; height: 24px; border: 1px solid #4a4d9a; border-radius: 5px; background: #252850; color: #8f8cff; font-size: 18px; line-height: 1; cursor: pointer; }
|
||||||
|
.image-add:hover { background: #4542a8; color: #fff; }
|
||||||
|
.resource-divider { height: 1px; margin: 9px 0 7px; background: #2a3549; }
|
||||||
|
.resource-color-row .blend-select { max-width: 88px; }
|
||||||
|
.resource-color-picker .hex { margin-left: 0; }
|
||||||
|
.random-color-hint { margin: -5px 0 9px 68px; color: #66758e; font-size: 9px; line-height: 1.45; }
|
||||||
|
.hex-input { width: 72px; padding: 4px 6px; border: 1px solid #344158; border-radius: 5px; outline: none; background: #101725; color: #aeb8cb; font-size: 11px; font-family: monospace; }
|
||||||
|
.hex-input:focus { border-color: #7774ff; color: #fff; }
|
||||||
|
.resource-alpha-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 10px; color: #8a93bb; font-size: 12px; }
|
||||||
|
.alpha-modes { display: flex; gap: 3px; }
|
||||||
|
.alpha-modes button { padding: 4px 8px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: #8d98ae; font-size: 11px; cursor: pointer; }
|
||||||
|
.alpha-modes button:hover { background: #273246; }
|
||||||
|
.alpha-modes button.on { border-color: #7774ff; background: #605df0; color: #fff; }
|
||||||
|
.resource-alpha-curve { margin-top: 7px; }
|
||||||
|
.file-input { display: none; }
|
||||||
|
.hex { font-size: 11px; color: #889; margin-left: auto; font-variant-numeric: tabular-nums; }
|
||||||
|
.inp.tiny { width: 46px; flex: 0 0 46px; text-align: center; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 4. 粒子属性 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('attr')">
|
||||||
|
<span class="caret">{{ collapsed.attr ? '▸' : '▾' }}</span><span>粒子属性</span>
|
||||||
|
<button class="group-reset" title="重置粒子属性参数" @click.stop="resetModule('attr')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body attr-body" v-show="!collapsed.attr">
|
||||||
|
<div class="attr-section-title">粒子初始参数</div>
|
||||||
|
|
||||||
|
<ParticleAttributeControl
|
||||||
|
label="初始生命周期"
|
||||||
|
:allow-curve="false"
|
||||||
|
v-model:mode="sys.config.lifeMode"
|
||||||
|
v-model:value-min="sys.config.lifeMin"
|
||||||
|
v-model:value-max="sys.config.lifeMax"
|
||||||
|
v-model:curve="sys.config.lifeCurve"
|
||||||
|
:min="0.1" :max="10" :step="0.1"
|
||||||
|
curve-label="随机采样 →"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ParticleAttributeControl
|
||||||
|
label="初始速度"
|
||||||
|
:allow-curve="false"
|
||||||
|
v-model:mode="sys.config.speedMode"
|
||||||
|
v-model:value-min="sys.config.speedMin"
|
||||||
|
v-model:value-max="sys.config.speedMax"
|
||||||
|
v-model:curve="sys.config.speedCurve"
|
||||||
|
:min="0" :max="1000" :step="1"
|
||||||
|
curve-label="随机采样 →"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="attr-axis-head">
|
||||||
|
<span>初始缩放</span>
|
||||||
|
<div class="axis-modes">
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.scaleAxisMode === 'uniform' }" @click="sys.config.scaleAxisMode = 'uniform'">统一</button>
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.scaleAxisMode === 'separate' }" @click="sys.config.scaleAxisMode = 'separate'">XY分离</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
:label="sys.config.scaleAxisMode === 'uniform' ? '初始缩放' : '缩放 X'"
|
||||||
|
:allow-curve="false"
|
||||||
|
v-model:mode="sys.config.scaleMode"
|
||||||
|
v-model:value-min="sys.config.scaleMin"
|
||||||
|
v-model:value-max="sys.config.scaleMax"
|
||||||
|
v-model:curve="sys.config.scaleCurve"
|
||||||
|
:min="0" :max="5" :step="0.05"
|
||||||
|
curve-label="随机采样 →"
|
||||||
|
/>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
v-if="sys.config.scaleAxisMode === 'separate'"
|
||||||
|
label="缩放 Y"
|
||||||
|
:show-modes="false"
|
||||||
|
:allow-curve="false"
|
||||||
|
v-model:mode="sys.config.scaleMode"
|
||||||
|
v-model:value-min="sys.config.scaleYMin"
|
||||||
|
v-model:value-max="sys.config.scaleYMax"
|
||||||
|
v-model:curve="sys.config.scaleYCurve"
|
||||||
|
:min="0" :max="5" :step="0.05"
|
||||||
|
curve-label="随机采样 →"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ParticleAttributeControl
|
||||||
|
label="初始旋转"
|
||||||
|
allow-direction
|
||||||
|
:allow-curve="false"
|
||||||
|
v-model:mode="sys.config.initialRotationMode"
|
||||||
|
v-model:value-min="sys.config.initialRotationMin"
|
||||||
|
v-model:value-max="sys.config.initialRotationMax"
|
||||||
|
v-model:curve="sys.config.initialRotationCurve"
|
||||||
|
:min="-360" :max="360" :step="1"
|
||||||
|
curve-label="随机采样 →"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="attr-section-title life-title">粒子生命周期变化参数</div>
|
||||||
|
|
||||||
|
<div class="life-item">
|
||||||
|
<label class="switch-row">
|
||||||
|
<input type="checkbox" v-model="sys.config.speedOverLifeEnabled" />
|
||||||
|
<span class="switch-ui"></span><span>生命周期内速度</span>
|
||||||
|
</label>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
v-if="sys.config.speedOverLifeEnabled"
|
||||||
|
label="速度倍率"
|
||||||
|
:mode="sys.config.speedOverLifeMode"
|
||||||
|
@update:mode="setSpeedOverLifeMode"
|
||||||
|
v-model:value-min="sys.config.speedOverLifeMin"
|
||||||
|
v-model:value-max="sys.config.speedOverLifeMax"
|
||||||
|
v-model:curve="sys.config.speedOverLifeCurve"
|
||||||
|
:default-curve="FLAT_CURVE"
|
||||||
|
:min="0" :max="3" :step="0.05"
|
||||||
|
curve-label="生命周期 →"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="life-item">
|
||||||
|
<label class="switch-row">
|
||||||
|
<input type="checkbox" v-model="sys.config.scaleOverLifeEnabled" @change="prepareScaleOverLifeCurve" />
|
||||||
|
<span class="switch-ui"></span><span>生命周期内缩放</span>
|
||||||
|
</label>
|
||||||
|
<template v-if="sys.config.scaleOverLifeEnabled">
|
||||||
|
<div class="axis-modes life-axis">
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.scaleOverLifeAxisMode === 'uniform' }" @click="sys.config.scaleOverLifeAxisMode = 'uniform'">统一</button>
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.scaleOverLifeAxisMode === 'separate' }" @click="sys.config.scaleOverLifeAxisMode = 'separate'">XY分离</button>
|
||||||
|
</div>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
:label="sys.config.scaleOverLifeAxisMode === 'uniform' ? '缩放倍率' : '倍率 X'"
|
||||||
|
:mode="sys.config.scaleOverLifeMode"
|
||||||
|
@update:mode="setScaleOverLifeMode"
|
||||||
|
v-model:value-min="sys.config.scaleOverLifeMin"
|
||||||
|
v-model:value-max="sys.config.scaleOverLifeMax"
|
||||||
|
v-model:curve="sys.config.scaleOverLifeCurve"
|
||||||
|
:default-curve="FLAT_CURVE"
|
||||||
|
:min="0" :max="3" :step="0.05"
|
||||||
|
curve-label="生命周期 →"
|
||||||
|
/>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
v-if="sys.config.scaleOverLifeAxisMode === 'separate'"
|
||||||
|
label="倍率 Y"
|
||||||
|
:show-modes="false"
|
||||||
|
v-model:mode="sys.config.scaleOverLifeMode"
|
||||||
|
v-model:value-min="sys.config.scaleYOverLifeMin"
|
||||||
|
v-model:value-max="sys.config.scaleYOverLifeMax"
|
||||||
|
v-model:curve="sys.config.scaleYOverLifeCurve"
|
||||||
|
:default-curve="FLAT_CURVE"
|
||||||
|
:min="0" :max="3" :step="0.05"
|
||||||
|
curve-label="生命周期 →"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="life-item">
|
||||||
|
<label class="switch-row">
|
||||||
|
<input type="checkbox" v-model="sys.config.rotationOverLifeEnabled" />
|
||||||
|
<span class="switch-ui"></span><span>生命周期内旋转</span>
|
||||||
|
</label>
|
||||||
|
<ParticleAttributeControl
|
||||||
|
v-if="sys.config.rotationOverLifeEnabled"
|
||||||
|
label="旋转速度"
|
||||||
|
v-model:mode="sys.config.rotationOverLifeMode"
|
||||||
|
v-model:value-min="sys.config.rotationSpeedMin"
|
||||||
|
v-model:value-max="sys.config.rotationSpeedMax"
|
||||||
|
v-model:curve="sys.config.rotationOverLifeCurve"
|
||||||
|
:default-curve="LINEAR_CURVE"
|
||||||
|
:min="-720" :max="720" :step="10"
|
||||||
|
curve-label="生命周期 →"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { sys, collapsed, toggle, resetModule, setSpeedOverLifeMode, prepareScaleOverLifeCurve, setScaleOverLifeMode, LINEAR_CURVE, FLAT_CURVE } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-reset {
|
||||||
|
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
|
||||||
|
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.attr-body { padding: 10px; background: #151b27; }
|
||||||
|
.attr-section-title { margin: 2px 0 8px; color: #9ca8c0; font-size: 12px; font-weight: 700; }
|
||||||
|
.attr-section-title.life-title { margin-top: 14px; padding-top: 12px; border-top: 1px solid #2d3950; }
|
||||||
|
.attr-axis-head { display: flex; align-items: center; justify-content: space-between; margin: 10px 1px -2px; color: #aeb8cf; font-size: 12px; font-weight: 600; }
|
||||||
|
.axis-modes { display: flex; gap: 4px; }
|
||||||
|
.axis-mode { padding: 4px 8px; border: 1px solid #344158; border-radius: 5px; background: #273246; color: #aeb8cf; font-size: 11px; cursor: pointer; }
|
||||||
|
.axis-mode.on { border-color: #7774ff; background: #605df0; color: #fff; }
|
||||||
|
.life-item { padding: 9px 0; border-bottom: 1px solid #2a3549; }
|
||||||
|
.switch-row { display: flex; align-items: center; gap: 9px; color: #b8c1d5; font-size: 12px; cursor: pointer; }
|
||||||
|
.switch-row input { position: absolute; opacity: 0; pointer-events: none; }
|
||||||
|
.switch-ui { position: relative; width: 32px; height: 18px; border-radius: 10px; background: #354258; transition: background .16s ease; }
|
||||||
|
.switch-ui::after { content: ''; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: transform .16s ease; }
|
||||||
|
.switch-row input:checked + .switch-ui { background: #625ff1; }
|
||||||
|
.switch-row input:checked + .switch-ui::after { transform: translateX(14px); }
|
||||||
|
.life-axis { margin: 9px 0 2px 42px; }
|
||||||
|
.switch-row input:disabled + .switch-ui { opacity: 0.45; cursor: not-allowed; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div class="spine-export-actions">
|
||||||
|
<button type="button" class="export-spine-json" :disabled="exportBusy" :title="exportBusy ? '正在重新计算并烘焙动画' : `导出 Spine ${exportSettings.spineVersion} JSON`" @click="exportSpineJson">
|
||||||
|
{{ exportBusy && exportTarget === 'json' ? '正在准备导出…' : '导出Spine Json' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="export-spine-images" :disabled="exportBusy" title="单独下载当前粒子系统使用的图片 ZIP" @click="exportSpineImages">
|
||||||
|
{{ exportBusy && exportTarget === 'images' ? '正在整理图片…' : '导出 images 包' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="export-spine-project" :disabled="exportBusy || !connectorReady" :title="connectorButtonTitle" @click="exportSpineProject">
|
||||||
|
<span>{{ exportBusy && exportTarget === 'spine' ? '正在生成工程…' : '导出 .spine 包' }}</span>
|
||||||
|
<small v-if="!exportBusy && connectorDisabledReason">{{ connectorDisabledReason }}</small>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="connector-status" :class="connectorStatusClass">
|
||||||
|
<span class="connector-dot"></span>{{ connectorStatusText }}
|
||||||
|
<a v-if="connectorAuthorizationUrl" :href="connectorAuthorizationUrl" target="_blank" rel="noopener" title="在本机连接器中授权当前网站">授权当前网站</a>
|
||||||
|
<button type="button" title="重新检测连接器" @click="refreshConnectorHealth">刷新</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="exportMessage" class="export-message" :class="{ error: exportError }">
|
||||||
|
{{ exportMessage }}
|
||||||
|
<a v-if="connectorPairUrl" :href="connectorPairUrl" target="_blank" rel="noopener" @click="connectorPairUrl = ''">授权当前网站</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { exportSettings, exportBusy, exportTarget, exportMessage, exportError, connectorPairUrl, connectorStatusText, connectorStatusClass, connectorReady, connectorAuthorizationUrl, connectorDisabledReason, connectorButtonTitle, refreshConnectorHealth, exportSpineJson, exportSpineProject, exportSpineImages } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.spine-export-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 10px; }
|
||||||
|
.export-spine-json { width: 100%; padding: 9px 8px; flex: 0 0 auto; border: 1px solid #4f4b9a; border-radius: 7px; background: #403b91; color: #f1f0ff; font-size: 12px; font-weight: 700; cursor: pointer; }
|
||||||
|
.export-spine-json:hover { border-color: #7774ff; background: #514bc0; }
|
||||||
|
.export-spine-json:disabled { cursor: wait; opacity: .68; }
|
||||||
|
.export-spine-project { grid-column: 1 / -1; width: 100%; padding: 7px 8px; border: 1px solid #287d69; border-radius: 7px; background: #196854; color: #edfff9; font-size: 12px; font-weight: 700; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; }
|
||||||
|
.export-spine-project small { color: inherit; font-size: 9px; font-weight: 500; line-height: 1.1; }
|
||||||
|
.export-spine-project:hover:not(:disabled) { border-color: #47c89f; background: #238267; }
|
||||||
|
.export-spine-project:disabled { cursor: not-allowed; opacity: .48; }
|
||||||
|
.export-spine-images { width: 100%; padding: 8px; border: 1px solid #3b5f8b; border-radius: 7px; background: #243e64; color: #eaf3ff; font-size: 12px; font-weight: 700; cursor: pointer; }
|
||||||
|
.export-spine-images:hover:not(:disabled) { border-color: #6099dc; background: #315581; }
|
||||||
|
.export-spine-images:disabled { cursor: wait; opacity: .55; }
|
||||||
|
.connector-status { display: flex; align-items: center; justify-content: center; gap: 5px; padding-top: 6px; color: #717b99; font-size: 10px; }
|
||||||
|
.connector-status .connector-dot { width: 6px; height: 6px; border-radius: 50%; background: #66718f; }
|
||||||
|
.connector-status.ready { color: #70d8ae; }
|
||||||
|
.connector-status.ready .connector-dot { background: #49c993; box-shadow: 0 0 5px #49c99388; }
|
||||||
|
.connector-status.warning { color: #d4aa67; }
|
||||||
|
.connector-status.warning .connector-dot { background: #d4aa67; }
|
||||||
|
.connector-status button { padding: 0 3px; border: 0; background: transparent; color: #8290b6; font-size: 10px; cursor: pointer; }
|
||||||
|
.connector-status button:hover { color: #c5caff; }
|
||||||
|
.connector-status a { padding: 0 3px; color: #a8b0ff; font-size: 10px; text-decoration: underline; }
|
||||||
|
.connector-status a:hover { color: #d8dcff; }
|
||||||
|
.export-message { padding: 7px 5px 0; color: #70d8ae; font-size: 10px; line-height: 1.45; text-align: center; }
|
||||||
|
.export-message.error { color: #ff858f; }
|
||||||
|
.export-message a { display: inline-block; margin-left: 5px; color: #9da7ff; text-decoration: underline; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 6. 修改器 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head" @click="toggle('mods')">
|
||||||
|
<span class="caret">{{ collapsed.mods ? '▸' : '▾' }}</span><span>修改器</span>
|
||||||
|
<button class="group-reset" title="重置修改器参数" @click.stop="resetModule('mods')">重置</button>
|
||||||
|
</div>
|
||||||
|
<div class="group-body" v-show="!collapsed.mods">
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.noise" />
|
||||||
|
<span class="switch-ui"></span><span>开启移动噪声</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.noise" class="modifier-params">
|
||||||
|
<NumSlider label="强度" :min="0" :max="100" :step="1" v-model="sys.config.noiseAmt" />
|
||||||
|
<NumSlider label="波动范围" :min="0" :max="500" :step="1" v-model="sys.config.noiseRange" />
|
||||||
|
<NumSlider label="时间流速" :min="0" :max="10" :step="0.1" v-model="sys.config.noiseSpeed" />
|
||||||
|
<div class="modifier-hint">强度控制影响比例 · 波动范围控制最大偏移 · 时间流速控制变化速度</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.useGravity" />
|
||||||
|
<span class="switch-ui"></span><span>开启重力</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.useGravity" class="modifier-params gravity-params">
|
||||||
|
<div class="modifier-mode-row">
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.gravityMode === 'fixed' }" @click="sys.config.gravityMode = 'fixed'">固定</button>
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.gravityMode === 'random' }" @click="sys.config.gravityMode = 'random'">随机</button>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="sys.config.gravityMode === 'fixed'" label="重力强度" :min="-3000" :max="3000" :step="10" v-model="sys.config.gravityMin" />
|
||||||
|
<template v-else>
|
||||||
|
<NumSlider label="强度 min" :min="-3000" :max="3000" :step="10" v-model="sys.config.gravityMin" />
|
||||||
|
<NumSlider label="强度 max" :min="-3000" :max="3000" :step="10" v-model="sys.config.gravityMax" />
|
||||||
|
</template>
|
||||||
|
<NumSlider label="方向" :min="-360" :max="360" :step="1" v-model="sys.config.gravityDirection" />
|
||||||
|
<div class="gravity-curve-title"><span>生命周期内重力</span><span>纵轴:强度 0~1</span></div>
|
||||||
|
<CurveEditor v-model="sys.config.gravityCurve" :default-value="FLAT_CURVE" />
|
||||||
|
<div class="gravity-curve-axis"><span>0</span><span>生命周期 →</span><span>1</span></div>
|
||||||
|
<div class="modifier-hint">0° 向右 · -90° 向下 · 负强度会反转方向</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.wind" />
|
||||||
|
<span class="switch-ui"></span><span>开启风力</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.wind" class="modifier-params gravity-params">
|
||||||
|
<div class="modifier-mode-row">
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.windMode === 'fixed' }" @click="sys.config.windMode = 'fixed'">固定</button>
|
||||||
|
<button class="axis-mode" :class="{ on: sys.config.windMode === 'random' }" @click="sys.config.windMode = 'random'">随机</button>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="sys.config.windMode === 'fixed'" label="风力强度" :min="-3000" :max="3000" :step="10" v-model="sys.config.windMin" />
|
||||||
|
<template v-else>
|
||||||
|
<NumSlider label="强度 min" :min="-3000" :max="3000" :step="10" v-model="sys.config.windMin" />
|
||||||
|
<NumSlider label="强度 max" :min="-3000" :max="3000" :step="10" v-model="sys.config.windMax" />
|
||||||
|
</template>
|
||||||
|
<NumSlider label="方向" :min="-360" :max="360" :step="1" v-model="sys.config.windDirection" />
|
||||||
|
<div class="gravity-curve-title"><span>生命周期内风力</span><span>纵轴:强度 0~1</span></div>
|
||||||
|
<CurveEditor v-model="sys.config.windCurve" :default-value="FLAT_CURVE" />
|
||||||
|
<div class="gravity-curve-axis"><span>0</span><span>生命周期 →</span><span>1</span></div>
|
||||||
|
<div class="modifier-hint">0° 向右 · -90° 向下 · 负强度会反转方向</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.forceField" />
|
||||||
|
<span class="switch-ui"></span><span>开启力场</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.forceField" class="modifier-params">
|
||||||
|
<NumSlider label="中心 X" :min="-5000" :max="5000" :step="1" v-model="sys.config.forceCenterX" />
|
||||||
|
<NumSlider label="中心 Y" :min="-5000" :max="5000" :step="1" v-model="sys.config.forceCenterY" />
|
||||||
|
<NumSlider label="力场强度" :min="-300" :max="300" :step="1" v-model="sys.config.forceStrength" />
|
||||||
|
<NumSlider label="力场半径" :min="1" :max="2000" :step="1" v-model="sys.config.forceRadius" />
|
||||||
|
<label class="row"><span>衰减模式</span>
|
||||||
|
<select v-model="sys.config.forceFalloff" class="inp">
|
||||||
|
<option value="constant">恒定(范围内力度不变)</option>
|
||||||
|
<option value="linear">线性衰减(边缘为 0)</option>
|
||||||
|
<option value="inverse">距离反比(中心最强)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class="modifier-hint">正数吸向中心 · 负数向外排斥 · 可在画布内拖动力场圆</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.attraction" />
|
||||||
|
<span class="switch-ui"></span><span>开启吸附</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.attraction" class="modifier-params">
|
||||||
|
<NumSlider label="吸附点 X" :min="-5000" :max="5000" :step="1" v-model="sys.config.attractionCenterX" />
|
||||||
|
<NumSlider label="吸附点 Y" :min="-5000" :max="5000" :step="1" v-model="sys.config.attractionCenterY" />
|
||||||
|
<NumSlider label="吸附半径" :min="0" :max="2000" :step="1" v-model="sys.config.attractionRadius" />
|
||||||
|
<NumSlider label="吸附时长" :min="0.05" :max="30" :step="0.05" v-model="sys.config.attractionDuration" />
|
||||||
|
<NumSlider label="延迟时间" :min="0" :max="30" :step="0.05" v-model="sys.config.attractionDelay" />
|
||||||
|
<label class="row"><span>延迟方式</span>
|
||||||
|
<select v-model="sys.config.attractionDelayMode" class="inp">
|
||||||
|
<option value="global">统一时间</option>
|
||||||
|
<option value="particle">单粒子生命</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="row"><span>吸附方式</span>
|
||||||
|
<select v-model="sys.config.attractionPath" class="inp">
|
||||||
|
<option value="linear">直线</option>
|
||||||
|
<option value="curve">曲线</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<div class="modifier-hint">延迟后从当前位置开始吸附 · 到达红色范围即死亡 · 可在画布内拖动吸附圆</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.trail" />
|
||||||
|
<span class="switch-ui"></span><span>开启拖尾</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.trail" class="modifier-params trail-params">
|
||||||
|
<div class="trail-section-head">
|
||||||
|
<div class="trail-section-title">拖尾独立图片</div>
|
||||||
|
<button class="image-add" title="增加拖尾图片资源" @click="addTrailResource">+</button>
|
||||||
|
</div>
|
||||||
|
<ImageResourceCard
|
||||||
|
v-for="resource in sys.config.trailResources"
|
||||||
|
:key="resource.id"
|
||||||
|
:resource="resource"
|
||||||
|
:resource-count="sys.config.trailResources.length"
|
||||||
|
default-file="trail.png"
|
||||||
|
name-placeholder="trail"
|
||||||
|
:show-folder="false"
|
||||||
|
@pick="openTrailTexturePicker"
|
||||||
|
@reset="resetTrailTexture"
|
||||||
|
@remove="removeTrailResource"
|
||||||
|
@update-weight="setTrailResourceWeight"
|
||||||
|
@toggle-lock="toggleTrailResourceLock"
|
||||||
|
>
|
||||||
|
<div class="resource-divider"></div>
|
||||||
|
<NumSlider label="旋转" :min="-360" :max="360" :step="1" :model-value="resource.rotation" @update:model-value="onTrailResourceRotationChange(resource.id, $event)" />
|
||||||
|
<NumSlider label="拖尾骨骼数" :min="2" :max="16" :step="1" :model-value="resource.boneCount" @update:model-value="onTrailResourceBoneCountChange(resource.id, $event)" />
|
||||||
|
<NumSlider label="拖尾比例 (%)" :min="0" :max="100" :step="1" v-model="resource.trailRatio" />
|
||||||
|
<label class="row"><span>拖尾长度</span>
|
||||||
|
<select v-model="resource.lengthMode" class="inp">
|
||||||
|
<option value="fixed">固定</option>
|
||||||
|
<option value="random">随机范围</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<NumSlider v-if="resource.lengthMode === 'fixed'" label="长度比例 (%)" :min="0" :max="500" :step="1" v-model="resource.length" />
|
||||||
|
<template v-else>
|
||||||
|
<NumSlider label="长度最小 (%)" :min="0" :max="500" :step="1" v-model="resource.lengthMin" />
|
||||||
|
<NumSlider label="长度最大 (%)" :min="0" :max="500" :step="1" v-model="resource.lengthMax" />
|
||||||
|
</template>
|
||||||
|
<label class="row"><span>拖尾宽度</span>
|
||||||
|
<select v-model="resource.widthSizeMode" class="inp">
|
||||||
|
<option value="fixed">固定</option>
|
||||||
|
<option value="random">随机范围</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<NumSlider v-if="resource.widthSizeMode === 'fixed'" label="宽度比例 (%)" :min="0" :max="500" :step="1" v-model="resource.width" />
|
||||||
|
<template v-else>
|
||||||
|
<NumSlider label="宽度最小 (%)" :min="0" :max="500" :step="1" v-model="resource.widthMin" />
|
||||||
|
<NumSlider label="宽度最大 (%)" :min="0" :max="500" :step="1" v-model="resource.widthMax" />
|
||||||
|
</template>
|
||||||
|
<label class="row"><span>宽度缩放</span>
|
||||||
|
<select v-model="resource.widthMode" class="inp">
|
||||||
|
<option value="particle">跟随粒子</option>
|
||||||
|
<option value="fixed">固定</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="switch-row trail-sub-switch">
|
||||||
|
<input type="checkbox" v-model="resource.lifeLengthEnabled" />
|
||||||
|
<span class="switch-ui"></span><span>生命周期长度曲线</span>
|
||||||
|
</label>
|
||||||
|
<CurveEditor v-if="resource.lifeLengthEnabled" v-model="resource.lifeLengthCurve" :default-value="FLAT_CURVE" />
|
||||||
|
|
||||||
|
<label class="switch-row trail-sub-switch">
|
||||||
|
<input type="checkbox" v-model="resource.shapeEnabled" />
|
||||||
|
<span class="switch-ui"></span><span>形状曲线(首端 → 尾端宽度)</span>
|
||||||
|
</label>
|
||||||
|
<CurveEditor v-if="resource.shapeEnabled" v-model="resource.shapeCurve" :default-value="TRAIL_SHAPE_CURVE" />
|
||||||
|
|
||||||
|
<div class="trail-mode-head"><span>颜色模式</span>
|
||||||
|
<div class="alpha-modes">
|
||||||
|
<button class="axis-mode" :class="{ on: resource.colorMode === 'particle' }" @click="resource.colorMode = 'particle'">跟随粒子</button>
|
||||||
|
<button class="axis-mode" :class="{ on: resource.colorMode === 'fixed' }" @click="resource.colorMode = 'fixed'">固定</button>
|
||||||
|
<button class="axis-mode" :class="{ on: resource.colorMode === 'curve' }" @click="resource.colorMode = 'curve'">曲线</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label class="row"><span>混合模式</span>
|
||||||
|
<select v-model="resource.blend" class="inp">
|
||||||
|
<option value="normal">正常</option>
|
||||||
|
<option value="add">相加</option>
|
||||||
|
<option value="multiply">相乘</option>
|
||||||
|
<option value="screen">滤色</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label v-if="resource.colorMode === 'fixed'" class="row trail-fixed-color"><span>固定颜色</span>
|
||||||
|
<input class="colr" type="color" v-model="resource.color" />
|
||||||
|
<input class="hex-input" :value="resource.color" @change="setTrailColor(resource, $event)" @keydown.enter.prevent="commitTrailColor(resource, $event)" />
|
||||||
|
</label>
|
||||||
|
<ColorGradientEditor v-else-if="resource.colorMode === 'curve'" v-model="resource.colorGradient" />
|
||||||
|
|
||||||
|
<div class="trail-mode-head"><span>透明度模式</span>
|
||||||
|
<div class="alpha-modes">
|
||||||
|
<button class="axis-mode" :class="{ on: resource.alphaMode === 'particle' }" @click="resource.alphaMode = 'particle'">跟随粒子</button>
|
||||||
|
<button class="axis-mode" :class="{ on: resource.alphaMode === 'fixed' }" @click="resource.alphaMode = 'fixed'">固定</button>
|
||||||
|
<button class="axis-mode" :class="{ on: resource.alphaMode === 'curve' }" @click="resource.alphaMode = 'curve'">曲线</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="resource.alphaMode === 'fixed'" label="透明度" :min="0" :max="1" :step="0.01" v-model="resource.alpha" />
|
||||||
|
<CurveEditor v-if="resource.alphaMode === 'curve'" v-model="resource.alphaCurve" :default-value="TRAIL_ALPHA_CURVE" />
|
||||||
|
</ImageResourceCard>
|
||||||
|
<input ref="trailTextureFileInput" class="file-input" type="file" accept="image/png,image/jpeg,image/webp" @change="onTrailTextureFile" />
|
||||||
|
|
||||||
|
<div class="trail-grid-row">
|
||||||
|
<NumSlider label="网格行数" :min="2" :max="16" :step="1" :model-value="sys.config.trailGridRows" @update:model-value="onTrailRigChange('trailGridRows', $event)" />
|
||||||
|
<NumSlider label="网格列数" :min="2" :max="32" :step="1" :model-value="sys.config.trailGridCols" @update:model-value="onTrailRigChange('trailGridCols', $event)" />
|
||||||
|
</div>
|
||||||
|
<div class="modifier-hint">每个粒子按资源比例生成一组同级拖尾骨骼;随机长度与宽度在粒子出生时确定,相同种子可稳定复现</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.emitterFollow" :disabled="sys.config.streamBehavior === 'loop'" />
|
||||||
|
<span class="switch-ui"></span><span>开启发射器跟随</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.emitterFollow && sys.config.streamBehavior !== 'loop'" class="modifier-params emitter-follow-params">
|
||||||
|
<label class="field-block"><span>跟随模式</span><select :value="sys.config.emitterFollowMode" class="inp" @change="onEmitterFollowModeChange($event, sys.config)">
|
||||||
|
<option value="none">无</option>
|
||||||
|
<option value="path">跟随路径</option>
|
||||||
|
<option v-if="selectedSpineBone || sys.config.emitterFollowMode === 'bone'" value="bone">选中的骨骼</option>
|
||||||
|
</select></label>
|
||||||
|
<template v-if="sys.config.emitterFollowMode === 'path'">
|
||||||
|
<label class="field-block"><span>跟随路径</span><select v-model.number="sys.config.emitterFollowPathId" class="inp">
|
||||||
|
<option :value="0">无</option>
|
||||||
|
<option v-for="path in store.paths" :key="path.id" :value="path.id">{{ path.name }}</option>
|
||||||
|
</select></label>
|
||||||
|
<label class="field-block"><span>发射器角度模式</span><select v-model="sys.config.emitterAngleMode" class="inp">
|
||||||
|
<option value="fixed">固定角度</option>
|
||||||
|
<option value="direction">跟随移动方向</option>
|
||||||
|
</select></label>
|
||||||
|
<div class="gravity-curve-title"><span>移动进度曲线 (0→1)</span></div>
|
||||||
|
<CurveEditor v-model="sys.config.emitterFollowCurve" :default-value="LINEAR_CURVE" />
|
||||||
|
<div class="field-grid follow-grid">
|
||||||
|
<label class="compact-field"><span>移动时长 (s)</span><input v-model.number="sys.config.emitterFollowDuration" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>跟随空间</span><select v-model="sys.config.emitterFollowSpace" class="inp">
|
||||||
|
<option value="local">局部空间(GEN 移动)</option>
|
||||||
|
<option value="world">世界空间(粒子生成点移动)</option>
|
||||||
|
</select></label>
|
||||||
|
</div>
|
||||||
|
<label class="field-block"><span>移动方向</span><select v-model="sys.config.emitterFollowDirection" class="inp">
|
||||||
|
<option value="forward">正向(起点 → 终点)</option>
|
||||||
|
<option value="reverse">反向(终点 → 起点)</option>
|
||||||
|
</select></label>
|
||||||
|
<NumSlider label="偏移起始点 (0-1)" :min="0" :max="1" :step="0.01" v-model="sys.config.emitterFollowOffset" />
|
||||||
|
<div class="modifier-hint">局部空间会移动整个发射器;世界空间只移动新粒子的生成点,已出生粒子留在世界位置。</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="sys.config.emitterFollowMode === 'bone'">
|
||||||
|
<div v-if="boundSpineBone" class="bone-follow-target">跟随目标:<strong>{{ boundSpineBone.spine.name }} / {{ boundSpineBone.boneName }}</strong></div>
|
||||||
|
<div v-else class="bone-follow-missing">尚未绑定有效骨骼,请先在 Spine 层级树中选择骨骼。</div>
|
||||||
|
<button
|
||||||
|
v-if="selectedSpineBone && (!boundSpineBone || selectedSpineBone.spine.id !== boundSpineBone.spine.id || selectedSpineBone.boneName !== boundSpineBone.boneName)"
|
||||||
|
class="bone-follow-rebind"
|
||||||
|
@click="bindSelectedBone(sys.config)"
|
||||||
|
>{{ boundSpineBone ? '重新绑定' : '绑定' }}当前选中骨骼:{{ selectedSpineBone.spine.name }} / {{ selectedSpineBone.boneName }}</button>
|
||||||
|
<template v-if="boundSpineBone">
|
||||||
|
<label class="field-block"><span>跟随空间</span><select v-model="sys.config.emitterFollowSpace" class="inp">
|
||||||
|
<option value="local">本地空间(随骨骼移动)</option>
|
||||||
|
<option value="world">世界空间(粒子生成后保持位置)</option>
|
||||||
|
</select></label>
|
||||||
|
<div class="field-grid follow-grid">
|
||||||
|
<label class="compact-field"><span>位移偏移 X</span><input v-model.number="sys.config.emitterFollowBoneOffsetX" type="number" step="1" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>位移偏移 Y</span><input v-model.number="sys.config.emitterFollowBoneOffsetY" type="number" step="1" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<div class="bone-follow-transform-grid">
|
||||||
|
<label class="compact-field"><span>旋转偏移</span><input v-model.number="sys.config.emitterFollowBoneRotation" type="number" step="1" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>缩放偏移 X</span><input v-model.number="sys.config.emitterFollowBoneScaleX" type="number" step="0.05" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>缩放偏移 Y</span><input v-model.number="sys.config.emitterFollowBoneScaleY" type="number" step="0.05" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-hint">发射器只跟随所选 Spine 骨骼的位置,不继承 Spine 的旋转或缩放;粒子变换使用粒子系统自身参数和上方偏移。</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-item">
|
||||||
|
<label class="switch-row modifier-switch">
|
||||||
|
<input type="checkbox" v-model="sys.config.collision" />
|
||||||
|
<span class="switch-ui"></span><span>开启碰撞</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="sys.config.collision" class="modifier-params collision-filter-params">
|
||||||
|
<div class="collision-filter-title">参与的碰撞对象</div>
|
||||||
|
<div class="collision-filter-tags">
|
||||||
|
<span
|
||||||
|
v-for="collider in selectedCollisionBodies"
|
||||||
|
:key="collider.id"
|
||||||
|
class="collision-filter-tag"
|
||||||
|
:class="{ disabled: !collider.enabled }"
|
||||||
|
:title="collider.enabled ? collider.name : `${collider.name}(碰撞体已禁用)`"
|
||||||
|
>
|
||||||
|
<span>{{ collider.name }}</span>
|
||||||
|
<button type="button" :aria-label="`清除碰撞对象 ${collider.name}`" @click="removeCollisionBody(collider.id)">×</button>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="collision-filter-add"
|
||||||
|
:class="{ active: collisionPickerOpen }"
|
||||||
|
:disabled="availableCollisionBodies.length === 0"
|
||||||
|
:title="availableCollisionBodies.length ? '加入场景碰撞对象' : '没有可加入的碰撞对象'"
|
||||||
|
@click="collisionPickerOpen = !collisionPickerOpen"
|
||||||
|
>+</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedCollisionBodies.length === 0" class="collision-filter-empty">未选择碰撞对象,当前粒子不会发生碰撞</div>
|
||||||
|
<div v-if="collisionPickerOpen && availableCollisionBodies.length" class="collision-filter-picker">
|
||||||
|
<button
|
||||||
|
v-for="collider in availableCollisionBodies"
|
||||||
|
:key="collider.id"
|
||||||
|
type="button"
|
||||||
|
@click="addCollisionBody(collider.id)"
|
||||||
|
>
|
||||||
|
<span>{{ collider.name }}</span>
|
||||||
|
<small>{{ collider.enabled ? '可用' : '已禁用' }}</small>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modifier-hint">只有已加入且启用的场景碰撞对象会参与当前粒子系统的碰撞</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NumSlider label="阻尼" :min="0" :max="2" :step="0.05" v-model="sys.config.damping" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { store, sys, trailTextureFileInput, addTrailResource, openTrailTexturePicker, onTrailRigChange, onTrailResourceBoneCountChange, onTrailResourceRotationChange, onTrailTextureFile, resetTrailTexture, removeTrailResource, setTrailResourceWeight, toggleTrailResourceLock, setTrailColor, commitTrailColor, selectedSpineBone, boundSpineBone, collisionPickerOpen, selectedCollisionBodies, availableCollisionBodies, addCollisionBody, removeCollisionBody, collapsed, toggle, onEmitterFollowModeChange, bindSelectedBone, resetModule, select, LINEAR_CURVE, FLAT_CURVE, TRAIL_ALPHA_CURVE, TRAIL_SHAPE_CURVE } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.empty { color: #667; font-size: 12px; padding: 8px 0; }
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-reset {
|
||||||
|
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
|
||||||
|
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.divider { height: 1px; background: #2a2a3c; margin: 8px 0; }
|
||||||
|
.field-block { display: flex; flex-direction: column; gap: 5px; margin: 10px 0; color: #8f9ab3; font-size: 11px; }
|
||||||
|
.field-block > .inp { width: 100%; box-sizing: border-box; min-height: 30px; }
|
||||||
|
.field-block small { color: #5f6d86; line-height: 1.5; }
|
||||||
|
.field-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; align-items: end; }
|
||||||
|
.compact-field { display: flex; min-width: 0; flex-direction: column; gap: 5px; margin: 5px 0; color: #8f9ab3; font-size: 11px; }
|
||||||
|
.compact-field .inp { width: 100%; min-height: 30px; box-sizing: border-box; }
|
||||||
|
.tag { font-size: 10px; color: #667; background: #1c1c2a; border-radius: 4px; padding: 1px 5px; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.inp { flex: 1; min-width: 0; padding: 3px 5px; background: #1a1a28; border: 1px solid #2e2e44; border-radius: 5px; color: #dde; font-size: 12px; }
|
||||||
|
.colr { width: 28px; height: 22px; border: none; background: none; cursor: pointer; }
|
||||||
|
.placeholder { color: #667; font-size: 11px; padding: 6px 0; }
|
||||||
|
.image-add { width: 26px; height: 24px; border: 1px solid #4a4d9a; border-radius: 5px; background: #252850; color: #8f8cff; font-size: 18px; line-height: 1; cursor: pointer; }
|
||||||
|
.image-add:hover { background: #4542a8; color: #fff; }
|
||||||
|
.resource-divider { height: 1px; margin: 9px 0 7px; background: #2a3549; }
|
||||||
|
.hex-input { width: 72px; padding: 4px 6px; border: 1px solid #344158; border-radius: 5px; outline: none; background: #101725; color: #aeb8cb; font-size: 11px; font-family: monospace; }
|
||||||
|
.hex-input:focus { border-color: #7774ff; color: #fff; }
|
||||||
|
.alpha-modes { display: flex; gap: 3px; }
|
||||||
|
.alpha-modes button { padding: 4px 8px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: #8d98ae; font-size: 11px; cursor: pointer; }
|
||||||
|
.alpha-modes button:hover { background: #273246; }
|
||||||
|
.alpha-modes button.on { border-color: #7774ff; background: #605df0; color: #fff; }
|
||||||
|
.file-input { display: none; }
|
||||||
|
.hex { font-size: 11px; color: #889; margin-left: auto; font-variant-numeric: tabular-nums; }
|
||||||
|
.inp.tiny { width: 46px; flex: 0 0 46px; text-align: center; }
|
||||||
|
.axis-mode { padding: 4px 8px; border: 1px solid #344158; border-radius: 5px; background: #273246; color: #aeb8cf; font-size: 11px; cursor: pointer; }
|
||||||
|
.axis-mode.on { border-color: #7774ff; background: #605df0; color: #fff; }
|
||||||
|
.switch-row { display: flex; align-items: center; gap: 9px; color: #b8c1d5; font-size: 12px; cursor: pointer; }
|
||||||
|
.switch-row input { position: absolute; opacity: 0; pointer-events: none; }
|
||||||
|
.switch-ui { position: relative; width: 32px; height: 18px; border-radius: 10px; background: #354258; transition: background .16s ease; }
|
||||||
|
.switch-ui::after { content: ''; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: transform .16s ease; }
|
||||||
|
.switch-row input:checked + .switch-ui { background: #625ff1; }
|
||||||
|
.switch-row input:checked + .switch-ui::after { transform: translateX(14px); }
|
||||||
|
.modifier-item { padding: 8px 0 10px; border-bottom: 1px solid #2a3549; }
|
||||||
|
.modifier-switch { font-weight: 600; }
|
||||||
|
.modifier-params { margin: 8px 0 0 10px; padding: 2px 0 2px 10px; border-left: 2px solid #344158; }
|
||||||
|
.modifier-hint { margin: 7px 0 0 2px; color: #647086; font-size: 9px; line-height: 1.5; }
|
||||||
|
.collision-filter-params { position: relative; padding-top: 5px; padding-bottom: 5px; }
|
||||||
|
.collision-filter-title { margin-bottom: 8px; color: #9ba7bd; font-size: 11px; font-weight: 600; }
|
||||||
|
.collision-filter-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
||||||
|
.collision-filter-tag { display: inline-flex; min-width: 0; align-items: center; gap: 6px; padding: 5px 5px 5px 8px; border: 1px solid #625ff1; border-radius: 6px; background: #514dcf; color: #fff; font-size: 11px; }
|
||||||
|
.collision-filter-tag > span { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.collision-filter-tag.disabled { border-color: #4b5364; background: #343b4b; color: #929bad; }
|
||||||
|
.collision-filter-tag button { width: 18px; height: 18px; padding: 0; border: 0; border-radius: 4px; background: rgba(11, 15, 26, 0.22); color: inherit; line-height: 16px; cursor: pointer; }
|
||||||
|
.collision-filter-tag button:hover { background: rgba(11, 15, 26, 0.5); color: #fff; }
|
||||||
|
.collision-filter-add { width: 30px; height: 30px; padding: 0; border: 1px dashed #536079; border-radius: 6px; background: #182131; color: #98a5bd; font-size: 18px; line-height: 28px; cursor: pointer; }
|
||||||
|
.collision-filter-add:hover, .collision-filter-add.active { border-color: #7774ff; background: #262c52; color: #fff; }
|
||||||
|
.collision-filter-add:disabled { opacity: 0.42; cursor: not-allowed; }
|
||||||
|
.collision-filter-empty { margin-top: 7px; color: #6f7b91; font-size: 10px; }
|
||||||
|
.collision-filter-picker { display: grid; gap: 4px; margin-top: 7px; padding: 5px; border: 1px solid #344158; border-radius: 6px; background: #101725; }
|
||||||
|
.collision-filter-picker button { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 8px; border: 0; border-radius: 4px; background: transparent; color: #c7d0e3; font-size: 11px; text-align: left; cursor: pointer; }
|
||||||
|
.collision-filter-picker button:hover { background: #29344a; color: #fff; }
|
||||||
|
.collision-filter-picker button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.collision-filter-picker button small { flex: none; color: #68758d; font-size: 9px; }
|
||||||
|
.switch-row input:disabled + .switch-ui { opacity: 0.45; cursor: not-allowed; }
|
||||||
|
.bone-follow-target { margin: 7px 0 9px; padding: 7px 8px; overflow: hidden; color: #7f8ba3; background: #121a29; border: 1px solid #2f3c53; border-radius: 5px; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.bone-follow-target strong { color: #c8d0e4; font-weight: 600; }
|
||||||
|
.bone-follow-missing { margin: 7px 0 9px; padding: 7px 8px; color: #d49aa3; background: #2a1e26; border: 1px solid #513540; border-radius: 5px; font-size: 10px; line-height: 1.5; }
|
||||||
|
.bone-follow-rebind { width: 100%; min-height: 28px; margin: 0 0 8px; padding: 4px 8px; overflow: hidden; border: 1px solid #4a436e; border-radius: 5px; background: #2d2947; color: #c8c2ee; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
|
||||||
|
.bone-follow-rebind:hover { border-color: #7165bd; background: #3a335d; color: #fff; }
|
||||||
|
.bone-follow-transform-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
|
||||||
|
.modifier-mode-row { display: flex; justify-content: flex-end; gap: 4px; margin-bottom: 6px; }
|
||||||
|
.gravity-curve-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 10px 0 6px; color: #9ba7bd; font-size: 11px; }
|
||||||
|
.gravity-curve-title span:last-child { color: #657188; font-size: 9px; }
|
||||||
|
.gravity-curve-axis { display: flex; justify-content: space-between; margin: 3px 34px 0 1px; color: #5f6b82; font-size: 9px; }
|
||||||
|
.gravity-params :deep(.ce-svg) { height: 96px; background: #0f1625; border-color: #30405a; }
|
||||||
|
.trail-params { padding-right: 2px; }
|
||||||
|
.trail-section-title { margin: 3px 0 8px; color: #aeb8cf; font-size: 12px; font-weight: 700; }
|
||||||
|
.trail-fixed-color .hex-input { min-width: 0; flex: 1; }
|
||||||
|
.trail-mode-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 10px 0 6px; color: #9ba7bd; font-size: 11px; }
|
||||||
|
.trail-sub-switch { margin: 11px 0 7px; padding-top: 9px; border-top: 1px solid #2a3549; }
|
||||||
|
.trail-grid-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 8px; }
|
||||||
|
.trail-grid-row :deep(.ns) { min-width: 0; flex-wrap: wrap; align-items: center; }
|
||||||
|
.trail-grid-row :deep(.ns-label) { width: 100%; }
|
||||||
|
.trail-grid-row :deep(.ns-range) { min-width: 0; }
|
||||||
|
.trail-grid-row :deep(.ns-num) { width: 42px; }
|
||||||
|
.trail-params :deep(.ce-svg) { height: 92px; background: #0f1625; border-color: #30405a; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<div class="group config-management">
|
||||||
|
<div class="group-head" @click="toggle('config')">
|
||||||
|
<span class="caret">{{ collapsed.config ? '▸' : '▾' }}</span><span>配置管理</span>
|
||||||
|
</div>
|
||||||
|
<div class="group-body config-management-body" v-show="!collapsed.config">
|
||||||
|
<div class="config-toolbar">
|
||||||
|
<button title="保存全部编辑器配置 (Ctrl+S)" @click="saveConfig">保存配置</button>
|
||||||
|
<button title="加载本地编辑器配置" @click="configFileInput?.click()">加载配置</button>
|
||||||
|
<select v-model="selectedPreset" aria-label="预设列表" :disabled="!presetOptions.length || presetLoading" @pointerdown="refreshPresets" @focus="refreshPresets" @change="loadSelectedPreset">
|
||||||
|
<option value="">{{ presetOptions.length ? '选择预设' : '无预设文件' }}</option>
|
||||||
|
<option v-for="preset in presetOptions" :key="preset.file" :value="preset.file">{{ preset.name }}</option>
|
||||||
|
</select>
|
||||||
|
<input ref="configFileInput" hidden type="file" accept=".json,application/json" @change="onConfigFileChange" />
|
||||||
|
</div>
|
||||||
|
<div v-if="configMessage" class="config-message" :class="{ error: configError }">{{ configMessage }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { configFileInput, selectedPreset, presetOptions, presetLoading, configMessage, configError, saveConfig, onConfigFileChange, refreshPresets, loadSelectedPreset, collapsed, toggle, select } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.config-management-body { padding-bottom: 2px; }
|
||||||
|
.config-toolbar { display: grid; grid-template-columns: auto auto minmax(0,1fr); gap: 6px; margin-bottom: 10px; }
|
||||||
|
.config-toolbar button,.config-toolbar select {
|
||||||
|
min-width: 0; height: 30px; box-sizing: border-box; border: 1px solid #3b4864; border-radius: 6px;
|
||||||
|
background: #263149; color: #d6def0; font-size: 11px;
|
||||||
|
}
|
||||||
|
.config-toolbar button { padding: 0 9px; cursor: pointer; }
|
||||||
|
.config-toolbar button:hover { border-color: #7169dc; background: #343d68; color: #fff; }
|
||||||
|
.config-toolbar select { padding: 0 7px; overflow: hidden; text-overflow: ellipsis; }
|
||||||
|
.config-toolbar select:disabled { color: #68738a; background: #1b2130; }
|
||||||
|
.config-message { margin: -4px 0 9px; color: #83b9a2; font-size: 10px; }
|
||||||
|
.config-message.error { color: #e8959e; }
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 1. 场景对象:创建入口始终可用,即使当前没有粒子系统。 -->
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-head scene-head" @click="toggle('scene')">
|
||||||
|
<span class="caret">{{ collapsed.scene ? '▸' : '▾' }}</span><span>场景对象</span>
|
||||||
|
<div class="scene-actions">
|
||||||
|
<button class="scene-add" title="添加粒子系统" @click.stop="addSys">+粒子</button>
|
||||||
|
<button class="scene-add" title="添加碰撞体" @click.stop="addCollider">+碰撞</button>
|
||||||
|
<button class="scene-add" title="添加路径" @click.stop="addPath">+路径</button>
|
||||||
|
<button class="scene-add" title="添加 Spine" @click.stop="addSpine">+Spine</button>
|
||||||
|
<button class="scene-add" :disabled="!canCopyObject" title="复制当前选中的场景对象" @click.stop="copySelectedObject">+Copy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="group-body" v-show="!collapsed.scene">
|
||||||
|
<div class="tree scene-tree">
|
||||||
|
<div
|
||||||
|
v-for="(system, i) in store.systems"
|
||||||
|
:key="`particle-${system.id}`"
|
||||||
|
class="tree-item scene-object-item"
|
||||||
|
:class="{ on: activeObjectType === 'particle' && system.id === store.activeObjectId }"
|
||||||
|
@click="select(i)"
|
||||||
|
>
|
||||||
|
<span class="tdot">●</span>
|
||||||
|
<span class="tlabel">{{ system.config.name || 'ParticleSystem' + (i + 1) }}</span>
|
||||||
|
<button class="scene-object-eye" :class="{ off: system.visible === false }" :title="system.visible === false ? '显示粒子系统' : '隐藏粒子系统'" :aria-label="system.visible === false ? '显示粒子系统' : '隐藏粒子系统'" @click.stop="system.visible = system.visible === false">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="system.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
|
||||||
|
</button>
|
||||||
|
<span class="scene-object-kind">粒子</span>
|
||||||
|
<button class="scene-object-delete" title="删除粒子系统" @click.stop="store.removeSystem(system.id)">✕</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="collider in store.colliders"
|
||||||
|
:key="`collision-${collider.id}`"
|
||||||
|
class="tree-item scene-object-item"
|
||||||
|
:class="{ on: activeObjectType === 'collision' && collider.id === store.activeObjectId }"
|
||||||
|
@click="store.selectSceneObject('collision', collider.id)"
|
||||||
|
>
|
||||||
|
<span class="tdot collision-dot">■</span>
|
||||||
|
<span class="tlabel">{{ collider.name }}</span>
|
||||||
|
<button class="scene-object-eye" :class="{ off: collider.visible === false }" :title="collider.visible === false ? '显示碰撞体' : '隐藏碰撞体'" :aria-label="collider.visible === false ? '显示碰撞体' : '隐藏碰撞体'" @click.stop="collider.visible = collider.visible === false">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="collider.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
|
||||||
|
</button>
|
||||||
|
<span class="scene-object-kind">碰撞</span>
|
||||||
|
<button class="scene-object-delete" title="删除碰撞体" @click.stop="store.removeCollider(collider.id)">✕</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="path in store.paths"
|
||||||
|
:key="`path-${path.id}`"
|
||||||
|
class="tree-item scene-object-item"
|
||||||
|
:class="{ on: activeObjectType === 'path' && path.id === store.activeObjectId }"
|
||||||
|
@click="store.selectSceneObject('path', path.id)"
|
||||||
|
>
|
||||||
|
<span class="tdot path-dot">◇</span>
|
||||||
|
<span class="tlabel">{{ path.name }}</span>
|
||||||
|
<button class="scene-object-eye" :class="{ off: path.visible === false }" :title="path.visible === false ? '显示路径' : '隐藏路径'" :aria-label="path.visible === false ? '显示路径' : '隐藏路径'" @click.stop="path.visible = path.visible === false">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="path.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
|
||||||
|
</button>
|
||||||
|
<span class="scene-object-kind">路径</span>
|
||||||
|
<button class="scene-object-delete" title="删除路径" @click.stop="store.removePath(path.id)">✕</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="spine in store.spines"
|
||||||
|
:key="`spine-${spine.id}`"
|
||||||
|
class="tree-item scene-object-item"
|
||||||
|
:class="{ on: activeObjectType === 'spine' && spine.id === store.activeObjectId }"
|
||||||
|
@click="store.selectSceneObject('spine', spine.id)"
|
||||||
|
>
|
||||||
|
<span class="tdot spine-dot">◆</span>
|
||||||
|
<span class="tlabel">{{ spine.name }}</span>
|
||||||
|
<button class="scene-object-eye" :class="{ off: spine.visible === false }" :title="spine.visible === false ? '显示 Spine' : '隐藏 Spine'" :aria-label="spine.visible === false ? '显示 Spine' : '隐藏 Spine'" @click.stop="spine.visible = spine.visible === false">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"/><circle cx="12" cy="12" r="2.8"/><path v-if="spine.visible === false" class="eye-slash" d="M4 4l16 16"/></svg>
|
||||||
|
</button>
|
||||||
|
<span class="scene-object-kind">Spine</span>
|
||||||
|
<button class="scene-object-delete" title="删除 Spine" @click.stop="store.removeSpine(spine.id)">✕</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="!store.systems.length && !store.colliders.length && !store.paths.length && !store.spines.length" class="empty">场景中暂无对象</div>
|
||||||
|
</div>
|
||||||
|
<template v-if="activeObjectType === 'particle' && sys">
|
||||||
|
<label class="row"><span>名称</span><input v-model="sys.config.name" class="inp" /></label>
|
||||||
|
<NumSlider label="位置X" :min="-2000" :max="2000" :step="1" v-model="sys.config.centerX" />
|
||||||
|
<NumSlider label="位置Y" :min="-2000" :max="2000" :step="1" v-model="sys.config.centerY" />
|
||||||
|
<NumSlider label="偏移X" :min="-2000" :max="2000" :step="1" v-model="sys.config.offsetX" />
|
||||||
|
<NumSlider label="偏移Y" :min="-2000" :max="2000" :step="1" v-model="sys.config.offsetY" />
|
||||||
|
<NumSlider label="旋转" :min="-360" :max="360" :step="1" v-model="sys.config.rootRotation" />
|
||||||
|
<NumSlider label="缩放X" :min="0.05" :max="10" :step="0.05" v-model="sys.config.rootScaleX" />
|
||||||
|
<NumSlider label="缩放Y" :min="0.05" :max="10" :step="0.05" v-model="sys.config.rootScaleY" />
|
||||||
|
<div class="seed-row scene-seed">
|
||||||
|
<label for="system-seed">随机种子</label>
|
||||||
|
<input id="system-seed" class="seed-input" type="number" min="0" step="1" v-model.number="sys.config.seed" title="控制粒子系统内全部随机属性、资源选择与修改器随机采样" />
|
||||||
|
<button class="seed-random" title="生成新的全局随机种子" @click="randomizeSeed">⚄</button>
|
||||||
|
</div>
|
||||||
|
<div class="seed-hint">控制初始随机参数、资源选择和修改器随机采样;相同种子可复现相同效果</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="activeCollider">
|
||||||
|
<div class="object-editor-title">碰撞体属性</div>
|
||||||
|
<label class="field-block"><span>碰撞体名称</span><input v-model="activeCollider.name" class="inp" /></label>
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="field-block"><span>类型</span><select v-model="activeCollider.shape" class="inp">
|
||||||
|
<option value="circle">圆形</option><option value="ellipse">椭圆</option><option value="rect">矩形</option><option value="polygon">多边形</option><option value="custom">自定义</option>
|
||||||
|
</select></label>
|
||||||
|
<label class="field-block check-field"><span>启用</span><input v-model="activeCollider.enabled" type="checkbox" /></label>
|
||||||
|
</div>
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="compact-field"><span>X 位置</span><input v-model.number="activeCollider.x" type="number" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>Y 位置</span><input v-model.number="activeCollider.y" type="number" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<NumSlider label="旋转" :min="-360" :max="360" :step="1" v-model="activeCollider.rotation" />
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="compact-field"><span>缩放 X</span><input v-model.number="activeCollider.scaleX" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>缩放 Y</span><input v-model.number="activeCollider.scaleY" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="activeCollider.shape === 'circle'" label="半径" :min="1" :max="1000" :step="1" v-model="activeCollider.radius" />
|
||||||
|
<div v-else-if="activeCollider.shape === 'ellipse'" class="field-grid">
|
||||||
|
<label class="compact-field"><span>X 半径</span><input v-model.number="activeCollider.radiusX" type="number" min="1" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>Y 半径</span><input v-model.number="activeCollider.radiusY" type="number" min="1" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<div v-else class="field-grid">
|
||||||
|
<label class="compact-field"><span>宽度</span><input v-model.number="activeCollider.width" type="number" min="1" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>高度</span><input v-model.number="activeCollider.height" type="number" min="1" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<NumSlider v-if="activeCollider.shape === 'polygon' || activeCollider.shape === 'custom'" label="边数" :min="3" :max="16" :step="1" v-model="activeCollider.sides" />
|
||||||
|
<label class="field-block"><span>碰撞响应</span><select v-model="activeCollider.response" class="inp">
|
||||||
|
<option value="physics">物理(弹跳/摩擦)</option><option value="pause">暂停(停止不动)</option><option value="destroy">消失(立即销毁)</option>
|
||||||
|
</select></label>
|
||||||
|
<div v-if="activeCollider.response === 'physics'" class="field-grid">
|
||||||
|
<label class="compact-field"><span>弹性 (0-1)</span><input v-model.number="activeCollider.elasticity" type="number" min="0" max="1" step="0.01" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>摩擦 (0-1)</span><input v-model.number="activeCollider.friction" type="number" min="0" max="1" step="0.01" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<div class="collider-follow-section">
|
||||||
|
<label class="follow-path"><input v-model="activeCollider.followPath" type="checkbox" />跟随路径移动</label>
|
||||||
|
<template v-if="activeCollider.followPath">
|
||||||
|
<label class="field-block"><span>目标路径</span><select v-model.number="activeCollider.followPathId" class="inp">
|
||||||
|
<option :value="0">(未选择)</option>
|
||||||
|
<option v-for="path in store.paths" :key="path.id" :value="path.id">{{ path.name }}</option>
|
||||||
|
</select></label>
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="compact-field"><span>移动时长(秒)</span><input v-model.number="activeCollider.followDuration" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>曲线模式</span><select v-model="activeCollider.followCurveMode" class="inp">
|
||||||
|
<option>跟随导出</option><option>线性</option><option>贝塞尔</option>
|
||||||
|
</select></label>
|
||||||
|
</div>
|
||||||
|
<label class="follow-path"><input v-model="activeCollider.followPathRotation" type="checkbox" />方向沿路径</label>
|
||||||
|
<div class="collider-curve-title">移动进度曲线 <small>横轴:实时/移动时长 纵轴:路径进度(0=起点 → 1=终点)</small></div>
|
||||||
|
<CurveEditor
|
||||||
|
:model-value="activeCollider.followCurve || LINEAR_CURVE"
|
||||||
|
:default-value="LINEAR_CURVE"
|
||||||
|
:mode="resolvedCurveEditorMode(activeCollider.followCurveMode)"
|
||||||
|
@update:model-value="activeCollider.followCurve = $event"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="activePath">
|
||||||
|
<div class="object-editor-title">路径属性</div>
|
||||||
|
<label class="field-block"><span>路径名称</span><input v-model="activePath.name" class="inp" /></label>
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="compact-field"><span>X 位置</span><input v-model.number="activePath.x" type="number" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>Y 位置</span><input v-model.number="activePath.y" type="number" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<NumSlider label="旋转" :min="-360" :max="360" :step="1" v-model="activePath.rotation" />
|
||||||
|
<div class="field-grid">
|
||||||
|
<label class="compact-field"><span>缩放 X</span><input v-model.number="activePath.scaleX" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
<label class="compact-field"><span>缩放 Y</span><input v-model.number="activePath.scaleY" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||||
|
</div>
|
||||||
|
<label class="setting-line"><span>闭合路径</span><input v-model="activePath.closed" type="checkbox" /></label>
|
||||||
|
<label class="setting-line"><span>路径颜色</span><input v-model="activePath.color" type="color" class="path-color" /></label>
|
||||||
|
<div class="path-help"><span>操作说明:</span><ul>
|
||||||
|
<li>双击空白处/曲线:新建锚点</li><li>双击锚点:删除节点</li><li>双击第二下按住拖动:新建并拉出手柄</li>
|
||||||
|
<li>拖动锚点移动位置,拖动手柄调整曲线</li><li>Shift+拖动手柄:两侧完全对称</li><li>Alt+拖动手柄:独立控制单侧</li>
|
||||||
|
<li>Ctrl+点击/框选:加选节点</li><li>Alt+点击/框选:减选节点</li><li>Alt+单击空白处:清除全部选中</li><li>选中多个节点后可整体拖动</li>
|
||||||
|
</ul></div>
|
||||||
|
<label class="field-block"><span>应用预设形状</span><select v-model="activePath.preset" class="inp">
|
||||||
|
<option value="">选择预设...</option><option value="circle">圆形</option><option value="square">方形</option><option value="polygon">多边形</option><option value="star">星形</option>
|
||||||
|
</select></label>
|
||||||
|
<button class="path-action primary" @click="applyPathPreset">应用预设</button>
|
||||||
|
<button class="path-action" @click="clearPathAnchors">清空锚点</button>
|
||||||
|
</template>
|
||||||
|
<SpineObjectPanel v-else-if="activeSpine" :spine-object="activeSpine" />
|
||||||
|
<div v-else class="panel-hint">使用右上角按钮创建场景对象</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { useParticlePanelContext } from './particlePanelContext'
|
||||||
|
const { store, resolvedCurveEditorMode, sys, activeObjectType, activeCollider, activePath, activeSpine, canCopyObject, collapsed, toggle, select, addSys, addCollider, addPath, addSpine, copySelectedObject, applyPathPreset, clearPathAnchors, randomizeSeed, LINEAR_CURVE } = useParticlePanelContext()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.panel { display: flex; width: 100%; height: 100%; flex-direction: column; overflow: hidden; background: #14141f; border-left: 1px solid #2a2a3c; padding: 12px; box-sizing: border-box; }
|
||||||
|
.empty, .panel-hint { color: #667; font-size: 12px; padding: 8px 0; }
|
||||||
|
.group { border: 1px solid #2a2a3c; border-radius: 8px; margin-bottom: 10px; overflow: hidden; }
|
||||||
|
.group-head {
|
||||||
|
display: flex; align-items: center; gap: 6px; padding: 7px 10px; cursor: pointer;
|
||||||
|
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
|
||||||
|
}
|
||||||
|
.group-head:hover { background: #20202e; }
|
||||||
|
.scene-head { padding-right: 6px; }
|
||||||
|
.scene-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
|
||||||
|
.scene-add {
|
||||||
|
height: 24px; padding: 0 6px; border: 1px solid #3a4a6a; border-radius: 5px;
|
||||||
|
background: #2e3a55; color: #cdf; font-size: 10px; white-space: nowrap; cursor: pointer;
|
||||||
|
}
|
||||||
|
.scene-add:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||||
|
.scene-add:disabled { opacity: 0.42; cursor: not-allowed; border-color: #343a4b; background: #252a38; color: #778095; }
|
||||||
|
.group-body { padding: 8px 10px 12px; }
|
||||||
|
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||||
|
.tree { margin-bottom: 8px; }
|
||||||
|
.tree-item { display: flex; align-items: center; gap: 6px; padding: 5px 6px; font-size: 12px; color: #bcd; border-radius: 6px; margin-bottom: 2px; }
|
||||||
|
.tree-item.on { background: #26324a; color: #ffe; }
|
||||||
|
.tree-item .tdot { color: #ffcc33; font-size: 10px; }
|
||||||
|
.tree-item .tdot.dim { color: #556; }
|
||||||
|
.tree-item .tdot.collision-dot { color: #ff7070; }
|
||||||
|
.tree-item .tdot.path-dot { color: #ff933e; font-size: 14px; }
|
||||||
|
.tree-item .tdot.spine-dot { color: #c08cff; font-size: 11px; }
|
||||||
|
.tree-item .tlabel { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.scene-tree { padding-bottom: 8px; border-bottom: 1px solid #2a2a3c; }
|
||||||
|
.scene-object-item { min-height: 28px; cursor: pointer; }
|
||||||
|
.scene-object-item:hover { background: #202b40; }
|
||||||
|
.scene-object-kind { color: #75829c; font-size: 10px; }
|
||||||
|
.scene-object-eye {
|
||||||
|
width: 24px; height: 22px; padding: 3px; flex: 0 0 24px; border: 1px solid transparent; border-radius: 5px;
|
||||||
|
background: transparent; color: #9aa6bf; cursor: pointer;
|
||||||
|
}
|
||||||
|
.scene-object-eye:hover { border-color: #4b5972; background: #29354a; color: #e1e7f4; }
|
||||||
|
.scene-object-eye.off { color: #59647a; }
|
||||||
|
.scene-object-eye svg { display: block; width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
||||||
|
.scene-object-eye .eye-slash { stroke: #d47b88; stroke-width: 2; }
|
||||||
|
.scene-object-delete {
|
||||||
|
width: 24px; height: 22px; padding: 0; border: 1px solid #59414a; border-radius: 5px;
|
||||||
|
background: #3a272f; color: #efb4bd; font-size: 11px; cursor: pointer;
|
||||||
|
}
|
||||||
|
.scene-object-delete:hover { border-color: #a45c69; background: #57303a; color: #fff; }
|
||||||
|
.object-editor-title { margin: 10px 0 8px; padding-top: 8px; border-top: 1px solid #2a2a3c; color: #cbd4e8; font-weight: 700; }
|
||||||
|
.field-block { display: flex; flex-direction: column; gap: 5px; margin: 10px 0; color: #8f9ab3; font-size: 11px; }
|
||||||
|
.field-block > .inp { width: 100%; box-sizing: border-box; min-height: 30px; }
|
||||||
|
.field-block small { color: #5f6d86; line-height: 1.5; }
|
||||||
|
.field-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; align-items: end; }
|
||||||
|
.compact-field { display: flex; min-width: 0; flex-direction: column; gap: 5px; margin: 5px 0; color: #8f9ab3; font-size: 11px; }
|
||||||
|
.compact-field .inp { width: 100%; min-height: 30px; box-sizing: border-box; }
|
||||||
|
.check-field { align-items: flex-start; }
|
||||||
|
.check-field input { width: 18px; height: 18px; margin: 4px 0; accent-color: #4f7ee8; }
|
||||||
|
.follow-path, .setting-line { display: flex; align-items: center; gap: 8px; margin: 12px 0 4px; color: #aeb8cf; font-size: 12px; }
|
||||||
|
.setting-line { justify-content: space-between; }
|
||||||
|
.follow-path input, .setting-line input[type='checkbox'] { width: 17px; height: 17px; accent-color: #4f7ee8; }
|
||||||
|
.collider-follow-section { margin-top: 12px; padding-top: 4px; border-top: 1px solid #30394d; }
|
||||||
|
.collider-curve-title { display: flex; flex-direction: column; gap: 3px; margin: 12px 0 6px; color: #aeb8cf; font-size: 12px; }
|
||||||
|
.collider-curve-title small { color: #67738c; font-size: 10px; line-height: 1.45; }
|
||||||
|
.path-color { width: 48px; height: 28px; padding: 0; border: 1px solid #3a4963; background: transparent; cursor: pointer; }
|
||||||
|
.path-help { margin: 12px 0; color: #7d89a2; font-size: 11px; line-height: 1.55; }
|
||||||
|
.path-help > span { color: #aeb8cf; }
|
||||||
|
.path-help ul { margin: 6px 0 0; padding-left: 20px; }
|
||||||
|
.path-help li { margin: 3px 0; }
|
||||||
|
.path-action { width: 100%; height: 31px; margin-top: 7px; border: 0; border-radius: 5px; background: #33425b; color: #c9d1e1; cursor: pointer; }
|
||||||
|
.path-action:hover { filter: brightness(1.12); }
|
||||||
|
.path-action.primary { background: #f05408; color: #fff; }
|
||||||
|
.row { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 12px; color: #aab; }
|
||||||
|
.row > span:first-child { width: 60px; flex-shrink: 0; color: #8a93bb; }
|
||||||
|
.inp { flex: 1; min-width: 0; padding: 3px 5px; background: #1a1a28; border: 1px solid #2e2e44; border-radius: 5px; color: #dde; font-size: 12px; }
|
||||||
|
.inp.tiny { width: 46px; flex: 0 0 46px; text-align: center; }
|
||||||
|
.seed-row { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: 1px solid #2d3950; }
|
||||||
|
.seed-row label { width: 64px; color: #8f9bb5; font-size: 12px; }
|
||||||
|
.seed-input { min-width: 0; flex: 1; padding: 6px 8px; border: 1px solid #344158; border-radius: 6px; outline: none; background: #0f1625; color: #e2e7f2; font-size: 12px; }
|
||||||
|
.seed-input:focus { border-color: #625ff1; }
|
||||||
|
.seed-random { width: 34px; height: 30px; border: 1px solid #3a4860; border-radius: 6px; background: #2c3950; color: #d7def0; cursor: pointer; }
|
||||||
|
.seed-random:hover { background: #394964; }
|
||||||
|
.scene-seed { margin-top: 10px; }
|
||||||
|
.seed-hint { margin: 5px 0 1px 72px; color: #657188; font-size: 9px; line-height: 1.45; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { inject, provide, type InjectionKey } from 'vue'
|
||||||
|
import { useParticlePanel } from './useParticlePanel'
|
||||||
|
|
||||||
|
type ParticlePanelContext = ReturnType<typeof useParticlePanel>
|
||||||
|
const particlePanelKey: InjectionKey<ParticlePanelContext> = Symbol('particle-panel')
|
||||||
|
|
||||||
|
export function provideParticlePanel() {
|
||||||
|
const context = useParticlePanel()
|
||||||
|
provide(particlePanelKey, context)
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useParticlePanelContext() {
|
||||||
|
const context = inject(particlePanelKey)
|
||||||
|
if (!context) throw new Error('Particle panel sections require a ParticlePanel parent')
|
||||||
|
return context
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const LINEAR_CURVE = [{ x: 0, y: 0 }, { x: 1, y: 1 }]
|
||||||
|
export const FLAT_CURVE = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
export const ALPHA_CURVE = [{ x: 0, y: 0 }, { x: 0.2, y: 1 }, { x: 0.8, y: 1 }, { x: 1, y: 0 }]
|
||||||
|
export const RESOURCE_ALPHA_CURVE = [{ x: 0, y: 0 }, { x: 0.1, y: 1 }, { x: 0.9, y: 1 }, { x: 1, y: 0 }]
|
||||||
|
export const TRAIL_ALPHA_CURVE = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
export const TRAIL_SHAPE_CURVE = [{ x: 0, y: 1 }, { x: 1, y: 1 }]
|
||||||
|
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
|
||||||
|
import { computed, markRaw, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue'
|
||||||
|
import { Texture } from 'pixi.js'
|
||||||
|
import { useParticleStore } from '../../store/particleStore'
|
||||||
|
import type { ParticleSystem } from '../../store/particleStore'
|
||||||
|
import { defaultConfig, ensureEmitterConfig } from '../../core/particleEmitter'
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { downloadEditorConfig, loadEditorConfigFile, loadEditorConfigText } from '../../editor/editorConfig'
|
||||||
|
import { beginTransfer, readResponseTextWithProgress } from '../../editor/transferProgress'
|
||||||
|
import { defaultSpineExportSettings, ensureSpineExportSettings } from '../../export/spineExportSettings'
|
||||||
|
import { buildSpineJson, downloadSpineJson } from '../../export/spineJsonExporter'
|
||||||
|
import { validateSpineJson } from '../../export/spineJsonValidator'
|
||||||
|
import { buildSpineImagesArchive, downloadSpineImagesArchive } from '../../export/spineImagesExporter'
|
||||||
|
import {
|
||||||
|
SpineConnectorError,
|
||||||
|
checkSpineConnector,
|
||||||
|
convertWithSpineConnector,
|
||||||
|
downloadSpineProject,
|
||||||
|
type SpineConnectorHealth,
|
||||||
|
} from '../../export/spineConnectorClient'
|
||||||
|
import { automaticLoopTransitionFrames } from '../../core/loopSegmentTiming'
|
||||||
|
|
||||||
|
|
||||||
|
export function useParticleExport(store: ReturnType<typeof useParticleStore>) {
|
||||||
|
store.settings.exportSettings = ensureSpineExportSettings(store.settings.exportSettings)
|
||||||
|
const exportSettings = computed(() => store.settings.exportSettings)
|
||||||
|
const exportBusy = ref(false)
|
||||||
|
const exportTarget = ref<'json' | 'spine' | 'images' | null>(null)
|
||||||
|
const exportMessage = ref('')
|
||||||
|
const exportError = ref(false)
|
||||||
|
const connectorHealth = ref<SpineConnectorHealth | null>(null)
|
||||||
|
const connectorPairUrl = ref('')
|
||||||
|
const connectorExportErrorCode = ref('')
|
||||||
|
|
||||||
|
const connectorStatusText = computed(() => {
|
||||||
|
const health = connectorHealth.value
|
||||||
|
if (!health) return '正在检测本地 Spine 连接器…'
|
||||||
|
if (!health.available) return '本地 Spine 连接器未启动'
|
||||||
|
if (!health.spineFound) return '连接器已启动,但未找到 Spine'
|
||||||
|
if (!health.authorized) return '连接器已启动,当前网站尚未授权'
|
||||||
|
return '连接器可用 · Spine 已就绪'
|
||||||
|
})
|
||||||
|
const connectorStatusClass = computed(() => ({
|
||||||
|
ready: !!connectorHealth.value?.available && !!connectorHealth.value?.spineFound && !!connectorHealth.value?.authorized,
|
||||||
|
warning: !!connectorHealth.value?.available && (!connectorHealth.value?.spineFound || !connectorHealth.value?.authorized),
|
||||||
|
}))
|
||||||
|
const connectorReady = computed(() => !!connectorHealth.value?.available && !!connectorHealth.value?.spineFound && !!connectorHealth.value?.authorized)
|
||||||
|
const connectorAuthorizationUrl = computed(() => {
|
||||||
|
const health = connectorHealth.value
|
||||||
|
if (!health?.available || !health.spineFound || health.authorized || typeof window === 'undefined') return ''
|
||||||
|
return `http://127.0.0.1:27843/pair?origin=${encodeURIComponent(window.location.origin)}`
|
||||||
|
})
|
||||||
|
const connectorDisabledReason = computed(() => {
|
||||||
|
const health = connectorHealth.value
|
||||||
|
if (!health) return '检测中'
|
||||||
|
if (!health.available) return '未启动'
|
||||||
|
if (!health.spineFound) return '未找到 Spine'
|
||||||
|
if (!health.authorized) return '未授权'
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
const connectorButtonTitle = computed(() => {
|
||||||
|
if (exportBusy.value) return '正在重新计算并生成 Spine 工程'
|
||||||
|
if (connectorDisabledReason.value) return `暂不可用:${connectorDisabledReason.value}`
|
||||||
|
return `调用本机 Spine 自动生成 ${exportSettings.value.spineVersion} 工程`
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refreshConnectorHealth()
|
||||||
|
connectorHealthTimer = window.setInterval(refreshConnectorHealth, 3000)
|
||||||
|
})
|
||||||
|
|
||||||
|
let connectorHealthTimer: number | undefined
|
||||||
|
let connectorHealthRequestId = 0
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (connectorHealthTimer !== undefined) window.clearInterval(connectorHealthTimer)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function refreshConnectorHealth() {
|
||||||
|
const requestId = ++connectorHealthRequestId
|
||||||
|
const health = await checkSpineConnector()
|
||||||
|
if (requestId !== connectorHealthRequestId) return
|
||||||
|
connectorHealth.value = health
|
||||||
|
const code = connectorExportErrorCode.value
|
||||||
|
const resolved = code === 'CONNECTOR_OFFLINE'
|
||||||
|
? health.available
|
||||||
|
: code === 'SPINE_NOT_FOUND'
|
||||||
|
? health.available && health.spineFound
|
||||||
|
: code === 'ORIGIN_NOT_ALLOWED'
|
||||||
|
? health.available && health.spineFound && health.authorized
|
||||||
|
: !!code && health.available && health.spineFound && health.authorized
|
||||||
|
if (resolved) {
|
||||||
|
connectorExportErrorCode.value = ''
|
||||||
|
connectorPairUrl.value = ''
|
||||||
|
if (exportError.value) {
|
||||||
|
exportError.value = false
|
||||||
|
exportMessage.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPaint() {
|
||||||
|
return new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepareExportFrames(onProgress?: (progress: number) => void) {
|
||||||
|
store.timeline.playing = false
|
||||||
|
store.timeline.frame = 0
|
||||||
|
store.timeline.recorded = false
|
||||||
|
for (const system of store.systems) system.frames = undefined
|
||||||
|
store.recalcTotalFrames()
|
||||||
|
await nextPaint()
|
||||||
|
store.timeline.playing = true
|
||||||
|
const expectedMs = store.timeline.totalFrames / Math.max(1, store.timeline.fps) * 1000
|
||||||
|
const deadline = performance.now() + Math.max(8000, expectedMs * 2.5 + 3000)
|
||||||
|
while (!store.timeline.recorded) {
|
||||||
|
if (performance.now() > deadline) throw new Error('重新计算动画超时,请确认页面仍在运行后重试')
|
||||||
|
onProgress?.(Math.min(0.98, store.timeline.frame / Math.max(1, store.timeline.totalFrames - 1)))
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||||
|
}
|
||||||
|
onProgress?.(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportSpineJson() {
|
||||||
|
if (exportBusy.value) return
|
||||||
|
exportBusy.value = true
|
||||||
|
exportTarget.value = 'json'
|
||||||
|
exportError.value = false
|
||||||
|
connectorExportErrorCode.value = ''
|
||||||
|
connectorPairUrl.value = ''
|
||||||
|
exportMessage.value = '正在重新计算粒子动画…'
|
||||||
|
try {
|
||||||
|
await prepareExportFrames()
|
||||||
|
const result = buildSpineJson({ systems: store.systems as unknown as ParticleSystem[], timeline: store.timeline, settings: exportSettings.value })
|
||||||
|
validateSpineJson(result.text, exportSettings.value.spineVersion)
|
||||||
|
downloadSpineJson(result, exportSettings.value.fileName)
|
||||||
|
const warning = result.warnings.length ? `;${result.warnings.length} 条资源提示(缺图仍已导出)` : ''
|
||||||
|
exportMessage.value = `已导出 Spine ${exportSettings.value.spineVersion}:${result.particleSystemCount} 个粒子系统、${result.animationCount} 个动画、${result.boneCount} 根骨骼${warning}`
|
||||||
|
} catch (error) {
|
||||||
|
exportError.value = true
|
||||||
|
exportMessage.value = error instanceof Error ? error.message : String(error)
|
||||||
|
} finally {
|
||||||
|
exportBusy.value = false
|
||||||
|
exportTarget.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportSpineProject() {
|
||||||
|
if (exportBusy.value) return
|
||||||
|
exportBusy.value = true
|
||||||
|
exportTarget.value = 'spine'
|
||||||
|
exportError.value = false
|
||||||
|
connectorExportErrorCode.value = ''
|
||||||
|
connectorPairUrl.value = ''
|
||||||
|
exportMessage.value = '正在重新计算粒子动画…'
|
||||||
|
const transfer = beginTransfer('导出 Spine 包', '正在重新计算粒子动画…')
|
||||||
|
try {
|
||||||
|
await transfer.paint()
|
||||||
|
await prepareExportFrames((progress) => transfer.update(progress * 18, '正在重新计算粒子动画…'))
|
||||||
|
transfer.update(20, '正在生成 Spine JSON…')
|
||||||
|
const result = buildSpineJson({ systems: store.systems as unknown as ParticleSystem[], timeline: store.timeline, settings: exportSettings.value })
|
||||||
|
validateSpineJson(result.text, exportSettings.value.spineVersion)
|
||||||
|
exportMessage.value = '正在整理图片并调用本机 Spine…'
|
||||||
|
const projectName = exportSettings.value.fileName.replace(/\.json$/i, '') || 'SpineParticle'
|
||||||
|
const converted = await convertWithSpineConnector({
|
||||||
|
projectName,
|
||||||
|
spineVersion: exportSettings.value.spineVersion,
|
||||||
|
skeletonJson: result.text,
|
||||||
|
systems: store.systems as unknown as ParticleSystem[],
|
||||||
|
}, ({ phase, progress }) => {
|
||||||
|
if (phase === 'images') transfer.update(progress == null ? null : 22 + progress * 18, '正在整理工程图片…')
|
||||||
|
else if (phase === 'upload') transfer.update(progress == null ? null : 40 + progress * 30, '正在上传数据到 Spine 连接器…')
|
||||||
|
else if (phase === 'processing') transfer.update(null, 'Spine 正在转换工程,请稍候…')
|
||||||
|
else transfer.update(progress == null ? null : 82 + progress * 16, '正在下载生成的 Spine 工程包…')
|
||||||
|
})
|
||||||
|
transfer.update(99, '正在交给浏览器下载…')
|
||||||
|
downloadSpineProject(converted.blob, converted.fileName)
|
||||||
|
exportMessage.value = `已生成 ${converted.fileName}:包含 .spine 工程和 ${converted.imageCount} 张图片,Spine ${converted.spineEditorVersion}`
|
||||||
|
await refreshConnectorHealth()
|
||||||
|
await transfer.finish('Spine 工程包已开始下载')
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
|
exportError.value = true
|
||||||
|
connectorExportErrorCode.value = error instanceof SpineConnectorError ? error.code : ''
|
||||||
|
if (error instanceof SpineConnectorError && error.pairUrl) connectorPairUrl.value = error.pairUrl
|
||||||
|
exportMessage.value = errorMessage
|
||||||
|
await refreshConnectorHealth()
|
||||||
|
await transfer.fail(errorMessage)
|
||||||
|
} finally {
|
||||||
|
exportBusy.value = false
|
||||||
|
exportTarget.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportSpineImages() {
|
||||||
|
if (exportBusy.value) return
|
||||||
|
exportBusy.value = true
|
||||||
|
exportTarget.value = 'images'
|
||||||
|
exportError.value = false
|
||||||
|
connectorExportErrorCode.value = ''
|
||||||
|
connectorPairUrl.value = ''
|
||||||
|
exportMessage.value = '正在整理当前粒子系统使用的图片…'
|
||||||
|
try {
|
||||||
|
const archive = await buildSpineImagesArchive({
|
||||||
|
systems: store.systems as unknown as ParticleSystem[],
|
||||||
|
imagesPath: exportSettings.value.imagesPath,
|
||||||
|
})
|
||||||
|
const fileName = downloadSpineImagesArchive(archive.blob, exportSettings.value.fileName)
|
||||||
|
exportMessage.value = `已生成 ${fileName}:${archive.folder}/ 内包含 ${archive.imageCount} 张图片`
|
||||||
|
} catch (error) {
|
||||||
|
exportError.value = true
|
||||||
|
exportMessage.value = error instanceof Error ? error.message : String(error)
|
||||||
|
} finally {
|
||||||
|
exportBusy.value = false
|
||||||
|
exportTarget.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return { exportSettings, exportBusy, exportTarget, exportMessage, exportError, connectorHealth, connectorPairUrl, connectorExportErrorCode, connectorStatusText, connectorStatusClass, connectorReady, connectorAuthorizationUrl, connectorDisabledReason, connectorButtonTitle, connectorHealthTimer, connectorHealthRequestId, refreshConnectorHealth, nextPaint, prepareExportFrames, exportSpineJson, exportSpineProject, exportSpineImages }
|
||||||
|
}
|
||||||
@@ -0,0 +1,458 @@
|
|||||||
|
|
||||||
|
import { computed, markRaw, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue'
|
||||||
|
import { Texture } from 'pixi.js'
|
||||||
|
import { useParticleStore } from '../../store/particleStore'
|
||||||
|
import type { ParticleSystem } from '../../store/particleStore'
|
||||||
|
import { defaultConfig, ensureEmitterConfig } from '../../core/particleEmitter'
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { downloadEditorConfig, loadEditorConfigFile, loadEditorConfigText } from '../../editor/editorConfig'
|
||||||
|
import { beginTransfer, readResponseTextWithProgress } from '../../editor/transferProgress'
|
||||||
|
import { defaultSpineExportSettings, ensureSpineExportSettings } from '../../export/spineExportSettings'
|
||||||
|
import { buildSpineJson, downloadSpineJson } from '../../export/spineJsonExporter'
|
||||||
|
import { validateSpineJson } from '../../export/spineJsonValidator'
|
||||||
|
import { buildSpineImagesArchive, downloadSpineImagesArchive } from '../../export/spineImagesExporter'
|
||||||
|
import {
|
||||||
|
SpineConnectorError,
|
||||||
|
checkSpineConnector,
|
||||||
|
convertWithSpineConnector,
|
||||||
|
downloadSpineProject,
|
||||||
|
type SpineConnectorHealth,
|
||||||
|
} from '../../export/spineConnectorClient'
|
||||||
|
import { automaticLoopTransitionFrames } from '../../core/loopSegmentTiming'
|
||||||
|
|
||||||
|
import { LINEAR_CURVE, FLAT_CURVE, ALPHA_CURVE, RESOURCE_ALPHA_CURVE, TRAIL_ALPHA_CURVE, TRAIL_SHAPE_CURVE } from './particlePanelCurves'
|
||||||
|
import { useParticleExport } from './useParticleExport'
|
||||||
|
import { useProjectPresets } from './useProjectPresets'
|
||||||
|
import { useParticleResources } from './useParticleResources'
|
||||||
|
|
||||||
|
/** 每个参数面板创建一次状态,子分组共享选择、折叠状态与操作。 */
|
||||||
|
export function useParticlePanel() {
|
||||||
|
const store = useParticleStore()
|
||||||
|
const { exportSettings, exportBusy, exportTarget, exportMessage, exportError, connectorHealth, connectorPairUrl, connectorExportErrorCode, connectorStatusText, connectorStatusClass, connectorReady, connectorAuthorizationUrl, connectorDisabledReason, connectorButtonTitle, connectorHealthTimer, connectorHealthRequestId, refreshConnectorHealth, nextPaint, prepareExportFrames, exportSpineJson, exportSpineProject, exportSpineImages } = useParticleExport(store)
|
||||||
|
const { configFileInput, selectedPreset, presetOptions, presetLoading, configMessage, configError, saveConfig, onConfigFileChange, refreshPresets, loadSelectedPreset } = useProjectPresets(store)
|
||||||
|
function resolvedCurveEditorMode(mode: '跟随导出' | '线性' | '贝塞尔') {
|
||||||
|
if (mode === '跟随导出') return exportSettings.value.keyframeCurve === 'bezier' ? 'bezier' : 'linear'
|
||||||
|
return mode === '贝塞尔' ? 'bezier' : 'linear'
|
||||||
|
}
|
||||||
|
// 默认场景始终有一个可编辑的粒子系统;热更新保留现有系统时不重复创建。
|
||||||
|
if (!store.systems.length) store.addSystem()
|
||||||
|
|
||||||
|
const activeIdx = computed(() => {
|
||||||
|
const i = store.systems.findIndex((s) => s.id === store.activeId)
|
||||||
|
return i >= 0 ? i : 0
|
||||||
|
})
|
||||||
|
const sys = computed(() => store.systems[activeIdx.value] || null)
|
||||||
|
const { textureFileInput, sequenceFileInput, pendingResourceId, trailTextureFileInput, pendingTrailResourceId, addTrailResource, openTrailTexturePicker, onTrailRigChange, onTrailResourceBoneCountChange, onTrailResourceRotationChange, onTrailTextureFile, resetTrailTexture, removeTrailResource, rebalanceTrailWeights, setTrailResourceWeight, toggleTrailResourceLock, setTrailColor, commitTrailColor, addImageResource, openTexturePicker, openSequencePicker, onTextureFile, onSequenceFiles, readFileAsDataUrl, resetTexture, normalizeHex, setResourceFixedColor, commitResourceFixedColor, removeImageResource, roundWeight, distributeImageWeights, rebalanceImageWeights, setResourceWeight, toggleResourceLock } = useParticleResources(sys)
|
||||||
|
const automaticLoopDuration = computed(() => sys.value
|
||||||
|
? automaticLoopTransitionFrames(sys.value.config, store.timeline.fps)
|
||||||
|
: 30)
|
||||||
|
const loopDurationFrames = computed(() => {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return 1
|
||||||
|
return Math.max(1, Math.ceil(Number(config.loopDurationFrames) || 1))
|
||||||
|
})
|
||||||
|
const activeObjectType = computed(() => store.activeObjectType)
|
||||||
|
const activeCollider = computed(() => store.activeObjectType === 'collision'
|
||||||
|
? store.colliders.find((item) => item.id === store.activeObjectId) || null
|
||||||
|
: null)
|
||||||
|
const activePath = computed(() => store.activeObjectType === 'path'
|
||||||
|
? store.paths.find((item) => item.id === store.activeObjectId) || null
|
||||||
|
: null)
|
||||||
|
const activeSpine = computed(() => store.activeObjectType === 'spine'
|
||||||
|
? store.spines.find((item) => item.id === store.activeObjectId) || null
|
||||||
|
: null)
|
||||||
|
const canCopyObject = computed(() => {
|
||||||
|
if (store.activeObjectType === 'particle') return store.systems.some((item) => item.id === store.activeObjectId)
|
||||||
|
if (store.activeObjectType === 'collision') return store.colliders.some((item) => item.id === store.activeObjectId)
|
||||||
|
if (store.activeObjectType === 'path') return store.paths.some((item) => item.id === store.activeObjectId)
|
||||||
|
return store.spines.some((item) => item.id === store.activeObjectId)
|
||||||
|
})
|
||||||
|
const selectedSpineBone = computed(() => {
|
||||||
|
const spine = store.spines.find((item) => item.selectedBone && item.bones.some((bone) => bone.name === item.selectedBone))
|
||||||
|
return spine ? { spine, boneName: spine.selectedBone } : null
|
||||||
|
})
|
||||||
|
const boundSpineBone = computed(() => {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || config.emitterFollowMode !== 'bone') return null
|
||||||
|
const spine = store.spines.find((item) => item.id === config.emitterFollowSpineId)
|
||||||
|
if (!spine || !spine.bones.some((bone) => bone.name === config.emitterFollowBoneName)) return null
|
||||||
|
return { spine, boneName: config.emitterFollowBoneName }
|
||||||
|
})
|
||||||
|
const collisionPickerOpen = ref(false)
|
||||||
|
const selectedCollisionBodies = computed(() => {
|
||||||
|
const ids = new Set(sys.value?.config.collisionColliderIds || [])
|
||||||
|
return store.colliders.filter((collider) => ids.has(collider.id))
|
||||||
|
})
|
||||||
|
const availableCollisionBodies = computed(() => {
|
||||||
|
const ids = new Set(sys.value?.config.collisionColliderIds || [])
|
||||||
|
return store.colliders.filter((collider) => !ids.has(collider.id))
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => [sys.value?.id, sys.value?.config.collision], () => {
|
||||||
|
collisionPickerOpen.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
function addCollisionBody(colliderId: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || config.collisionColliderIds.includes(colliderId)) return
|
||||||
|
config.collisionColliderIds.push(colliderId)
|
||||||
|
collisionPickerOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCollisionBody(colliderId: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
config.collisionColliderIds = config.collisionColliderIds.filter((id) => id !== colliderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容开发热更新和旧场景:缺少显隐字段的对象按“显示”处理。
|
||||||
|
watchEffect(() => {
|
||||||
|
for (const system of store.systems) if (system.visible == null) system.visible = true
|
||||||
|
for (const collider of store.colliders) if (collider.visible == null) collider.visible = true
|
||||||
|
for (const path of store.paths) if (path.visible == null) path.visible = true
|
||||||
|
for (const spine of store.spines) if (spine.visible == null) spine.visible = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 开发热更新会保留旧的 Pinia 对象;渲染属性控件前先补齐新增字段。
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!sys.value) return
|
||||||
|
const config = ensureEmitterConfig(sys.value.config as any)
|
||||||
|
// 初始属性不再提供曲线模式;旧配置自动回退为随机。
|
||||||
|
if (config.lifeMode === 'curve') config.lifeMode = 'random'
|
||||||
|
if (config.speedMode === 'curve') config.speedMode = 'random'
|
||||||
|
if (config.scaleMode === 'curve') config.scaleMode = 'random'
|
||||||
|
if (config.initialRotationMode === 'curve') config.initialRotationMode = 'random'
|
||||||
|
if (config.mode === 'stream' && (!(config.duration > 0))) config.duration = 1
|
||||||
|
if (config.streamBehavior === 'loop') {
|
||||||
|
if (config.emitterFollow || config.emitterFollowMode !== 'none') resetParticleFollow(config)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 每个粒子系统永久保存自己的 Spine ID 与骨骼名;层级树当前选择不会覆盖既有绑定。
|
||||||
|
// 仅当已绑定的 Spine 或骨骼确实不存在时,才回到“无”模式。
|
||||||
|
watchEffect(() => {
|
||||||
|
const colliderIds = new Set(store.colliders.map((collider) => collider.id))
|
||||||
|
for (const system of store.systems) {
|
||||||
|
const config = ensureEmitterConfig(system.config as any)
|
||||||
|
const validCollisionIds = config.collisionColliderIds.filter((id) => colliderIds.has(id))
|
||||||
|
if (validCollisionIds.length !== config.collisionColliderIds.length) config.collisionColliderIds = validCollisionIds
|
||||||
|
if (config.spawnPathId > 0 && !store.paths.some((path) => path.id === config.spawnPathId)) {
|
||||||
|
config.spawnPathId = 0
|
||||||
|
if (config.emissionAngleMode === 'path-normal' || config.emissionAngleMode === 'path-tangent') config.emissionAngleMode = 'fixed'
|
||||||
|
}
|
||||||
|
if (config.emitterFollowMode !== 'bone') continue
|
||||||
|
const spine = store.spines.find((item) => item.id === config.emitterFollowSpineId)
|
||||||
|
const targetExists = !!spine?.bones.some((bone) => bone.name === config.emitterFollowBoneName)
|
||||||
|
if (!targetExists) {
|
||||||
|
config.emitterFollowMode = 'none'
|
||||||
|
config.emitterFollowSpineId = 0
|
||||||
|
config.emitterFollowBoneName = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// HMR 或旧场景中的碰撞体缺少路径跟随字段时即时补齐。
|
||||||
|
watchEffect(() => {
|
||||||
|
for (const collider of store.colliders) {
|
||||||
|
if (collider.followPathId == null) collider.followPathId = 0
|
||||||
|
if (!(collider.followDuration > 0)) collider.followDuration = 2
|
||||||
|
if (collider.followPathRotation == null) collider.followPathRotation = false
|
||||||
|
if (!['跟随导出', '线性', '贝塞尔'].includes(collider.followCurveMode)) collider.followCurveMode = '跟随导出'
|
||||||
|
if (!Array.isArray(collider.followCurve) || collider.followCurve.length < 2) collider.followCurve = [{ x: 0, y: 0 }, { x: 1, y: 1 }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const collapsed = reactive<Record<string, boolean>>({
|
||||||
|
config: true,
|
||||||
|
scene: false,
|
||||||
|
emitMode: true,
|
||||||
|
shape: true,
|
||||||
|
attr: true,
|
||||||
|
look: true,
|
||||||
|
mods: true,
|
||||||
|
export: false,
|
||||||
|
})
|
||||||
|
function toggle(key: string) { collapsed[key] = !collapsed[key] }
|
||||||
|
|
||||||
|
function setEmitMode(mode: 'stream' | 'burst') {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
config.mode = mode
|
||||||
|
if (mode === 'stream' && (!(config.duration > 0))) config.duration = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoopDurationFrames(value: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const frames = Math.min(1800, Math.max(1, Math.ceil(Number(value) || 1)))
|
||||||
|
config.loopDurationFrames = frames
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetParticleFollow(config: any) {
|
||||||
|
const defaults = defaultConfig()
|
||||||
|
config.emitterFollow = false
|
||||||
|
config.emitterFollowMode = 'none'
|
||||||
|
config.emitterFollowPathId = defaults.emitterFollowPathId
|
||||||
|
config.emitterFollowSpineId = defaults.emitterFollowSpineId
|
||||||
|
config.emitterFollowBoneName = defaults.emitterFollowBoneName
|
||||||
|
config.emitterFollowBoneOffsetX = defaults.emitterFollowBoneOffsetX
|
||||||
|
config.emitterFollowBoneOffsetY = defaults.emitterFollowBoneOffsetY
|
||||||
|
config.emitterFollowBoneRotation = defaults.emitterFollowBoneRotation
|
||||||
|
config.emitterFollowBoneScaleX = defaults.emitterFollowBoneScaleX
|
||||||
|
config.emitterFollowBoneScaleY = defaults.emitterFollowBoneScaleY
|
||||||
|
config.emitterAngleMode = defaults.emitterAngleMode
|
||||||
|
config.emitterFollowCurve = defaults.emitterFollowCurve.map((point) => ({ ...point }))
|
||||||
|
config.emitterFollowDuration = defaults.emitterFollowDuration
|
||||||
|
config.emitterFollowSpace = defaults.emitterFollowSpace
|
||||||
|
config.emitterFollowDirection = defaults.emitterFollowDirection
|
||||||
|
config.emitterFollowOffset = defaults.emitterFollowOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStreamBehavior(behavior: 'normal' | 'loop') {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || config.streamBehavior === behavior) return
|
||||||
|
const entering = behavior === 'loop'
|
||||||
|
const message = entering
|
||||||
|
? '进入循环持续模式后,粒子跟随设置会失效并恢复默认值。是否继续?'
|
||||||
|
: '退出循环持续模式后,粒子跟随设置会失效并恢复默认值。是否继续?'
|
||||||
|
if (!window.confirm(message)) return
|
||||||
|
resetParticleFollow(config)
|
||||||
|
config.streamBehavior = behavior
|
||||||
|
if (!entering) {
|
||||||
|
config.generateLoopStartAnimation = false
|
||||||
|
config.generateLoopEndAnimation = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoopSection(section: 'start' | 'end', event: Event) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const checked = (event.target as HTMLInputElement).checked
|
||||||
|
if (section === 'start') {
|
||||||
|
config.generateLoopStartAnimation = checked
|
||||||
|
} else {
|
||||||
|
config.generateLoopEndAnimation = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoopSectionDuration(section: 'start' | 'end', event: Event) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const frames = Math.min(1800, Math.max(1, Math.ceil(Number((event.target as HTMLInputElement).value) || 30)))
|
||||||
|
if (section === 'start') config.loopStartDurationFrames = frames
|
||||||
|
else config.loopEndDurationFrames = frames
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoopCustomDuration(section: 'start' | 'end', event: Event) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const checked = (event.target as HTMLInputElement).checked
|
||||||
|
if (section === 'start') config.loopStartUseCustomDuration = checked
|
||||||
|
else config.loopEndUseCustomDuration = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEmitterFollowModeChange(event: Event, config: {
|
||||||
|
emitterFollowMode: 'none' | 'path' | 'bone'
|
||||||
|
emitterFollowSpineId: number
|
||||||
|
emitterFollowBoneName: string
|
||||||
|
}) {
|
||||||
|
const mode = (event.target as HTMLSelectElement).value as 'none' | 'path' | 'bone'
|
||||||
|
config.emitterFollowMode = mode
|
||||||
|
if (mode !== 'bone') {
|
||||||
|
config.emitterFollowSpineId = 0
|
||||||
|
config.emitterFollowBoneName = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bindSelectedBone(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindSelectedBone(config: {
|
||||||
|
emitterFollowMode: 'none' | 'path' | 'bone'
|
||||||
|
emitterFollowSpineId: number
|
||||||
|
emitterFollowBoneName: string
|
||||||
|
}) {
|
||||||
|
const target = selectedSpineBone.value
|
||||||
|
if (!target) {
|
||||||
|
config.emitterFollowMode = 'none'
|
||||||
|
config.emitterFollowSpineId = 0
|
||||||
|
config.emitterFollowBoneName = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.emitterFollowMode = 'bone'
|
||||||
|
config.emitterFollowSpineId = target.spine.id
|
||||||
|
config.emitterFollowBoneName = target.boneName
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResetModule = 'emitMode' | 'shape' | 'attr' | 'look' | 'mods' | 'export'
|
||||||
|
|
||||||
|
const MODULE_RESET_KEYS: Record<Exclude<ResetModule, 'export'>, string[]> = {
|
||||||
|
emitMode: [
|
||||||
|
'mode', 'rate', 'streamBehavior', 'loopDurationFrames',
|
||||||
|
'generateLoopStartAnimation', 'loopStartUseCustomDuration', 'loopStartDurationFrames',
|
||||||
|
'generateLoopEndAnimation', 'loopEndUseCustomDuration', 'loopEndDurationFrames',
|
||||||
|
'burstCount', 'burstLoop', 'delay', 'duration',
|
||||||
|
],
|
||||||
|
shape: [
|
||||||
|
'shape', 'shapeRangeVersion',
|
||||||
|
'innerRadius', 'outerRadius', 'innerRectW', 'innerRectH', 'outerRectW', 'outerRectH', 'spawnPathId', 'spawnPathDistribution',
|
||||||
|
'radius', 'rectW', 'rectH', 'emissionAngleMode', 'direction', 'spread', 'coneDirection', 'coneAngle',
|
||||||
|
],
|
||||||
|
attr: [
|
||||||
|
'lifeMin', 'lifeMax', 'lifeMode', 'lifeCurve',
|
||||||
|
'speedMin', 'speedMax', 'speedMode', 'speedCurve',
|
||||||
|
'scaleMin', 'scaleMax', 'scaleMode', 'scaleAxisMode', 'scaleYMin', 'scaleYMax',
|
||||||
|
'scaleCurve', 'scaleYCurve', 'scaleEndRatio',
|
||||||
|
'initialRotationMode', 'initialRotationMin', 'initialRotationMax', 'initialRotationCurve',
|
||||||
|
'rotationSpeedMin', 'rotationSpeedMax',
|
||||||
|
'speedOverLifeEnabled', 'speedOverLifeMode', 'speedOverLifeMin', 'speedOverLifeMax', 'speedOverLifeCurve',
|
||||||
|
'scaleOverLifeEnabled', 'scaleOverLifeMode', 'scaleOverLifeAxisMode',
|
||||||
|
'scaleOverLifeMin', 'scaleOverLifeMax', 'scaleYOverLifeMin', 'scaleYOverLifeMax',
|
||||||
|
'scaleOverLifeCurve', 'scaleYOverLifeCurve',
|
||||||
|
'rotationOverLifeEnabled', 'rotationOverLifeMode', 'rotationOverLifeCurve',
|
||||||
|
],
|
||||||
|
look: [
|
||||||
|
'texture', 'imageResources', 'imageWeightVersion',
|
||||||
|
'alpha', 'alphaEnd', 'colorStart', 'colorEnd', 'colorOverLife', 'blend',
|
||||||
|
'alphaMode', 'curveMode', 'alphaCurve', 'textureName', 'textureFolder',
|
||||||
|
'alphaScale', 'anchorX', 'anchorY', 'independentAlpha',
|
||||||
|
],
|
||||||
|
mods: [
|
||||||
|
'noise', 'noiseAmt', 'noiseRange', 'noiseSpeed',
|
||||||
|
'useGravity', 'gravity', 'gravityMode', 'gravityMin', 'gravityMax', 'gravityDirection', 'gravityCurve',
|
||||||
|
'wind', 'windX', 'windY', 'windMode', 'windMin', 'windMax', 'windDirection', 'windCurve',
|
||||||
|
'forceField', 'forceStrength', 'forceRadius', 'forceCenterX', 'forceCenterY', 'forceFalloff',
|
||||||
|
'attraction', 'attractionCenterX', 'attractionCenterY', 'attractionRadius',
|
||||||
|
'attractionDuration', 'attractionDelay', 'attractionDelayMode', 'attractionPath',
|
||||||
|
'trail', 'trailConfigVersion', 'trailResources', 'trailResourceVersion',
|
||||||
|
'trailTexture', 'trailTextureName', 'trailPreviewUrl', 'trailColorMode', 'trailColor',
|
||||||
|
'trailColorGradient', 'trailAlphaMode', 'trailAlpha', 'trailAlphaCurve',
|
||||||
|
'trailBoneCount', 'trailLength', 'trailWidth', 'trailWidthMode',
|
||||||
|
'trailLifeLengthEnabled', 'trailLifeLengthCurve', 'trailShapeEnabled', 'trailShapeCurve',
|
||||||
|
'trailGridRows', 'trailGridCols', 'collision', 'collisionColliderIds', 'pathFollow',
|
||||||
|
'emitterFollow', 'emitterFollowMode', 'emitterFollowPathId',
|
||||||
|
'emitterFollowSpineId', 'emitterFollowBoneName',
|
||||||
|
'emitterFollowBoneOffsetX', 'emitterFollowBoneOffsetY', 'emitterFollowBoneRotation',
|
||||||
|
'emitterFollowBoneScaleX', 'emitterFollowBoneScaleY', 'emitterAngleMode',
|
||||||
|
'emitterFollowCurve', 'emitterFollowDuration', 'emitterFollowSpace',
|
||||||
|
'emitterFollowDirection', 'emitterFollowOffset', 'damping',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetModule(module: ResetModule) {
|
||||||
|
const config = sys.value?.config as any
|
||||||
|
if (module === 'export') {
|
||||||
|
store.settings.exportSettings = defaultSpineExportSettings()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!config) return
|
||||||
|
const defaults = defaultConfig() as any
|
||||||
|
for (const key of MODULE_RESET_KEYS[module]) config[key] = defaults[key]
|
||||||
|
// 重置资源模块后,Stage 会在下一帧重新挂载默认 star/trail 纹理。
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeExportFps() {
|
||||||
|
exportSettings.value.fps = Math.min(240, Math.max(1, Math.round(Number(exportSettings.value.fps) || 30)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(i: number) { store.selectSystem(store.systems[i]?.id ?? 0) }
|
||||||
|
function addSys() { store.addSystem() }
|
||||||
|
function addCollider() { store.addCollider() }
|
||||||
|
function addPath() { store.addPath() }
|
||||||
|
function addSpine() { store.addSpine() }
|
||||||
|
function copySelectedObject() { store.duplicateSelectedObject() }
|
||||||
|
function pathAnchor(x: number, y: number, inX = 0, inY = 0, outX = 0, outY = 0) {
|
||||||
|
return { x, y, inX, inY, outX, outY }
|
||||||
|
}
|
||||||
|
function applyPathPreset() {
|
||||||
|
const path = activePath.value
|
||||||
|
if (!path || !path.preset) return
|
||||||
|
if (path.preset === 'circle') {
|
||||||
|
const radius = 110, handle = radius * 0.5522848
|
||||||
|
path.points = [
|
||||||
|
pathAnchor(radius, 0, 0, -handle, 0, handle),
|
||||||
|
pathAnchor(0, radius, handle, 0, -handle, 0),
|
||||||
|
pathAnchor(-radius, 0, 0, handle, 0, -handle),
|
||||||
|
pathAnchor(0, -radius, -handle, 0, handle, 0),
|
||||||
|
]
|
||||||
|
path.closed = true
|
||||||
|
} else if (path.preset === 'square') {
|
||||||
|
path.points = [pathAnchor(-100, -100), pathAnchor(100, -100), pathAnchor(100, 100), pathAnchor(-100, 100)]
|
||||||
|
path.closed = true
|
||||||
|
} else {
|
||||||
|
const sides = path.preset === 'star' ? 10 : 6
|
||||||
|
path.points = Array.from({ length: sides }, (_, index) => {
|
||||||
|
const radius = path.preset === 'star' && index % 2 ? 50 : 110
|
||||||
|
const angle = -Math.PI * 0.5 + index / sides * Math.PI * 2
|
||||||
|
return pathAnchor(Math.cos(angle) * radius, Math.sin(angle) * radius)
|
||||||
|
})
|
||||||
|
path.closed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function clearPathAnchors() {
|
||||||
|
if (activePath.value) activePath.value.points = []
|
||||||
|
}
|
||||||
|
function randomizeSeed() {
|
||||||
|
if (sys.value) sys.value.config.seed = Math.floor(Math.random() * 999999) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpeedOverLifeMode(mode: 'fixed' | 'random' | 'curve' | 'direction') {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || mode === 'direction') return
|
||||||
|
config.speedOverLifeMode = mode
|
||||||
|
// 旧默认是 1~1,曲线映射后永远为 1;切换曲线时改为有效倍率范围。
|
||||||
|
if (mode === 'curve' && config.speedOverLifeMin === config.speedOverLifeMax) {
|
||||||
|
config.speedOverLifeMin = 0
|
||||||
|
config.speedOverLifeMax = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareScaleOverLifeCurve() {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || config.scaleOverLifeMode !== 'curve') return
|
||||||
|
if (config.scaleOverLifeMin === config.scaleOverLifeMax) {
|
||||||
|
config.scaleOverLifeMin = 0
|
||||||
|
config.scaleOverLifeMax = 1
|
||||||
|
}
|
||||||
|
if (config.scaleYOverLifeMin === config.scaleYOverLifeMax) {
|
||||||
|
config.scaleYOverLifeMin = 0
|
||||||
|
config.scaleYOverLifeMax = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScaleOverLifeMode(mode: 'fixed' | 'random' | 'curve' | 'direction') {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config || mode === 'direction') return
|
||||||
|
config.scaleOverLifeMode = mode
|
||||||
|
prepareScaleOverLifeCurve()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各发射器形状的默认参数:切换形状时应用(只影响形状相关字段,不牵连其他)
|
||||||
|
const SHAPE_DEFAULTS: Record<string, Partial<any>> = {
|
||||||
|
point: { emissionAngleMode: 'random', direction: 0, spread: 360 },
|
||||||
|
circle: { innerRadius: 0, outerRadius: 100, emissionAngleMode: 'fixed', direction: -90, spread: 0 },
|
||||||
|
rect: { innerRectW: 0, innerRectH: 0, outerRectW: 200, outerRectH: 100, emissionAngleMode: 'fixed', direction: -90, spread: 0 },
|
||||||
|
cone: { innerRadius: 0, outerRadius: 100, coneAngle: 90, coneDirection: 90, emissionAngleMode: 'random', direction: 90, spread: 90 },
|
||||||
|
path: { spawnPathId: 0, spawnPathDistribution: 'fixed', emissionAngleMode: 'fixed', direction: 0, spread: 0 },
|
||||||
|
}
|
||||||
|
function onShapeChange() {
|
||||||
|
const cfg = sys.value?.config
|
||||||
|
if (!cfg) return
|
||||||
|
const d = SHAPE_DEFAULTS[cfg.shape]
|
||||||
|
if (!d) return
|
||||||
|
Object.assign(cfg, d)
|
||||||
|
}
|
||||||
|
function onSpawnPathChange() {
|
||||||
|
const cfg = sys.value?.config
|
||||||
|
if (!cfg || cfg.spawnPathId > 0) return
|
||||||
|
if (cfg.emissionAngleMode === 'path-normal' || cfg.emissionAngleMode === 'path-tangent') cfg.emissionAngleMode = 'fixed'
|
||||||
|
}
|
||||||
|
// 重置/删除图片 → 回到默认 star.png
|
||||||
|
|
||||||
|
|
||||||
|
return { store, exportSettings, exportBusy, exportTarget, exportMessage, exportError, connectorPairUrl, connectorStatusText, connectorStatusClass, connectorReady, connectorAuthorizationUrl, connectorDisabledReason, connectorButtonTitle, refreshConnectorHealth, exportSpineJson, exportSpineProject, exportSpineImages, configFileInput, selectedPreset, presetOptions, presetLoading, configMessage, configError, saveConfig, onConfigFileChange, refreshPresets, loadSelectedPreset, resolvedCurveEditorMode, sys, textureFileInput, sequenceFileInput, trailTextureFileInput, addTrailResource, openTrailTexturePicker, onTrailRigChange, onTrailResourceBoneCountChange, onTrailResourceRotationChange, onTrailTextureFile, resetTrailTexture, removeTrailResource, setTrailResourceWeight, toggleTrailResourceLock, setTrailColor, commitTrailColor, addImageResource, openTexturePicker, openSequencePicker, onTextureFile, onSequenceFiles, resetTexture, setResourceFixedColor, commitResourceFixedColor, removeImageResource, setResourceWeight, toggleResourceLock, automaticLoopDuration, loopDurationFrames, activeObjectType, activeCollider, activePath, activeSpine, canCopyObject, selectedSpineBone, boundSpineBone, collisionPickerOpen, selectedCollisionBodies, availableCollisionBodies, addCollisionBody, removeCollisionBody, collapsed, toggle, setEmitMode, setLoopDurationFrames, setStreamBehavior, setLoopSection, setLoopSectionDuration, setLoopCustomDuration, onEmitterFollowModeChange, bindSelectedBone, resetModule, normalizeExportFps, select, addSys, addCollider, addPath, addSpine, copySelectedObject, applyPathPreset, clearPathAnchors, randomizeSeed, setSpeedOverLifeMode, prepareScaleOverLifeCurve, setScaleOverLifeMode, onShapeChange, onSpawnPathChange, LINEAR_CURVE, FLAT_CURVE, ALPHA_CURVE, RESOURCE_ALPHA_CURVE, TRAIL_ALPHA_CURVE, TRAIL_SHAPE_CURVE }
|
||||||
|
}
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
|
||||||
|
import { computed, markRaw, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue'
|
||||||
|
import { Texture } from 'pixi.js'
|
||||||
|
import { useParticleStore } from '../../store/particleStore'
|
||||||
|
import type { ParticleSystem } from '../../store/particleStore'
|
||||||
|
import { defaultConfig, ensureEmitterConfig } from '../../core/particleEmitter'
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { downloadEditorConfig, loadEditorConfigFile, loadEditorConfigText } from '../../editor/editorConfig'
|
||||||
|
import { beginTransfer, readResponseTextWithProgress } from '../../editor/transferProgress'
|
||||||
|
import { defaultSpineExportSettings, ensureSpineExportSettings } from '../../export/spineExportSettings'
|
||||||
|
import { buildSpineJson, downloadSpineJson } from '../../export/spineJsonExporter'
|
||||||
|
import { validateSpineJson } from '../../export/spineJsonValidator'
|
||||||
|
import { buildSpineImagesArchive, downloadSpineImagesArchive } from '../../export/spineImagesExporter'
|
||||||
|
import {
|
||||||
|
SpineConnectorError,
|
||||||
|
checkSpineConnector,
|
||||||
|
convertWithSpineConnector,
|
||||||
|
downloadSpineProject,
|
||||||
|
type SpineConnectorHealth,
|
||||||
|
} from '../../export/spineConnectorClient'
|
||||||
|
import { automaticLoopTransitionFrames } from '../../core/loopSegmentTiming'
|
||||||
|
|
||||||
|
|
||||||
|
export function useParticleResources(sys: import('vue').ComputedRef<ReturnType<typeof useParticleStore>['systems'][number] | null>) {
|
||||||
|
const textureFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const sequenceFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const pendingResourceId = ref<number | null>(null)
|
||||||
|
const trailTextureFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const pendingTrailResourceId = ref<number | null>(null)
|
||||||
|
|
||||||
|
function addTrailResource() {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const nextId = Math.max(0, ...config.trailResources.map((resource) => resource.id)) + 1
|
||||||
|
config.trailResources.push({
|
||||||
|
id: nextId,
|
||||||
|
texture: null,
|
||||||
|
textureName: 'trail',
|
||||||
|
previewUrl: '/images/trail.png',
|
||||||
|
weight: 100,
|
||||||
|
locked: false,
|
||||||
|
rotation: 0,
|
||||||
|
colorMode: 'particle',
|
||||||
|
color: '#ffffff',
|
||||||
|
colorGradient: [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ffffff' }],
|
||||||
|
blend: 'normal',
|
||||||
|
alphaMode: 'particle',
|
||||||
|
alpha: 1,
|
||||||
|
alphaCurve: TRAIL_ALPHA_CURVE.map((point) => ({ ...point })),
|
||||||
|
boneCount: 3,
|
||||||
|
trailRatio: 100,
|
||||||
|
lengthMode: 'fixed',
|
||||||
|
length: 100,
|
||||||
|
lengthMin: 100,
|
||||||
|
lengthMax: 100,
|
||||||
|
widthSizeMode: 'fixed',
|
||||||
|
width: 100,
|
||||||
|
widthMin: 100,
|
||||||
|
widthMax: 100,
|
||||||
|
widthMode: 'particle',
|
||||||
|
lifeLengthEnabled: false,
|
||||||
|
lifeLengthCurve: FLAT_CURVE.map((point) => ({ ...point })),
|
||||||
|
shapeEnabled: false,
|
||||||
|
shapeCurve: TRAIL_SHAPE_CURVE.map((point) => ({ ...point })),
|
||||||
|
})
|
||||||
|
rebalanceTrailWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTrailTexturePicker(resourceId: number) {
|
||||||
|
pendingTrailResourceId.value = resourceId
|
||||||
|
trailTextureFileInput.value?.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrailRigKey = 'trailGridRows' | 'trailGridCols'
|
||||||
|
function onTrailRigChange(key: TrailRigKey, value: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const maximum = key === 'trailGridCols' ? 32 : 16
|
||||||
|
config[key] = Math.min(maximum, Math.max(2, Math.round(Number(value) || 2)))
|
||||||
|
// 只触发骨骼和蒙皮重建;所有正在使用的拖尾图片与粒子资源选择保持不变。
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTrailResourceBoneCountChange(resourceId: number, value: number) {
|
||||||
|
const resource = sys.value?.config.trailResources.find((item) => item.id === resourceId)
|
||||||
|
if (!resource) return
|
||||||
|
resource.boneCount = Math.min(16, Math.max(2, Math.round(Number(value) || 2)))
|
||||||
|
// 骨骼数量包含在该资源的蒙皮签名中,运行层会保留图片并重新绑定。
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTrailResourceRotationChange(resourceId: number, value: number) {
|
||||||
|
const resource = sys.value?.config.trailResources.find((item) => item.id === resourceId)
|
||||||
|
if (!resource) return
|
||||||
|
resource.rotation = Math.max(-360, Math.min(360, Number(value) || 0))
|
||||||
|
// 旋转属于图片默认方向;运行层将新的角度写入 UV 签名并重新绑定蒙皮。
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onTrailTextureFile(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
const file = input.files?.[0]
|
||||||
|
const config = sys.value?.config
|
||||||
|
const resource = config?.trailResources.find((item) => item.id === pendingTrailResourceId.value)
|
||||||
|
if (!file || !resource) { input.value = ''; return }
|
||||||
|
const dataUrl = await readFileAsDataUrl(file)
|
||||||
|
resource.texture = markRaw(await Texture.fromURL(dataUrl)) as any
|
||||||
|
resource.previewUrl = dataUrl
|
||||||
|
resource.textureName = file.name.replace(/\.[^.]+$/, '') || 'trail'
|
||||||
|
input.value = ''
|
||||||
|
pendingTrailResourceId.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetTrailTexture(resourceId: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
const resource = config?.trailResources.find((item) => item.id === resourceId)
|
||||||
|
if (!resource) return
|
||||||
|
resource.texture = markRaw(await Texture.fromURL('/images/trail.png')) as any
|
||||||
|
resource.previewUrl = '/images/trail.png'
|
||||||
|
resource.textureName = 'trail'
|
||||||
|
resource.weight = 100
|
||||||
|
resource.locked = false
|
||||||
|
resource.rotation = 0
|
||||||
|
resource.boneCount = 3
|
||||||
|
resource.trailRatio = 100
|
||||||
|
resource.lengthMode = 'fixed'
|
||||||
|
resource.length = 100
|
||||||
|
resource.lengthMin = 100
|
||||||
|
resource.lengthMax = 100
|
||||||
|
resource.widthSizeMode = 'fixed'
|
||||||
|
resource.width = 100
|
||||||
|
resource.widthMin = 100
|
||||||
|
resource.widthMax = 100
|
||||||
|
resource.widthMode = 'particle'
|
||||||
|
resource.lifeLengthEnabled = false
|
||||||
|
resource.lifeLengthCurve = FLAT_CURVE.map((point) => ({ ...point }))
|
||||||
|
resource.shapeEnabled = false
|
||||||
|
resource.shapeCurve = TRAIL_SHAPE_CURVE.map((point) => ({ ...point }))
|
||||||
|
resource.colorMode = 'particle'
|
||||||
|
resource.color = '#ffffff'
|
||||||
|
resource.colorGradient = [
|
||||||
|
{ position: 0, color: '#ffffff' },
|
||||||
|
{ position: 1, color: '#ffffff' },
|
||||||
|
]
|
||||||
|
resource.blend = 'normal'
|
||||||
|
resource.alphaMode = 'particle'
|
||||||
|
resource.alpha = 1
|
||||||
|
resource.alphaCurve = TRAIL_ALPHA_CURVE.map((point) => ({ ...point }))
|
||||||
|
rebalanceTrailWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTrailResource(resourceId: number) {
|
||||||
|
const resources = sys.value?.config.trailResources
|
||||||
|
if (!resources || resources.length <= 1) return
|
||||||
|
const index = resources.findIndex((resource) => resource.id === resourceId)
|
||||||
|
if (index >= 0) resources.splice(index, 1)
|
||||||
|
rebalanceTrailWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebalanceTrailWeights() {
|
||||||
|
const resources = sys.value?.config.trailResources
|
||||||
|
if (!resources?.length) return
|
||||||
|
if (resources.length === 1) {
|
||||||
|
resources[0].weight = 100
|
||||||
|
resources[0].locked = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const locked = resources.filter((resource) => resource.locked)
|
||||||
|
const unlocked = resources.filter((resource) => !resource.locked)
|
||||||
|
let lockedTotal = locked.reduce((sum, resource) => sum + Math.max(0, Number(resource.weight) || 0), 0)
|
||||||
|
if (lockedTotal > 100) {
|
||||||
|
distributeImageWeights(locked, 100, true)
|
||||||
|
lockedTotal = 100
|
||||||
|
}
|
||||||
|
distributeImageWeights(unlocked, 100 - lockedTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTrailResourceWeight(resourceId: number, value: number) {
|
||||||
|
const resources = sys.value?.config.trailResources
|
||||||
|
if (!resources?.length) return
|
||||||
|
if (resources.length === 1) {
|
||||||
|
resources[0].weight = 100
|
||||||
|
resources[0].locked = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const resource = resources.find((item) => item.id === resourceId)
|
||||||
|
if (!resource || resource.locked) return
|
||||||
|
const lockedTotal = resources
|
||||||
|
.filter((item) => item.id !== resourceId && item.locked)
|
||||||
|
.reduce((sum, item) => sum + Math.max(0, Number(item.weight) || 0), 0)
|
||||||
|
const available = Math.max(0, 100 - lockedTotal)
|
||||||
|
const others = resources.filter((item) => item.id !== resourceId && !item.locked)
|
||||||
|
resource.weight = roundWeight(Math.min(available, Math.max(0, Number(value) || 0)))
|
||||||
|
if (!others.length) resource.weight = roundWeight(available)
|
||||||
|
else distributeImageWeights(others, available - resource.weight, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTrailResourceLock(resourceId: number) {
|
||||||
|
const resources = sys.value?.config.trailResources
|
||||||
|
if (!resources || resources.length === 1) return
|
||||||
|
const resource = resources.find((item) => item.id === resourceId)
|
||||||
|
if (resource) resource.locked = !resource.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTrailColor(resource: { color: string }, event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
resource.color = normalizeHex(input.value, resource.color)
|
||||||
|
input.value = resource.color
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitTrailColor(resource: { color: string }, event: KeyboardEvent) {
|
||||||
|
setTrailColor(resource, event)
|
||||||
|
;(event.target as HTMLInputElement).blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addImageResource() {
|
||||||
|
const config = sys.value?.config
|
||||||
|
if (!config) return
|
||||||
|
const nextId = Math.max(0, ...config.imageResources.map((resource) => resource.id)) + 1
|
||||||
|
config.imageResources.push({
|
||||||
|
id: nextId,
|
||||||
|
texture: config.texture,
|
||||||
|
textureName: 'star',
|
||||||
|
textureFolder: '',
|
||||||
|
previewUrl: '/images/star.png',
|
||||||
|
imageMode: 'fixed',
|
||||||
|
sequencePlayback: 'loop-forward',
|
||||||
|
sequenceFps: 30,
|
||||||
|
sequenceFrames: [],
|
||||||
|
weight: 100,
|
||||||
|
locked: false,
|
||||||
|
scale: 1,
|
||||||
|
anchorX: 0.5,
|
||||||
|
anchorY: 0.5,
|
||||||
|
colorMode: 'fixed',
|
||||||
|
colorStart: '#ffffff',
|
||||||
|
colorEnd: '#ff4d4d',
|
||||||
|
colorGradient: [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ff4d4d' }],
|
||||||
|
blend: 'normal',
|
||||||
|
independentAlpha: 'off',
|
||||||
|
alpha: 1,
|
||||||
|
alphaCurveMode: '跟随导出',
|
||||||
|
alphaCurve: RESOURCE_ALPHA_CURVE.map((point) => ({ ...point })),
|
||||||
|
} as any)
|
||||||
|
rebalanceImageWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTexturePicker(resourceId: number) {
|
||||||
|
pendingResourceId.value = resourceId
|
||||||
|
textureFileInput.value?.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSequencePicker(resourceId: number) {
|
||||||
|
pendingResourceId.value = resourceId
|
||||||
|
sequenceFileInput.value?.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onTextureFile(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
const file = input.files?.[0]
|
||||||
|
const config = sys.value?.config
|
||||||
|
const resource = config?.imageResources.find((item) => item.id === pendingResourceId.value)
|
||||||
|
if (!file || !resource) { input.value = ''; return }
|
||||||
|
const dataUrl = await readFileAsDataUrl(file)
|
||||||
|
const texture = markRaw(await Texture.fromURL(dataUrl))
|
||||||
|
resource.texture = texture as any
|
||||||
|
resource.previewUrl = dataUrl
|
||||||
|
resource.textureFolder = ''
|
||||||
|
resource.textureName = file.name.replace(/\.[^.]+$/, '') || 'particle'
|
||||||
|
input.value = ''
|
||||||
|
pendingResourceId.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSequenceFiles(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
const files = Array.from(input.files || []).sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }))
|
||||||
|
const config = sys.value?.config
|
||||||
|
const resource = config?.imageResources.find((item) => item.id === pendingResourceId.value)
|
||||||
|
if (!files.length || !resource) { input.value = ''; return }
|
||||||
|
resource.sequenceFrames = await Promise.all(files.map(async (file) => {
|
||||||
|
const previewUrl = await readFileAsDataUrl(file)
|
||||||
|
return {
|
||||||
|
name: file.name,
|
||||||
|
previewUrl,
|
||||||
|
texture: markRaw(await Texture.fromURL(previewUrl)) as any,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
resource.imageMode = 'sequence'
|
||||||
|
input.value = ''
|
||||||
|
pendingResourceId.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFileAsDataUrl(file: File) {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => resolve(String(reader.result || ''))
|
||||||
|
reader.onerror = () => reject(reader.error)
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTexture(resourceId: number) {
|
||||||
|
const config = sys.value?.config
|
||||||
|
const resource = config?.imageResources.find((item) => item.id === resourceId)
|
||||||
|
if (!config || !resource) return
|
||||||
|
resource.texture = config.texture
|
||||||
|
resource.textureName = 'star'
|
||||||
|
resource.textureFolder = ''
|
||||||
|
resource.previewUrl = '/images/star.png'
|
||||||
|
resource.imageMode = 'fixed'
|
||||||
|
resource.sequencePlayback = 'loop-forward'
|
||||||
|
resource.sequenceFps = 30
|
||||||
|
resource.sequenceFrames = []
|
||||||
|
resource.scale = 1
|
||||||
|
resource.anchorX = 0.5
|
||||||
|
resource.anchorY = 0.5
|
||||||
|
resource.colorMode = 'fixed'
|
||||||
|
resource.colorStart = '#ffffff'
|
||||||
|
resource.colorEnd = '#ff4d4d'
|
||||||
|
resource.colorGradient = [{ position: 0, color: '#ffffff' }, { position: 1, color: '#ff4d4d' }]
|
||||||
|
resource.blend = 'normal'
|
||||||
|
resource.independentAlpha = 'off'
|
||||||
|
resource.alpha = 1
|
||||||
|
resource.alphaCurveMode = '跟随导出'
|
||||||
|
resource.alphaCurve = RESOURCE_ALPHA_CURVE.map((point) => ({ ...point }))
|
||||||
|
resource.locked = false
|
||||||
|
rebalanceImageWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHex(value: string, fallback: string) {
|
||||||
|
let hex = value.trim().replace(/^#/, '')
|
||||||
|
if (/^[0-9a-f]{3}$/i.test(hex)) hex = hex.split('').map((character) => character + character).join('')
|
||||||
|
return /^[0-9a-f]{6}$/i.test(hex) ? `#${hex.toLowerCase()}` : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
function setResourceFixedColor(resource: { colorStart: string }, event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
resource.colorStart = normalizeHex(input.value, resource.colorStart)
|
||||||
|
input.value = resource.colorStart
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitResourceFixedColor(resource: { colorStart: string }, event: KeyboardEvent) {
|
||||||
|
setResourceFixedColor(resource, event)
|
||||||
|
;(event.target as HTMLInputElement).blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeImageResource(resourceId: number) {
|
||||||
|
const resources = sys.value?.config.imageResources
|
||||||
|
if (!resources || resources.length <= 1) return
|
||||||
|
const index = resources.findIndex((resource) => resource.id === resourceId)
|
||||||
|
if (index >= 0) resources.splice(index, 1)
|
||||||
|
rebalanceImageWeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundWeight(value: number) {
|
||||||
|
return Math.round(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WeightResource = { weight: number; locked: boolean }
|
||||||
|
|
||||||
|
function distributeImageWeights(resources: WeightResource[], total: number, proportional = false) {
|
||||||
|
if (!resources.length) return
|
||||||
|
const safeTotal = Math.max(0, roundWeight(total))
|
||||||
|
const sourceTotal = proportional
|
||||||
|
? resources.reduce((sum, resource) => sum + Math.max(0, Number(resource.weight) || 0), 0)
|
||||||
|
: 0
|
||||||
|
let assigned = 0
|
||||||
|
resources.forEach((resource, index) => {
|
||||||
|
const isLast = index === resources.length - 1
|
||||||
|
const next = isLast
|
||||||
|
? roundWeight(safeTotal - assigned)
|
||||||
|
: roundWeight(sourceTotal > 0
|
||||||
|
? safeTotal * Math.max(0, Number(resource.weight) || 0) / sourceTotal
|
||||||
|
: safeTotal / resources.length)
|
||||||
|
resource.weight = next
|
||||||
|
assigned += next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebalanceImageWeights() {
|
||||||
|
const resources = sys.value?.config.imageResources
|
||||||
|
if (!resources?.length) return
|
||||||
|
if (resources.length === 1) {
|
||||||
|
resources[0].weight = 100
|
||||||
|
resources[0].locked = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const locked = resources.filter((resource) => resource.locked)
|
||||||
|
const unlocked = resources.filter((resource) => !resource.locked)
|
||||||
|
let lockedTotal = locked.reduce((sum, resource) => sum + Math.max(0, Number(resource.weight) || 0), 0)
|
||||||
|
if (lockedTotal > 100) {
|
||||||
|
distributeImageWeights(locked, 100, true)
|
||||||
|
lockedTotal = 100
|
||||||
|
}
|
||||||
|
distributeImageWeights(unlocked, 100 - lockedTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setResourceWeight(resourceId: number, value: number) {
|
||||||
|
const resources = sys.value?.config.imageResources
|
||||||
|
if (!resources?.length) return
|
||||||
|
if (resources.length === 1) {
|
||||||
|
resources[0].weight = 100
|
||||||
|
resources[0].locked = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const resource = resources.find((item) => item.id === resourceId)
|
||||||
|
if (!resource || resource.locked) return
|
||||||
|
const lockedTotal = resources
|
||||||
|
.filter((item) => item.id !== resourceId && item.locked)
|
||||||
|
.reduce((sum, item) => sum + Math.max(0, Number(item.weight) || 0), 0)
|
||||||
|
const available = Math.max(0, 100 - lockedTotal)
|
||||||
|
const others = resources.filter((item) => item.id !== resourceId && !item.locked)
|
||||||
|
resource.weight = roundWeight(Math.min(available, Math.max(0, Number(value) || 0)))
|
||||||
|
if (!others.length) {
|
||||||
|
resource.weight = roundWeight(available)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
distributeImageWeights(others, available - resource.weight, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleResourceLock(resourceId: number) {
|
||||||
|
const resources = sys.value?.config.imageResources
|
||||||
|
if (!resources || resources.length === 1) return
|
||||||
|
const resource = resources.find((item) => item.id === resourceId)
|
||||||
|
if (resource) resource.locked = !resource.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return { textureFileInput, sequenceFileInput, pendingResourceId, trailTextureFileInput, pendingTrailResourceId, addTrailResource, openTrailTexturePicker, onTrailRigChange, onTrailResourceBoneCountChange, onTrailResourceRotationChange, onTrailTextureFile, resetTrailTexture, removeTrailResource, rebalanceTrailWeights, setTrailResourceWeight, toggleTrailResourceLock, setTrailColor, commitTrailColor, addImageResource, openTexturePicker, openSequencePicker, onTextureFile, onSequenceFiles, readFileAsDataUrl, resetTexture, normalizeHex, setResourceFixedColor, commitResourceFixedColor, removeImageResource, roundWeight, distributeImageWeights, rebalanceImageWeights, setResourceWeight, toggleResourceLock }
|
||||||
|
}
|
||||||
|
import { LINEAR_CURVE, FLAT_CURVE, ALPHA_CURVE, RESOURCE_ALPHA_CURVE, TRAIL_ALPHA_CURVE, TRAIL_SHAPE_CURVE } from './particlePanelCurves'
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
import { computed, markRaw, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue'
|
||||||
|
import { Texture } from 'pixi.js'
|
||||||
|
import { useParticleStore } from '../../store/particleStore'
|
||||||
|
import type { ParticleSystem } from '../../store/particleStore'
|
||||||
|
import { defaultConfig, ensureEmitterConfig } from '../../core/particleEmitter'
|
||||||
|
import NumSlider from '../NumSlider.vue'
|
||||||
|
import CurveEditor from '../CurveEditor.vue'
|
||||||
|
import ParticleAttributeControl from '../ParticleAttributeControl.vue'
|
||||||
|
import ColorGradientEditor from '../ColorGradientEditor.vue'
|
||||||
|
import ImageResourceCard from '../ImageResourceCard.vue'
|
||||||
|
import SpineObjectPanel from '../../spine/SpineObjectPanel.vue'
|
||||||
|
import { downloadEditorConfig, loadEditorConfigFile, loadEditorConfigText } from '../../editor/editorConfig'
|
||||||
|
import { beginTransfer, readResponseTextWithProgress } from '../../editor/transferProgress'
|
||||||
|
import { defaultSpineExportSettings, ensureSpineExportSettings } from '../../export/spineExportSettings'
|
||||||
|
import { buildSpineJson, downloadSpineJson } from '../../export/spineJsonExporter'
|
||||||
|
import { validateSpineJson } from '../../export/spineJsonValidator'
|
||||||
|
import { buildSpineImagesArchive, downloadSpineImagesArchive } from '../../export/spineImagesExporter'
|
||||||
|
import {
|
||||||
|
SpineConnectorError,
|
||||||
|
checkSpineConnector,
|
||||||
|
convertWithSpineConnector,
|
||||||
|
downloadSpineProject,
|
||||||
|
type SpineConnectorHealth,
|
||||||
|
} from '../../export/spineConnectorClient'
|
||||||
|
import { automaticLoopTransitionFrames } from '../../core/loopSegmentTiming'
|
||||||
|
|
||||||
|
|
||||||
|
export function useProjectPresets(store: ReturnType<typeof useParticleStore>) {
|
||||||
|
const configFileInput = ref<HTMLInputElement | null>(null)
|
||||||
|
const selectedPreset = ref('')
|
||||||
|
const presetOptions = ref<Array<{ name: string; file: string }>>([])
|
||||||
|
const presetLoading = ref(false)
|
||||||
|
const configMessage = ref('')
|
||||||
|
const configError = ref(false)
|
||||||
|
function saveConfig() {
|
||||||
|
downloadEditorConfig(store)
|
||||||
|
configError.value = false
|
||||||
|
configMessage.value = '配置已保存到本地'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onConfigFileChange(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement
|
||||||
|
const file = input.files?.[0]
|
||||||
|
if (!file) return
|
||||||
|
configMessage.value = '正在加载配置…'
|
||||||
|
configError.value = false
|
||||||
|
try {
|
||||||
|
await loadEditorConfigFile(store, file)
|
||||||
|
configMessage.value = `已加载:${file.name}`
|
||||||
|
} catch (error) {
|
||||||
|
configError.value = true
|
||||||
|
configMessage.value = error instanceof Error ? error.message : String(error)
|
||||||
|
} finally {
|
||||||
|
input.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshPresets() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/Preset/index.json?t=${Date.now()}`, { cache: 'no-store' })
|
||||||
|
if (!response.ok) throw new Error('无法读取预设列表')
|
||||||
|
const presets = await response.json()
|
||||||
|
presetOptions.value = Array.isArray(presets)
|
||||||
|
? presets.filter((item): item is { name: string; file: string } => !!item && typeof item.name === 'string' && typeof item.file === 'string')
|
||||||
|
: []
|
||||||
|
} catch {
|
||||||
|
presetOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSelectedPreset() {
|
||||||
|
const file = selectedPreset.value
|
||||||
|
if (!file) return
|
||||||
|
presetLoading.value = true
|
||||||
|
configError.value = false
|
||||||
|
configMessage.value = '正在加载预设…'
|
||||||
|
const transfer = beginTransfer('加载预设', `正在下载 ${file}…`)
|
||||||
|
try {
|
||||||
|
await transfer.paint()
|
||||||
|
const response = await fetch(`/Preset/${encodeURIComponent(file)}`, { cache: 'no-store' })
|
||||||
|
if (!response.ok) throw new Error(`无法读取预设:${file}`)
|
||||||
|
const text = await readResponseTextWithProgress(response, (progress) => {
|
||||||
|
transfer.update(progress == null ? null : progress * 55, `正在下载 ${file}…`)
|
||||||
|
})
|
||||||
|
transfer.update(58, '正在解析预设配置…')
|
||||||
|
transfer.update(70, '正在解析并恢复预设资源…')
|
||||||
|
await loadEditorConfigText(store, text)
|
||||||
|
configMessage.value = `已加载预设:${file.replace(/\.json$/i, '')}`
|
||||||
|
await transfer.finish('预设加载完成')
|
||||||
|
} catch (error) {
|
||||||
|
configError.value = true
|
||||||
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
configMessage.value = `${message},请检查预设文件命名`
|
||||||
|
await transfer.fail(message)
|
||||||
|
} finally {
|
||||||
|
presetLoading.value = false
|
||||||
|
selectedPreset.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(refreshPresets)
|
||||||
|
|
||||||
|
return { configFileInput, selectedPreset, presetOptions, presetLoading, configMessage, configError, saveConfig, onConfigFileChange, refreshPresets, loadSelectedPreset }
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { Text, type Application, type Container, type Graphics } from 'pixi.js'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { useParticleStore, CollisionBody, ScenePath } from '../../store/particleStore'
|
||||||
|
import type { ParticleState } from '../../core/particleTypes'
|
||||||
|
import type { SpineRuntimeLayer } from '../../spine/SpineRuntimeLayer'
|
||||||
|
import type { BonePreview, SceneTransformTarget } from './stageTypes'
|
||||||
|
import { pathLocalToWorld } from './stageGeometry'
|
||||||
|
type EditorStore = ReturnType<typeof useParticleStore>
|
||||||
|
|
||||||
|
// 自适应刻度步进(仿时间轴):返回 1/2/5 序列里的最合适步长,保证屏幕间距合理
|
||||||
|
export function niceStep(target: number, minStep = 0): number {
|
||||||
|
if (target <= 0) return 1
|
||||||
|
const pow = Math.pow(10, Math.floor(Math.log10(target)))
|
||||||
|
const candidates = [1, 2, 5, 10, 20, 50, 100]
|
||||||
|
let best = 1
|
||||||
|
for (const c of candidates) {
|
||||||
|
if (c * pow >= target) { best = c * pow; break }
|
||||||
|
}
|
||||||
|
return Math.max(best, minStep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 画无限长 x/y 轴 + 自适应整数刻度 + 数值(0,0 在画布中心)
|
||||||
|
export function drawAxis(store: EditorStore, app: Application | null, axisGfx: Graphics | null, axisLabelLayer: Container | null, axisLabels: Text[]) {
|
||||||
|
if (!axisGfx) return
|
||||||
|
const g = axisGfx
|
||||||
|
g.clear()
|
||||||
|
const S = store.settings
|
||||||
|
if (!S.tickEnabled) { // 未启用刻度:隐藏整个坐标轴(轴+刻度+数值)
|
||||||
|
for (const t of axisLabels) t.visible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const cx = 0, cy = 0 // 世界原点(中心)
|
||||||
|
const halfW = app!.screen.width / store.editor.viewScale
|
||||||
|
const halfH = app!.screen.height / store.editor.viewScale
|
||||||
|
const axisColor = Number('0x' + S.axisColor.replace('#', ''))
|
||||||
|
// 轴线(贯穿到视口边缘外)
|
||||||
|
g.lineStyle(1.5, axisColor, 0.35)
|
||||||
|
g.moveTo(-halfW, cy); g.lineTo(halfW, cy) // x 轴
|
||||||
|
g.moveTo(cx, -halfH); g.lineTo(cx, halfH) // y 轴
|
||||||
|
// 刻度步进:目标 ~50px 一个刻度(世界单位 = px/viewScale)
|
||||||
|
const sc = store.editor.viewScale
|
||||||
|
const step = niceStep(50 / sc)
|
||||||
|
// 数值显示范围:只在中心 ±range 内标数值(避免远端堆积/干扰);范围随视口半宽
|
||||||
|
const rangeW = halfW
|
||||||
|
const rangeH = halfH
|
||||||
|
// 刻度线(贯穿整个视口;仅启用刻度时画)
|
||||||
|
const tickW = Math.max(1, S.tickWidth)
|
||||||
|
if (S.tickEnabled) {
|
||||||
|
for (let x = 0; x <= halfW; x += step) {
|
||||||
|
const v = Math.round(x / step) * step
|
||||||
|
if (v === 0) continue
|
||||||
|
g.lineStyle(tickW, axisColor, 0.25)
|
||||||
|
g.moveTo(v, cy - 4); g.lineTo(v, cy + 4)
|
||||||
|
g.moveTo(-v, cy - 4); g.lineTo(-v, cy + 4)
|
||||||
|
}
|
||||||
|
for (let y = 0; y <= halfH; y += step) {
|
||||||
|
const v = Math.round(y / step) * step
|
||||||
|
if (v === 0) continue
|
||||||
|
g.lineStyle(tickW, axisColor, 0.25)
|
||||||
|
g.moveTo(cx - 4, y); g.lineTo(cx + 4, y)
|
||||||
|
g.moveTo(cx - 4, -y); g.lineTo(cx + 4, -y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 数值标签(文字放 axisLabelLayer,不随 world 缩放,尺寸/清晰度恒定)
|
||||||
|
// 只在中心 ±range 内标注数值,超出不显示,避免看不清/干扰
|
||||||
|
const cxS = app!.screen.width / 2 + store.editor.viewX
|
||||||
|
const cyS = app!.screen.height * 0.5 + store.editor.viewY
|
||||||
|
const fs = S.axisFontSize // 字号由系统设置手动控制(默认 12px)
|
||||||
|
const labelColor = Number('0x' + S.axisColor.replace('#', ''))
|
||||||
|
while (axisLabels.length < 64) {
|
||||||
|
const t = new Text('', { fontFamily: 'monospace', fontSize: fs, fill: labelColor })
|
||||||
|
t.anchor.set(0.5, 0.5)
|
||||||
|
axisLabelLayer!.addChild(t)
|
||||||
|
axisLabels.push(t)
|
||||||
|
}
|
||||||
|
let li = 0
|
||||||
|
const showLbl = S.showAxisLabels && S.tickEnabled
|
||||||
|
// x 轴正/负数值(放在轴下方一点)
|
||||||
|
for (let x = 0; x <= halfW; x += step) {
|
||||||
|
const v = Math.round(x / step) * step
|
||||||
|
if (v === 0 || !showLbl || v > rangeW) continue
|
||||||
|
const lbl = axisLabels[li++]; if (!lbl) break
|
||||||
|
lbl.style.fontSize = fs
|
||||||
|
lbl.text = String(v); lbl.position.set(cxS + v * sc, cyS + 9); lbl.visible = true
|
||||||
|
const lvb = axisLabels[li++]; if (!lvb) break
|
||||||
|
lvb.style.fontSize = fs
|
||||||
|
lvb.text = String(-v); lvb.position.set(cxS - v * sc, cyS + 9); lvb.visible = true
|
||||||
|
}
|
||||||
|
// y 轴(数值放在轴右侧)
|
||||||
|
for (let y = 0; y <= halfH; y += step) {
|
||||||
|
const v = Math.round(y / step) * step
|
||||||
|
if (v === 0 || !showLbl || v > rangeH) continue
|
||||||
|
const lbl = axisLabels[li++]; if (!lbl) break
|
||||||
|
lbl.style.fontSize = fs
|
||||||
|
lbl.text = String(v); lbl.position.set(cxS + 8, cyS - y * sc); lbl.visible = true
|
||||||
|
const lvb = axisLabels[li++]; if (!lvb) break
|
||||||
|
lvb.style.fontSize = fs
|
||||||
|
lvb.text = String(-v); lvb.position.set(cxS + 8, cyS + y * sc); lvb.visible = true
|
||||||
|
}
|
||||||
|
// 隐藏多余标签
|
||||||
|
while (li < axisLabels.length) { const t = axisLabels[li++]; if (t) t.visible = false }
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import type { Application, Container, Graphics, Text } from 'pixi.js'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { useParticleStore, CollisionBody, ScenePath } from '../../store/particleStore'
|
||||||
|
import type { ParticleState } from '../../core/particleTypes'
|
||||||
|
import type { SpineRuntimeLayer } from '../../spine/SpineRuntimeLayer'
|
||||||
|
import type { BonePreview, SceneTransformTarget } from './stageTypes'
|
||||||
|
import { pathLocalToWorld } from './stageGeometry'
|
||||||
|
type EditorStore = ReturnType<typeof useParticleStore>
|
||||||
|
|
||||||
|
|
||||||
|
export function appendStateBones(target: BonePreview[], state: ParticleState, parentX: number, parentY: number) {
|
||||||
|
// 粒子快照同时保存局部坐标和烘焙世界坐标。骨骼调试图层挂在 world 下,必须使用
|
||||||
|
// 世界坐标,才能与发生位移、旋转、缩放或跟随后的粒子保持逐点重合。
|
||||||
|
const hasWorldPosition = Number.isFinite(state.worldX) && Number.isFinite(state.worldY)
|
||||||
|
const hasWorldRotation = Number.isFinite(state.worldRotation)
|
||||||
|
target.push({
|
||||||
|
boneName: state.boneName,
|
||||||
|
x: hasWorldPosition ? state.worldX! : state.x,
|
||||||
|
y: hasWorldPosition ? -state.worldY! : state.y,
|
||||||
|
rotation: hasWorldRotation ? -state.worldRotation! : -state.rotation,
|
||||||
|
parentX,
|
||||||
|
parentY,
|
||||||
|
})
|
||||||
|
if (!state.trailState?.bones.length) return
|
||||||
|
state.trailState.bones.forEach((bone) => {
|
||||||
|
// 导出结构中拖尾骨骼全部直接挂在粒子系统骨骼下,画布连线也保持同级语义。
|
||||||
|
const hasBoneWorldPosition = Number.isFinite(bone.worldX) && Number.isFinite(bone.worldY)
|
||||||
|
const hasBoneWorldRotation = Number.isFinite(bone.worldRotation)
|
||||||
|
target.push({
|
||||||
|
boneName: bone.boneName,
|
||||||
|
x: hasBoneWorldPosition ? bone.worldX! : bone.x,
|
||||||
|
y: hasBoneWorldPosition ? -bone.worldY! : bone.y,
|
||||||
|
rotation: hasBoneWorldRotation ? -bone.worldRotation! : bone.rotation,
|
||||||
|
parentX,
|
||||||
|
parentY,
|
||||||
|
trail: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function drawDirectionalAxis(
|
||||||
|
g: Graphics,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
directionX: number,
|
||||||
|
directionY: number,
|
||||||
|
length: number,
|
||||||
|
width: number,
|
||||||
|
color: number,
|
||||||
|
) {
|
||||||
|
const negativeLength = length * 0.62
|
||||||
|
const tipX = x + directionX * length
|
||||||
|
const tipY = y + directionY * length
|
||||||
|
const headLength = Math.max(2.5, length * 0.32)
|
||||||
|
const headWidth = Math.max(1.7, length * 0.22)
|
||||||
|
const baseX = tipX - directionX * headLength
|
||||||
|
const baseY = tipY - directionY * headLength
|
||||||
|
const perpendicularX = -directionY
|
||||||
|
const perpendicularY = directionX
|
||||||
|
g.lineStyle(width, color, 0.95)
|
||||||
|
g.moveTo(x - directionX * negativeLength, y - directionY * negativeLength)
|
||||||
|
g.lineTo(baseX, baseY)
|
||||||
|
g.lineStyle(0)
|
||||||
|
g.beginFill(color, 0.95)
|
||||||
|
g.drawPolygon([
|
||||||
|
tipX, tipY,
|
||||||
|
baseX + perpendicularX * headWidth, baseY + perpendicularY * headWidth,
|
||||||
|
baseX - perpendicularX * headWidth, baseY - perpendicularY * headWidth,
|
||||||
|
])
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function drawBones(bones: BonePreview[], store: EditorStore, boneGfx: Graphics | null) {
|
||||||
|
if (!boneGfx) return
|
||||||
|
const g = boneGfx
|
||||||
|
g.clear()
|
||||||
|
if (!store.settings.showBones) return
|
||||||
|
const cx = 0, cy = 0
|
||||||
|
const viewScale = Math.max(0.25, Math.abs(store.editor.viewScale) || 1)
|
||||||
|
const pointSize = Math.min(10, Math.max(0.1, Number(store.settings.bonePointDrawSize) || 1))
|
||||||
|
const axisSize = Math.min(10, Math.max(0.1, Number(store.settings.boneAxisDrawSize) || 2))
|
||||||
|
// 所有尺寸先定义为屏幕像素,再除以画布缩放,保证缩放画布时视觉大小不变。
|
||||||
|
const pointRadius = 2.5 * pointSize / viewScale
|
||||||
|
const forwardLength = 4.5 * axisSize / viewScale
|
||||||
|
const forwardWidth = Math.max(0.65, axisSize * 0.625) / viewScale
|
||||||
|
// root → 每颗粒子骨骼 的连线
|
||||||
|
for (const b of bones) {
|
||||||
|
const color = b.trail ? 0xff4d8d : 0xff8c5a
|
||||||
|
g.lineStyle(1, color, b.trail ? 0.65 : 0.35)
|
||||||
|
g.moveTo(b.parentX ?? cx, b.parentY ?? cy)
|
||||||
|
g.lineTo(b.x, b.y)
|
||||||
|
// 局部坐标轴为两根交叉轴线,仅在 +X、+Y 端绘制箭头。
|
||||||
|
const angle = b.rotation * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
drawDirectionalAxis(g, b.x, b.y, cos, sin, forwardLength, forwardWidth, 0xef5350)
|
||||||
|
drawDirectionalAxis(g, b.x, b.y, sin, -cos, forwardLength, forwardWidth, 0x55c96b)
|
||||||
|
// 骨骼点
|
||||||
|
g.beginFill(color, 0.9)
|
||||||
|
g.drawCircle(b.x, b.y, pointRadius)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import type { Application, Container, Graphics, Text } from 'pixi.js'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { useParticleStore, CollisionBody, ScenePath } from '../../store/particleStore'
|
||||||
|
import type { ParticleState } from '../../core/particleTypes'
|
||||||
|
import type { SpineRuntimeLayer } from '../../spine/SpineRuntimeLayer'
|
||||||
|
import type { BonePreview, SceneTransformTarget } from './stageTypes'
|
||||||
|
import { pathLocalToWorld } from './stageGeometry'
|
||||||
|
type EditorStore = ReturnType<typeof useParticleStore>
|
||||||
|
import { normalizedRadialRange, normalizedRectRange } from '../../core/emitterShape'
|
||||||
|
|
||||||
|
|
||||||
|
export function drawOrigin(store: EditorStore, emitterPreviewTransform: Map<number, { x: number; y: number; rotation: number }>, originGfx: Graphics | null) {
|
||||||
|
if (!originGfx) return
|
||||||
|
const g = originGfx
|
||||||
|
g.clear()
|
||||||
|
if (!store.settings.showEmitter) return
|
||||||
|
// 每个系统对应一个发射点(坐标点);没有系统时不画
|
||||||
|
if (!store.systems.length) return
|
||||||
|
for (const sys of store.systems) {
|
||||||
|
if (sys.visible === false) continue
|
||||||
|
const preview = emitterPreviewTransform.get(sys.id)
|
||||||
|
const cx = preview?.x ?? sys.config.centerX ?? 0
|
||||||
|
const cy = -(preview?.y ?? sys.config.centerY ?? 0)
|
||||||
|
const active = store.activeObjectType === 'particle' && sys.id === store.activeId
|
||||||
|
const col = active ? 0xffcc33 : 0x8a94b8
|
||||||
|
const opacity = active ? 0.4 : 0.2
|
||||||
|
g.lineStyle(1, col, opacity)
|
||||||
|
g.drawCircle(cx, cy, 10)
|
||||||
|
g.beginFill(col, active ? 0.8 : 0.4)
|
||||||
|
g.drawCircle(cx, cy, 3)
|
||||||
|
g.endFill()
|
||||||
|
g.lineStyle(1, col, active ? 0.3 : 0.15)
|
||||||
|
g.moveTo(cx - 16, cy); g.lineTo(cx + 16, cy)
|
||||||
|
g.moveTo(cx, cy - 16); g.lineTo(cx, cy + 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制当前选中系统的发射器形状范围(点/路径不额外绘制),随参数实时响应。
|
||||||
|
export function drawShapeRange(store: EditorStore, emitterPreviewTransform: Map<number, { x: number; y: number; rotation: number }>, shapeGfx: Graphics | null) {
|
||||||
|
if (!shapeGfx) return
|
||||||
|
const g = shapeGfx
|
||||||
|
g.clear()
|
||||||
|
if (!store.settings.showEmitter) return
|
||||||
|
const sys = store.systems.find((s) => s.id === store.activeId)
|
||||||
|
if (!sys || sys.visible === false) return
|
||||||
|
const c = sys.config
|
||||||
|
const preview = emitterPreviewTransform.get(sys.id)
|
||||||
|
// root 中心(发射器容器 position = centerX/-centerY),数学坐标
|
||||||
|
const cx = preview?.x ?? c.centerX ?? 0
|
||||||
|
const cy = -(preview?.y ?? c.centerY ?? 0)
|
||||||
|
const shape = c.shape
|
||||||
|
if (shape === 'point' || shape === 'path') return
|
||||||
|
// 相对 root 中心的局部点(lx,ly,数学坐标)先加偏移(发射源 = root 中心 + offset),再应用 root 缩放+旋转,加 root 中心 → 世界坐标
|
||||||
|
const sxr = c.rootScaleX ?? 1, syr = c.rootScaleY ?? 1
|
||||||
|
const rot = -(preview?.rotation ?? c.rootRotation ?? 0) * Math.PI / 180 // 数学上正 → 屏幕:逆时针为正
|
||||||
|
const cs = Math.cos(rot), sn = Math.sin(rot)
|
||||||
|
const oxo = c.offsetX ?? 0, oyo = c.offsetY ?? 0
|
||||||
|
const pt = (lx: number, ly: number): [number, number] => {
|
||||||
|
// 对齐粒子:局部 y 为数学上正 → 屏幕取负;发射源偏移 offsetY(数学上正)→ 屏幕也取负
|
||||||
|
const X = lx + oxo, Y = -ly - oyo
|
||||||
|
const x = X * sxr, y = Y * syr
|
||||||
|
return [cx + x * cs - y * sn, cy + (x * sn + y * cs)]
|
||||||
|
}
|
||||||
|
if (shape === 'circle') {
|
||||||
|
const range = normalizedRadialRange(c.innerRadius, c.outerRadius)
|
||||||
|
g.lineStyle(2, 0x3ce86a, 0.7)
|
||||||
|
// 圆环:以 root 变换后的椭圆近似(逐点)
|
||||||
|
const steps = 48
|
||||||
|
for (const radius of [range.outer, range.inner]) {
|
||||||
|
if (radius <= 0) continue
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const a = i / steps * Math.PI * 2
|
||||||
|
const [px, py] = pt(Math.cos(a) * radius, Math.sin(a) * radius)
|
||||||
|
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (shape === 'rect') {
|
||||||
|
const range = normalizedRectRange(c)
|
||||||
|
g.lineStyle(2, 0x4a8cff, 0.7)
|
||||||
|
for (const [width, height] of [[range.outerW, range.outerH], [range.innerW, range.innerH]]) {
|
||||||
|
if (width <= 0 || height <= 0) continue
|
||||||
|
const hw = width / 2, hh = height / 2
|
||||||
|
const p1 = pt(-hw, -hh), p2 = pt(hw, -hh), p3 = pt(hw, hh), p4 = pt(-hw, hh)
|
||||||
|
g.moveTo(p1[0], p1[1]); g.lineTo(p2[0], p2[1]); g.lineTo(p3[0], p3[1]); g.lineTo(p4[0], p4[1]); g.lineTo(p1[0], p1[1])
|
||||||
|
}
|
||||||
|
} else if (shape === 'cone') {
|
||||||
|
const range = normalizedRadialRange(c.innerRadius, c.outerRadius)
|
||||||
|
const half = (c.coneAngle / 2) * Math.PI / 180
|
||||||
|
const base = c.coneDirection * Math.PI / 180
|
||||||
|
// 锥形环带的两条边、内弧和外弧。
|
||||||
|
const innerEdge1 = pt(Math.cos(base - half) * range.inner, Math.sin(base - half) * range.inner)
|
||||||
|
const innerEdge2 = pt(Math.cos(base + half) * range.inner, Math.sin(base + half) * range.inner)
|
||||||
|
const outerEdge1 = pt(Math.cos(base - half) * range.outer, Math.sin(base - half) * range.outer)
|
||||||
|
const outerEdge2 = pt(Math.cos(base + half) * range.outer, Math.sin(base + half) * range.outer)
|
||||||
|
g.lineStyle(2, 0x3ce86a, 0.7)
|
||||||
|
g.moveTo(innerEdge1[0], innerEdge1[1]); g.lineTo(outerEdge1[0], outerEdge1[1])
|
||||||
|
g.moveTo(innerEdge2[0], innerEdge2[1]); g.lineTo(outerEdge2[0], outerEdge2[1])
|
||||||
|
g.lineStyle(1, 0x3ce86a, 0.5)
|
||||||
|
const steps = 24
|
||||||
|
for (const radius of [range.outer, range.inner]) {
|
||||||
|
if (radius <= 0) continue
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const a = base - half + (2 * half * i / steps)
|
||||||
|
const [px, py] = pt(Math.cos(a) * radius, Math.sin(a) * radius)
|
||||||
|
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制当前选中系统的全局力场;不应用粒子系统 root 变换。 */
|
||||||
|
export function drawForceField(store: EditorStore, forceFieldGfx: Graphics | null) {
|
||||||
|
if (!forceFieldGfx) return
|
||||||
|
const g = forceFieldGfx
|
||||||
|
g.clear()
|
||||||
|
const sys = store.systems.find((item) => item.id === store.activeId)
|
||||||
|
if (sys?.visible === false) return
|
||||||
|
const cfg = sys?.config
|
||||||
|
if (!cfg?.forceField || cfg.forceRadius <= 0) return
|
||||||
|
const x = cfg.forceCenterX
|
||||||
|
const y = -cfg.forceCenterY
|
||||||
|
const color = cfg.forceStrength >= 0 ? 0x58c7ff : 0xff6b7a
|
||||||
|
g.lineStyle(2, color, 0.85)
|
||||||
|
g.beginFill(color, 0.08)
|
||||||
|
g.drawCircle(x, y, cfg.forceRadius)
|
||||||
|
g.endFill()
|
||||||
|
g.lineStyle(1, color, 0.7)
|
||||||
|
g.moveTo(x - 9, y); g.lineTo(x + 9, y)
|
||||||
|
g.moveTo(x, y - 9); g.lineTo(x, y + 9)
|
||||||
|
g.beginFill(color, 0.95)
|
||||||
|
g.drawCircle(x, y, 4)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制全局吸附点与吸附完成半径;半径为 0 时显示为红色点。 */
|
||||||
|
export function drawAttraction(store: EditorStore, attractionGfx: Graphics | null) {
|
||||||
|
if (!attractionGfx) return
|
||||||
|
const g = attractionGfx
|
||||||
|
g.clear()
|
||||||
|
const sys = store.systems.find((item) => item.id === store.activeId)
|
||||||
|
if (sys?.visible === false) return
|
||||||
|
const cfg = sys?.config
|
||||||
|
if (!cfg?.attraction) return
|
||||||
|
const x = cfg.attractionCenterX
|
||||||
|
const y = -cfg.attractionCenterY
|
||||||
|
const radius = Math.max(0, cfg.attractionRadius)
|
||||||
|
const color = 0xff4d5f
|
||||||
|
if (radius > 0) {
|
||||||
|
g.lineStyle(2, color, 0.95)
|
||||||
|
g.beginFill(color, 0.09)
|
||||||
|
g.drawCircle(x, y, radius)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
g.lineStyle(1, color, 0.85)
|
||||||
|
g.moveTo(x - 10, y); g.lineTo(x + 10, y)
|
||||||
|
g.moveTo(x, y - 10); g.lineTo(x, y + 10)
|
||||||
|
g.beginFill(color, 1)
|
||||||
|
g.drawCircle(x, y, radius > 0 ? 4 : 6)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
import type { Application, Container, Graphics, Text } from 'pixi.js'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { useParticleStore, CollisionBody, ScenePath } from '../../store/particleStore'
|
||||||
|
import type { ParticleState } from '../../core/particleTypes'
|
||||||
|
import type { SpineRuntimeLayer } from '../../spine/SpineRuntimeLayer'
|
||||||
|
import type { BonePreview, SceneTransformTarget } from './stageTypes'
|
||||||
|
import { pathLocalToWorld } from './stageGeometry'
|
||||||
|
type EditorStore = ReturnType<typeof useParticleStore>
|
||||||
|
|
||||||
|
/** 标出层级树当前选中的骨骼;发射器骨骼跟随复用同一目标点。 */
|
||||||
|
export function drawBoneFollowTargets(store: EditorStore, spineRuntime: SpineRuntimeLayer | null, boneFollowTargetGfx: Graphics | null) {
|
||||||
|
if (!boneFollowTargetGfx) return
|
||||||
|
const g = boneFollowTargetGfx
|
||||||
|
g.parent?.addChild(g)
|
||||||
|
g.clear()
|
||||||
|
const targets = new Map<string, { spineId: number; boneName: string }>()
|
||||||
|
for (const spine of store.spines) {
|
||||||
|
if (spine.visible === false || !spine.selectedBone || spine.status !== 'ready') continue
|
||||||
|
targets.set(`${spine.id}:${spine.selectedBone}`, { spineId: spine.id, boneName: spine.selectedBone })
|
||||||
|
}
|
||||||
|
for (const system of store.systems) {
|
||||||
|
if (system.visible === false) continue
|
||||||
|
const config = system.config
|
||||||
|
if (!config.emitterFollow || config.emitterFollowMode !== 'bone') continue
|
||||||
|
if (!config.emitterFollowSpineId || !config.emitterFollowBoneName) continue
|
||||||
|
const spine = store.spines.find((item) => item.id === config.emitterFollowSpineId)
|
||||||
|
if (!spine || spine.visible === false) continue
|
||||||
|
targets.set(`${config.emitterFollowSpineId}:${config.emitterFollowBoneName}`, {
|
||||||
|
spineId: config.emitterFollowSpineId,
|
||||||
|
boneName: config.emitterFollowBoneName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const item of targets.values()) {
|
||||||
|
const target = spineRuntime?.getBoneWorldTransform(item.spineId, item.boneName)
|
||||||
|
if (!target) continue
|
||||||
|
const x = target.x
|
||||||
|
const y = -target.y
|
||||||
|
g.lineStyle(2, 0xc48cff, 0.95)
|
||||||
|
g.beginFill(0xc48cff, 0.2)
|
||||||
|
g.drawCircle(x, y, 8)
|
||||||
|
g.endFill()
|
||||||
|
g.beginFill(0xf2e7ff, 1)
|
||||||
|
g.drawCircle(x, y, 3.5)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前场景对象的变换手柄:跟随其位置、旋转和缩放。
|
||||||
|
export function drawGizmo(activeTool: Ref<'' | 'translate' | 'rotate' | 'scale'>, selectedTransformTarget: () => SceneTransformTarget | null, gizmoGfx: Graphics | null) {
|
||||||
|
if (!gizmoGfx) return
|
||||||
|
const g = gizmoGfx
|
||||||
|
g.clear()
|
||||||
|
if (!activeTool.value) return
|
||||||
|
const target = selectedTransformTarget()
|
||||||
|
if (!target) return
|
||||||
|
const cx = target.x
|
||||||
|
const cy = -target.y
|
||||||
|
const sxr = target.scaleX, syr = target.scaleY
|
||||||
|
const rot = -target.rotation * Math.PI / 180
|
||||||
|
const cs = Math.cos(rot), sn = Math.sin(rot)
|
||||||
|
// 局部点(数学坐标,ly 上正) → 屏幕点(应用 root 缩放+旋转+平移到 root 中心)
|
||||||
|
const pt = (lx: number, ly: number): [number, number] => {
|
||||||
|
const x = lx * sxr, y = (-ly) * syr
|
||||||
|
return [cx + x * cs - y * sn, cy + (x * sn + y * cs)]
|
||||||
|
}
|
||||||
|
const line = (ax: number, ay: number, bx: number, by: number) => {
|
||||||
|
const a = pt(ax, ay), b = pt(bx, by)
|
||||||
|
g.moveTo(a[0], a[1]); g.lineTo(b[0], b[1])
|
||||||
|
}
|
||||||
|
const R = 26
|
||||||
|
// 中心点
|
||||||
|
g.lineStyle(2, 0x35d0ff, 0.9)
|
||||||
|
g.beginFill(0x35d0ff, 0.25)
|
||||||
|
g.drawCircle(cx, cy, 6)
|
||||||
|
g.endFill()
|
||||||
|
if (activeTool.value === 'translate') {
|
||||||
|
// 十字箭头
|
||||||
|
g.lineStyle(2, 0x35d0ff, 0.9)
|
||||||
|
line(-R, 0, R, 0); line(0, -R, 0, R)
|
||||||
|
line(R - 6, -4, R, 0); line(R, 0, R - 6, 4) // 右箭头
|
||||||
|
line(-(R - 6), -4, -R, 0); line(-R, 0, -(R - 6), 4) // 左箭头
|
||||||
|
line(-4, R - 6, 0, R); line(0, R, 4, R - 6) // 上箭头
|
||||||
|
line(-4, -(R - 6), 0, -R); line(0, -R, 4, -(R - 6)) // 下箭头
|
||||||
|
} else if (activeTool.value === 'rotate') {
|
||||||
|
// 旋转外圈(经缩放/旋转后可能是椭圆)
|
||||||
|
g.lineStyle(2, 0x35d0ff, 0.8)
|
||||||
|
const steps = 48
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const a = i / steps * Math.PI * 2
|
||||||
|
const [px, py] = pt(Math.cos(a) * R, Math.sin(a) * R)
|
||||||
|
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py)
|
||||||
|
}
|
||||||
|
// 指向点:固定于局部顶部(0° 刻度在 12 点方向),经 pt(含 root 旋转)后随 rootRotation 旋转。
|
||||||
|
// 默认(rootRotation=0)时指向点位于顶部;旋转时它随 root 一起转,反映当前旋转角。
|
||||||
|
const [tx, ty] = pt(0, R)
|
||||||
|
g.beginFill(0x35d0ff, 0.95)
|
||||||
|
g.drawCircle(tx, ty, 4)
|
||||||
|
g.endFill()
|
||||||
|
} else if (activeTool.value === 'scale') {
|
||||||
|
// 四角方点(经缩放/旋转后为旋转矩形)
|
||||||
|
g.lineStyle(2, 0x35d0ff, 0.9)
|
||||||
|
const corners: [number, number][] = [[-R, -R], [R, -R], [R, R], [-R, R]]
|
||||||
|
const pts = corners.map(([ox, oy]) => pt(ox, oy))
|
||||||
|
g.moveTo(pts[0][0], pts[0][1]); g.lineTo(pts[1][0], pts[1][1]); g.lineTo(pts[2][0], pts[2][1]); g.lineTo(pts[3][0], pts[3][1]); g.lineTo(pts[0][0], pts[0][1])
|
||||||
|
g.beginFill(0x35d0ff, 0.9)
|
||||||
|
for (const p of pts) g.drawRect(p[0] - 3, p[1] - 3, 6, 6)
|
||||||
|
g.endFill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function colorNumber(value: string, fallback = 0xffffff) {
|
||||||
|
const parsed = Number.parseInt(value.replace('#', ''), 16)
|
||||||
|
return Number.isFinite(parsed) ? parsed : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function drawCollisionBodies(store: EditorStore, previewColliders: CollisionBody[], collisionGfx: Graphics | null) {
|
||||||
|
if (!collisionGfx) return
|
||||||
|
const g = collisionGfx
|
||||||
|
g.clear()
|
||||||
|
for (const collider of previewColliders) {
|
||||||
|
if (collider.visible === false) continue
|
||||||
|
const selected = store.activeObjectType === 'collision' && store.activeObjectId === collider.id
|
||||||
|
const color = collider.enabled ? (selected ? 0xff5e72 : 0xd4475b) : 0x586174
|
||||||
|
const angle = collider.rotation * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(collider.scaleX ?? 1))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(collider.scaleY ?? 1))
|
||||||
|
const point = (x: number, y: number) => ({
|
||||||
|
x: collider.x + cos * x * scaleX - sin * y * scaleY,
|
||||||
|
y: -(collider.y + sin * x * scaleX + cos * y * scaleY),
|
||||||
|
})
|
||||||
|
let points: Array<{ x: number; y: number }> = []
|
||||||
|
if (collider.shape === 'circle' || collider.shape === 'ellipse') {
|
||||||
|
const radiusX = collider.shape === 'circle' ? collider.radius : collider.radiusX
|
||||||
|
const radiusY = collider.shape === 'circle' ? collider.radius : collider.radiusY
|
||||||
|
points = Array.from({ length: 49 }, (_, index) => {
|
||||||
|
const theta = index / 48 * Math.PI * 2
|
||||||
|
return point(Math.cos(theta) * radiusX, Math.sin(theta) * radiusY)
|
||||||
|
})
|
||||||
|
} else if (collider.shape === 'rect') {
|
||||||
|
const hw = collider.width * 0.5, hh = collider.height * 0.5
|
||||||
|
points = [point(-hw, -hh), point(hw, -hh), point(hw, hh), point(-hw, hh), point(-hw, -hh)]
|
||||||
|
} else {
|
||||||
|
const sides = Math.max(3, Math.min(16, Math.round(collider.sides)))
|
||||||
|
points = Array.from({ length: sides + 1 }, (_, index) => {
|
||||||
|
const theta = Math.PI * 0.5 + (index % sides) / sides * Math.PI * 2
|
||||||
|
return point(Math.cos(theta) * collider.width * 0.5, Math.sin(theta) * collider.height * 0.5)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!points.length) continue
|
||||||
|
g.lineStyle(selected ? 2.5 : 1.5, color, collider.enabled ? 0.95 : 0.45)
|
||||||
|
g.beginFill(color, selected ? 0.12 : 0.055)
|
||||||
|
g.moveTo(points[0].x, points[0].y)
|
||||||
|
for (let i = 1; i < points.length; i++) g.lineTo(points[i].x, points[i].y)
|
||||||
|
g.endFill()
|
||||||
|
const center = point(0, 0)
|
||||||
|
g.lineStyle(1, color, 0.8)
|
||||||
|
g.moveTo(center.x - 7, center.y); g.lineTo(center.x + 7, center.y)
|
||||||
|
g.moveTo(center.x, center.y - 7); g.lineTo(center.x, center.y + 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function drawPaths(store: EditorStore, selectedPathNodes: Set<number>, pathGfx: Graphics | null) {
|
||||||
|
if (!pathGfx) return
|
||||||
|
const g = pathGfx
|
||||||
|
g.clear()
|
||||||
|
for (const path of store.paths) {
|
||||||
|
if (path.visible === false || !path.enabled || !path.points.length) continue
|
||||||
|
const selected = store.activeObjectType === 'path' && store.activeObjectId === path.id
|
||||||
|
const color = colorNumber(path.color, 0xff933e)
|
||||||
|
const screenPoint = (x: number, y: number) => {
|
||||||
|
const worldPoint = pathLocalToWorld(path, x, y)
|
||||||
|
return { x: worldPoint.x, y: -worldPoint.y }
|
||||||
|
}
|
||||||
|
const first = path.points[0]
|
||||||
|
const start = screenPoint(first.x, first.y)
|
||||||
|
g.lineStyle(selected ? 2.5 : 1.8, color, selected ? 1 : 0.75)
|
||||||
|
g.moveTo(start.x, start.y)
|
||||||
|
const segmentCount = path.closed ? path.points.length : path.points.length - 1
|
||||||
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
|
const a = path.points[i]
|
||||||
|
const b = path.points[(i + 1) % path.points.length]
|
||||||
|
const controlA = screenPoint(a.x + a.outX, a.y + a.outY)
|
||||||
|
const controlB = screenPoint(b.x + b.inX, b.y + b.inY)
|
||||||
|
const end = screenPoint(b.x, b.y)
|
||||||
|
g.bezierCurveTo(controlA.x, controlA.y, controlB.x, controlB.y, end.x, end.y)
|
||||||
|
}
|
||||||
|
if (!selected) continue
|
||||||
|
path.points.forEach((anchor, index) => {
|
||||||
|
const point = screenPoint(anchor.x, anchor.y)
|
||||||
|
const input = screenPoint(anchor.x + anchor.inX, anchor.y + anchor.inY)
|
||||||
|
const output = screenPoint(anchor.x + anchor.outX, anchor.y + anchor.outY)
|
||||||
|
const hasInput = Math.hypot(anchor.inX, anchor.inY) > 0.01
|
||||||
|
const hasOutput = Math.hypot(anchor.outX, anchor.outY) > 0.01
|
||||||
|
g.lineStyle(1, 0xa9b4cb, 0.65)
|
||||||
|
if (hasInput) { g.moveTo(point.x, point.y); g.lineTo(input.x, input.y) }
|
||||||
|
if (hasOutput) { g.moveTo(point.x, point.y); g.lineTo(output.x, output.y) }
|
||||||
|
if (hasInput) { g.beginFill(0xd5dcec, 0.95); g.drawCircle(input.x, input.y, 3); g.endFill() }
|
||||||
|
if (hasOutput) { g.beginFill(0xd5dcec, 0.95); g.drawCircle(output.x, output.y, 3); g.endFill() }
|
||||||
|
g.beginFill(selectedPathNodes.has(index) ? 0xffffff : color, 1)
|
||||||
|
g.lineStyle(1.5, selectedPathNodes.has(index) ? 0x5d7cff : 0xffffff, 0.95)
|
||||||
|
g.drawCircle(point.x, point.y, selectedPathNodes.has(index) ? 5 : 4)
|
||||||
|
g.endFill()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import type { ScenePath } from '../../store/particleStore'
|
||||||
|
|
||||||
|
|
||||||
|
export function pathLocalToWorld(path: ScenePath, x: number, y: number) {
|
||||||
|
const angle = path.rotation * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(path.scaleX ?? 1))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(path.scaleY ?? 1))
|
||||||
|
return { x: path.x + cos * x * scaleX - sin * y * scaleY, y: path.y + sin * x * scaleX + cos * y * scaleY }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function worldToPathLocal(path: ScenePath, x: number, y: number) {
|
||||||
|
const angle = path.rotation * Math.PI / 180
|
||||||
|
const cos = Math.cos(angle), sin = Math.sin(angle)
|
||||||
|
const scaleX = Math.max(0.0001, Math.abs(path.scaleX ?? 1))
|
||||||
|
const scaleY = Math.max(0.0001, Math.abs(path.scaleY ?? 1))
|
||||||
|
const dx = x - path.x, dy = y - path.y
|
||||||
|
return { x: (cos * dx + sin * dy) / scaleX, y: (-sin * dx + cos * dy) / scaleY }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type FollowCurvePoint = { x: number; y: number; inX?: number; inY?: number; outX?: number; outY?: number }
|
||||||
|
|
||||||
|
|
||||||
|
export function followCurveValue(points: FollowCurvePoint[], t: number, mode: 'linear' | 'bezier' = 'linear') {
|
||||||
|
if (!points.length) return t
|
||||||
|
const sorted = [...points].sort((a, b) => a.x - b.x)
|
||||||
|
if (t <= sorted[0].x) return sorted[0].y
|
||||||
|
for (let index = 1; index < sorted.length; index++) {
|
||||||
|
const a = sorted[index - 1], b = sorted[index]
|
||||||
|
if (t <= b.x) {
|
||||||
|
let amount = (t - a.x) / Math.max(0.0001, b.x - a.x)
|
||||||
|
if (mode === 'bezier') {
|
||||||
|
const x1 = a.x + (a.outX ?? (b.x - a.x) / 3)
|
||||||
|
const x2 = b.x + (b.inX ?? -(b.x - a.x) / 3)
|
||||||
|
let low = 0, high = 1
|
||||||
|
for (let step = 0; step < 18; step++) {
|
||||||
|
const middle = (low + high) / 2
|
||||||
|
if (cubicPoint(a.x, x1, x2, b.x, middle) < t) low = middle
|
||||||
|
else high = middle
|
||||||
|
}
|
||||||
|
amount = (low + high) / 2
|
||||||
|
return cubicPoint(a.y, a.y + (a.outY ?? (b.y - a.y) / 3), b.y + (b.inY ?? -(b.y - a.y) / 3), b.y, amount)
|
||||||
|
}
|
||||||
|
return a.y + (b.y - a.y) * amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sorted[sorted.length - 1].y
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function cubicPoint(a: number, b: number, c: number, d: number, t: number) {
|
||||||
|
const u = 1 - t
|
||||||
|
return u * u * u * a + 3 * u * u * t * b + 3 * u * t * t * c + t * t * t * d
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
// 粒子→骨骼映射:每个活动粒子 = 一根挂在 root 下的骨骼
|
||||||
|
export type BonePreview = { boneName: string; x: number; y: number; rotation: number; parentX?: number; parentY?: number; trail?: boolean }
|
||||||
|
|
||||||
|
|
||||||
|
export type SceneTransformTarget = {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
rotation: number
|
||||||
|
scaleX: number
|
||||||
|
scaleY: number
|
||||||
|
setPosition: (x: number, y: number) => void
|
||||||
|
setRotation: (rotation: number) => void
|
||||||
|
setScale: (scaleX: number, scaleY: number) => void
|
||||||
|
}
|
||||||
Reference in new issue
Block a user