import ObjectRenderer from "../2d/renderers/webgl/ObjectRenderer";
import { WebglRenderer } from "../2d/renderers/WebglRenderer";
import { Camera } from "./cameras/Camera";
import { LightsConfig, ShadowType } from "./Scene3D";
import { GLShader, GLBuffer, VertexArrayObject } from "../glCore";
import { Mesh3D } from "./Mesh3D";
import { BaseShader } from "./shaders/BaseShader";
import { LightShader } from "./shaders/LightShader";
import { hex2rgb, rgb2hex } from "../2d/utils";
import { Matrix4 } from "./math/Matrix4";
import { BLEND_MODES } from "../2d/const";
import { BatchBuffer } from "../2d/renderers/webgl/BatchBuffer";
import { BaseMaterial, RenderSideType } from "./materials/BaseMaterial";
import { getCusShader } from "./shaders/getCusShader";
import { Geometry, VaoBufferInt } from ".";
import { SkinnedMesh3D } from "./bones/SkinnedMesh3D";
import { nextPow2 } from "../2d/utils"
import { BaseTexture, Texture } from "../2d/texture";
import { Vector3 } from "./math/Vector3";
import { UniformType } from "./materials/ShaderMaterial";
import RenderTarget from "../2d/renderers/renderTarget/RenderTarget";
import { ShadowShader } from "./shaders/ShadowShader";
import BaseRenderTexture from "../2d/texture/BaseRenderTexture";
import RenderTexture from "../2d/texture/RenderTexture";
import { OrthographicCamera } from "./cameras/OrthographicCamera";
import TextureMatrix from "../2d/texture/TextureMatrix";

let tempVec3 = new Vector3()
let tempMatrix4 = new Matrix4();
//需要管理渲染的状态 resetToDefault  resetTo3D
export class D3Renderer extends ObjectRenderer {
    /**
     * 赋值的相机，需要里面的worldMatrixInverse和projectionMatrix
     */
    camera: Camera;
    /**
     * 灯光数据，用来初始化着色器和着色器传值
     */
    lightsConfig: LightsConfig;
    /**
     * 雾化参数
     */
    fog: { color: Float32Array, near: number, far: number /*density: number*/ };
    /**
     * 视窗的方法
     */
    viewport: { x: number, y: number, width: number, height: number };
    /**
     * 阴影类型
     */
    shadowType: ShadowType;
    /**
     * 顶点着色器可传的最大uniform通道
     */
    maxVertexUniforms: number
    floatVertexTextures: boolean;
    private curLightkey: string;
    constructor(renderer: WebglRenderer) {
        super(renderer);
    }
    onContextChange() {
        var gl = this.renderer.gl;
        this.maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
        var maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
        var vertexTextures = maxVertexTextures > 0;
        var floatFragmentTextures = !!gl.getExtension("OES_texture_float");
        //传入的数据纹理暂时有问题，是UNPACK_ALIGNMENT还是UNPACK_FLIP_Y_WEBGL的问题未知，所以不管先
        this.floatVertexTextures = false//vertexTextures && floatFragmentTextures;
    }
    start() {
        //设置3d状态机属性
        // this.renderer.state.resetTo3D();
        //自行设置状态机，原先的有太多问题
        //允许深度检测
        this.renderer.state.setDepthTest(1);
        //混色模式重新设置
        // this.renderer.state.setBlendMode(BLEND_MODES.NORMAL_NPM);//让着色器里预乘
        //设置逆时针为正面，下面根据材质自行设置
        //this.renderer.state.setFrontFace(0);
        //设置剔除，下面根据材质自行设置
        //this.setCullFace(1);
    }
    stop() {
        this.flush();
        //原先的视窗需要重新//讲道理应该渲染器自己搞，算了
        this.renderer._activeRenderTarget.activate();
        // this.renderer.rootRenderTarget.activate();

        this.renderer.state.setState(this.renderer.state.defaultState)
    }

