code: def make_material_atlas(
      image: torch.Tensor,
      faces_verts_uvs: torch.Tensor,
      texture_size: int,
  ) -> torch.Tensor:
      r"""
      Given a single texture image and the uv coordinates for all the
      face vertices, create a square texture map per face using
      the formulation from [1].
  
      For a triangle with vertices (v0, v1, v2) we can create a barycentric coordinate system
      with the x axis being the vector (v0 - v2) and the y axis being the vector (v1 - v2).
      The barycentric coordinates range from [0, 1] in the +x and +y direction so this creates
      a triangular texture space with vertices at (0, 1), (0, 0) and (1, 0).
  
      The per face texture map is of shape (texture_size, texture_size, 3)
      which is a square. To map a triangular texture to a square grid, each
      triangle is parametrized as follows (e.g. R = texture_size = 3):
  
      The triangle texture is first divided into RxR = 9 subtriangles which each
      map to one grid cell. The numbers in the grid cells and triangles show the mapping.
  
      ..code-block::python
  
          Triangular Texture Space:
  
                1
                  |\
                  |6 \
                  |____\
                  |\  7 |\
                  |3 \  |4 \
                  |____\|____\
                  |\ 8  |\  5 |\
                  |0 \  |1 \  |2 \
                  |____\|____\|____\
                 0                   1
  
          Square per face texture map:
  
                 R ____________________
                  |      |      |      |
                  |  6   |  7   |  8   |
                  |______|______|______|
                  |      |      |      |
                  |  3   |  4   |  5   |
                  |______|______|______|
                  |      |      |      |
                  |  0   |  1   |  2   |
                  |______|______|______|
                 0                      R
  
  
      The barycentric coordinates of each grid cell are calculated using the
      xy coordinates:
  
      ..code-block::python
  
              The cartesian coordinates are:
  
              Grid 1:
  
                 R ____________________
                  |      |      |      |
                  |  20  |  21  |  22  |
                  |______|______|______|
                  |      |      |      |
                  |  10  |  11  |  12  |
                  |______|______|______|
                  |      |      |      |
                  |  00  |  01  |  02  |
                  |______|______|______|
                 0                      R
  
              where 02 means y = 0, x = 2
  
          Now consider this subset of the triangle which corresponds to
          grid cells 0 and 8:
  
          ..code-block::python
  
              1/R  ________
                  |\    8  |
                  |  \     |
                  | 0   \  |
                  |_______\|
                 0          1/R
  
          The centroids of the triangles are:
              0: (1/3, 1/3) * 1/R
              8: (2/3, 2/3) * 1/R
  
      For each grid cell we can now calculate the centroid `(c_y, c_x)`
      of the corresponding texture triangle:
          - if `(x + y) < R`, then offset the centroid of
              triangle 0 by `(y, x) * (1/R)`
          - if `(x + y) > R`, then offset the centroid of
              triangle 8 by `((R-1-y), (R-1-x)) * (1/R)`.
  
      This is equivalent to updating the portion of Grid 1
      above the diagonal, replacing `(y, x)` with `((R-1-y), (R-1-x))`:
  
      ..code-block::python
  
                R _____________________
                  |      |      |      |
                  |  20  |  01  |  00  |
                  |______|______|______|
                  |      |      |      |
                  |  10  |  11  |  10  |
                  |______|______|______|
                  |      |      |      |
                  |  00  |  01  |  02  |
                  |______|______|______|
                 0                      R
  
      The barycentric coordinates (w0, w1, w2) are then given by:
  
      ..code-block::python
  
          w0 = c_x
          w1 = c_y
          w2 = 1- w0 - w1
  
      Args:
          image: FloatTensor of shape (H, W, 3)
          faces_verts_uvs: uv coordinates for each vertex in each face  (F, 3, 2)
          texture_size: int
  
      Returns:
          atlas: a FloatTensor of shape (F, texture_size, texture_size, 3) giving a
              per face texture map.
  
      [1] Liu et al, 'Soft Rasterizer: A Differentiable Renderer for Image-based
          3D Reasoning', ICCV 2019
      """
      R = texture_size
      device = faces_verts_uvs.device
      rng = torch.arange(R, device=device)
  
      # Meshgrid returns (row, column) i.e (Y, X)
      # Change order to (X, Y) to make the grid.
      Y, X = meshgrid_ij(rng, rng)
      # pyre-fixme[28]: Unexpected keyword argument `axis`.
      grid = torch.stack([X, Y], axis=-1)  # (R, R, 2)
  
      # Grid cells below the diagonal: x + y < R.
      below_diag = grid.sum(-1) < R
  
      # map a [0, R] grid -> to a [0, 1] barycentric coordinates of
      # the texture triangle centroids.
      bary = torch.zeros((R, R, 3), device=device)  # (R, R, 3)
      slc = torch.arange(2, device=device)[:, None]
      # w0, w1
      bary[below_diag, slc] = ((grid[below_diag] + 1.0 / 3.0) / R).T
      # w0, w1 for above diagonal grid cells.
      bary[~below_diag, slc] = (
          ((R - 1.0 - grid[~below_diag]) + 2.0 / 3.0) / R
      ).T
      # w2 = 1. - w0 - w1
      bary[..., -1] = 1 - bary[..., :2].sum(dim=-1)
  
      # Calculate the uv position in the image for each pixel
      # in the per face texture map
      # (F, 1, 1, 3, 2) * (R, R, 3, 1) -> (F, R, R, 3, 2) -> (F, R, R, 2)
      uv_pos = (faces_verts_uvs[:, None, None] * bary[..., None]).sum(
          -2
      )
  
      # bi-linearly interpolate the textures from the images
      # using the uv coordinates given by uv_pos.
      textures = _bilinear_interpolation_grid_sample(image, uv_pos)
  
      return textures
  
file_path: pytorch3d/pytorch3d/io/mtl_io.py
imports: import os
  import warnings
  from typing import Dict, List, Optional, Tuple
  import numpy
  import torch
  import torch.nn.functional
  from iopath.common.file_io import PathManager
  from pytorch3d.common.compat import meshgrid_ij
  from pytorch3d.common.datatypes import Device
  from pytorch3d.io.utils import _open_file, _read_image
label_desc: Use image node_id 2 for calling a global function with example usage: make_material_atlas(image, faces_verts_uvs, texture_size) and returns: textures