light_shadow.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from .logger import *
  2. from .package_level import *
  3. from .f_curve_animatable import *
  4. import bpy
  5. from mathutils import Color, Vector
  6. # used in Light constructor, never formally defined in Babylon, but used in babylonFileLoader
  7. POINT_LIGHT = 0
  8. DIRECTIONAL_LIGHT = 1
  9. SPOT_LIGHT = 2
  10. HEMI_LIGHT = 3
  11. #used in ShadowGenerators
  12. NO_SHADOWS = 'NONE'
  13. STD_SHADOWS = 'STD'
  14. POISSON_SHADOWS = 'POISSON'
  15. VARIANCE_SHADOWS = 'VARIANCE'
  16. BLUR_VARIANCE_SHADOWS = 'BLUR_VARIANCE'
  17. #===============================================================================
  18. class Light(FCurveAnimatable):
  19. def __init__(self, light, meshesAndNodes):
  20. if light.parent and light.parent.type != 'ARMATURE':
  21. self.parentId = light.parent.name
  22. self.name = light.name
  23. Logger.log('processing begun of light (' + light.data.type + '): ' + self.name)
  24. self.define_animations(light, False, True, False)
  25. light_type_items = {'POINT': POINT_LIGHT, 'SUN': DIRECTIONAL_LIGHT, 'SPOT': SPOT_LIGHT, 'HEMI': HEMI_LIGHT, 'AREA': POINT_LIGHT}
  26. self.light_type = light_type_items[light.data.type]
  27. if self.light_type == POINT_LIGHT:
  28. self.position = light.location
  29. if hasattr(light.data, 'use_sphere'):
  30. if light.data.use_sphere:
  31. self.range = light.data.distance
  32. elif self.light_type == DIRECTIONAL_LIGHT:
  33. self.position = light.location
  34. self.direction = Light.get_direction(light.matrix_local)
  35. elif self.light_type == SPOT_LIGHT:
  36. self.position = light.location
  37. self.direction = Light.get_direction(light.matrix_local)
  38. self.angle = light.data.spot_size
  39. self.exponent = light.data.spot_blend * 2
  40. if light.data.use_sphere:
  41. self.range = light.data.distance
  42. else:
  43. # Hemi
  44. matrix_local = light.matrix_local.copy()
  45. matrix_local.translation = Vector((0, 0, 0))
  46. self.direction = (Vector((0, 0, -1)) * matrix_local)
  47. self.direction = scale_vector(self.direction, -1)
  48. self.groundColor = Color((0, 0, 0))
  49. self.intensity = light.data.energy
  50. self.diffuse = light.data.color if light.data.use_diffuse else Color((0, 0, 0))
  51. self.specular = light.data.color if light.data.use_specular else Color((0, 0, 0))
  52. # inclusion section
  53. if light.data.use_own_layer:
  54. lampLayer = getLayer(light)
  55. self.includedOnlyMeshesIds = []
  56. for mesh in meshesAndNodes:
  57. if mesh.layer == lampLayer:
  58. self.includedOnlyMeshesIds.append(mesh.name)
  59. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  60. @staticmethod
  61. def get_direction(matrix):
  62. return (matrix.to_3x3() * Vector((0.0, 0.0, -1.0))).normalized()
  63. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  64. def to_scene_file(self, file_handler):
  65. file_handler.write('{')
  66. write_string(file_handler, 'name', self.name, True)
  67. write_string(file_handler, 'id', self.name)
  68. write_float(file_handler, 'type', self.light_type)
  69. if hasattr(self, 'parentId' ): write_string(file_handler, 'parentId' , self.parentId )
  70. if hasattr(self, 'position' ): write_vector(file_handler, 'position' , self.position )
  71. if hasattr(self, 'direction' ): write_vector(file_handler, 'direction' , self.direction )
  72. if hasattr(self, 'angle' ): write_float (file_handler, 'angle' , self.angle )
  73. if hasattr(self, 'exponent' ): write_float (file_handler, 'exponent' , self.exponent )
  74. if hasattr(self, 'groundColor'): write_color (file_handler, 'groundColor', self.groundColor)
  75. if hasattr(self, 'range' ): write_float (file_handler, 'range' , self.range )
  76. write_float(file_handler, 'intensity', self.intensity)
  77. write_color(file_handler, 'diffuse', self.diffuse)
  78. write_color(file_handler, 'specular', self.specular)
  79. if hasattr(self, 'includedOnlyMeshesIds'):
  80. file_handler.write(',"includedOnlyMeshesIds":[')
  81. first = True
  82. for meshId in self.includedOnlyMeshesIds:
  83. if first != True:
  84. file_handler.write(',')
  85. first = False
  86. file_handler.write('"' + meshId + '"')
  87. file_handler.write(']')
  88. super().to_scene_file(file_handler) # Animations
  89. file_handler.write('}')
  90. #===============================================================================
  91. class ShadowGenerator:
  92. def __init__(self, lamp, meshesAndNodes, scene):
  93. Logger.log('processing begun of shadows for light: ' + lamp.name)
  94. self.lightId = lamp.name
  95. self.mapSize = lamp.data.shadowMapSize
  96. self.shadowBias = lamp.data.shadowBias
  97. if lamp.data.shadowMap == VARIANCE_SHADOWS:
  98. self.useVarianceShadowMap = True
  99. elif lamp.data.shadowMap == POISSON_SHADOWS:
  100. self.usePoissonSampling = True
  101. elif lamp.data.shadowMap == BLUR_VARIANCE_SHADOWS:
  102. self.useBlurVarianceShadowMap = True
  103. self.shadowBlurScale = lamp.data.shadowBlurScale
  104. self.shadowBlurBoxOffset = lamp.data.shadowBlurBoxOffset
  105. # .babylon specific section
  106. self.shadowCasters = []
  107. for mesh in meshesAndNodes:
  108. if (mesh.castShadows):
  109. self.shadowCasters.append(mesh.name)
  110. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  111. def to_scene_file(self, file_handler):
  112. file_handler.write('{')
  113. write_int(file_handler, 'mapSize', self.mapSize, True)
  114. write_string(file_handler, 'lightId', self.lightId)
  115. write_float(file_handler, 'bias', self.shadowBias)
  116. if hasattr(self, 'useVarianceShadowMap') :
  117. write_bool(file_handler, 'useVarianceShadowMap', self.useVarianceShadowMap)
  118. elif hasattr(self, 'usePoissonSampling'):
  119. write_bool(file_handler, 'usePoissonSampling', self.usePoissonSampling)
  120. elif hasattr(self, 'useBlurVarianceShadowMap'):
  121. write_bool(file_handler, 'useBlurVarianceShadowMap', self.useBlurVarianceShadowMap)
  122. write_int(file_handler, 'blurScale', self.shadowBlurScale)
  123. write_int(file_handler, 'blurBoxOffset', self.shadowBlurBoxOffset)
  124. file_handler.write(',"renderList":[')
  125. first = True
  126. for caster in self.shadowCasters:
  127. if first != True:
  128. file_handler.write(',')
  129. first = False
  130. file_handler.write('"' + caster + '"')
  131. file_handler.write(']')
  132. file_handler.write('}')
  133. #===============================================================================
  134. bpy.types.Lamp.autoAnimate = bpy.props.BoolProperty(
  135. name='Auto launch animations',
  136. description='',
  137. default = False
  138. )
  139. bpy.types.Lamp.shadowMap = bpy.props.EnumProperty(
  140. name='Shadow Map',
  141. description='',
  142. items = ((NO_SHADOWS , 'None' , 'No Shadow Maps'),
  143. (STD_SHADOWS , 'Standard' , 'Use Standard Shadow Maps'),
  144. (POISSON_SHADOWS , 'Poisson' , 'Use Poisson Sampling'),
  145. (VARIANCE_SHADOWS , 'Variance' , 'Use Variance Shadow Maps'),
  146. (BLUR_VARIANCE_SHADOWS, 'Blur Variance', 'Use Blur Variance Shadow Maps')
  147. ),
  148. default = NO_SHADOWS
  149. )
  150. bpy.types.Lamp.shadowMapSize = bpy.props.IntProperty(
  151. name='Shadow Map Size',
  152. description='',
  153. default = 512
  154. )
  155. bpy.types.Lamp.shadowBias = bpy.props.FloatProperty(
  156. name='Shadow Bias',
  157. description='',
  158. default = 0.00005
  159. )
  160. bpy.types.Lamp.shadowBlurScale = bpy.props.IntProperty(
  161. name='Blur Scale',
  162. description='',
  163. default = 2
  164. )
  165. bpy.types.Lamp.shadowBlurBoxOffset = bpy.props.IntProperty(
  166. name='Blur Box Offset',
  167. description='',
  168. default = 0
  169. )
  170. #===============================================================================
  171. class LightPanel(bpy.types.Panel):
  172. bl_label = get_title()
  173. bl_space_type = 'PROPERTIES'
  174. bl_region_type = 'WINDOW'
  175. bl_context = 'data'
  176. @classmethod
  177. def poll(cls, context):
  178. ob = context.object
  179. return ob is not None and isinstance(ob.data, bpy.types.Lamp)
  180. def draw(self, context):
  181. ob = context.object
  182. layout = self.layout
  183. layout.prop(ob.data, 'shadowMap')
  184. layout.prop(ob.data, 'shadowMapSize')
  185. layout.prop(ob.data, 'shadowBias')
  186. box = layout.box()
  187. box.label(text="Blur Variance Shadows")
  188. box.prop(ob.data, 'shadowBlurScale')
  189. box.prop(ob.data, 'shadowBlurBoxOffset')
  190. layout.prop(ob.data, 'autoAnimate')