| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- 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")!
- this.maxCount = fillerData.data.maxAreaCountOfGroup
- 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)
- }
- }
- }
- }
|