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)
|