text: import { Cartesian3 } from "../../Source/Cesium.js";
  import { Color } from "../../Source/Cesium.js";
  import { defaultValue } from "../../Source/Cesium.js";
  import { defined } from "../../Source/Cesium.js";
  import { Ellipsoid } from "../../Source/Cesium.js";
  import { GeometryInstance } from "../../Source/Cesium.js";
  import { Rectangle } from "../../Source/Cesium.js";
  import { RectangleGeometry } from "../../Source/Cesium.js";
  import { Resource } from "../../Source/Cesium.js";
  import { Material } from "../../Source/Cesium.js";
  import { MaterialAppearance } from "../../Source/Cesium.js";
  import { PolylineCollection } from "../../Source/Cesium.js";
  import { FeatureDetection } from "../../Source/Cesium.js";
  import { Primitive } from "../../Source/Cesium.js";
  import { TextureMagnificationFilter } from "../../Source/Cesium.js";
  import { TextureMinificationFilter } from "../../Source/Cesium.js";
  import createScene from "../createScene.js";
  import pollToPromise from "../pollToPromise.js";
  
  describe(
    "Scene/Material",
    function () {
      var scene;
  
      var rectangle = Rectangle.fromDegrees(-10.0, -10.0, 10.0, 10.0);
      var polygon;
      var backgroundColor = [0, 0, 128, 255];
      var polylines;
      var polyline;
  
      beforeAll(function () {
        scene = createScene();
        Color.fromBytes(
          backgroundColor[0],
          backgroundColor[1],
          backgroundColor[2],
          backgroundColor[3],
          scene.backgroundColor
        );
        scene.primitives.destroyPrimitives = false;
        scene.camera.setView({ destination: rectangle });
      });
  
      afterAll(function () {
        scene.destroyForSpecs();
      });
  
      beforeEach(function () {
        var vertexFormat = MaterialAppearance.MaterialSupport.ALL.vertexFormat;
  
        polygon = new Primitive({
          geometryInstances: new GeometryInstance({
            geometry: new RectangleGeometry({
              vertexFormat: vertexFormat,
              rectangle: rectangle,
            }),
          }),
          asynchronous: false,
        });
  
        polygon.appearance = new MaterialAppearance({
          materialSupport: MaterialAppearance.MaterialSupport.ALL,
          translucent: false,
          closed: true,
        });
  
        polylines = new PolylineCollection();
        polyline = polylines.add({
          positions: Cartesian3.fromDegreesArray(
            [-50.0, 0.0, 50.0, 0.0],
            Ellipsoid.WGS84
          ),
          width: 5.0,
        });
      });
  
      afterEach(function () {
        scene.primitives.removeAll();
        polygon = polygon && polygon.destroy();
        polylines = polylines && polylines.destroy();
      });
  
      function renderMaterial(material, ignoreBackground, callback) {
        ignoreBackground = defaultValue(ignoreBackground, false);
        polygon.appearance.material = material;
        if (!ignoreBackground) {
          expect(scene).toRender(backgroundColor);
        }
  
        scene.primitives.removeAll();
        scene.primitives.add(polygon);
  
        expect(scene).toRenderAndCall(function (rgba) {
          expect(rgba).not.toEqual(backgroundColor);
          if (defined(callback)) {
            callback(rgba);
          }
        });
      }
  
      function renderPolylineMaterial(material) {
        polyline.material = material;
        expect(scene).toRender(backgroundColor);
  
        scene.primitives.removeAll();
        scene.primitives.add(polylines);
  
        var result;
        expect(scene).toRenderAndCall(function (rgba) {
          result = rgba;
          expect(rgba).not.toEqual(backgroundColor);
        });
        return result;
      }
  
      function verifyMaterial(type) {
        var material = new Material({
          strict: true,
          fabric: {
            type: type,
          },
        });
        renderMaterial(material);
      }
  
      function verifyPolylineMaterial(type) {
        var material = new Material({
          strict: true,
          fabric: {
            type: type,
          },
        });
        renderPolylineMaterial(material);
      }
  
      it("draws Color built-in material", function () {
        verifyMaterial("Color");
      });
  
      it("draws Image built-in material", function () {
        verifyMaterial("Image");
      });
  
      it("draws DiffuseMap built-in material", function () {
        verifyMaterial("DiffuseMap");
      });
  
      it("draws AlphaMap built-in material", function () {
        verifyMaterial("AlphaMap");
      });
  
      it("draws SpecularMap built-in material", function () {
        verifyMaterial("SpecularMap");
      });
  
      it("draws EmissionMap built-in material", function () {
        verifyMaterial("EmissionMap");
      });
  
      it("draws BumpMap built-in material", function () {
        verifyMaterial("BumpMap");
      });
  
      it("draws NormalMap built-in material", function () {
        verifyMaterial("NormalMap");
      });
  
      it("draws Grid built-in material", function () {
        verifyMaterial("Grid");
      });
  
      it("draws Stripe built-in material", function () {
        verifyMaterial("Stripe");
      });
  
      it("draws Checkerboard built-in material", function () {
        verifyMaterial("Checkerboard");
      });
  
      it("draws Dot built-in material", function () {
        verifyMaterial("Dot");
      });
  
      it("draws Water built-in material", function () {
        verifyMaterial("Water");
      });
  
      it("draws RimLighting built-in material", function () {
        verifyMaterial("RimLighting");
      });
  
      it("draws Fade built-in material", function () {
        verifyMaterial("Fade");
      });
  
      it("draws PolylineArrow built-in material", function () {
        verifyPolylineMaterial("PolylineArrow");
      });
  
      it("draws PolylineDash built-in material", function () {
        verifyPolylineMaterial("PolylineDash");
      });
  
      it("draws PolylineGlow built-in material", function () {
        verifyPolylineMaterial("PolylineGlow");
      });
  
      it("draws PolylineOutline built-in material", function () {
        verifyPolylineMaterial("PolylineOutline");
      });
  
      it("gets the material type", function () {
        var material = new Material({
          strict: true,
          fabric: {
            type: "Color",
          },
        });
        expect(material.type).toEqual("Color");
      });
  
      it("creates opaque/translucent materials", function () {
        var material = new Material({
          translucent: true,
          strict: true,
          fabric: {
            type: "Color",
          },
        });
        expect(material.isTranslucent()).toEqual(true);
  
        material = new Material({
          translucent: false,
          strict: true,
          fabric: {
            type: "Color",
          },
        });
        expect(material.isTranslucent()).toEqual(false);
      });
  
      it("creates a new material type and builds off of it", function () {
        var material1 = new Material({
          strict: true,
          fabric: {
            type: "New",
            components: {
              diffuse: "vec3(0.0, 0.0, 0.0)",
            },
          },
        });
  
        var material2 = new Material({
          strict: true,
          fabric: {
            materials: {
              first: {
                type: "New",
              },
            },
            components: {
              diffuse: "first.diffuse",
            },
          },
        });
  
        renderMaterial(material1);
        renderMaterial(material2, true);
      });
  
      it("accesses material properties after construction", function () {
        var material = new Material({
          strict: true,
          fabric: {
            materials: {
              first: {
                type: "DiffuseMap",
              },
            },
            uniforms: {
              value: {
                x: 0.0,
                y: 0.0,
                z: 0.0,
              },
            },
            components: {
              diffuse: "value + first.diffuse",
            },
          },
        });
        material.uniforms.value.x = 1.0;
        material.materials.first.uniforms.repeat.x = 2.0;
  
        renderMaterial(material);
      });
  
      it("creates a material inside a material inside a material", function () {
        var material = new Material({
          strict: true,
          fabric: {
            materials: {
              first: {
                materials: {
                  second: {
                    components: {
                      diffuse: "vec3(0.0, 0.0, 0.0)",
                    },
                  },
                },
                components: {
                  diffuse: "second.diffuse",
                },
              },
            },
            components: {
              diffuse: "first.diffuse",
            },
          },
        });
        renderMaterial(material);
      });
  
      it("creates a material with an image uniform", function () {
        var material = new Material({
          strict: true,
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: "./Data/Images/Blue.png",
            },
          },
        });
        renderMaterial(material);
      });
  
      it("creates a material with an image resource uniform", function () {
        var material = new Material({
          strict: true,
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: new Resource("./Data/Images/Blue.png"),
            },
          },
        });
        renderMaterial(material);
      });
  
      it("creates a material with an image canvas uniform", function () {
        var canvas = document.createElement("canvas");
        var context2D = canvas.getContext("2d");
        context2D.width = 1;
        context2D.height = 1;
        context2D.fillStyle = "rgb(0,0,255)";
        context2D.fillRect(0, 0, 1, 1);
  
        var material = new Material({
          strict: true,
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: canvas,
            },
          },
        });
  
        renderMaterial(material);
      });
  
      it("creates a material with an KTX2 compressed image uniform", function () {
        var compressedUrl;
        if (FeatureDetection.supportsBasis(scene)) {
          compressedUrl = "./Data/Images/Green4x4.ktx2";
        } else {
          return;
        }
  
        var material = new Material({
          strict: true,
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: compressedUrl,
            },
          },
        });
        renderMaterial(material);
      });
  
      it("creates a material with a cube map uniform", function () {
        var material = new Material({
          strict: true,
          fabric: {
            uniforms: {
              cubeMap: {
                positiveX: "./Data/Images/Blue.png",
                negativeX: "./Data/Images/Blue.png",
                positiveY: "./Data/Images/Blue.png",
                negativeY: "./Data/Images/Blue.png",
                positiveZ: "./Data/Images/Blue.png",
                negativeZ: "./Data/Images/Blue.png",
              },
            },
            source:
              "uniform samplerCube cubeMap;\n" +
              "czm_material czm_getMaterial(czm_materialInput materialInput)\n" +
              "{\n" +
              "    czm_material material = czm_getDefaultMaterial(materialInput);\n" +
              "    material.diffuse = textureCube(cubeMap, vec3(1.0)).xyz;\n" +
              "    return material;\n" +
              "}\n",
          },
        });
        renderMaterial(material);
      });
  
      it("does not crash if source uniform is formatted differently", function () {
        var material = new Material({
          strict: true,
          fabric: {
            uniforms: {
              cubeMap: {
                positiveX: "./Data/Images/Blue.png",
                negativeX: "./Data/Images/Blue.png",
                positiveY: "./Data/Images/Blue.png",
                negativeY: "./Data/Images/Blue.png",
                positiveZ: "./Data/Images/Blue.png",
                negativeZ: "./Data/Images/Blue.png",
              },
            },
            source:
              "uniform   samplerCube   cubeMap  ;\r\n" +
              "czm_material czm_getMaterial(czm_materialInput materialInput)\r\n" +
              "{\r\n" +
              "    czm_material material = czm_getDefaultMaterial(materialInput);\r\n" +
              "    material.diffuse = textureCube(cubeMap, vec3(1.0)).xyz;\r\n" +
              "    return material;\r\n" +
              "}",
          },
        });
        renderMaterial(material);
      });
  
      it("creates a material with a boolean uniform", function () {
        var material = new Material({
          strict: true,
          fabric: {
            uniforms: {
              value: true,
            },
            components: {
              diffuse: "float(value) * vec3(1.0)",
            },
          },
        });
        renderMaterial(material);
      });
  
      it("create a material with a matrix uniform", function () {
        var material1 = new Material({
          strict: true,
          fabric: {
            uniforms: {
              value: [0.5, 0.5, 0.5, 0.5],
            },
            components: {
              diffuse: "vec3(value[0][0], value[0][1], value[1][0])",
              alpha: "value[1][1]",
            },
          },
        });
        renderMaterial(material1);
  
        var material2 = new Material({
          strict: true,
          fabric: {
            uniforms: {
              value: [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
            },
            components: {
              diffuse: "vec3(value[0][0], value[0][1], value[1][0])",
              alpha: "value[2][2]",
            },
          },
        });
        renderMaterial(material2, true);
  
        var material3 = new Material({
          strict: true,
          fabric: {
            uniforms: {
              value: [
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
                0.5,
              ],
            },
            components: {
              diffuse: "vec3(value[0][0], value[0][1], value[1][0])",
              alpha: "value[3][3]",
            },
          },
        });
        renderMaterial(material3, true);
      });
  
      it("creates a material using unusual uniform and material names", function () {
        var material = new Material({
          strict: true,
          fabric: {
            uniforms: {
              i: 0.5,
            },
            materials: {
              d: {
                type: "Color",
              },
              diffuse: {
                type: "Color",
              },
            },
            components: {
              diffuse: "(d.diffuse + diffuse.diffuse)*i",
              specular: "i",
            },
          },
        });
        renderMaterial(material);
      });
  
      it("create a material using fromType", function () {
        var material = Material.fromType("Color");
        renderMaterial(material);
      });
  
      it("create material using fromType and overide default uniforms", function () {
        var material1 = Material.fromType("Color", {
          color: new Color(0.0, 1.0, 0.0, 1.0),
        });
  
        renderMaterial(material1, false, function (rgba) {
          expect(rgba).toEqual([0, 255, 0, 255]);
        });
      });
  
      it("create multiple materials from the same type", function () {
        var material1 = Material.fromType("Color", {
          color: new Color(0.0, 1.0, 0.0, 1.0),
        });
  
        var material2 = Material.fromType("Color", {
          color: new Color(1.0, 0.0, 0.0, 1.0),
        });
  
        expect(material1.shaderSource).toEqual(material2.shaderSource);
  
        renderMaterial(material2, false, function (rgba) {
          expect(rgba).toEqual([255, 0, 0, 255]);
        });
        renderMaterial(material1, true, function (rgba) {
          expect(rgba).toEqual([0, 255, 0, 255]);
        });
      });
  
      it("create material with sub-materials of the same type", function () {
        var material = new Material({
          fabric: {
            materials: {
              color1: {
                type: "Color",
                uniforms: {
                  color: new Color(0.0, 1.0, 0.0, 1.0),
                },
              },
              color2: {
                type: "Color",
                uniforms: {
                  color: new Color(0.0, 0.0, 1.0, 1.0),
                },
              },
            },
            components: {
              diffuse: "color1.diffuse + color2.diffuse",
            },
          },
        });
  
        renderMaterial(material, false, function (rgba) {
          expect(rgba).toEqual([0, 255, 255, 255]);
        });
      });
  
      it("creates material with custom texture filter", function () {
        var materialLinear = new Material({
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: "./Data/Images/BlueOverRed.png",
            },
          },
          minificationFilter: TextureMinificationFilter.LINEAR,
          magnificationFilter: TextureMagnificationFilter.LINEAR,
        });
  
        var materialNearest = new Material({
          fabric: {
            type: "DiffuseMap",
            uniforms: {
              image: "./Data/Images/BlueOverRed.png",
            },
          },
          minificationFilter: TextureMinificationFilter.NEAREST,
          magnificationFilter: TextureMagnificationFilter.NEAREST,
        });
  
        var purple = [127, 0, 127, 255];
  
        var ignoreBackground = true;
        renderMaterial(materialLinear, ignoreBackground); // Populate the scene with the primitive prior to updating
        return pollToPromise(function () {
          var imageLoaded = materialLinear._loadedImages.length !== 0;
          scene.renderForSpecs();
          return imageLoaded;
        })
          .then(function () {
            renderMaterial(materialLinear, ignoreBackground, function (rgba) {
              expect(rgba).toEqualEpsilon(purple, 1);
            });
          })
          .then(function () {
            renderMaterial(materialNearest, ignoreBackground); // Populate the scene with the primitive prior to updating
            return pollToPromise(function () {
              var imageLoaded = materialNearest._loadedImages.length !== 0;
              scene.renderForSpecs();
              return imageLoaded;
            }).then(function () {
              renderMaterial(materialNearest, ignoreBackground, function (rgba) {
                expect(rgba).not.toEqualEpsilon(purple, 1);
              });
            });
          });
      });
  
      it("throws with source and components in same template", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              components: {
                diffuse: "vec3(0.0, 0.0, 0.0)",
              },
              source:
                "czm_material czm_getMaterial(czm_materialInput materialInput)\n{\n" +
                "czm_material material = czm_getDefaultMaterial(materialInput);\n" +
                "return material;\n}\n",
            },
          });
        }).toThrowDeveloperError();
  
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              type: "DiffuseMap",
              components: {
                diffuse: "vec3(0.0, 0.0, 0.0)",
              },
            },
          });
        }).toThrowDeveloperError();
      });
  
      it("throws with duplicate names in materials and uniforms", function () {
        expect(function () {
          return new Material({
            strict: false,
            fabric: {
              uniforms: {
                first: 0.0,
                second: 0.0,
              },
              materials: {
                second: {},
              },
            },
          });
        }).toThrowDeveloperError();
      });
  
      it("throws with invalid template type", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              invalid: 3.0,
            },
          });
        }).toThrowDeveloperError();
      });
  
      it("throws with invalid component type", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              components: {
                difuse: "vec3(0.0, 0.0, 0.0)",
              },
            },
          });
        }).toThrowDeveloperError();
      });
  
      it("throws with invalid uniform type", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              uniforms: {
                value: {
                  x: 0.0,
                  y: 0.0,
                  z: 0.0,
                  w: 0.0,
                  t: 0.0,
                },
              },
            },
          });
        }).toThrowDeveloperError();
  
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              uniforms: {
                value: [0.0, 0.0, 0.0, 0.0, 0.0],
              },
            },
          });
        }).toThrowDeveloperError();
      });
  
      it("throws with unused channels", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              uniforms: {
                nonexistant: "rgb",
              },
            },
          });
        }).toThrowDeveloperError();
  
        // If strict is false, unused uniform strings are ignored.
        var material = new Material({
          strict: false,
          fabric: {
            uniforms: {
              nonexistant: "rgb",
            },
          },
        });
        renderMaterial(material);
      });
  
      it("throws with unused uniform", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              uniforms: {
                first: {
                  x: 0.0,
                  y: 0.0,
                  z: 0.0,
                },
              },
            },
          });
        }).toThrowDeveloperError();
  
        // If strict is false, unused uniforms are ignored.
        var material = new Material({
          strict: false,
          fabric: {
            uniforms: {
              first: {
                x: 0.0,
                y: 0.0,
                z: 0.0,
              },
            },
          },
        });
        renderMaterial(material);
      });
  
      it("throws with unused material", function () {
        expect(function () {
          return new Material({
            strict: true,
            fabric: {
              materials: {
                first: {
                  type: "DiffuseMap",
                },
              },
            },
          });
        }).toThrowDeveloperError();
  
        // If strict is false, unused materials are ignored.
        var material = new Material({
          strict: false,
          fabric: {
            materials: {
              first: {
                type: "DiffuseMap",
              },
            },
          },
        });
        renderMaterial(material);
      });
  
      it("throws with invalid type sent to fromType", function () {
        expect(function () {
          return Material.fromType("Nothing");
        }).toThrowDeveloperError();
      });
  
      it("destroys material with texture", function () {
        var material = Material.fromType(Material.DiffuseMapType);
        material.uniforms.image = "./Data/Images/Green.png";
  
        renderMaterial(material);
  
        return pollToPromise(function () {
          var result = material._loadedImages.length !== 0;
          scene.renderForSpecs();
          return result;
        }).then(function () {
          material.destroy();
          expect(material.isDestroyed()).toEqual(true);
        });
      });
  
      it("destroys sub-materials", function () {
        var material = new Material({
          strict: true,
          fabric: {
            materials: {
              diffuseMap: {
                type: "DiffuseMap",
              },
            },
            uniforms: {
              value: {
                x: 0.0,
                y: 0.0,
                z: 0.0,
              },
            },
            components: {
              diffuse: "value + diffuseMap.diffuse",
            },
          },
        });
        material.materials.diffuseMap.uniforms.image = "./Data/Images/Green.png";
  
        renderMaterial(material);
  
        return pollToPromise(function () {
          var result = material.materials.diffuseMap._loadedImages.length !== 0;
          scene.renderForSpecs();
          return result;
        }).then(function () {
          var diffuseMap = material.materials.diffuseMap;
          material.destroy();
          expect(material.isDestroyed()).toEqual(true);
          expect(diffuseMap.isDestroyed()).toEqual(true);
        });
      });
  
      it("does not destroy default material", function () {
        var material = Material.fromType(Material.DiffuseMapType);
        renderMaterial(material);
        material.destroy();
      });
    },
    "WebGL"
  );
  