
import { DEG_TO_RAD, RAD_TO_DEG } from "../../2d/const";
import { Matrix } from "../../2d/math";
import { WebglRenderer } from "../../2d/renderers/WebglRenderer";
import { Texture } from "../../2d/texture";
import TextureMatrix from "../../2d/texture/TextureMatrix";
import { GLShader } from "../../glCore";
import { Vector2 } from "../math/Vector2";
import { ShaderMaterial, UniformType } from "./ShaderMaterial";

const sprite3dVertexShader =
    `precision mediump float;
attribute vec3 aPosition;
attribute vec2 aTextureCoord;
varying vec2 vTextureCoord;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat4 uModelMatrix;

uniform vec2 center;

uniform float rotation;

uniform mat3 uvTransform;

void main() {
    vTextureCoord = ( uvTransform * vec3( aTextureCoord, 1 ) ).xy;
    vec4 mvPosition = uViewMatrix * uModelMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
    vec2 scale;
	scale.x = length( vec3( uModelMatrix[ 0 ].x, uModelMatrix[ 0 ].y, uModelMatrix[ 0 ].z ) );
	scale.y = length( vec3( uModelMatrix[ 1 ].x, uModelMatrix[ 1 ].y, uModelMatrix[ 1 ].z ) );
    
    #ifndef USE_SIZEATTENUATION
        bool isPerspective = ( uProjectionMatrix[ 2 ][ 3 ] == - 1.0 );
        if ( isPerspective ) scale *= - mvPosition.z;
    #endif

    vec2 alignedPosition = ( aPosition.xy - ( center - vec2( 0.5 ) ) ) * scale;

	vec2 rotatedPosition;
	rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
	rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;

	mvPosition.xy += rotatedPosition;

	gl_Position = uProjectionMatrix * mvPosition;
}`;
const sprite3dFragmentShader =
    `precision mediump float;
uniform vec3 color;
uniform float alpha;
uniform sampler2D map;
uniform vec4 uFrameUvs;
varying vec2 vTextureCoord;
void main() {
    vec4 mapColor = texture2D( map, vTextureCoord );
    vec2 s = step(vec2(uFrameUvs.x,uFrameUvs.y),vTextureCoord) - step(vec2(uFrameUvs.z,uFrameUvs.w),vTextureCoord);
    mapColor *= abs(s.x * s.y);
    gl_FragColor = vec4( color*alpha, alpha )*mapColor;
}`;

export class Sprite3dMaterial extends ShaderMaterial {
    sizeAttenuation: boolean = true;
    constructor() {
        super(sprite3dVertexShader, sprite3dFragmentShader, {
            center: { type: UniformType.vector2, value: new Vector2(0.5, 0.5) },
            rotation: { type: UniformType.float, value: 0.0 },
            color: { type: UniformType.color, value: 0xffffff },
            alpha: { type: UniformType.float, value: 1.0 },
            map: { type: UniformType.texture, value: Texture.WHITE },
            uvTransform: { type: UniformType.matrix3, value: new Matrix() },
            uFrameUvs: { type: UniformType.any, value: [0, 0, 1, 1] },
        });
        this._instanceType = "Sprite3dMaterial";
    }
    getShader(renderer: WebglRenderer): GLShader {
        const id = "" + renderer.CONTEXT_UID + this.sizeAttenuation;
        if (!this.shaders[id]) {
            //生成
            this.shaders[id] = new GLShader(
                renderer.gl,
                (this.sizeAttenuation ? '#define USE_SIZEATTENUATION\n' : "") + this.vertexShader,
                this.fragmentShader
            );
            this.shaders[id]["_glShaderKey"] = `customShader${"id" + id}${"instanceId" + this.instanceId}${"sizeAttenuation" + this.sizeAttenuation}`;
        };
        return this.shaders[id];
    }
    get center() {
        return this.uniforms.center.value;
    }
    set center(v: Vector2) {
        this.uniforms.center.value = v;
    }
    get rotation() {
        return this.uniforms.rotation.value * RAD_TO_DEG;
    }
    set rotation(v: number) {
        this.uniforms.rotation.value = v * DEG_TO_RAD;
    }
    get color() {
        return this.uniforms.color.value;
    }
    set color(v: number) {
        this.uniforms.color.value = v;
    }
    get alpha() {
        return this.uniforms.alpha.value;
    }
    set alpha(v: number) {
        this.uniforms.alpha.value = v;
    }
    get map() {
        return this.uniforms.map.value;
    }
    set map(v: Texture) {
        this.uniforms.map.value = v;
        //纹理坐标矩阵
        //没加的加一个
        if (!v.transform) v.transform = new TextureMatrix(v);
        //更新下
        v.transform.update();
        //赋值偏移
        (this.uniforms.uvTransform.value as Matrix).copy(v.transform.mapCoord);
        //对于透明像素有裁切的
        const { x0, y0, x2, y2 } = v._uvs;
        this.uniforms.uFrameUvs.value = [x0, y0, x2, y2];
    }
}