text: #!/usr/bin/env python
  
  #
  # author: hao su, charles qi
  # last modified: jun 19, 2015
  # render model views. dataset distribution is specified by a distribution file containing samples
  # input: ONE model obj file path (along with texture file in the same dir), rendered img output path, distr file path (contains multiple view - azimuth, elevation, tilt, distance - one per line)
  # usage: blender blank.blend --background --python ../render_model_views.py -- ./sample_model/aeroplane_model.obj ./tmprender/ ./views.txt
  # see example dir for a demo
  #
  
  import bpy
  import bpy_extras
  from mathutils import Vector
  import math
  import sys
  import os
  import numpy as np
  import random
  
  '''
  def camPosToQuaternion(cx, cy, cz):
      camDist = math.sqrt(cx * cx + cy * cy + cz * cz)
      cx = cx / camDist
      cy = cy / camDist
      cz = cz / camDist
      axis = (-cz, 0, cx)
      angle = math.acos(cy)
      a = math.sqrt(2) / 2
      b = math.sqrt(2) / 2
      w1 = axis[0]
      w2 = axis[1]
      w3 = axis[2]
      c = math.cos(angle / 2)
      d = math.sin(angle / 2)
      q1 = a * c - b * d * w1
      q2 = b * c + a * d * w1
      q3 = a * d * w2 + b * d * w3
      q4 = -b * d * w2 + a * d * w3
      return (q1, q2, q3, q4)
  '''
  
  def quaternionFromYawPitchRoll(yaw, pitch, roll):
      c1 = math.cos(yaw / 2.0)
      c2 = math.cos(pitch / 2.0)
      c3 = math.cos(roll / 2.0)    
      s1 = math.sin(yaw / 2.0)
      s2 = math.sin(pitch / 2.0)
      s3 = math.sin(roll / 2.0)    
      q1 = c1 * c2 * c3 + s1 * s2 * s3
      q2 = c1 * c2 * s3 - s1 * s2 * c3
      q3 = c1 * s2 * c3 + s1 * c2 * s3
      q4 = s1 * c2 * c3 - c1 * s2 * s3
      return (q1, q2, q3, q4)
  
  def camPosToQuaternion(cx, cy, cz):
      q1a = 0
      q1b = 0
      q1c = math.sqrt(2) / 2
      q1d = math.sqrt(2) / 2
      camDist = math.sqrt(cx * cx + cy * cy + cz * cz)
      cx = cx / camDist
      cy = cy / camDist
      cz = cz / camDist    
      t = math.sqrt(cx * cx + cy * cy) 
      tx = cx / t
      ty = cy / t
      yaw = math.acos(ty)
      if tx > 0:
          yaw = 2 * math.pi - yaw
      pitch = 0
      # robust acos
      cos_value = min(max(tx * cx + ty * cy, -1), 1)
      roll = math.acos(cos_value)  # math.acos(tx * cx + ty * cy)
      if cz < 0:
          roll = -roll    
      print("%f %f %f" % (yaw, pitch, roll))
      q2a, q2b, q2c, q2d = quaternionFromYawPitchRoll(yaw, pitch, roll)    
      q1 = q1a * q2a - q1b * q2b - q1c * q2c - q1d * q2d
      q2 = q1b * q2a + q1a * q2b + q1d * q2c - q1c * q2d
      q3 = q1c * q2a - q1d * q2b + q1a * q2c + q1b * q2d
      q4 = q1d * q2a + q1c * q2b - q1b * q2c + q1a * q2d
      return (q1, q2, q3, q4)
  
  def camRotQuaternion(cx, cy, cz, theta): 
      theta = theta / 180.0 * math.pi
      camDist = math.sqrt(cx * cx + cy * cy + cz * cz)
      cx = -cx / camDist
      cy = -cy / camDist
      cz = -cz / camDist
      q1 = math.cos(theta * 0.5)
      q2 = -cx * math.sin(theta * 0.5)
      q3 = -cy * math.sin(theta * 0.5)
      q4 = -cz * math.sin(theta * 0.5)
      return (q1, q2, q3, q4)
  
  def quaternionProduct(qx, qy): 
      a = qx[0]
      b = qx[1]
      c = qx[2]
      d = qx[3]
      e = qy[0]
      f = qy[1]
      g = qy[2]
      h = qy[3]
      q1 = a * e - b * f - c * g - d * h
      q2 = a * f + b * e + c * h - d * g
      q3 = a * g - b * h + c * e + d * f
      q4 = a * h + b * g - c * f + d * e    
      return (q1, q2, q3, q4)
  
  def obj_centened_camera_pos(dist, azimuth_deg, elevation_deg):
      phi = float(elevation_deg) / 180 * math.pi
      theta = float(azimuth_deg) / 180 * math.pi
      x = (dist * math.cos(theta) * math.cos(phi))
      y = (dist * math.sin(theta) * math.cos(phi))
      z = (dist * math.sin(phi))
      return (x, y, z)
  
  """---------- main -----------"""
  # modelPath = '/orions2-zfs/projects/haosu/NEC/Data/skp_car/allInOne/1b62d195ae16df54e24d991342425323/1b62d195ae16df54e24d991342425323_normalized.obj' #sys.argv[6]
  # modelPath = '/orions2-zfs/projects/haosu/NEC/Data/skp_car/allInOne/1b62d195ae16df54e24d991342425323/tmp.obj' #sys.argv[6]
  # modelPath = './new.obj' #sys.argv[6]
  # outPath = './' #sys.argv[7]
  modelPath = sys.argv[6]
  outPath = sys.argv[7]
  # view
  distrPath = sys.argv[8]
  
  # example 45 for x, 10 for y, 0 for z, 2 for camrea distance
  renderParams = [[float(x) for x in line.strip().split(' ')] for line in open(distrPath).readlines()]
  numRendering = len(renderParams)
  
  # 2d skel
  # blender has error!
  '''
  modellen = len(modelPath)
  skelpath = modelPath[0:modellen-4]
  skelpath = skelpath + '.txt'
  skeletons = [[float(x) for x in line.strip().split(' ')] for line in open(skelpath).readlines()]
  '''
  
  bpy.ops.import_scene.obj(filepath=modelPath) 
  
  
  # bpy.data.objects[0].active_material=bpy.data.materials[0]
  bpy.context.scene.objects.active = bpy.data.objects[0]
  
  knob = bpy.data.objects[0]
  knob.rotation_mode = 'XYZ'
  knob.rotation_euler = ( 5/180*3.1416, 0, 0 )
  
  # blender默认用的是0.5，太强烈了。在渲染的时候你得把这个弄成在0.01～0.2之间采样吧
  # bpy.context.object.active_material.specular_intensity = 0
  # bpy.context.object.active_material.diffuse_intensity = np.random.normal(0.5, 0.1)
  # bpy.context.object.active_material.ambient = 0.8
  
  ma = bpy.data.materials
  for a in ma:
      a.specular_intensity = 0
      a.diffuse_intensity = np.random.normal(0.6, 0.15)
      a.ambient = 0.8
  
  bpy.ops.object.shade_smooth()  # 这个是搞smooth shading的
  
  
  # background is transparent
  bpy.context.scene.render.alpha_mode = 'TRANSPARENT'
  # bpy.context.scene.render.use_shadows = False
  # bpy.context.scene.render.use_raytrace = False
  
  bpy.data.objects['Lamp'].data.energy = 0
  
  # m.subsurface_scattering.use = True
  
  camObj = bpy.data.objects['Camera']
  # camObj.data.lens_unit = 'FOV'
  # camObj.data.angle = 0.2
  
  # set lights
  bpy.ops.object.select_all(action='TOGGLE')
  if 'Lamp' in list(bpy.data.objects.keys()):
      bpy.data.objects['Lamp'].select = True  # remove default light
  bpy.ops.object.delete()
  
  # YOUR CODE START HERE
  numLight = 4
  lightDist = 14.14
  # print(list(bpy.data.objects))
  
  # outlierProb = 0.2
  
  for param in renderParams:
      # t = random.random()
      # if t > outlierProb:
      #    azimuth_deg = param[0]
      #    elevation_deg = param[1]
      # else: # with a small probability generate a random position
      #    azimuth_deg = random.uniform(0, 360)
      #    elevation_deg = random.uniform(-90, 90)
      
      azimuth_deg = param[0]
      elevation_deg = param[1]
      theta_deg = param[2]
      rho = param[3]
      
      # clear default lights
      bpy.ops.object.select_by_type(type='LAMP')
      bpy.ops.object.delete(use_global=False)
      
      # set environment lighting
      # bpy.context.space_data.context = 'WORLD'
      bpy.context.scene.world.light_settings.use_environment_light = True
      bpy.context.scene.world.light_settings.environment_energy = np.random.uniform(0.8, 1.2)
      bpy.context.scene.world.light_settings.environment_color = 'PLAIN'
      
      
      # set point lights
      # random.randint(4, 8)
      pnum = 1
      for i in range(pnum):  # range(numLight):
          light_azimuth_deg = np.random.uniform(0, 360)
          light_elevation_deg = np.random.uniform(-90, 90)
          lx, ly, lz = obj_centened_camera_pos(lightDist, light_azimuth_deg, light_elevation_deg)
          bpy.ops.object.lamp_add(type='SUN', view_align=False, location=(lx, ly, lz))
          # bpy.ops.object.lamp_add(type='SUN')
          bpy.data.objects['Sun'].rotation_euler[0] = np.random.uniform(-90, 90) / 180 * 3.1416
          bpy.data.objects['Sun'].rotation_euler[1] = np.random.uniform(-90, 90) / 180 * 3.1416
          bpy.data.objects['Sun'].rotation_euler[2] = np.random.uniform(-90, 90) / 180 * 3.1416
          bpy.data.objects['Sun'].data.energy = np.random.normal(1.2, 0.3)
      
      
      cx, cy, cz = obj_centened_camera_pos(rho, azimuth_deg, elevation_deg)
      q1 = camPosToQuaternion(cx, cy, cz)
      q2 = camRotQuaternion(cx, cy, cz, theta_deg)
      q = quaternionProduct(q2, q1)
      
      camObj.location[0] = cx
      camObj.location[1] = cy
      camObj.location[2] = cz
      camObj.rotation_mode = 'QUATERNION'
      camObj.rotation_quaternion[0] = q[0]
      camObj.rotation_quaternion[1] = q[1]
      camObj.rotation_quaternion[2] = q[2]
      camObj.rotation_quaternion[3] = q[3]
      
      if np.random.uniform(-1, 1) > 0:
          # motion blur
          # switch on nodes
          bpy.context.scene.use_nodes = True
          tree = bpy.context.scene.node_tree
          links = tree.links
          
          # 3 layers
          composite = tree.nodes[0]
          renderlayer = tree.nodes[1]
          # tree.nodes[2]
          motionblur = tree.nodes.new('CompositorNodeVecBlur')  
          
          # 0 is image
          links.new(motionblur.outputs[0], composite.inputs[0])
          links.new(renderlayer.outputs[0], motionblur.inputs[0])
          
          vectorlen = np.random.normal(0, 12.0)
          vectordirection = np.random.uniform(-1, 1, size=3)
          
          # vector & length
          motionblur.inputs[2].default_value[0] = vectordirection[0] * vectorlen
          motionblur.inputs[2].default_value[1] = vectordirection[1] * vectorlen
          motionblur.inputs[2].default_value[2] = vectordirection[2] * vectorlen
          # the more the more time
          motionblur.samples = 4
      
      
      bpy.data.scenes['Scene'].render.filepath = os.path.join(outPath, './image_a%03d_e%03d_t%03d_d%03d.png' % (round(azimuth_deg), round(elevation_deg), round(theta_deg), round(rho)))
      bpy.ops.render.render(write_still=True)
          
      
      log = open(os.path.join(outPath, './image_a%03d_e%03d_t%03d_d%03d_cam.txt' % (round(azimuth_deg), round(elevation_deg), round(theta_deg), round(rho))), 'w')
      log.write('%f %f %f %f\n' % (cx, cy, cz, 0))
      log.write('%f %f %f %f\n' % (q[0], q[1], q[2], q[3]))      
      log.close()  
      
      '''
      cx, cy, cz = obj_centened_camera_pos(rho, azimuth_deg, elevation_deg)
      q1 = camPosToQuaternion(cx, cy, cz)
      q2 = camRotQuaternion(cx, cy, cz, theta_deg)
      q = quaternionProduct(q2, q1)
      camObj.location[0] = cx
      camObj.location[1] = cy 
      camObj.location[2] = cz
      camObj.rotation_mode = 'QUATERNION'
      camObj.rotation_quaternion[0] = q[0]
      camObj.rotation_quaternion[1] = q[1]
      camObj.rotation_quaternion[2] = q[2]
      camObj.rotation_quaternion[3] = q[3]
      bpy.data.scenes['Scene'].render.filepath = os.path.join(outPath, './image_a%03d_e%03d_t%03d_d%03d.png' % (round(azimuth_deg), round(elevation_deg), round(theta_deg), round(rho)))
      bpy.ops.render.render( write_still=True )
      '''
  