    private meshes: Mesh3D[] = [];
    //继续需要的数据
    render(obj: Mesh3D) {
        if (!obj.geometry || !obj.material) return;
        var mat = obj.material;
        var geo = obj.geometry;
        //提取渲染对象及材质，分组渲染，怎么考虑同一个geometry只需要处理一次attr，
        //分组按
        // if (!this.meshes.length) {
        //     this.meshes.push(obj);
        // } else {
        for (var i = 0; i < this.meshes.length; i++) {
            if (this.meshes[i].material == mat) {//没啥软用
                this.meshes.splice(i, 0, obj)
                return
            }
        }
        // }
        this.meshes.push(obj);
    }
    flush() {
        //生成一张阴影的深度贴图
        var shadow = getShadowMap(this.lightsConfig, this.meshes, this.renderer);
        if (shadow) {
            //绑定过帧缓存，viewport肯定被改了,该回去
            this.renderer.gl.viewport(
                this.viewport.x,
                this.viewport.y,
                this.viewport.width,
                this.viewport.height
            );
            //加阴影类型
            shadow.shadowType = this.shadowType;
        }

        //同一材质的模型一起处理，同一个着色器，webglRenderer里会判断，不会重复绑定
        var curShader: GLShader = null;
        var pointLightNum = this.lightsConfig.pointLights.length;
        var dirLightNum = this.lightsConfig.directionalLights.length;
        var gl: WebGLRenderingContext = this.renderer.gl;
        var textureManager = this.renderer.textureManager;
        for (var i = 0; i < this.meshes.length; i++) {
            let mesh = this.meshes[i];//怎么判断是否要重新绑定
            let mat: BaseMaterial = mesh.material;
            let geo = mesh.geometry;
            //自定义着色器用自行处理uniform;
            if (mat["isShaderMaterial"]) {
                let customShader = mat["getShader"](this.renderer);
                let uniforms = customShader.uniforms;
                let cusUniforms = mat["uniforms"];
                if (curShader !== customShader) {
                    curShader = customShader;
                    this.renderer.bindShader(curShader, false);
                    //这两个默认得加上
                    if (uniforms["uViewMatrix"]) uniforms["uViewMatrix"] = this.camera.worldMatrixInverse.toArray();
                    if (uniforms["uProjectionMatrix"]) uniforms["uProjectionMatrix"] = this.camera.projectionMatrix.toArray();
                }
                //模型矩阵也默认加上
                if (uniforms["uModelMatrix"]) uniforms["uModelMatrix"] = mesh._worldMatrix.toArray();
                //处理所有自定义属性
                for (let u in cusUniforms) {
                    let uniform = cusUniforms[u];
                    let type: UniformType = uniform.type;
                    let value = uniform.value;
                    switch (type) {
                        case UniformType.texture:
                            uniforms[u] = textureManager.bindTexture(value, undefined, false);
                            break;
                        case UniformType.color:
                            uniforms[u] = hex2rgb(value)
                            break;
                        case UniformType.matrix3:
                            uniforms[u] = value.toArray(true);
                            break;
                        case UniformType.vector2:
                        case UniformType.vector3:
                        case UniformType.vector4:
                        case UniformType.matrix4:
                            uniforms[u] = value.toArray();
                            break;
                        default:
                            uniforms[u] = value;
                            break;
                    }
                }
            } else {
                let maxBones = this.getMaxBones(mesh);
                let shader = getCusShader(this.renderer, mat, this.lightsConfig, mesh, maxBones, this.floatVertexTextures, this.fog, shadow);
                let uniforms = shader.uniforms;
                if (curShader !== shader) {//同一着色器只需要传一次相机参数和光照参数（因为都是场景全局）
                    curShader = shader;
                    //先绑定着色器，project不设置
                    this.renderer.bindShader(curShader, false);
                    //相机
                    uniforms["uViewMatrix"] = this.camera.worldMatrixInverse.toArray();
                    uniforms["uProjectionMatrix"] = this.camera.projectionMatrix.toArray();
                    //光照
                    if (mat._lightAffect) {
                        //只能给当前绑定的shader传值，否则location不存在
                        for (var j = 0; j < pointLightNum; j++) {
                            let key = "pointLights[" + j + "]";
                            uniforms[key].color = this.lightsConfig.pointLights[j].color;
                            uniforms[key].position = this.lightsConfig.pointLights[j].position;
                            uniforms[key].distance = this.lightsConfig.pointLights[j].distance;
                            uniforms[key].decay = this.lightsConfig.pointLights[j].decay;
                        }
                        for (var j = 0; j < dirLightNum; j++) {
                            let key = "directionalLights[" + j + "]", dl = this.lightsConfig.directionalLights[j];
                            uniforms[key].color = dl.color;
                            uniforms[key].direction = dl.direction;
                            uniforms[key].shadow = +dl.castShadow;
                            uniforms[key].shadowBias = dl.bias;
                            uniforms[key].shadowRadius = dl.radius;
                            uniforms[key].shadowMapSize = dl.mapSize;
                        }
                        uniforms.uAmbientLightColor = this.lightsConfig.ambientLightColor;
                        //暂时这么处理，只会一个灯光有影子
                        if (uniforms["uDirectionalShadowMatrix"]) {
                            uniforms["uDirectionalShadowMatrix"] = shadow.lightSpaceMatrix;
                            uniforms["uDirectionalShadowMap"] = textureManager.bindTexture(shadow.texture, undefined, false);
                        }
                    }
                }
                //是否带贴图，贴图考虑下图集，也就是texture的uv是否是01
                if (mat.map) {
                    let map = mat.map;
                    //找纹理，绑定纹理，多个纹理也是一样的方法
                    uniforms["uMap"] = textureManager.bindTexture(map, undefined, false);
                    //没加的加一个
                    if (!map.transform) map.transform = new TextureMatrix(map);
                    //更新下
                    map.transform.update();
                    //赋值偏移
                    uniforms["uUvTransform"] = map.transform.mapCoord.toArray(true);
                }
                //是否带环境贴图
                if (mat.envMap) {//环境贴图先不考虑图集的情况，一般一张大jpg
                    //找纹理，绑定纹理，多个纹理也是一样的方法
                    uniforms["uEnvMap"] = textureManager.bindTexture(mat.envMap, undefined, false);;
                    //反射率
                    uniforms["uReflectivity"] = mat.reflectivity;
                    //传相机位置
                    uniforms["uCameraPosition"] = tempVec3.setFromMatrixPosition(this.camera._worldMatrix).toArray();
                }
                //uniform参数修改，好像目前就几个，且固定的，以后得仿three建数组
                uniforms["uMatColor"] = mat.colorArr;
                uniforms["uMatAlpha"] = mat.alpha;
                //带雾化的
                if (mat.useFog && this.fog) {
                    uniforms["uFogColor"] = this.fog.color;
                    // uniforms["uFogDensity"] = this.fog.density;
                    uniforms["uFogNear"] = this.fog.near;
                    uniforms["uFogFar"] = this.fog.far;
                }
                //mesh的模型矩阵
                uniforms["uModelMatrix"] = mesh._worldMatrix.toArray();
                //法线矩阵
                if (uniforms.uNormalMatrix) {
                    var modelViewMatrix = tempMatrix4.multiplyMatrices(this.camera.worldMatrixInverse, mesh._worldMatrix)
                    var normalMatrix = modelViewMatrix.invert().transpose();
                    // var normalMatrix = mesh._worldMatrix.invert().transpose();
                    uniforms["uNormalMatrix"] = normalMatrix.toArray()
                    // this.setShaderUniform(curShader, "uNormalMatrix", normalMatrix.toArray());
                }
                //骨骼需要的uniform
                if (mat.skinning && maxBones > 0) {
                    //@ts-ignore
                    let sMesh: SkinnedMesh3D = mesh;
                    uniforms["uBindMatrix"] = sMesh.bindMatrix.toArray();
                    uniforms["uBindMatrixInverse"] = sMesh.bindMatrixInverse.toArray();
                    // sMesh.skeleton.update()
                    var skeleton = sMesh.skeleton;
                    if (skeleton) {
                        var bones = skeleton.bones;
                        if (this.floatVertexTextures) {
                            if (!skeleton.boneTexture) {
                                // layout (1 matrix = 4 pixels)
                                //      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
                                //  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
                                //       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
                                //       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
                                //       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
                                var size = Math.sqrt(bones.length * 4); // 4 pixels needed for 1 matrix
                                size = (function (value) {
                                    return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));
                                })(size);
                                size = Math.max(size, 4);
                                var boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel
                                boneMatrices.set(skeleton.boneMatrices); // copy current values
                                var boneTexture = new BaseTexture({ data: boneMatrices, width: size, height: size });
                                boneTexture.mipmap = false;
                                skeleton.boneMatrices = boneMatrices;
                                skeleton.boneTexture = boneTexture;
                                skeleton.boneTextureSize = size;
                            }
                            let location: number = textureManager.bindTexture(skeleton.boneTexture, undefined, false);
                            uniforms["uBoneTexture"] = location;
                            uniforms["uBoneTextureSize"] = skeleton.boneTextureSize;
                        } else {
                            uniforms["uBoneMatrices"] = sMesh.skeleton.boneMatrices;
                        }
                    }

                }
            }
            //几何属性
            var glVaoBuffer = generateVaoBuffer(geo, this.renderer);
            //@ts-ignore
            var vao = glVaoBuffer.vaos[curShader._glShaderKey];
            var attrs = curShader.attributes;
            if (!vao) {
                //@ts-ignore vao添加各种attribute
                vao = glVaoBuffer.vaos[curShader._glShaderKey] = this.renderer.createVao()
                    //索引
                    // .addIndex(glVaoBuffer.indexBuffer)
                    //顶点
                    .addAttribute(glVaoBuffer.attrBuffer, attrs.aPosition, gl.FLOAT, false, geo._vertByteSize, 0 * 4)
                //颜色
                // .addAttribute(glVaoBuffer.attrBuffer, attrs.aColor, gl.FLOAT, true, geo._vertByteSize, 3 * 4)
                //uv
                // .addAttribute(glVaoBuffer.attrBuffer, attrs.aTextureCoord, gl.FLOAT, true, geo._vertByteSize, 6 * 4)
                //法线
                // .addAttribute(glVaoBuffer.attrBuffer, attrs.aNormal, gl.FLOAT, false, geo._vertByteSize, 8 * 4);
                //颜色不一定有
                if (attrs.aColor) vao.addAttribute(glVaoBuffer.attrBuffer, attrs.aColor, gl.FLOAT, true, geo._vertByteSize, 3 * 4)
                //uv不一定有
                if (attrs.aTextureCoord) vao.addAttribute(glVaoBuffer.attrBuffer, attrs.aTextureCoord, gl.FLOAT, true, geo._vertByteSize, 6 * 4)
                //法线不一定有
                if (attrs.aNormal) vao.addAttribute(glVaoBuffer.attrBuffer, attrs.aNormal, gl.FLOAT, false, geo._vertByteSize, 8 * 4);

                //索引看情况加
                if (geo._indices) vao.addIndex(glVaoBuffer.indexBuffer);
                //骨骼的
                if (attrs.aSkinWeight) vao.addAttribute(glVaoBuffer.skinWeightBuffer, attrs.aSkinWeight, gl.FLOAT, false, 0, 0);
                if (attrs.aSkinIndex) vao.addAttribute(glVaoBuffer.skinIndexBuffer, attrs.aSkinIndex, gl.UNSIGNED_BYTE, false, 0, 0);
                //变形数据，首次直接addAttribute
                var uploadBufferDatas: UploadBufferData[];
                if (mesh.morphTargetInfluences && mat.morphTargets) {
                    uploadBufferDatas = addMorphtargetsAttr(
                        mesh.morphTargetInfluences,
                        geo,
                        glVaoBuffer,
                        vao,
                        curShader,
                        gl
                    )
                }
                // 首次需要绑定buffer到vao，先绑定vao再绑定buffer和上传数据（upload里传false）,其实bindVao貌似已经绑定过了，再说吧
                this.renderer.bindVao(vao);
                //打包顶点、颜色、uv、法线到_attrBuffer.vertices
                packGeometry(geo);
                //所有数据绑定buffer，其实可以在上面构造GLBuffer里传数据，现在还是单独处理吧，这样判断比较多
                //需要分别绑定
                glVaoBuffer.attrBuffer.upload(geo._attrBuffer.vertices, 0, false);//首次还是bind吧，必须bind，否则buffer溢出
                //有索引才改
                if (geo._indices) glVaoBuffer.indexBuffer.upload(geo._indices, 0, false);//首次还是bind吧
                //骨骼的
                if (attrs.aSkinWeight) glVaoBuffer.skinWeightBuffer.upload(new Float32Array(geo._skinWeight), 0, false)
                if (attrs.aSkinIndex) glVaoBuffer.skinIndexBuffer.upload(new Uint8Array(geo._skinIndex), 0, false)
                //还得在这里做数据的变形
                if (uploadBufferDatas) uploadBufferDatas.forEach((e) => {
                    e.buffer.upload(e.data, 0, false);
                })
            } else {
                //变形数据，暂时只有这个要做改变，其他的估计到时也要搞，TODO
                var uploadBufferDatas: UploadBufferData[];
                if (mesh.morphTargetInfluences && mat.morphTargets) {
                    uploadBufferDatas = addMorphtargetsAttr(
                        mesh.morphTargetInfluences,
                        geo,
                        glVaoBuffer,
                        vao,
                        curShader,
                        gl
                    )
                }
                //进过上面判断，必有uploadBufferDatas，TODO,到时考虑不传force，用vao.dirty判断是否bind
                this.renderer.bindVao(vao, !!uploadBufferDatas);
                //判断是否需要重新上传数据,顶点，颜色，uv，法线
                if (packGeometry(geo)) glVaoBuffer.attrBuffer.upload(geo._attrBuffer.vertices, 0, false);
                //不修改，估计都不要变，需要修改时候TODO.好多数据，包括vao可能也需要重新bind
                // glVaoBuffer.attrBuffer.upload(glVaoBuffer._attrBuffer.vertices, 0, true)
                // glVaoBuffer.indexBuffer.upload(glVaoBuffer._indexBuffer, 0, true)

                //变形的数据可能传入的会改变，所以需要upload，还要重新修改数据，有removeAttribute的情况，TODO
                //还得在这里做数据的变形，不绑定
                if (uploadBufferDatas) uploadBufferDatas.forEach((e) => {
                    e.buffer.upload(e.data, 0, false/*true*/);//貌似里面有修改的Attribute队列，所以还是绑定下buffer
                })
            }
            //根据材质切换渲染面
            if (mat.side == RenderSideType.DoubleSide) {
                this.renderer.state.setCullFace(0)//不开启剔除
            } else {
                this.renderer.state.setCullFace(1)
                //设置剔除面
                this.renderer.state.setFrontFace(mat.side)
            }
            //绘制
            vao.draw(mat["isPointsMaterial"] ? gl.POINTS : mat.wireframe ? gl.LINES : gl.TRIANGLES);
        }
        this.meshes.length = 0
    }

    private getMaxBones(object) {
        if (object.instanceType !== "SkinnedMesh3D") return 0;
        if (this.floatVertexTextures) return 1024;
        var skeleton = object.skeleton;
        var bones = skeleton.bones;
        var nVertexMatrices = Math.floor((this.maxVertexUniforms - 20) / 4);
        var maxBones = Math.min(nVertexMatrices, bones.length);
        if (maxBones < bones.length) {
            console.warn('Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.');
            return 0;
        }
        return maxBones;
    }
}

