AnimatableMask.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. import { coverRadius, fillRectangle } from "../base/2d";
  2. import { Animator } from "../base/Animator";
  3. import { m4 } from "../base/m4";
  4. import { Disposable, Scene } from "../base/Scene";
  5. import { createProgram, createShader } from "../base/utils";
  6. import { Area, Color } from "./common";
  7. import { FillerData } from "./FillerData";
  8. export class AnimatingArea {
  9. constructor(
  10. readonly area: Area,
  11. readonly x: number,
  12. readonly y: number,
  13. public progress: number,
  14. ) {}
  15. }
  16. export class AnimatableMask implements Disposable {
  17. private vertexShaderCode = /*glsl*/ `
  18. attribute vec2 a_position;
  19. attribute vec2 a_texCoord;
  20. attribute vec4 a_areaId;
  21. attribute vec2 a_center;
  22. attribute float a_progress;
  23. attribute float a_maxRadius;
  24. uniform mat4 u_matrix;
  25. varying vec4 v_areaId;
  26. varying vec2 v_center;
  27. varying float v_progress;
  28. varying float v_maxRadius;
  29. varying vec2 v_texCoord;
  30. varying vec2 v_position;
  31. void main() {
  32. gl_Position = u_matrix * vec4(a_position, 0, 1);
  33. v_areaId = a_areaId;
  34. v_center = a_center;
  35. v_progress = a_progress;
  36. v_maxRadius = a_maxRadius;
  37. v_texCoord = a_texCoord;
  38. v_position = a_position;
  39. }
  40. `;
  41. private fragmentShaderCode = /*glsl*/ `
  42. precision mediump float;
  43. uniform sampler2D u_map;
  44. //uniform sampler2D u_colored;
  45. //uniform sampler2D u_mask;
  46. varying vec4 v_areaId;
  47. varying vec2 v_center;
  48. varying float v_progress;
  49. varying float v_maxRadius;
  50. varying vec2 v_texCoord;
  51. varying vec2 v_position;
  52. void main() {
  53. vec4 map = texture2D(u_map, v_texCoord);
  54. float dist = distance(map, v_areaId);
  55. /*
  56. if(dist < 0.001) {
  57. if(v_progress >= 1.0 ) {
  58. gl_FragColor = vec4(1, 1, 0, 1);
  59. }else{
  60. float dist2 = distance(v_position, v_center);
  61. if(dist2 < v_maxRadius * v_progress) {
  62. gl_FragColor = vec4(1, 1, 0, 1);
  63. }else{
  64. gl_FragColor = vec4(0, 0, 0, 0);
  65. }
  66. }
  67. }else{
  68. gl_FragColor = vec4(0, 0, 0, 0);
  69. }
  70. */
  71. //gl_FragColor = vec4(1, 1, 0, 1);
  72. //gl_FragColor = map;
  73. vec4 colored = vec4(1, 1, 0, 1);
  74. if(dist < 0.001) {
  75. if( v_progress < 1.0 ) {
  76. float dist2 = distance(v_position, v_center);
  77. float r = v_maxRadius * v_progress + 0.001;
  78. if(dist2 < r) {
  79. float f = dist2 / r;
  80. if(f < v_progress) {
  81. gl_FragColor = colored;
  82. }else if(v_progress < 1.0){
  83. float a = (f - 1.0) / (v_progress - 1.0);
  84. gl_FragColor = vec4(colored.xyz, a);
  85. }else{
  86. gl_FragColor = colored;
  87. }
  88. }else {
  89. gl_FragColor = vec4(0,0,0,0);
  90. }
  91. }else{
  92. gl_FragColor = colored;
  93. }
  94. }else{
  95. gl_FragColor = vec4(0, 0, 0, 0);
  96. }
  97. }
  98. `;
  99. program: WebGLProgram;
  100. aPositionLoc: number;
  101. aTexcoordLoc: number;
  102. aAreaIdLoc: number;
  103. aCenterLoc: number;
  104. aProgressLoc: number;
  105. aMaxRadiusLoc: number;
  106. uMatrixLoc: WebGLUniformLocation;
  107. positionBuffer: WebGLBuffer;
  108. texCoordBuffer: WebGLBuffer;
  109. areaIdBuffer: WebGLBuffer;
  110. centerBuffer: WebGLBuffer;
  111. progressBuffer: WebGLBuffer;
  112. maxRadiusBuffer: WebGLBuffer;
  113. dispose(): void {
  114. let gl = this.scene.gl;
  115. gl.deleteBuffer(this.positionBuffer);
  116. gl.deleteBuffer(this.texCoordBuffer);
  117. gl.deleteBuffer(this.areaIdBuffer);
  118. gl.deleteBuffer(this.centerBuffer);
  119. gl.deleteBuffer(this.progressBuffer);
  120. gl.deleteBuffer(this.maxRadiusBuffer);
  121. gl.deleteProgram(this.program);
  122. }
  123. positionArray: Float32Array;
  124. texCoordArray: Float32Array;
  125. areaIdArray: Float32Array;
  126. centerArray: Float32Array;
  127. progressArray: Float32Array;
  128. maxRadiusArray: Float32Array;
  129. matrix: m4.Matrix4;
  130. texture: WebGLTexture;
  131. fb: WebGLFramebuffer;
  132. private animatingAreas: Array<AnimatingArea> = [];
  133. get width(): number {
  134. return this.fillerData.width;
  135. }
  136. get height(): number {
  137. return this.fillerData.height;
  138. }
  139. maxCount: number;
  140. constructor(
  141. private scene: Scene,
  142. private fillerData: FillerData,
  143. ) {
  144. const gl = scene.gl;
  145. this.program = createProgram(
  146. gl,
  147. createShader(gl, gl.VERTEX_SHADER, this.vertexShaderCode)!,
  148. createShader(gl, gl.FRAGMENT_SHADER, this.fragmentShaderCode)!,
  149. )!;
  150. this.aPositionLoc = gl.getAttribLocation(this.program, "a_position");
  151. this.aTexcoordLoc = gl.getAttribLocation(this.program, "a_texCoord");
  152. this.aAreaIdLoc = gl.getAttribLocation(this.program, "a_areaId");
  153. this.aCenterLoc = gl.getAttribLocation(this.program, "a_center");
  154. this.aProgressLoc = gl.getAttribLocation(this.program, "a_progress");
  155. this.aMaxRadiusLoc = gl.getAttribLocation(this.program, "a_maxRadius");
  156. this.uMatrixLoc = gl.getUniformLocation(this.program, "u_matrix")!;
  157. // 此处maxCount 等于分组最大区块数并不合理,假设每个颜色分组都是一个区块, 此处maxCount会等于1, 如果用户快速点击多个区块,会造成上一个区块的晕染无法完成
  158. // this.maxCount = fillerData.data.maxAreaCountOfGroup;
  159. this.maxCount = 5;
  160. this.positionBuffer = gl.createBuffer()!;
  161. this.texCoordBuffer = gl.createBuffer()!;
  162. this.areaIdBuffer = gl.createBuffer()!;
  163. this.centerBuffer = gl.createBuffer()!;
  164. this.progressBuffer = gl.createBuffer()!;
  165. this.maxRadiusBuffer = gl.createBuffer()!;
  166. this.matrix = m4.projectionNoflipY(fillerData.width, fillerData.height);
  167. this.texture = gl.createTexture()!;
  168. gl.bindTexture(gl.TEXTURE_2D, this.texture);
  169. gl.texImage2D(
  170. gl.TEXTURE_2D,
  171. 0,
  172. gl.RGBA,
  173. this.fillerData.width,
  174. this.fillerData.height,
  175. 0,
  176. gl.RGBA,
  177. gl.UNSIGNED_BYTE,
  178. null,
  179. );
  180. //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.fillerData.width, this.fillerData.height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, null)
  181. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  182. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  183. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  184. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  185. this.fb = gl.createFramebuffer()!;
  186. gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb);
  187. gl.framebufferTexture2D(
  188. gl.FRAMEBUFFER,
  189. gl.COLOR_ATTACHMENT0,
  190. gl.TEXTURE_2D,
  191. this.texture,
  192. 0,
  193. );
  194. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  195. this.positionArray = new Float32Array(this.maxCount * 12);
  196. this.texCoordArray = new Float32Array(this.maxCount * 12);
  197. this.areaIdArray = new Float32Array(this.maxCount * 24);
  198. this.centerArray = new Float32Array(this.maxCount * 12);
  199. this.progressArray = new Float32Array(this.maxCount * 6);
  200. this.maxRadiusArray = new Float32Array(this.maxCount * 6);
  201. }
  202. addArea(area: Area, cx: number, cy: number, duration = 800) {
  203. let aa = new AnimatingArea(area, cx, cy, 0);
  204. let animator = new Animator(
  205. duration,
  206. () => {
  207. aa.progress = animator.value();
  208. },
  209. () => {},
  210. );
  211. this.scene.addAnimator(animator);
  212. this.animatingAreas.push(aa);
  213. }
  214. fillPoint(
  215. arr: Float32Array,
  216. offset: number,
  217. x: number,
  218. y: number,
  219. count: number,
  220. ) {
  221. for (var i = 0; i < count; i++) {
  222. arr[offset + i * 2] = x;
  223. arr[offset + i * 2 + 1] = y;
  224. }
  225. }
  226. fillNumber(arr: Float32Array, offset: number, n: number, count: number) {
  227. for (var i = 0; i < count; i++) {
  228. arr[offset + i] = n;
  229. }
  230. }
  231. flush() {
  232. console.log("animationAreas.length=", this.animatingAreas.length);
  233. if (this.animatingAreas.length <= 0) return;
  234. var vertexOffset = 0;
  235. var colorOffset = 0;
  236. var nOffset = 0;
  237. const width = this.fillerData.width;
  238. const height = this.fillerData.height;
  239. for (var i = 0; i < this.animatingAreas.length; i++) {
  240. var aa = this.animatingAreas[i];
  241. var area = aa.area;
  242. fillRectangle(
  243. this.positionArray,
  244. vertexOffset,
  245. area.rect.x,
  246. area.rect.y,
  247. area.rect.width,
  248. area.rect.height,
  249. );
  250. fillRectangle(
  251. this.texCoordArray,
  252. vertexOffset,
  253. area.rect.x / width,
  254. area.rect.y / height,
  255. area.rect.width / width,
  256. area.rect.height / height,
  257. );
  258. this.fillPoint(this.centerArray, vertexOffset, aa.x, aa.y, 6);
  259. var color = new Color(area.id);
  260. color.fillFloatArray(this.areaIdArray, colorOffset, 6);
  261. var maxRadius = coverRadius(area.rect, aa.x, aa.y);
  262. // console.log('rect=', area.rect, maxRadius, aa.x, aa.y);
  263. //maxRadius =
  264. this.fillNumber(this.progressArray, nOffset, aa.progress, 6);
  265. this.fillNumber(this.maxRadiusArray, nOffset, maxRadius, 6);
  266. vertexOffset += 12;
  267. colorOffset += 24;
  268. nOffset += 6;
  269. }
  270. //console.log('areaIdArray', this.areaIdArray)
  271. const gl = this.scene.gl;
  272. gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
  273. gl.bufferData(gl.ARRAY_BUFFER, this.positionArray, gl.STATIC_DRAW);
  274. gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
  275. gl.bufferData(gl.ARRAY_BUFFER, this.texCoordArray, gl.STATIC_DRAW);
  276. gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
  277. gl.bufferData(gl.ARRAY_BUFFER, this.areaIdArray, gl.STATIC_DRAW);
  278. gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
  279. gl.bufferData(gl.ARRAY_BUFFER, this.centerArray, gl.STATIC_DRAW);
  280. gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
  281. gl.bufferData(gl.ARRAY_BUFFER, this.progressArray, gl.STATIC_DRAW);
  282. gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
  283. gl.bufferData(gl.ARRAY_BUFFER, this.maxRadiusArray, gl.STATIC_DRAW);
  284. this.draw(this.animatingAreas.length * 6);
  285. this.animatingAreas = this.animatingAreas.filter((aa) => aa.progress < 1);
  286. }
  287. private draw(n: number) {
  288. const gl = this.scene.gl;
  289. gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb);
  290. //console.log('fb=', this.progressArray)
  291. gl.useProgram(this.program);
  292. gl.viewport(0, 0, this.fillerData.width, this.fillerData.height);
  293. gl.enableVertexAttribArray(this.aPositionLoc);
  294. gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
  295. gl.vertexAttribPointer(this.aPositionLoc, 2, gl.FLOAT, false, 0, 0);
  296. gl.enableVertexAttribArray(this.aTexcoordLoc);
  297. gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
  298. gl.vertexAttribPointer(this.aTexcoordLoc, 2, gl.FLOAT, false, 0, 0);
  299. gl.enableVertexAttribArray(this.aAreaIdLoc);
  300. gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
  301. gl.vertexAttribPointer(this.aAreaIdLoc, 4, gl.FLOAT, false, 0, 0);
  302. gl.enableVertexAttribArray(this.aCenterLoc);
  303. gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
  304. gl.vertexAttribPointer(this.aCenterLoc, 2, gl.FLOAT, false, 0, 0);
  305. gl.enableVertexAttribArray(this.aProgressLoc);
  306. gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
  307. gl.vertexAttribPointer(this.aProgressLoc, 1, gl.FLOAT, false, 0, 0);
  308. gl.enableVertexAttribArray(this.aMaxRadiusLoc);
  309. gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
  310. gl.vertexAttribPointer(this.aMaxRadiusLoc, 1, gl.FLOAT, false, 0, 0);
  311. gl.uniformMatrix4fv(this.uMatrixLoc, false, this.matrix);
  312. gl.activeTexture(gl.TEXTURE0);
  313. gl.bindTexture(gl.TEXTURE_2D, this.fillerData.mapTexure);
  314. gl.drawArrays(gl.TRIANGLES, 0, n);
  315. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  316. }
  317. // 恢复上次已填色的部分
  318. initTask() {
  319. let taskLisk = this.fillerData.taskList;
  320. const width = this.fillerData.width;
  321. const height = this.fillerData.height;
  322. for (let task of taskLisk) {
  323. let area = this.fillerData.data.areaHash.get(task);
  324. if (area) {
  325. fillRectangle(
  326. this.positionArray,
  327. 0,
  328. area.rect.x,
  329. area.rect.y,
  330. area.rect.width,
  331. area.rect.height,
  332. );
  333. fillRectangle(
  334. this.texCoordArray,
  335. 0,
  336. area.rect.x / width,
  337. area.rect.y / height,
  338. area.rect.width / width,
  339. area.rect.height / height,
  340. );
  341. this.fillPoint(this.centerArray, 0, area.center.x, area.center.y, 6);
  342. var color = new Color(area.id);
  343. color.fillFloatArray(this.areaIdArray, 0, 6);
  344. var maxRadius = coverRadius(area.rect, area.center.x, area.center.y);
  345. // console.log('rect=', area.rect, maxRadius, area.center.x, area.center.y);
  346. this.fillNumber(this.progressArray, 0, 1, 6);
  347. this.fillNumber(this.maxRadiusArray, 0, maxRadius, 6);
  348. const gl = this.scene.gl;
  349. gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
  350. gl.bufferData(gl.ARRAY_BUFFER, this.positionArray, gl.STATIC_DRAW);
  351. gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
  352. gl.bufferData(gl.ARRAY_BUFFER, this.texCoordArray, gl.STATIC_DRAW);
  353. gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
  354. gl.bufferData(gl.ARRAY_BUFFER, this.areaIdArray, gl.STATIC_DRAW);
  355. gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
  356. gl.bufferData(gl.ARRAY_BUFFER, this.centerArray, gl.STATIC_DRAW);
  357. gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
  358. gl.bufferData(gl.ARRAY_BUFFER, this.progressArray, gl.STATIC_DRAW);
  359. gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
  360. gl.bufferData(gl.ARRAY_BUFFER, this.maxRadiusArray, gl.STATIC_DRAW);
  361. this.draw(6);
  362. }
  363. }
  364. }
  365. }