174 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			174 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | # Using IES profiles from http://www.derekjenson.com/3d-blog/ies-light-profiles | ||
|  | # IES parser based on: | ||
|  | # https://github.com/tobspr/RenderPipeline | ||
|  | # Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com> | ||
|  | # 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 re | ||
|  | import os | ||
|  | import math | ||
|  | import bpy | ||
|  | import random | ||
|  | 
 | ||
|  | def load(filepath): | ||
|  |     global _vertical_angles | ||
|  |     global _horizontal_angles | ||
|  |     global _candela_values | ||
|  |     KEYWORD_REGEX = re.compile(r"\[([A-Za-z0-8_-]+)\](.*)") | ||
|  | 
 | ||
|  |     PROFILES = [ | ||
|  |         "IESNA:LM-63-1986", | ||
|  |         "IESNA:LM-63-1991", | ||
|  |         "IESNA91", | ||
|  |         "IESNA:LM-63-1995", | ||
|  |         "IESNA:LM-63-2002", | ||
|  |         "ERCO Leuchten GmbH  BY: ERCO/LUM650/8701", | ||
|  |         "ERCO Leuchten GmbH" | ||
|  |     ] | ||
|  | 
 | ||
|  |     with open(filepath, "r") as handle: | ||
|  |         lines = handle.readlines() | ||
|  | 
 | ||
|  |     lines = [i.strip() for i in lines] | ||
|  | 
 | ||
|  |     # Parse version header | ||
|  |     first_line = lines.pop(0) | ||
|  |     if first_line not in PROFILES: | ||
|  |         raise "Unsupported Profile: " + first_line | ||
|  | 
 | ||
|  |     # Extracts the keywords | ||
|  |     keywords = {} | ||
|  |     while lines: | ||
|  |         line = lines.pop(0) | ||
|  |         if not line.startswith("["): | ||
|  |             if line != "TILT=NONE": | ||
|  |                 continue | ||
|  |             lines.insert(0, line) | ||
|  |             break | ||
|  |         else: | ||
|  |             match = KEYWORD_REGEX.match(line) | ||
|  |             if match: | ||
|  |                 key, val = match.group(1, 2) | ||
|  |                 keywords[key.strip()] = val.strip() | ||
|  |             else: | ||
|  |                 raise "Invalid keyword line: " + line | ||
|  | 
 | ||
|  |     # Next line should be TILT=NONE according to the spec | ||
|  |     if lines.pop(0) != "TILT=NONE": | ||
|  |         raise "Expected TILT=NONE line, but none found!" | ||
|  | 
 | ||
|  |     # From now on, lines do not matter anymore, instead everything is space seperated | ||
|  |     new_parts = (' '.join(lines)).replace(",", " ").split() | ||
|  | 
 | ||
|  |     def read_int(): | ||
|  |         return int(new_parts.pop(0)) | ||
|  | 
 | ||
|  |     def read_float(): | ||
|  |         return float(new_parts.pop(0)) | ||
|  | 
 | ||
|  |     # Amount of Lamps | ||
|  |     if read_int() != 1: | ||
|  |         raise "Only 1 Lamp supported!" | ||
|  | 
 | ||
|  |     # Extract various properties | ||
|  |     lumen_per_lamp = read_float() | ||
|  |     candela_multiplier = read_float() | ||
|  |     num_vertical_angles = read_int() | ||
|  |     num_horizontal_angles = read_int() | ||
|  | 
 | ||
|  |     if num_vertical_angles < 1 or num_horizontal_angles < 1: | ||
|  |         raise "Invalid of vertical/horizontal angles!" | ||
|  | 
 | ||
|  |     photometric_type = read_int() | ||
|  |     unit_type = read_int() | ||
|  | 
 | ||
|  |     # Check for a correct unit type, should be 1 for meters and 2 for feet | ||
|  |     if unit_type not in [1, 2]: | ||
|  |         raise "Invalid unit type" | ||
|  | 
 | ||