/**
 * 组转集合的所有属性到一个_attrBuffer
 * @param geo 
 * @returns {boolean} 返回是否需要upload数据，一般是vao已有的情况下需要判断，新建vao时是必要upload的
 */
function packGeometry(geo: Geometry): boolean {
    //几何的字节长度
    let byteLength = geo._vertices.length / 3 * geo._vertByteSize
    //不存在就建一个，要执行数据处理
    if (!geo._attrBuffer) {
        geo._attrBuffer = new BatchBuffer(byteLength);
    }
    //长度不一致了也需要，上面byteLength只计算顶点长度，其他，uv。normal啥的，直接给报错吧，要执行数据处理
    else if (geo._attrBuffer.vertices.byteLength !== byteLength) {
        //原来的销毁
        geo._attrBuffer.destroy();
        //新建一个
        geo._attrBuffer = new BatchBuffer(byteLength);
    }
    //数据也没有更新，直接return
    else if (!geo.attrBufferNeedUpdate) {
        return false;//表示没更新数据，一般这种情况只需要bindVao，而不需要再upload数据
    }
    const float32View = geo._attrBuffer.float32View;
    var count = 0;
    for (var j = 0; j < geo._vertices.length / 3; j++) {
        //顶点
        float32View[count++] = geo._vertices[j * 3];
        float32View[count++] = geo._vertices[j * 3 + 1];
        float32View[count++] = geo._vertices[j * 3 + 2];
        //@ts-ignore 颜色，以后搞成一位，现在有bug
        // geo._attrBuffer.uint32View[count++] = rgb2hex(geo._colors.slice(j * 3, j * 3 + 3)) + (1 * 255 << 24)
        float32View[count++] = geo._colors[j * 3];
        float32View[count++] = geo._colors[j * 3 + 1];
        float32View[count++] = geo._colors[j * 3 + 2];
        // float32View[count++] = 1;
        //uv
        float32View[count++] = geo._uvs[j * 2];
        float32View[count++] = geo._uvs[j * 2 + 1];
        //法线,可能不需要法线的情况，也加上先
        float32View[count++] = geo._normals[j * 3];
        float32View[count++] = geo._normals[j * 3 + 1];
        float32View[count++] = geo._normals[j * 3 + 2];
    }
    //标记下，数据不需要更新了
    geo.attrBufferNeedUpdate = false;
    //表示更新过数据
    return true;//不止要bindVao，还需要重新upload数据
}

