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 colorSpace = ( _currentRenderTarget == null ) ? outputColorSpace : ( _currentRenderTarget?.isXRRenderTarget == true ? _currentRenderTarget?.texture.colorSpace : LinearSRGBColorSpace );
  final envMap = ( material is MeshStandardMaterial ? cubeuvmaps.get( material.envMap ?? environment ) : cubemaps.get( material.envMap ?? environment ) );
  final vertexAlphas = material.vertexColors &&
    geometry?.attributes['color'] != null &&
    geometry?.attributes['color'].itemSize == 4;
  final vertexTangents = geometry?.attributes['tangent'] != null && (material.normalMap != null || (material is MeshPhysicalMaterial && material.anisotropy > 0));
  final morphTargets = geometry?.morphAttributes['position'] != null;
  final morphNormals = geometry?.morphAttributes['normal'] != null;
  final morphColors = geometry?.morphAttributes['color'] != null;

  int toneMapping = NoToneMapping;

  if ( material.toneMapped ) {
    if ( _currentRenderTarget == null || _currentRenderTarget?.isXRRenderTarget == true ) {
      toneMapping = toneMapping;
    }
  }

  final morphAttribute = geometry?.morphAttributes['position'] ?? geometry?.morphAttributes['normal'] ?? geometry?.morphAttributes['color'];
  final morphTargetsCount = ( morphAttribute != null ) ? morphAttribute.length : 0;

  final materialProperties = properties.get( material );
  final lights = currentRenderState?.state.lights;

  if (_clippingEnabled) {
    if (_localClippingEnabled || 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['outputColorSpace'] != colorSpace ) {
      needsProgramChange = true;
    } else if ( object is BatchedMesh && materialProperties['batching'] == false ) {
      needsProgramChange = true;
    } else if (object is! BatchedMesh && materialProperties['batching'] == true ) {
      needsProgramChange = true;
    }else if ( object is BatchedMesh && materialProperties['batchingColor'] == true && object.colorsTexture == null ) {
				needsProgramChange = true;
			} else if ( object is BatchedMesh && materialProperties['batchingColor'] == false && object.colorsTexture != null ) {
				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 ( object is InstancedMesh && materialProperties['instancingColor'] == true && object.instanceColor == null ) {
      needsProgramChange = true;
    } else if ( object is InstancedMesh && materialProperties['instancingColor'] == false && object.instanceColor != null ) {
      needsProgramChange = true;
    } else if ( object is InstancedMesh && materialProperties['instancingMorph'] == true && object.morphTexture == null ) {
      needsProgramChange = true;
    } else if ( object is InstancedMesh && materialProperties['instancingMorph'] == false && object.morphTexture != null ) {
      needsProgramChange = true;
    } else if ( materialProperties['envMap'] != envMap ) {
      needsProgramChange = true;
    } else if ( material.fog == true && 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 ( 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 WebGLUniforms? pUniformS = program?.getUniforms();
  final 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 ) {

    // common camera uniforms
    final reverseDepthBuffer = (state.buffers['depth'] as DepthBuffer).getReversed();

    if ( reverseDepthBuffer ) {
      _currentProjectionMatrix.setFrom( camera.projectionMatrix );
      toNormalizedProjectionMatrix( _currentProjectionMatrix );
      toReversedProjectionMatrix( _currentProjectionMatrix );
      pUniformS?.setValue( _gl, 'projectionMatrix', _currentProjectionMatrix );
    }
    else {
      pUniformS?.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
    }

    pUniformS?.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );

    final uCamPos = pUniformS?.map['cameraPosition'];

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

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

    // consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067

    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);
    }

    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
    }
  }

  // skinning and morph target uniforms must be set even if material didn't change
  // auto-setting of texture unit for bone and morph texture must go before other textures
  // otherwise textures used for skinning and morphing 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 ( skeleton.boneTexture == null ) skeleton.computeBoneTexture();
      pUniformS?.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
    }
  }

  if ( object is BatchedMesh ) {
    pUniformS?.setOptional( _gl, object, 'batchingTexture' );
    pUniformS?.setValue( _gl, 'batchingTexture', object.matricesTexture, textures );

    pUniformS?.setOptional( _gl, object, 'batchingIdTexture' );
    pUniformS?.setValue( _gl, 'batchingIdTexture', object.indirectTexture, textures );

    pUniformS?.setOptional( _gl, object, 'batchingColorTexture' );
    if ( object.colorsTexture != null ) {
      pUniformS?.setValue( _gl, 'batchingColorTexture', object.colorsTexture, textures );
    }
  }

  final morphAttributes = geometry?.morphAttributes;

  if ( morphAttributes?['position'] != null || morphAttributes?['normal'] != null || ( morphAttributes?['color'] != null ) ) {
    morphtargets.update( object, geometry!, program! );
  }

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

  // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512

  if ( material is MeshGouraudMaterial && material.envMap != null ) {
    mUniformS['envMap']['value'] = envMap;
    mUniformS['flipEnvMap']['value'] = ( envMap is CubeTexture && envMap.isRenderTargetTexture == false ) ? - 1 : 1;
  }

  if ( material is MeshStandardMaterial && material.envMap == null && scene.environment != null ) {
    mUniformS['envMapIntensity']['value'] = scene.environmentIntensity;
  }

  if ( refreshMaterial ) {
    pUniformS?.setValue( _gl, 'toneMappingExposure', toneMappingExposure );
    if ( materialProperties['needsLights'] == true) {
      markUniformsLightsNeedsUpdate( mUniformS, refreshLights );
    }

    // refresh uniforms common to several materials

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

    materials.refreshMaterialUniforms( mUniformS, material, _pixelRatio, _height, currentRenderState?.state.transmissionRenderTarget[ camera.id ] );
    WebGLUniforms.upload( _gl, getUniformList( materialProperties ), mUniformS, textures );
  }

  if ( material is ShaderMaterial && material.uniformsNeedUpdate == true ) {
    WebGLUniforms.upload( _gl, getUniformList( materialProperties ), mUniformS, textures );
    material.uniformsNeedUpdate = false;
  }

  if ( material is SpriteMaterial ) {
    pUniformS?.setValue( _gl, 'center', (object as Sprite).center );
  }

  // common matrices

  pUniformS?.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
  pUniformS?.setValue( _gl, 'normalMatrix', object.normalMatrix );
  pUniformS?.setValue( _gl, 'modelMatrix', object.matrixWorld );

  // UBOs

  if ( material is ShaderMaterial || material is RawShaderMaterial ) {
    late final List groups;
    if ( material is ShaderMaterial) {
      groups = material.uniformsGroups;
    }
    else if(material is RawShaderMaterial){
      groups = material.uniformsGroups;
    }

    for ( int i = 0, l = groups.length; i < l; i ++ ) {
      final group = groups[i];
      uniformsGroups.update( group, program );
      uniformsGroups.bind( group, program );
    }
  }

  return program!;
}