code: # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
  
  # Permission is hereby granted, free of charge, to any person obtaining a
  # copy of this software and associated documentation files (the "Software"),
  # to deal in the Software without restriction, including without limitation
  # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  # and/or sell copies of the Software, and to permit persons to whom the
  # Software is furnished to do so, subject to the following conditions:
  
  # The above copyright notice and this permission notice shall be included in
  # all copies or substantial portions of the Software.
  
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  # DEALINGS IN THE SOFTWARE.
  
  import numpy as np
  
  
  ##################################################################
  # symmetric over z axis
  def get_spherical_coords_z(X):
      # X is N x 3
      rad = np.linalg.norm(X, axis=1)
      # Inclination
      theta = np.arccos(X[:, 2] / rad)
      # Azimuth
      phi = np.arctan2(X[:, 1], X[:, 0])
  
      # Normalize both to be between [-1, 1]
      vv = (theta / np.pi) * 2 - 1
      uu = ((phi + np.pi) / (2 * np.pi)) * 2 - 1
      # Return N x 2
      return np.stack([uu, vv], 1)
  
  
  # symmetric over x axis
  def get_spherical_coords_x(X):
      # X is N x 3
      rad = np.linalg.norm(X, axis=1)
      # Inclination
      # y == 1
      # cos = 0
      # y == -1
      # cos = pi
      theta = np.arccos(X[:, 0] / rad)
      # Azimuth
      phi = np.arctan2(X[:, 2], X[:, 1])
  
      # Normalize both to be between [-1, 1]
      uu = (theta / np.pi) * 2 - 1
      vv = ((phi + np.pi) / (2 * np.pi)) * 2 - 1
      # Return N x 2
      return np.stack([uu, vv], 1)
  
  
  # symmetric spherical projection
  def get_symmetric_spherical_tex_coords(vertex_pos, symmetry_axis=1, up_axis=2, front_axis=0):
      # vertex_pos is N x 3
      length = np.linalg.norm(vertex_pos, axis=1)
      # Inclination
      theta = np.arccos(vertex_pos[:, front_axis] / length)
      # Azimuth
      phi = np.abs(np.arctan2(vertex_pos[:, symmetry_axis], vertex_pos[:, up_axis]))
  
      # Normalize both to be between [-1, 1]
      uu = (theta / np.pi) * 2 - 1
      # vv = ((phi + np.pi) / (2 * np.pi)) * 2 - 1
      vv = (phi / np.pi) * 2 - 1
      # Return N x 2
      return np.stack([uu, vv], 1)
  
  
  #########################################################################
  if __name__ == "__main__":
  
      from utils.utils_mesh import loadobj, savemeshtes
      import cv2
  
      p, f = loadobj("2.obj")
      uv = get_spherical_coords_x(p)
      uv[:, 0] = -uv[:, 0]
  
      uv[:, 1] = -uv[:, 1]
      uv = (uv + 1) / 2
      savemeshtes(p, uv, f, "./2_x.obj")
  
      tex = np.zeros(shape=(256, 512, 3), dtype=np.uint8)
      font = cv2.FONT_HERSHEY_SIMPLEX
      bottomLeftCornerOfText = (10, 200)
      fontScale = 5
      fontColor = (0, 255, 255)
      lineType = 2
  
      cv2.putText(tex, "Hello World!", bottomLeftCornerOfText, font, fontScale, fontColor, lineType)
      cv2.imshow("", tex)
      cv2.waitKey()
      cv2.imwrite("2_x.png", np.transpose(tex, [1, 0, 2]))
   from __future__ import print_function
  from __future__ import division
  
  from ..rasterizer import linear_rasterizer
  from ..utils import datanormalize
  from .fragment_shaders.frag_tex import fragmentshader
  from .vertex_shaders.perpsective import perspective_projection
  import torch
  import torch.nn as nn
  import numpy as np
  
  
  ##################################################################
  class TexRenderBatch(nn.Module):
      def __init__(self, height, width, filtering="nearest"):
          super(TexRenderBatch, self).__init__()
  
          self.height = height
          self.width = width
          self.filtering = filtering
  
      def forward(self, points, cameras, uv_bxpx2, texture_bx3xthxtw, ft_fx3=None):
          """
          points: b x [points_1xpx3, faces_fx3]
          cameras: [camera_rot_bx3x3, camera_pos_bx3, camera_proj_3x1]
          uv_bxpx2: b x [1xpx2]
          texture_bx3xthxtw: b x [1x3xthxtw]
          ft_fx3: b x [fx3]
          """
          b = len(points)
          assert b > 0, b
          points3d_1xfx9_list = []
          points2d_1xfx6_list = []
          normalz_1xfx1_list = []
          normal1_1xfx3_list = []
          uv_1xfx9_list = []
  
          single_intrinsic = True
          if cameras[2].ndim == 3:
              assert cameras[2].shape[0] == b
              single_intrinsic = False
  
          for i in range(b):
              ##############################################################
              # first, MVP projection in vertexshader
              points_1xpx3, faces_fx3 = points[i]
              if single_intrinsic:
                  cam_params = [cameras[0][i : i + 1], cameras[1][i : i + 1], cameras[2]]
              else:
                  cam_params = [cameras[0][i : i + 1], cameras[1][i : i + 1], cameras[2][i]]
              # use faces_fx3 as ft_fx3 if not given
              if ft_fx3 is None:
                  ft_fx3_single = faces_fx3
              else:
                  ft_fx3_single = ft_fx3[i]
  
              points3d_1xfx9, points2d_1xfx6, normal_1xfx3 = perspective_projection(points_1xpx3, faces_fx3, cam_params)
  
              ################################################################
              # normal
  
              # decide which faces are front and which faces are back
              normalz_1xfx1 = normal_1xfx3[:, :, 2:3]
              # normalz_bxfx1 = torch.abs(normalz_bxfx1)
  
              # normalize normal
              normal1_1xfx3 = datanormalize(normal_1xfx3, axis=2)
  
              ############################################################
              # second, rasterization
              uv_1xpx2 = uv_bxpx2[i]
  
              c0 = uv_1xpx2[:, ft_fx3_single[:, 0], :]
              c1 = uv_1xpx2[:, ft_fx3_single[:, 1], :]
              c2 = uv_1xpx2[:, ft_fx3_single[:, 2], :]
              mask = torch.ones_like(c0[:, :, :1])
              uv_1xfx9 = torch.cat((c0, mask, c1, mask, c2, mask), dim=2)
  
              # append data
              points3d_1xfx9_list.append(points3d_1xfx9)
              points2d_1xfx6_list.append(points2d_1xfx6)
              normalz_1xfx1_list.append(normalz_1xfx1)
              normal1_1xfx3_list.append(normal1_1xfx3)
              uv_1xfx9_list.append(uv_1xfx9)
  
          # put the object with larger depth earlier
  
          # imrender = torch.empty((1, self.height, self.width, 3), device=device, dtype=torch.float32)
          # improb_1xhxwx1 = torch.empty((1, self.height, self.width, 1), device=device, dtype=torch.float32)
          # fg_mask = torch.empty((1, self.height, self.width, 1), device=device, dtype=torch.float32)
          ren_ims = []
          ren_masks = []
          ren_probs = []
          for i in range(b):
              imfeat, improb_1xhxwx1_i = linear_rasterizer(
                  self.width,
                  self.height,
                  points3d_1xfx9_list[i],
                  points2d_1xfx6_list[i],
                  normalz_1xfx1_list[i],
                  uv_1xfx9_list[i],
              )
              imtexcoords = imfeat[:, :, :, :2]  # (1,H,W,2)
              hardmask = imfeat[:, :, :, 2:3]  # (1,H,W,1) mask
              # fragrement shader
              texture_1x3xthxtw = texture_bx3xthxtw[i]
              imrender_i = fragmentshader(imtexcoords, texture_1x3xthxtw, hardmask)
              ren_ims.append(imrender_i)  # 1HW3
              ren_probs.append(improb_1xhxwx1_i)
              ren_masks.append(hardmask)
  
          imrender = torch.cat(ren_ims, dim=0)  # bHW3
          improb_bxhxwx1 = torch.cat(ren_probs, dim=0)
          mask_bxhxwx1 = torch.cat(ren_masks, dim=0)
          # return imrender, improb_1xhxwx1, normal1_1xfx3_list
          return imrender, improb_bxhxwx1, normal1_1xfx3_list, mask_bxhxwx1
  
apis: numpy.arctan2 numpy.transpose numpy.zeros numpy.arccos numpy.stack numpy.linalg.norm
apis: torch.ones_like torch.cat