interface UploadBufferData {
    //几何上的GLbuffer的索引
    buffer: GLBuffer,
    //几何上buffer的数据
    data: any//Float32Array | number[],
    //是否要绑定
    // dontBind: boolean,
}

//每个几何的权重缓存
const influencesList = {};
//每个几何的通道对应数据索引
const attrChannelHash = {}
//给uniform传值用
var morphInfluences = new Float32Array(8);

/**
 * 更新变形数据，考虑是否要变动attribute，只有
 * 返回要upload的数据
 * @param objectInfluences mesh上的权重数组
 * @param geo 几何
 * @param vao 
 */
function addMorphtargetsAttr(
    objectInfluences: number[],
    geo: Geometry,
    glVaoBuffer: VaoBufferInt,
    vao: VertexArrayObject,
    shader: GLShader,
    gl: WebGLRenderingContext,
): UploadBufferData[] {
    var morphTargets = geo._morphPositions;
    //这里的判断是有问题的，讲道理应该是mat.morphNormals&&geo._morphNormals;但是现在变形动画都由glb提供，里面只要有法线变形就会设置mat.morphNormals=true
    var morphNormals = geo._morphNormals;

    var length = objectInfluences.length;//权重数据总长度，基本不会被手动修改，到时排序只取权重大的，最多8
    //记录需要修改的数据
    var uploadBufferDatas: UploadBufferData[] = [];

    //原来几何上的权重，第一个元素标识数据索引，第二个元素标识值，第三个标识现在用的通道
    var influences: [number, number][] = influencesList[geo.instanceId];
    //如果原先不存在
    if (!influences) {
        influences = [];
        for (var i = 0; i < length; i++) {
            influences[i] = [i, 0];
        }
        influencesList[geo.instanceId] = influences;
    }
    //通道对应的数据，确定哪些通道的数据要重新upload
    var attrChannel: number[] = attrChannelHash[geo.instanceId];
    if (!attrChannel) {
        attrChannel = [];//通道对应，数据索引
        attrChannel.length = 8;
        attrChannelHash[geo.instanceId] = attrChannel;
    }

    //vao上挂着有变形数据的移除，讲道理也是最多8
    for (var i = 0; i < 8/*length*/; i++) {
        var influence = influences[i];
        if (influence && influence[1] !== 0) {
            if (morphTargets) vao.removeAttribute('morphTarget' + i);
            if (morphNormals && i < 4) vao.removeAttribute('morphNormal' + i);
        }
    }
    //重新处理变形数据，这个得排所有的
    for (var i = 0; i < length; i++) {
        var influence = influences[i];
        influence[0] = i;
        influence[1] = objectInfluences[i];
    }
    //按权重大小排序，大的优先
    influences.sort(absNumericalSort);


    var attrs = shader.attributes;
    //只取8个，不传着色器里计算并不会报错
    //有法线时只用4个，
    var attrLen = morphNormals ? 4 : 8;
    for (var i = 0; i < attrLen; i++) {
        var influence = influences[i];
        if (influence) {
            //索引
            var index = influence[0];
            var value = influence[1];
            if (value) {
                if (morphTargets && morphTargets[index]) {
                    //buffer没有就加
                    let targetBuffer = glVaoBuffer.morphTargetBuffers[index];
                    if (!targetBuffer) {
                        targetBuffer = glVaoBuffer.morphTargetBuffers[index] = GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW)
                    }
                    //记录需要修改传数据，通道值对应的buffer值不一致时，需要upload
                    if (attrChannel[i] !== index) {
                        attrChannel[i] = index;
                        uploadBufferDatas.push({
                            buffer: targetBuffer,
                            data: new Float32Array(morphTargets[index]) //data.vertices
                        })
                    }
                    vao.addAttribute(
                        targetBuffer,
                        attrs['morphTarget' + i],
                        gl.FLOAT,
                        false,
                        0,//3 * 4,
                        0 * 4,
                        'morphTarget' + i,
                    )
                }
                if (morphNormals && morphNormals[index]) {//法线只有4个
                    //buffer没有就加
                    let normalBuffer = glVaoBuffer.morphNormalBuffers[index];
                    if (!normalBuffer) {
                        normalBuffer = glVaoBuffer.morphNormalBuffers[index] = GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW)
                    }
                    //记录需要修改传数据，通道值对应的buffer值不一致时，需要upload，用后四个记录
                    if (attrChannel[i + 4] !== index) {
                        attrChannel[i + 4] = index;
                        uploadBufferDatas.push({
                            buffer: normalBuffer,
                            data: new Float32Array(morphNormals[index])
                        })
                    }
                    vao.addAttribute(
                        glVaoBuffer.morphNormalBuffers[index],
                        attrs['morphNormal' + i],
                        gl.FLOAT,
                        false,
                        0,// 3 * 4,
                        0 * 4,
                        'morphNormal' + i,
                    )
                }
                morphInfluences[i] = value;
                continue;
            }
        }
        morphInfluences[i] = 0;
    }
    //着色器修改
    shader.uniforms["morphTargetInfluences"] = morphInfluences;
    //需要修改数据的
    return uploadBufferDatas
}

