| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- import { coverRadius, fillRectangle } from "../base/2d";
- import { Animator } from "../base/Animator";
- import { m4 } from "../base/m4";
- import { Disposable, Scene } from "../base/Scene";
- import { createProgram, createShader } from "../base/utils";
- import { Area, Color } from "./common";
- import { FillerData } from "./FillerData";
- export class AnimatingArea {
- constructor(
- readonly area: Area,
- readonly x: number,
- readonly y: number,
- public progress: number,
- ) {}
- }
- export class AnimatableMask implements Disposable {
- private vertexShaderCode = /*glsl*/ `
- attribute vec2 a_position;
- attribute vec2 a_texCoord;
-
- attribute vec4 a_areaId;
- attribute vec2 a_center;
- attribute float a_progress;
- attribute float a_maxRadius;
-
- uniform mat4 u_matrix;
-
- varying vec4 v_areaId;
- varying vec2 v_center;
- varying float v_progress;
- varying float v_maxRadius;
- varying vec2 v_texCoord;
- varying vec2 v_position;
- void main() {
- gl_Position = u_matrix * vec4(a_position, 0, 1);
- v_areaId = a_areaId;
- v_center = a_center;
- v_progress = a_progress;
- v_maxRadius = a_maxRadius;
- v_texCoord = a_texCoord;
- v_position = a_position;
-
- }
- `;
- private fragmentShaderCode = /*glsl*/ `
- precision mediump float;
- uniform sampler2D u_map;
- //uniform sampler2D u_colored;
- //uniform sampler2D u_mask;
- varying vec4 v_areaId;
- varying vec2 v_center;
- varying float v_progress;
- varying float v_maxRadius;
- varying vec2 v_texCoord;
- varying vec2 v_position;
-
- void main() {
- vec4 map = texture2D(u_map, v_texCoord);
- float dist = distance(map, v_areaId);
- /*
- if(dist < 0.001) {
- if(v_progress >= 1.0 ) {
- gl_FragColor = vec4(1, 1, 0, 1);
- }else{
- float dist2 = distance(v_position, v_center);
- if(dist2 < v_maxRadius * v_progress) {
- gl_FragColor = vec4(1, 1, 0, 1);
- }else{
- gl_FragColor = vec4(0, 0, 0, 0);
- }
- }
- }else{
- gl_FragColor = vec4(0, 0, 0, 0);
- }
- */
- //gl_FragColor = vec4(1, 1, 0, 1);
- //gl_FragColor = map;
- vec4 colored = vec4(1, 1, 0, 1);
-
- if(dist < 0.001) {
- if( v_progress < 1.0 ) {
- float dist2 = distance(v_position, v_center);
- float r = v_maxRadius * v_progress + 0.001;
- if(dist2 < r) {
- float f = dist2 / r;
- if(f < v_progress) {
- gl_FragColor = colored;
- }else if(v_progress < 1.0){
- float a = (f - 1.0) / (v_progress - 1.0);
- gl_FragColor = vec4(colored.xyz, a);
- }else{
- gl_FragColor = colored;
- }
- }else {
- gl_FragColor = vec4(0,0,0,0);
- }
- }else{
- gl_FragColor = colored;
- }
- }else{
- gl_FragColor = vec4(0, 0, 0, 0);
- }
- }
-
- `;
- program: WebGLProgram;
- aPositionLoc: number;
- aTexcoordLoc: number;
- aAreaIdLoc: number;
- aCenterLoc: number;
- aProgressLoc: number;
- aMaxRadiusLoc: number;
- uMatrixLoc: WebGLUniformLocation;
- positionBuffer: WebGLBuffer;
- texCoordBuffer: WebGLBuffer;
- areaIdBuffer: WebGLBuffer;
- centerBuffer: WebGLBuffer;
- progressBuffer: WebGLBuffer;
- maxRadiusBuffer: WebGLBuffer;
- dispose(): void {
- let gl = this.scene.gl;
- gl.deleteBuffer(this.positionBuffer);
- gl.deleteBuffer(this.texCoordBuffer);
- gl.deleteBuffer(this.areaIdBuffer);
- gl.deleteBuffer(this.centerBuffer);
- gl.deleteBuffer(this.progressBuffer);
- gl.deleteBuffer(this.maxRadiusBuffer);
- gl.deleteProgram(this.program);
- }
- positionArray: Float32Array;
- texCoordArray: Float32Array;
- areaIdArray: Float32Array;
- centerArray: Float32Array;
- progressArray: Float32Array;
- maxRadiusArray: Float32Array;
- matrix: m4.Matrix4;
- texture: WebGLTexture;
- fb: WebGLFramebuffer;
- private animatingAreas: Array<AnimatingArea> = [];
- get width(): number {
- return this.fillerData.width;
- }
- get height(): number {
- return this.fillerData.height;
- }
- maxCount: number;
- constructor(
- private scene: Scene,
- private fillerData: FillerData,
- ) {
- const gl = scene.gl;
- this.program = createProgram(
- gl,
- createShader(gl, gl.VERTEX_SHADER, this.vertexShaderCode)!,
- createShader(gl, gl.FRAGMENT_SHADER, this.fragmentShaderCode)!,
- )!;
- this.aPositionLoc = gl.getAttribLocation(this.program, "a_position");
- this.aTexcoordLoc = gl.getAttribLocation(this.program, "a_texCoord");
- this.aAreaIdLoc = gl.getAttribLocation(this.program, "a_areaId");
- this.aCenterLoc = gl.getAttribLocation(this.program, "a_center");
- this.aProgressLoc = gl.getAttribLocation(this.program, "a_progress");
- this.aMaxRadiusLoc = gl.getAttribLocation(this.program, "a_maxRadius");
- this.uMatrixLoc = gl.getUniformLocation(this.program, "u_matrix")!;
- // 此处maxCount 等于分组最大区块数并不合理,假设每个颜色分组都是一个区块, 此处maxCount会等于1, 如果用户快速点击多个区块,会造成上一个区块的晕染无法完成
- // this.maxCount = fillerData.data.maxAreaCountOfGroup;
- this.maxCount = 5;
- this.positionBuffer = gl.createBuffer()!;
- this.texCoordBuffer = gl.createBuffer()!;
- this.areaIdBuffer = gl.createBuffer()!;
- this.centerBuffer = gl.createBuffer()!;
- this.progressBuffer = gl.createBuffer()!;
- this.maxRadiusBuffer = gl.createBuffer()!;
- this.matrix = m4.projectionNoflipY(fillerData.width, fillerData.height);
- this.texture = gl.createTexture()!;
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texImage2D(
- gl.TEXTURE_2D,
- 0,
- gl.RGBA,
- this.fillerData.width,
- this.fillerData.height,
- 0,
- gl.RGBA,
- gl.UNSIGNED_BYTE,
- null,
- );
- //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.fillerData.width, this.fillerData.height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, null)
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- this.fb = gl.createFramebuffer()!;
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb);
- gl.framebufferTexture2D(
- gl.FRAMEBUFFER,
- gl.COLOR_ATTACHMENT0,
- gl.TEXTURE_2D,
- this.texture,
- 0,
- );
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.positionArray = new Float32Array(this.maxCount * 12);
- this.texCoordArray = new Float32Array(this.maxCount * 12);
- this.areaIdArray = new Float32Array(this.maxCount * 24);
- this.centerArray = new Float32Array(this.maxCount * 12);
- this.progressArray = new Float32Array(this.maxCount * 6);
- this.maxRadiusArray = new Float32Array(this.maxCount * 6);
- }
- addArea(area: Area, cx: number, cy: number, duration = 800) {
- let aa = new AnimatingArea(area, cx, cy, 0);
- let animator = new Animator(
- duration,
- () => {
- aa.progress = animator.value();
- },
- () => {},
- );
- this.scene.addAnimator(animator);
- this.animatingAreas.push(aa);
- }
- fillPoint(
- arr: Float32Array,
- offset: number,
- x: number,
- y: number,
- count: number,
- ) {
- for (var i = 0; i < count; i++) {
- arr[offset + i * 2] = x;
- arr[offset + i * 2 + 1] = y;
- }
- }
- fillNumber(arr: Float32Array, offset: number, n: number, count: number) {
- for (var i = 0; i < count; i++) {
- arr[offset + i] = n;
- }
- }
- flush() {
- console.log("animationAreas.length=", this.animatingAreas.length);
- if (this.animatingAreas.length <= 0) return;
- var vertexOffset = 0;
- var colorOffset = 0;
- var nOffset = 0;
- const width = this.fillerData.width;
- const height = this.fillerData.height;
- for (var i = 0; i < this.animatingAreas.length; i++) {
- var aa = this.animatingAreas[i];
- var area = aa.area;
- fillRectangle(
- this.positionArray,
- vertexOffset,
- area.rect.x,
- area.rect.y,
- area.rect.width,
- area.rect.height,
- );
- fillRectangle(
- this.texCoordArray,
- vertexOffset,
- area.rect.x / width,
- area.rect.y / height,
- area.rect.width / width,
- area.rect.height / height,
- );
- this.fillPoint(this.centerArray, vertexOffset, aa.x, aa.y, 6);
- var color = new Color(area.id);
- color.fillFloatArray(this.areaIdArray, colorOffset, 6);
- var maxRadius = coverRadius(area.rect, aa.x, aa.y);
- // console.log('rect=', area.rect, maxRadius, aa.x, aa.y);
- //maxRadius =
- this.fillNumber(this.progressArray, nOffset, aa.progress, 6);
- this.fillNumber(this.maxRadiusArray, nOffset, maxRadius, 6);
- vertexOffset += 12;
- colorOffset += 24;
- nOffset += 6;
- }
- //console.log('areaIdArray', this.areaIdArray)
- const gl = this.scene.gl;
- gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.positionArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.texCoordArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.areaIdArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.centerArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.progressArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.maxRadiusArray, gl.STATIC_DRAW);
- this.draw(this.animatingAreas.length * 6);
- this.animatingAreas = this.animatingAreas.filter((aa) => aa.progress < 1);
- }
- private draw(n: number) {
- const gl = this.scene.gl;
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb);
- //console.log('fb=', this.progressArray)
- gl.useProgram(this.program);
- gl.viewport(0, 0, this.fillerData.width, this.fillerData.height);
- gl.enableVertexAttribArray(this.aPositionLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
- gl.vertexAttribPointer(this.aPositionLoc, 2, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(this.aTexcoordLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
- gl.vertexAttribPointer(this.aTexcoordLoc, 2, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(this.aAreaIdLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
- gl.vertexAttribPointer(this.aAreaIdLoc, 4, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(this.aCenterLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
- gl.vertexAttribPointer(this.aCenterLoc, 2, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(this.aProgressLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
- gl.vertexAttribPointer(this.aProgressLoc, 1, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(this.aMaxRadiusLoc);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
- gl.vertexAttribPointer(this.aMaxRadiusLoc, 1, gl.FLOAT, false, 0, 0);
- gl.uniformMatrix4fv(this.uMatrixLoc, false, this.matrix);
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, this.fillerData.mapTexure);
- gl.drawArrays(gl.TRIANGLES, 0, n);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- }
- // 恢复上次已填色的部分
- initTask() {
- let taskLisk = this.fillerData.taskList;
- const width = this.fillerData.width;
- const height = this.fillerData.height;
- for (let task of taskLisk) {
- let area = this.fillerData.data.areaHash.get(task);
- if (area) {
- fillRectangle(
- this.positionArray,
- 0,
- area.rect.x,
- area.rect.y,
- area.rect.width,
- area.rect.height,
- );
- fillRectangle(
- this.texCoordArray,
- 0,
- area.rect.x / width,
- area.rect.y / height,
- area.rect.width / width,
- area.rect.height / height,
- );
- this.fillPoint(this.centerArray, 0, area.center.x, area.center.y, 6);
- var color = new Color(area.id);
- color.fillFloatArray(this.areaIdArray, 0, 6);
- var maxRadius = coverRadius(area.rect, area.center.x, area.center.y);
- // console.log('rect=', area.rect, maxRadius, area.center.x, area.center.y);
- this.fillNumber(this.progressArray, 0, 1, 6);
- this.fillNumber(this.maxRadiusArray, 0, maxRadius, 6);
- const gl = this.scene.gl;
- gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.positionArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.texCoordArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.areaIdBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.areaIdArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.centerArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.progressBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.progressArray, gl.STATIC_DRAW);
- gl.bindBuffer(gl.ARRAY_BUFFER, this.maxRadiusBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.maxRadiusArray, gl.STATIC_DRAW);
- this.draw(6);
- }
- }
- }
- }
|