#~ Builtin Modules:       bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bpy.utils, bgl, blf, mathutils
#~ Convenience Imports:   from mathutils import *; from math import *
#~ Convenience Variables: C = bpy.context, D = bpy.data
#~ 

import bpy
from bpy import data as D
from bpy import context as C
from mathutils import *
from math import *

import sys
import os.path
from os import path
import csv

"""Module for rendering rotation data set. 
   Usage:
   > blender.exe --background --python Renders.py -- <cameraCalibration> <model> 
   <referencePose> <offset> <outputDirectoryPath>
   
   Input arguments:
   * cameraCalibration - path to csv file containing camera calibration
   * model - path to the model in the .obj file format
   * referencePose - csv containing the reference model pose
   * offset - offset within one degree range
   * outputDirectoryPath - output directory for the rendered images
"""

__author__ = "Ondrej Klima"
__copyright__ = "Copyright 2020"
__credits__ = ["Ondrej Klima"]
__email__ = "iklima@fit.vutbr.cz"
__license__ = "BUT"
__version__ = "1.0"
__maintainer__ = "Ondrej Klima"

# Parsing input arguments
 
argv = sys.argv
try:
  argv = argv[argv.index("--") + 1:]
except ValueError:
  raise ValueError('Input arguments missing!')
  
try:
  cameraCalibrationFileName = argv[0]
except IndexError:
  raise IndexError('Camera pose file must be supplied on the command line')
if not path.isfile(cameraCalibrationFileName):
  raise ValueError('File "%s" does not exist!' % cameraCalibrationFileName)    

try:
  modelFileName = argv[1]
except IndexError:
  raise IndexError('Model object must be supplied on the command line')
if not path.isfile(modelFileName):
  raise ValueError('File "%s" does not exist!' % modelFileName)

try:
  referencePoseFileName = argv[2]
except IndexError:
  raise IndexError('Model reference pose file must be supplied on the command line')
if not path.isfile(referencePoseFileName):
  raise ValueError('File "%s" does not exist!' % referencePoseFileName)
  
try:
  offset = argv[3]
except IndexError:
  raise IndexError('Offset must be supplied on the command line')   

try:
  outputDirectory = argv[4]
except IndexError:
  raise IndexError('Output directory for the rendered images must be supplied on the command line.')
    

# Delete the implicit Cube
try:
  bpy.ops.object.select_all(action='DESELECT')
  bpy.data.objects['Cube'].select_set(True)
  bpy.ops.object.delete() 
except KeyError:
  print('"Cube" object not found')

# Load the object
productObject = bpy.ops.import_scene.obj(filepath=modelFileName)
prod_object = bpy.context.selected_objects[0]
print('Imported name: ', prod_object.name)

# Set camera parameters
with open(cameraCalibrationFileName, newline='') as csvfile:
  cameraReader = csv.reader(csvfile, delimiter=',', quotechar='|')
  cameraData = list(cameraReader)

bpy.data.objects['Camera'].rotation_euler[0] = radians(float(cameraData[0][0]))
bpy.data.objects['Camera'].rotation_euler[1] = radians(float(cameraData[0][1]))
bpy.data.objects['Camera'].rotation_euler[2] = radians(float(cameraData[0][2]))

bpy.data.objects['Camera'].location[0] = float(cameraData[0][3])
bpy.data.objects['Camera'].location[1] = float(cameraData[0][4])
bpy.data.objects['Camera'].location[2] = float(cameraData[0][5])

bpy.data.cameras['Camera'].lens = float(cameraData[0][6])

# reference pose 
with open(referencePoseFileName, newline='') as csvfile:
  productReferenceReader = csv.reader(csvfile, delimiter=',', quotechar='|')
  productReferenceData = list(productReferenceReader)

refProductRotation = [.0, .0, .0]
refProductLocation = [.0, .0, .0]

refProductRotation[0] = float(productReferenceData[0][0])
refProductRotation[1] = float(productReferenceData[0][1])
refProductRotation[2] = float(productReferenceData[0][2])

refProductLocation[0] = float(productReferenceData[0][3])
refProductLocation[1] = float(productReferenceData[0][4])
refProductLocation[2] = float(productReferenceData[0][5])

refProductRotationRad = [0, 0, 0]
for i in range(len(refProductRotation)):
  refProductRotationRad[i] = radians(refProductRotation[i])

# Set product pose
bpy.data.objects[prod_object.name].location = refProductLocation
bpy.data.objects[prod_object.name].rotation_euler = refProductRotation

# Rendering engine set up
bpy.data.scenes['Scene'].render.engine = 'CYCLES'
bpy.data.scenes['Scene'].cycles.feature_set = 'SUPPORTED'
bpy.data.scenes['Scene'].cycles.device = 'GPU'
bpy.data.scenes['Scene'].cycles.use_denoising = True
bpy.data.scenes['Scene'].cycles.denoiser = 'OPENIMAGEDENOISE'
bpy.data.scenes['Scene'].cycles.samples = 256

bpy.data.scenes['Scene'].render.resolution_y = 2048;

# Set up light
bpy.data.lights['Light'].type = 'SUN'
bpy.data.lights['Light'].energy = 5

# Rendering border
bpy.context.scene.render.border_min_x = 0.3
bpy.context.scene.render.border_min_y = 0.2
bpy.context.scene.render.border_max_x = 0.7
bpy.context.scene.render.border_max_y = 0.8

bpy.context.scene.render.use_border = True
bpy.context.scene.render.use_crop_to_border = True

bpy.data.objects[prod_object.name].hide_render = False     
bpy.context.scene.render.film_transparent = False  
  
for i in range(360):  
  # Model poses
  rotation = [i + float(offset), 0, 0] # rotation is in degree units 
  rotationRad = [0, 0, 0]

  for j in range(len(refProductRotation)):
    rotationRad[j] = radians(refProductRotation[j] + float(rotation[j]))
   
  bpy.data.objects[prod_object.name].rotation_euler = rotationRad  
    
  translation = [0, 0, 0]
  for j in range(len(refProductRotation)):
    bpy.data.objects[prod_object.name].location[j] = float(refProductLocation[j]) 
    
  bpy.data.objects[prod_object.name].hide_render = False     
  bpy.context.scene.render.film_transparent = True    
  bpy.context.scene.render.filepath = outputDirectory  + '\\%d.png' % i
  bpy.ops.render.render(write_still = True)