function absNumericalSort(a: [number, number], b: [number, number]) {
    return Math.abs(b[1]) - Math.abs(a[1]);
}

interface renderObjInt {



}

WebglRenderer.registerPlugin('d3', D3Renderer);



let shadowMap: RenderTexture;
//暂时只按照渲染器的id来缓存，到时可能还会有顶点或骨骼动画，再修改，TODO
let shadowShaders: { [key: string]: ShadowShader } = {};
/**
 * 光源相机
 */
// let shadowCamera: OrthographicCamera;
/**
 * 光源空间矩阵
 */
// let shadowMatrix: Matrix4;
/**
 * 生成影子深度图
 * @param lights 
 * @param meshes 
 * @param renderer 
 */
function getShadowMap(
    lights: LightsConfig,
    meshes: Mesh3D[],
    renderer: WebglRenderer
): { texture: Texture, lightSpaceMatrix: number[], shadowType: ShadowType } {
    //找直线光，就一个，设置了castShadow的
    var dirLight = (() => {
        if (!lights || !lights.directionalLights || !lights.directionalLights.length) return null
        for (var d of lights.directionalLights) {
            if (d.castShadow) return d;
        }
        return null
    })();
    if (!dirLight) return null;
    var castMeshes: Mesh3D[] = [], receiveMeshes: Mesh3D[] = [];
    if (meshes && meshes.length) {
        for (let m of meshes) {
            //找设置了投影的物体，且是有光感材质的
            if (m.castShadow && m.material._lightAffect) castMeshes.push(m);
            //找设置了接收影子的物体
            if (m.receiveShadow && m.material._lightAffect) receiveMeshes.push(m);
        }
    }
    if (!castMeshes.length || !receiveMeshes.length) return null;
    var gl = renderer.gl;
    //建一个帧缓存
    shadowMap = shadowMap || new RenderTexture(new BaseRenderTexture(dirLight.mapSize[0], dirLight.mapSize[1]));

    //然后就可以根据直线光生成光照矩阵的深度贴图了
    //绑定帧缓存
    renderer.bindRenderTexture(shadowMap, null);
    //设置清除颜色
    renderer.clear([1, 1, 1, 1]);
    //设置不使用混色
    renderer.state.setBlend(false);
    //绑定着色器
    var shadowShader =
        shadowShaders[renderer.CONTEXT_UID] ||
        (shadowShaders[renderer.CONTEXT_UID] = new ShadowShader(renderer.gl));
    renderer.bindShader(shadowShader, false);

    //赋值uniform
    var uniforms = shadowShader.uniforms;

    // var position = dirLight.position, tPosition = dirLight.tPosition;
    // //计算光照空间矩阵
    // shadowCamera = shadowCamera || new OrthographicCamera(- 15, 15, 15, - 15, 1, 50);
    // shadowCamera.position.set(position[0], position[1], position[2]);
    // shadowCamera.lookAt(tPosition[0], tPosition[1], tPosition[2]);
    // shadowCamera.updateWorldMatrix();
    // shadowMatrix = shadowMatrix || new Matrix4;
    // shadowMatrix.set(
    //     0.5, 0.0, 0.0, 0.5,
    //     0.0, 0.5, 0.0, 0.5,
    //     0.0, 0.0, 0.5, 0.5,
    //     0.0, 0.0, 0.0, 1.0
    // );
    // shadowMatrix.multiply(shadowCamera.projectionMatrix);
    // shadowMatrix.multiply(shadowCamera.worldMatrixInverse);
    // uniforms["uLightSpaceMatrix"] = new Matrix4().multiply(shadowCamera.projectionMatrix).multiply(shadowCamera.worldMatrixInverse).toArray();

    //全在scene3d里计算了
    uniforms["uLightSpaceMatrix"] = dirLight.shadowMatrixDepth//dirLight.shadowMatrix;
    //设置剔除
    renderer.state.setCullFace(1)
    //设置剔除面
    renderer.state.setFrontFace(1)

    castMeshes.forEach((m) => {
        var geo = m.geometry, mat = m.material;
        uniforms["uModelMatrix"] = m._worldMatrix.toArray();
        // uniforms["uMatAlpha"] = mat.alpha;
        //几何属性
        var glVaoBuffer = generateVaoBuffer(geo, renderer);
        var vao = glVaoBuffer.vaos[renderer.CONTEXT_UID];
        var attrs = shadowShader.attributes;
        if (!vao) {
            //vao添加各种attribute
            vao = glVaoBuffer.vaos[renderer.CONTEXT_UID] = renderer.createVao()
                //顶点
                .addAttribute(glVaoBuffer.attrBuffer, attrs.aPosition, gl.FLOAT, false, geo._vertByteSize, 0 * 4);
            //索引看情况加
            if (geo._indices) vao.addIndex(glVaoBuffer.indexBuffer);
            //骨骼的以后再说TODO，如果带了法线，上面还要加上addAttribute(
            // if (attrs.aSkinWeight) vao.addAttribute(glVaoBuffer.skinWeightBuffer, attrs.aSkinWeight, gl.FLOAT, false, 0, 0);
            // if (attrs.aSkinIndex) vao.addAttribute(glVaoBuffer.skinIndexBuffer, attrs.aSkinIndex, gl.UNSIGNED_BYTE, false, 0, 0);
            //变形数据，首次直接addAttribute
            // var uploadBufferDatas: UploadBufferData[];
            // if (mesh.morphTargetInfluences && mat.morphTargets) {
            //     uploadBufferDatas = addMorphtargetsAttr(
            //         mesh.morphTargetInfluences,
            //         geo,
            //         glVaoBuffer,
            //         vao,
            //         curShader,
            //         gl
            //     )
            // }
            renderer.bindVao(vao);
            //打包顶点数据到vertices;不判断了，必要upload
            packGeometry(geo)
            //需要分别绑定
            glVaoBuffer.attrBuffer.upload(geo._attrBuffer.vertices, 0, false);//首次还是bind吧，必须bind，否则buffer溢出
            //有索引才改
            if (geo._indices) glVaoBuffer.indexBuffer.upload(geo._indices, 0, false);//首次还是bind吧
            //骨骼的
            // if (attrs.aSkinWeight) glVaoBuffer.skinWeightBuffer.upload(new Float32Array(geo._skinWeight), 0, false)
            // if (attrs.aSkinIndex) glVaoBuffer.skinIndexBuffer.upload(new Uint8Array(geo._skinIndex), 0, false)
            // //还得在这里做数据的变形
            // if (uploadBufferDatas) uploadBufferDatas.forEach((e) => {
            //     e.buffer.upload(e.data, 0, false);
            // })
        } else {
            //变形数据，暂时只有这个要做改变，其他的估计到时也要搞，TODO
            // var uploadBufferDatas: UploadBufferData[];
            // if (mesh.morphTargetInfluences && mat.morphTargets) {
            //     uploadBufferDatas = addMorphtargetsAttr(
            //         mesh.morphTargetInfluences,
            //         geo,
            //         glVaoBuffer,
            //         vao,
            //         curShader,
            //         gl
            //     )
            // }
            //进过上面判断，必有uploadBufferDatas，TODO,到时考虑不传force，用vao.dirty判断是否bind
            renderer.bindVao(vao/*, !!uploadBufferDatas*/);
            //判断是否需要重新上传数据
            if (packGeometry(geo)) glVaoBuffer.attrBuffer.upload(geo._attrBuffer.vertices, 0, false);
            //不修改，估计都不要变，需要修改时候TODO.好多数据，包括vao可能也需要重新bind
            // glVaoBuffer.attrBuffer.upload(glVaoBuffer._attrBuffer.vertices, 0, true)
            // glVaoBuffer.indexBuffer.upload(glVaoBuffer._indexBuffer, 0, true)

            //变形的数据可能传入的会改变，所以需要upload，还要重新修改数据，有removeAttribute的情况，TODO
            //还得在这里做数据的变形，不绑定
            // if (uploadBufferDatas) uploadBufferDatas.forEach((e) => {
            //     e.buffer.upload(e.data, 0, false/*true*/);//貌似里面有修改的Attribute队列，所以还是绑定下buffer
            // })
        }

        //绘制
        // vao.draw(mat.wireframe ? gl.LINES : gl.TRIANGLES);
        vao.draw(mat["isPointsMaterial"] ? gl.POINTS : mat.wireframe ? gl.LINES : gl.TRIANGLES)
    })

    //纹理绑定回去
    renderer.bindRenderTexture(null, null);
    //混色设置回去
    renderer.state.setBlend(true);
    // renderer.state.setBlendMode(BLEND_MODES.NORMAL_NPM)//先不设置，让着色器里预乘

    //还有viewport还原回场景的，这部分放在flush吧


    return { texture: shadowMap, lightSpaceMatrix: dirLight.shadowMatrix, shadowType: null }
}


