void PbrDrawable::draw()

in src/esp/gfx/PbrDrawable.cpp [87:215]


void PbrDrawable::draw(const Mn::Matrix4& transformationMatrix,
                       Mn::SceneGraph::Camera3D& camera) {
  CORRADE_ASSERT(glMeshExists(),
                 "PbrDrawable::draw() : GL mesh doesn't exist", );

  updateShader()
      .updateShaderLightParameters()
      .updateShaderLightDirectionParameters(transformationMatrix, camera);

  // ABOUT PbrShader::Flag::DoubleSided:
  //
  // "Specifies whether the material is double sided. When this value is false,
  // back-face culling is enabled. When this value is true, back-face culling is
  // disabled and double sided lighting is enabled. The back-face must have its
  // normals reversed before the lighting equation is evaluated."
  // See here:
  // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/schema/material.schema.json

  // HOWEVER, WE CANNOT DISABLE BACK FACE CULLING (that is why the following
  // code is commented out) since it causes lighting artifacts ("dashed lines")
  // on hard edges. (maybe due to potential numerical issues? we do not know
  // yet.)
  /*
  if ((flags_ & PbrShader::Flag::DoubleSided) && glIsEnabled(GL_CULL_FACE)) {
    Mn::GL::Renderer::disable(Mn::GL::Renderer::Feature::FaceCulling);
  }
  */
  Mn::Matrix4 modelMatrix =
      camera.cameraMatrix().inverted() * transformationMatrix;

  (*shader_)
      // e.g., semantic mesh has its own per vertex annotation, which has been
      // uploaded to GPU so simply pass 0 to the uniform "objectId" in the
      // fragment shader
      .setObjectId(
          static_cast<RenderCamera&>(camera).useDrawableIds()
              ? drawableId_
              : (materialData_->perVertexObjectId ? 0 : node_.getSemanticId()))
      .setProjectionMatrix(camera.projectionMatrix())
      .setViewMatrix(camera.cameraMatrix())
      .setModelMatrix(modelMatrix)  // NOT modelview matrix!
      .setNormalMatrix(modelMatrix.normalMatrix())
      .setCameraWorldPosition(
          camera.object().absoluteTransformationMatrix().translation())
      .setBaseColor(materialData_->baseColor)
      .setRoughness(materialData_->roughness)
      .setMetallic(materialData_->metallic)
      .setEmissiveColor(materialData_->emissiveColor);

  // TODO:
  // IN PbrShader class, we set the resonable defaults for the
  // PbrShader::PbrEquationScales. Here we need a smart way to reset it
  // just in case user would like to do so during the run-time.

  if ((flags_ & PbrShader::Flag::BaseColorTexture) &&
      (materialData_->baseColorTexture != nullptr)) {
    shader_->bindBaseColorTexture(*materialData_->baseColorTexture);
  }

  if (flags_ &
      (PbrShader::Flag::RoughnessTexture | PbrShader::Flag::MetallicTexture)) {
    Magnum::GL::Texture2D* metallicRoughnessTexture =
        materialData_->roughnessTexture;
    if (!metallicRoughnessTexture) {
      metallicRoughnessTexture = materialData_->metallicTexture;
    }
    CORRADE_ASSERT(metallicRoughnessTexture,
                   "PbrDrawable::draw(): texture pointer cannot be nullptr if "
                   "RoughnessTexture or MetallicTexture is enabled.", );
    shader_->bindMetallicRoughnessTexture(*metallicRoughnessTexture);
  }

  if ((flags_ & PbrShader::Flag::NormalTexture) &&
      (materialData_->normalTexture != nullptr)) {
    shader_->bindNormalTexture(*materialData_->normalTexture);
  }

  if ((flags_ & PbrShader::Flag::EmissiveTexture) &&
      (materialData_->emissiveTexture != nullptr)) {
    shader_->bindEmissiveTexture(*materialData_->emissiveTexture);
  }

  if ((flags_ & PbrShader::Flag::TextureTransformation) &&
      (materialData_->textureMatrix != Mn::Matrix3{})) {
    shader_->setTextureMatrix(materialData_->textureMatrix);
  }

  // setup image based lighting for the shader
  if (flags_ & PbrShader::Flag::ImageBasedLighting) {
    CORRADE_INTERNAL_ASSERT(pbrIbl_);
    shader_->bindIrradianceCubeMap(  // TODO: HDR Color
        pbrIbl_->getIrradianceMap().getTexture(CubeMap::TextureType::Color));
    shader_->bindBrdfLUT(pbrIbl_->getBrdfLookupTable());
    shader_->bindPrefilteredMap(
        // TODO: HDR Color
        pbrIbl_->getPrefilteredMap().getTexture(CubeMap::TextureType::Color));
    shader_->setPrefilteredMapMipLevels(
        pbrIbl_->getPrefilteredMap().getMipmapLevels());
  }

  if (flags_ & PbrShader::Flag::ShadowsVSM) {
    CORRADE_INTERNAL_ASSERT(shadowMapManger_ && shadowMapKeys_);
    CORRADE_ASSERT(shadowMapKeys_->size() <= 3,
                   "PbrDrawable::draw: the number of shadow maps exceeds the "
                   "maximum (current it is 3).", );
    for (int iShadow = 0; iShadow < shadowMapKeys_->size(); ++iShadow) {
      Mn::Resource<CubeMap> shadowMap =
          (*shadowMapManger_).get<CubeMap>((*shadowMapKeys_)[iShadow]);

      CORRADE_INTERNAL_ASSERT(shadowMap);

      if (flags_ & PbrShader::Flag::ShadowsVSM) {
        shader_->bindPointShadowMap(
            iShadow,
            shadowMap->getTexture(CubeMap::TextureType::VarianceShadowMap));
      }
    }
  }

  shader_->draw(getMesh());

  // WE stopped supporting doubleSided material due to lighting artifacts on
  // hard edges. See comments at the beginning of this function.
  /*
  if ((flags_ & PbrShader::Flag::DoubleSided) && !glIsEnabled(GL_CULL_FACE)) {
    Mn::GL::Renderer::enable(Mn::GL::Renderer::Feature::FaceCulling);
  }
  */
}