|  |     width = read_float() | ||
|  |     length = read_float() | ||
|  |     height = read_float() | ||
|  |     ballast_factor = read_float() | ||
|  |     future_use = read_float() | ||
|  |     input_watts = read_float() | ||
|  | 
 | ||
|  |     _vertical_angles = [read_float() for i in range(num_vertical_angles)] | ||
|  |     _horizontal_angles = [read_float() for i in range(num_horizontal_angles)] | ||
|  | 
 | ||
|  |     _candela_values = [] | ||
|  |     candela_scale = 0.0 | ||
|  | 
 | ||
|  |     for i in range(num_horizontal_angles): | ||
|  |         vertical_data = [read_float() for i in range(num_vertical_angles)] | ||
|  |         candela_scale = max(candela_scale, max(vertical_data)) | ||
|  |         _candela_values += vertical_data | ||
|  | 
 | ||
|  |     # Rescale values, divide by maximum | ||
|  |     _candela_values = [i / candela_scale for i in _candela_values] | ||
|  |     generate_texture() | ||
|  | 
 | ||
|  | def generate_texture(): | ||
|  |     tex = bpy.data.images.new("iestexture", width=128, height=128, float_buffer=True) # R16 | ||
|  |     resolution_vertical = 128 | ||
|  |     resolution_horizontal = 128 | ||
|  | 
 | ||
|  |     for vert in range(resolution_vertical): | ||
|  |         for horiz in range(resolution_horizontal): | ||
|  |             vert_angle = vert / (resolution_vertical - 1.0) | ||
|  |             vert_angle = math.cos(vert_angle * math.pi) * 90.0 + 90.0 | ||
|  |             horiz_angle = horiz / (resolution_horizontal - 1.0) * 360.0 | ||
|  |             candela = get_candela_value(vert_angle, horiz_angle) | ||
|  |             x = vert | ||
|  |             y = horiz | ||
|  |             i = x + y * resolution_horizontal | ||
|  |             tex.pixels[i * 4] = candela | ||
|  |             tex.pixels[i * 4 + 1] = candela | ||
|  |             tex.pixels[i * 4 + 2] = candela | ||
|  |             tex.pixels[i * 4 + 3] = 1.0 | ||
|  | 
 | ||
|  | def get_candela_value(vertical_angle, horizontal_angle): | ||
|  |     # Assume a dataset without horizontal angles | ||
|  |     return get_vertical_candela_value(0, vertical_angle) | ||
|  | 
 | ||
|  | def get_vertical_candela_value(horizontal_angle_idx, vertical_angle): | ||
|  |     if vertical_angle < 0.0: | ||
|  |         return 0.0 | ||
|  | 
 | ||
|  |     if vertical_angle > _vertical_angles[len(_vertical_angles) - 1]: | ||
|  |         return 0.0 | ||
|  | 
 | ||
|  |     for vertical_index in range(1, len(_vertical_angles)): | ||
|  |         curr_angle = _vertical_angles[vertical_index] | ||
|  |         if curr_angle > vertical_angle: | ||
|  |             prev_angle = _vertical_angles[vertical_index - 1] | ||
|  |             prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx) | ||
|  |             curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx) | ||
|  |             lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle) | ||
|  |             assert lerp >= 0.0 and lerp <= 1.0 | ||
|  |             return curr_value * lerp + prev_value * (1.0 - lerp) | ||
|  |     return 0.0 | ||
|  | 
 | ||
|  | def get_candela_value_from_index(vertical_angle_idx, horizontal_angle_idx): | ||
|  |     index = vertical_angle_idx + horizontal_angle_idx * len(_vertical_angles) | ||
|  |     return _candela_values[index] | ||
|  | 
 | ||
|  | filepath = "/Users/onek8/Desktop/ies/JellyFish.ies" | ||
|  | load(filepath) |