function generateVaoBuffer(geo: Geometry, renderer: WebglRenderer) {
    var gl = renderer.gl;
    var glVaoBuffer = geo._glVaoBuffer[renderer.CONTEXT_UID];
    if (!glVaoBuffer) {
        glVaoBuffer = geo._glVaoBuffer[renderer.CONTEXT_UID] = {
            attrBuffer: GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW),
            vaos: {}
        }
        //有索引
        if (geo._indices) glVaoBuffer.indexBuffer = GLBuffer.createIndexBuffer(gl, null, gl.STATIC_DRAW);
        //TODO 有变形顶点，数据直接干进去，有性能问题再说 ，还是先不绑了，影响下面的vao
        if (geo._morphPositions) {//长度可以根据位置，还是根据，可能很多，用到时再建
            glVaoBuffer.morphTargetBuffers = []; //mesh.morphTargetInfluences.length
        }
        //有变形法线
        if (geo._morphNormals) {
            glVaoBuffer.morphNormalBuffers = [];
        }
        //骨骼
        if (geo._skinWeight) glVaoBuffer.skinWeightBuffer = GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW);
        if (geo._skinIndex) glVaoBuffer.skinIndexBuffer = GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW);
        //计算一次，打包所有的顶点属性，位置，颜色，uv，法线，感觉不合理，有修改也该执行
        // packGeometry(geo);//放外面自行处理了，反正这方法和上面的处理完全没关系
    }
    return glVaoBuffer
}
//判断数字数组，如果有其他地方也用到，放到utils里，通用后把注释放开
function isArrayEqual(arr1: any[] | Float32Array, arr2: any[] | Float32Array) {
    // if (!arr1 || !arr2) return false;
    if (arr1.length !== arr2.length) return false;
    let len = arr1.length;
    for (var i = 0; i < len; i++) {
        // var type1 = typeof arr1[i];
        // var type2 = typeof arr2[i];
        // if (type1 !== type2) return false;
        // //数字注意精度
        // if (type1 === "number" && type2 === "number") {
        if (Math.abs(arr1[i] - arr2[i]) > 0.00001) return false
        // }
        // else if (arr1[i] !== arr2[i]) {
        //     return false;
        // }
    }
    return true;
}