setProgram method

WebGLProgram setProgram(
  1. Camera camera,
  2. Object3D? scene,
  3. BufferGeometry? geometry,
  4. Material material,
  5. Object3D object,
)

Implementation

WebGLProgram setProgram(Camera camera, Object3D? scene, BufferGeometry? geometry, Material material, Object3D object) {
  if (scene is! Scene) scene = _emptyScene;
  // scene could be a Mesh, Line, Points, ...
  textures.resetTextureUnits();

  final fog = scene.fog;
  final environment = material is MeshStandardMaterial ? scene.environment : null;
  final encoding = (_currentRenderTarget == null)
      ? outputEncoding
      : (_currentRenderTarget!.isXRRenderTarget == true ? _currentRenderTarget!.texture.encoding : LinearEncoding);

  Texture? envMap;
  if (material is MeshStandardMaterial) {
    envMap = cubeuvmaps.get(material.envMap ?? environment);
  } else {
    envMap = cubemaps.get(material.envMap ?? environment);
  }

  bool vertexAlphas = material.vertexColors == true &&
      geometry != null &&
      geometry.attributes["color"] != null &&
      geometry.attributes["color"].itemSize == 4;
  bool vertexTangents = material.normalMap != null && geometry != null && geometry.attributes["tangent"] != null;
  bool morphTargets = geometry != null && geometry.morphAttributes["position"] != null;
  bool morphNormals = geometry != null && geometry.morphAttributes["normal"] != null;
  bool morphColors = geometry != null && geometry.morphAttributes["color"] != null;

  int toneMapping = material.toneMapped ? this.toneMapping : NoToneMapping;

  List<BufferAttribute>? morphAttribute = geometry != null
      ? (geometry.morphAttributes["position"] ??
          geometry.morphAttributes["normal"] ??
          geometry.morphAttributes["color"])
      : null;
  int morphTargetsCount = (morphAttribute != null) ? morphAttribute.length : 0;

  Map<String, dynamic> materialProperties = properties.get(material);
  WebGLLights lights = currentRenderState!.state.lights;

  if (_clippingEnabled == true) {
    if (_localClippingEnabled == true || camera != _currentCamera) {
      final useCache = camera == _currentCamera && material.id == _currentMaterialId;

      // we might want to call this function with some ClippingGroup
      // object instead of the material, once it becomes feasible
      // (#8465, #8379)
      clipping.setState(material, camera, useCache);
    }
  }

  //

  bool needsProgramChange = false;

  if (material.version == materialProperties["__version"]) {
    if (materialProperties["needsLights"] != null &&
        (materialProperties["lightsStateVersion"] != lights.state.version)) {
      needsProgramChange = true;
    } else if (materialProperties["outputEncoding"] != encoding) {
      needsProgramChange = true;
    } else if (object is InstancedMesh && materialProperties["instancing"] == false) {
      needsProgramChange = true;
    } else if (object is! InstancedMesh && materialProperties["instancing"] == true) {
      needsProgramChange = true;
    } else if (object is SkinnedMesh && materialProperties["skinning"] == false) {
      needsProgramChange = true;
    } else if (object is! SkinnedMesh && materialProperties["skinning"] == true) {
      needsProgramChange = true;
    } else if (materialProperties["envMap"] != envMap) {
      needsProgramChange = true;
    } else if (material.fog && materialProperties["fog"] != fog) {
      needsProgramChange = true;
    } else if (materialProperties["numClippingPlanes"] != null &&
        (materialProperties["numClippingPlanes"] != clipping.numPlanes ||
            materialProperties["numIntersection"] != clipping.numIntersection)) {
      needsProgramChange = true;
    } else if (materialProperties["vertexAlphas"] != vertexAlphas) {
      needsProgramChange = true;
    } else if (materialProperties["vertexTangents"] != vertexTangents) {
      needsProgramChange = true;
    } else if (materialProperties["morphTargets"] != morphTargets) {
      needsProgramChange = true;
    } else if (materialProperties["morphNormals"] != morphNormals) {
      needsProgramChange = true;
    } else if (materialProperties["morphColors"] != morphColors) {
      needsProgramChange = true;
    } else if (materialProperties["toneMapping"] != toneMapping) {
      needsProgramChange = true;
    } else if (capabilities.isWebGL2 == true && materialProperties["morphTargetsCount"] != morphTargetsCount) {
      needsProgramChange = true;
    }
  } else {
    needsProgramChange = true;
    materialProperties["__version"] = material.version;
  }

  WebGLProgram? program = materialProperties["currentProgram"];

  if (needsProgramChange) {
    program = getProgram(material, scene, object);
  }

  bool refreshProgram = false;
  bool refreshMaterial = false;
  bool refreshLights = false;

  final pUniforms = program!.getUniforms();
  Map<String, dynamic> mUniforms = materialProperties["uniforms"];

  if (state.useProgram(program.program)) {
    refreshProgram = true;
    refreshMaterial = true;
    refreshLights = true;
  }

  if (material.id != _currentMaterialId) {
    _currentMaterialId = material.id;

    refreshMaterial = true;
  }

  if (refreshProgram || _currentCamera != camera) {
    pUniforms.setValue(_gl, 'projectionMatrix', camera.projectionMatrix, textures);

    if (capabilities.logarithmicDepthBuffer) {
      pUniforms.setValue(_gl, 'logDepthBufFC', 2.0 / (math.log(camera.far + 1.0) / math.ln2), textures);
    }

    if (_currentCamera != camera) {
      _currentCamera = camera;

      // lighting uniforms depend on the camera so enforce an update
      // now, in case this material supports lights - or later, when
      // the next material that does gets activated:

      refreshMaterial = true; // set to true on material change
      refreshLights = true; // remains set until update done

    }

    // load material specific uniforms
    // (shader material also gets them for the sake of genericity)

    if (material is ShaderMaterial ||
        material is MeshPhongMaterial ||
        material is MeshToonMaterial ||
        material is MeshStandardMaterial ||
        material.envMap != null) {
      final uCamPos = pUniforms.map["cameraPosition"];

      if (uCamPos != null) {
        uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld));
      }
    }

    if (material is MeshPhongMaterial ||
        material is MeshToonMaterial ||
        material is MeshLambertMaterial ||
        material is MeshBasicMaterial ||
        material is MeshStandardMaterial ||
        material is ShaderMaterial) {
      pUniforms.setValue(_gl, 'isOrthographic', camera is OrthographicCamera, textures);
    }

    if (material is MeshPhongMaterial ||
        material is MeshToonMaterial ||
        material is MeshLambertMaterial ||
        material is MeshBasicMaterial ||
        material is MeshStandardMaterial ||
        material is ShaderMaterial ||
        material is ShadowMaterial ||
        object is SkinnedMesh) {
      pUniforms.setValue(_gl, 'viewMatrix', camera.matrixWorldInverse, textures);
    }
  }

  // skinning uniforms must be set even if material didn't change
  // auto-setting of texture unit for bone texture must go before other textures
  // otherwise textures used for skinning can take over texture units reserved for other material textures

  if (object is SkinnedMesh) {
    pUniforms.setOptional(_gl, object, 'bindMatrix');
    pUniforms.setOptional(_gl, object, 'bindMatrixInverse');

    final skeleton = object.skeleton;

    if (skeleton != null) {
      if (capabilities.floatVertexTextures) {
        if (skeleton.boneTexture == null) skeleton.computeBoneTexture();

        pUniforms.setValue(_gl, 'boneTexture', skeleton.boneTexture, textures);
        pUniforms.setValue(_gl, 'boneTextureSize', skeleton.boneTextureSize, textures);
      } else {
        console.warning('WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.');
      }
    }
  }

  final morphAttributes = geometry!.morphAttributes;

  if (morphAttributes["position"] != null ||
      morphAttributes["normal"] != null ||
      (morphAttributes["color"] != null && capabilities.isWebGL2 == true)) {
    morphtargets.update(object, geometry, material, program);
  }

  if (refreshMaterial || materialProperties["receiveShadow"] != object.receiveShadow) {
    materialProperties["receiveShadow"] = object.receiveShadow;
    pUniforms.setValue(_gl, 'receiveShadow', object.receiveShadow, textures);
  }

  // print(" setProgram .......... material: ${material.type} ");

  if (refreshMaterial) {
    pUniforms.setValue(_gl, 'toneMappingExposure', toneMappingExposure, textures);

    if (materialProperties["needsLights"]) {
      // the current material requires lighting info

      // note: all lighting uniforms are always set correctly
      // they simply reference the renderer's state for their
      // values
      //
      // use the current material's .needsUpdate flags to set
      // the GL state when required

      markUniformsLightsNeedsUpdate(mUniforms, refreshLights);
    }

    // refresh uniforms common to several materials

    if (fog != null && material.fog) {
      materials.refreshFogUniforms(mUniforms, fog);
    }

    materials.refreshMaterialUniforms(mUniforms, material, _pixelRatio, _height, _transmissionRenderTarget);
    WebGLUniforms.upload(_gl, materialProperties["uniformsList"], mUniforms, textures);
  }

  if (material is ShaderMaterial && material.uniformsNeedUpdate == true) {
    WebGLUniforms.upload(_gl, materialProperties["uniformsList"], mUniforms, textures);
    material.uniformsNeedUpdate = false;
  }

  if (material is SpriteMaterial) {
    dynamic c = object;
    pUniforms.setValue(_gl, 'center', c.center, textures);
  }

  // common matrices

  pUniforms.setValue(_gl, 'modelViewMatrix', object.modelViewMatrix, textures);
  pUniforms.setValue(_gl, 'normalMatrix', object.normalMatrix, textures);
  pUniforms.setValue(_gl, 'modelMatrix', object.matrixWorld, textures);

  return program;
}