Formatting and fixes

* Moved: some existing functions into new separate files to improve code
readability (detailed in __init__.py docstring)

* Remove: max_intersections deprecated in pov 3.8

* Add: Validate utf-8 characters with specific API function at session's
first script init

* Add : Icons to some text fields and inviting labels

* Change default camera normal perturbation value to non zero since its
use is first driven by a boolean toggle

* Change: lists (vectors and indices) are now exported in one line by
default for better manual scene overview and debugging

* Change: a couple of tooltips corrections

* Change : renamed many variables and functions to snake_case according
to recommanded style guides

* Fix : Heightfield primitive (forward slashes were expected for
displacement texture path)

* Fix : Text nippet insertion operator

* Fix : added console print tip to check executable path on failure to
process

* Fix : tweaked finished render say command for Linux

* Fix : interface of some shader nodes broken since 2.8 api changes

* Fix : export hair particles
This commit is contained in:
Maurice Raybaud 2021-05-26 01:34:50 +02:00
parent 051d4f7d59
commit fad5186bb6
37 changed files with 19597 additions and 18964 deletions

4
presets/pov/light/02_(5400K)_High_Noon_Sun.py Normal file → Executable file
View File

@ -9,8 +9,8 @@ lampdata = bpy.context.object.data
lampdata.shape = 'SQUARE'
lampdata.size = 30000000#0.02
#lampdata.size_y = 0.02
lampdata.shadow_ray_samples_x = 2
#lampdata.shadow_ray_samples_y = 3
lampdata.pov.shadow_ray_samples_x = 2
#lampdata.pov.shadow_ray_samples_y = 3
lampdata.color = (1.0, 1.0, 1.0)
lampdata.energy = 1.094316#91193 #lux
lampdata.distance =695699968

4
presets/pov/light/03_(6000K)_Daylight_Window.py Normal file → Executable file
View File

@ -6,8 +6,8 @@ lampdata = bpy.context.object.data
lampdata.size = 1.2
lampdata.size_y = 2.10
lampdata.shadow_ray_samples_x = 2
lampdata.shadow_ray_samples_y = 3
lampdata.pov.shadow_ray_samples_x = 2
lampdata.pov.shadow_ray_samples_y = 3
lampdata.color = (1.0, 1.0, 1.0)
lampdata.energy = 1.094316#91193 #lux
lampdata.distance = 1.0

View File

@ -6,8 +6,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.038
lampdata.size_y = 2.40284
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (1.0, 0.95686274766922, 0.9490200281143188)
lampdata.energy = 4.45304#4775lm/21.446(=lux)*0.004(distance) *2 for distance is the point of half strength 6200lm?
lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

View File

@ -6,8 +6,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.038
lampdata.size_y = 1.2192
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (0.901, 1.0, 0.979)
lampdata.energy = 2.14492#2300lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

View File

@ -6,8 +6,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.026
lampdata.size_y = 0.59
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (0.95686274766922, 1.0, 0.9803921580314636)
lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

View File

@ -7,8 +7,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.026
lampdata.size_y = 0.59
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (0.8313725590705872, 0.9215686321258545, 1.0)
lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

View File

@ -7,8 +7,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.026
lampdata.size_y = 0.59
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (1.0, 0.95686274766922, 0.8980392217636108)
lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

View File

@ -6,8 +6,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.016
lampdata.size_y = 1.149
lampdata.shadow_ray_samples_x = 1
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 1
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (1.0, 0.83, 0.986274528503418)
lampdata.energy = 4.66287 #0.93257#4.66287#5000lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 0.1 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

4
presets/pov/light/21_(2700K)_7W_OLED_Panel.py Normal file → Executable file
View File

@ -7,8 +7,8 @@ lampdata = bpy.context.object.data
lampdata.size = 0.033
lampdata.size_y = 0.133
lampdata.shadow_ray_samples_x = 2
lampdata.shadow_ray_samples_y = 2
lampdata.pov.shadow_ray_samples_x = 2
lampdata.pov.shadow_ray_samples_y = 2
lampdata.color = (1.0, 0.8292156958580017, 0.6966666865348816)
lampdata.energy = 0.83932#900lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength
lampdata.distance = 1.18 #dist values multiplied by 10 for area lights for same power as bulb/spot/...

5735
render_povray/__init__.py Normal file → Executable file

File diff suppressed because it is too large Load Diff

307
render_povray/base_ui.py Executable file
View File

@ -0,0 +1,307 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""User interface imports and preferences for the addon."""
# import addon_utils
# from time import sleep
import bpy
from bpy.app.handlers import persistent
# from bpy.utils import register_class, unregister_class
# from bpy.types import (
# Operator,
# Menu,
# UIList,
# Panel,
# Brush,
# Material,
# Light,
# World,
# ParticleSettings,
# FreestyleLineStyle,
# )
# from bl_operators.presets import AddPresetBase
from . import (
render_gui,
scenography_gui,
object_gui,
shading_gui,
texturing_gui,
shading_nodes, # for POV specific nodes
scripting_gui,
)
############# POV-Centric WORKSPACE #############
@persistent
def povCentricWorkspace(dummy):
"""Set up a POV centric Workspace if addon was activated and saved as default renderer.
This would bring a _RestrictData error because UI needs to be fully loaded before
workspace changes so registering this function in bpy.app.handlers is needed.
By default handlers are freed when loading new files, but here we want the handler
to stay running across multiple files as part of this add-on. That is why the
bpy.app.handlers.persistent decorator is used (@persistent) above.
"""
# Scripting workspace may have been altered from factory though, so should
# we put all within a Try... Except AttributeErrors ? Any better solution ?
# Should it simply not run when opening existing file? be a preferences operator to create
# Moray like workspace
if 'Scripting' in bpy.data.workspaces:
wsp = bpy.data.workspaces.get('Scripting')
context = bpy.context
if context.scene.render.engine == 'POVRAY_RENDER' and wsp is not None:
bpy.ops.workspace.duplicate({'workspace': wsp})
bpy.data.workspaces['Scripting.001'].name = 'POV'
# Already done it would seem, but explicitly make this workspace the active one
context.window.workspace = bpy.data.workspaces['POV']
pov_screen = bpy.data.workspaces['POV'].screens[0]
pov_workspace = pov_screen.areas
pov_window = context.window
try:
# Already outliners but invert both types
pov_workspace[1].spaces[0].display_mode = 'LIBRARIES'
pov_workspace[3].spaces[0].display_mode = 'VIEW_LAYER'
except AttributeError:
# But not necessarily outliners in existing blend files
pass
override = bpy.context.copy()
for area in pov_workspace:
if area.type == 'VIEW_3D':
for region in [r for r in area.regions if r.type == 'WINDOW']:
for space in area.spaces:
if space.type == 'VIEW_3D':
# override['screen'] = pov_screen
override['area'] = area
override['region'] = region
# bpy.data.workspaces['POV'].screens[0].areas[6].spaces[0].width = 333 # Read only,
# how do we set ?
# This has a glitch:
# bpy.ops.screen.area_move(override, x=(area.x + area.width), y=(area.y + 5), delta=100)
# bpy.ops.screen.area_move(override, x=(area.x + 5), y=area.y, delta=-100)
bpy.ops.screen.space_type_set_or_cycle(
override, space_type='TEXT_EDITOR'
)
space.show_region_ui = True
# bpy.ops.screen.region_scale(override)
# bpy.ops.screen.region_scale()
break
elif area.type == 'CONSOLE':
for region in [r for r in area.regions if r.type == 'WINDOW']:
for space in area.spaces:
if space.type == 'CONSOLE':
override['screen'] = pov_screen
override['window'] = pov_window
override['area'] = area
override['region'] = region
area_x = area.x + (area.width / 2)
area_y = area.y + area.height
bpy.ops.screen.space_type_set_or_cycle(override, space_type='INFO')
try:
if area == pov_workspace[6] and bpy.ops.screen.area_move.poll(
override
):
# bpy.ops.screen.area_move(override, x = area_x, y = area_y, delta = -300)
pass
# pov_window.cursor_warp(area_x, area_y-300) # Is manual move emulation necessary
# despite the delta?
except IndexError:
# Not necessarily so many areas in existing blend files
pass
break
elif area.type == 'INFO':
for region in [r for r in area.regions if r.type == 'WINDOW']:
for space in area.spaces:
if space.type == 'INFO':
# override['screen'] = pov_screen
override['area'] = area
override['region'] = region
bpy.ops.screen.space_type_set_or_cycle(
override, space_type='CONSOLE'
)
break
elif area.type == 'TEXT_EDITOR':
for region in [r for r in area.regions if r.type == 'WINDOW']:
for space in area.spaces:
if space.type == 'TEXT_EDITOR':
# override['screen'] = pov_screen
override['area'] = area
override['region'] = region
# bpy.ops.screen.space_type_set_or_cycle(space_type='VIEW_3D')
# space.type = 'VIEW_3D'
bpy.ops.screen.space_type_set_or_cycle(
override, space_type='VIEW_3D'
)
# bpy.ops.screen.area_join(override, cursor=(area.x, area.y + area.height))
break
if area.type == 'VIEW_3D':
for region in [r for r in area.regions if r.type == 'WINDOW']:
for space in area.spaces:
if space.type == 'VIEW_3D':
# override['screen'] = pov_screen
override['area'] = area
override['region'] = region
bpy.ops.screen.region_quadview(override)
space.region_3d.view_perspective = 'CAMERA'
# bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'TEXT_EDITOR')
# bpy.ops.screen.region_quadview(override)
elif area.type == 'OUTLINER':
for region in [
r for r in area.regions if r.type == 'HEADER' and (r.y - area.y)
]:
for space in area.spaces:
if space.display_mode == 'LIBRARIES':
override['area'] = area
override['region'] = region
override['window'] = pov_window
bpy.ops.screen.region_flip(override)
bpy.data.workspaces.update()
'''
for window in bpy.context.window_manager.windows:
for area in [a for a in window.screen.areas if a.type == 'VIEW_3D']:
for region in [r for r in area.regions if r.type == 'WINDOW']:
context_override = {
'window': window,
'screen': window.screen,
'area': area,
'region': region,
'space_data': area.spaces.active,
'scene': bpy.context.scene
}
bpy.ops.view3d.camera_to_view(context_override)
'''
else:
print(
"\nPOV centric workspace available if you set render option\n"
"and save it in default file with CTRL+U"
)
else:
print(
"\nThe factory 'Scripting' workspace is needed before POV centric "
"\nworkspace may activate when POV is set as your default renderer"
)
####################################UTF-8###################################
# Check and fix all strings in current .blend file to be valid UTF-8 Unicode
# sometimes needed for old, 2.4x / 2.6x area files
bpy.ops.wm.blend_strings_utf8_validate()
def check_material(mat):
"""Allow use of material properties buttons rather than nodes."""
if mat is not None:
if mat.use_nodes:
if not mat.node_tree: # FORMERLY : #mat.active_node_material is not None:
return True
return False
return True
return False
def simple_material(mat):
"""Test if a material uses nodes."""
if (mat is not None) and (not mat.use_nodes):
return True
return False
def pov_context_tex_datablock(context):
"""Recreate texture context type as deprecated in blender 2.8."""
idblock = context.brush
if idblock and context.scene.texture_context == 'OTHER':
return idblock
# idblock = bpy.context.active_object.active_material
idblock = context.view_layer.objects.active.active_material
if idblock and context.scene.texture_context == 'MATERIAL':
return idblock
idblock = context.scene.world
if idblock and context.scene.texture_context == 'WORLD':
return idblock
idblock = context.light
if idblock and context.scene.texture_context == 'LIGHT':
return idblock
if context.particle_system and context.scene.texture_context == 'PARTICLES':
idblock = context.particle_system.settings
return idblock
idblock = context.line_style
if idblock and context.scene.texture_context == 'LINESTYLE':
return idblock
# class TextureTypePanel(TextureButtonsPanel):
# @classmethod
# def poll(cls, context):
# tex = context.texture
# engine = context.scene.render.engine
# return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES))
def register():
render_gui.register()
scenography_gui.register()
object_gui.register()
shading_gui.register()
texturing_gui.register()
shading_nodes.register()
scripting_gui.register()
if not povCentricWorkspace in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.append(povCentricWorkspace)
def unregister():
if povCentricWorkspace in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(povCentricWorkspace)
scripting_gui.unregister()
shading_nodes.unregister()
texturing_gui.unregister()
shading_gui.unregister()
object_gui.unregister()
scenography_gui.unregister()
render_gui.register()

247
render_povray/df3.py → render_povray/df3_library.py Normal file → Executable file
View File

@ -52,18 +52,19 @@ import sys
# -+-+-+- Start df3 Class -+-+-+-
class df3:
__version__ = '0.2'
__arraytype__ = 'f'
__struct4byte__ = '>I'
__struct2byte__ = '>H'
__struct4byte__ = '>I'
__struct2byte__ = '>H'
__struct2byte3__ = '>HHH'
__struct1byte__ = '>B'
__array4byte__ = 'I'
__array2byte__ = 'H'
__array1byte__ = 'B'
__struct1byte__ = '>B'
__array4byte__ = 'I'
__array2byte__ = 'H'
__array1byte__ = 'B'
def __init__(self, x=1, y=1, z=1):
self.maxX = x
@ -73,7 +74,7 @@ class df3:
def clone(self, indf3):
self.voxel = array.array(self.__arraytype__)
for i in range(indf3.sizeX()*indf3.sizeY()*indf3.sizeZ()):
for i in range(indf3.sizeX() * indf3.sizeY() * indf3.sizeZ()):
self.voxel[i] = indf3.voxel[i]
return self
@ -98,35 +99,41 @@ class df3:
#### Voxel Access Functions
def get(self, x, y, z):
return self.voxel[self.__voxa__(x,y,z)]
return self.voxel[self.__voxa__(x, y, z)]
def getB(self, x, y, z):
if (x > self.sizeX() or x < 0): return 0
if (y > self.sizeX() or y < 0): return 0
if (z > self.sizeX() or z < 0): return 0
if x > self.sizeX() or x < 0:
return 0
if y > self.sizeX() or y < 0:
return 0
if z > self.sizeX() or z < 0:
return 0
return self.voxel[self.__voxa__(x,y,z)]
return self.voxel[self.__voxa__(x, y, z)]
def set(self, x, y, z, val):
self.voxel[self.__voxa__(x,y,z)] = val
self.voxel[self.__voxa__(x, y, z)] = val
def setB(self, x, y, z, val):
if (x > self.sizeX() or x < 0): return
if (y > self.sizeX() or y < 0): return
if (z > self.sizeX() or z < 0): return
if x > self.sizeX() or x < 0:
return
if y > self.sizeX() or y < 0:
return
if z > self.sizeX() or z < 0:
return
self.voxel[self.__voxa__(x,y,z)] = val
self.voxel[self.__voxa__(x, y, z)] = val
#### Scalar Functions
def mult(self, val):
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
self.voxel[i] = self.voxel[i] * val
return self
def add(self, val):
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
self.voxel[i] = self.voxel[i] + val
return self
@ -134,8 +141,8 @@ class df3:
def max(self):
tmp = self.voxel[0]
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
if (self.voxel[i] > tmp):
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
if self.voxel[i] > tmp:
tmp = self.voxel[i]
return tmp
@ -143,8 +150,8 @@ class df3:
def min(self):
tmp = self.voxel[0]
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
if (self.voxel[i] < tmp):
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
if self.voxel[i] < tmp:
tmp = self.voxel[i]
return tmp
@ -152,30 +159,31 @@ class df3:
#### Vector Functions
def compare(self, indf3):
if (self.__samesize__(indf3) == 0): return 0
if self.__samesize__(indf3) == 0:
return 0
if (self.voxel == indf3.voxel):
if self.voxel == indf3.voxel:
return 1
return 0
def multV(self, indf3):
if (self.__samesize__(indf3) == 0):
if self.__samesize__(indf3) == 0:
print("Cannot multiply voxels - not same size")
return
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
self.voxel[i] = self.voxel[i]*indf3.voxel[i]
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
self.voxel[i] = self.voxel[i] * indf3.voxel[i]
return self
def addV(self, indf3):
if (self.__samesize__(indf3) == 0):
if self.__samesize__(indf3) == 0:
print("Cannot add voxels - not same size")
return
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
self.voxel[i] = self.voxel[i]+indf3.voxel[i]
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
self.voxel[i] = self.voxel[i] + indf3.voxel[i]
return self
@ -183,31 +191,31 @@ class df3:
fx = filt.sizeX()
fy = filt.sizeY()
fz = filt.sizeZ()
if (fx % 2 != 1):
if fx % 2 != 1:
print("Incompatible filter - must be odd number of X")
return self
if (fy % 2 != 1):
if fy % 2 != 1:
print("Incompatible filter - must be odd number of Y")
return self
if (fz % 2 != 1):
if fz % 2 != 1:
print("Incompatible filter - must be odd number of Z")
return self
fdx = (fx-1)/2
fdy = (fy-1)/2
fdz = (fz-1)/2
flen = fx*fy*fz
fdx = (fx - 1) / 2
fdy = (fy - 1) / 2
fdz = (fz - 1) / 2
flen = fx * fy * fz
newV = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ());
newV = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ())
for x in range(self.sizeX()):
for y in range(self.sizeY()):
for z in range(self.sizeZ()):
rip = self.__rip__(x-fdx, x+fdx, y-fdy, y+fdy, z-fdz, z+fdz)
rip = self.__rip__(x - fdx, x + fdx, y - fdy, y + fdy, z - fdz, z + fdz)
tmp = 0.0
for i in range(flen):
tmp += rip[i]*filt.voxel[i]
newV[self.__voxa__(x,y,z)] = tmp
tmp += rip[i] * filt.voxel[i]
newV[self.__voxa__(x, y, z)] = tmp
self.voxel = newV
@ -221,64 +229,67 @@ class df3:
z = self.sizeZ()
try:
f = open(file, 'wb');
f = open(file, 'wb')
except:
print("Could not open " + file + " for write");
print("Could not open " + file + " for write")
return
f.write(struct.pack(self.__struct2byte3__, x, y, z));
f.write(struct.pack(self.__struct2byte3__, x, y, z))
tmp = self.__toInteger__(pow(2,depth)-1, rescale)
tmp = self.__toInteger__(pow(2, depth) - 1, rescale)
if (depth > 16): # 32-bit
for i in range( x*y*z ):
if depth > 16: # 32-bit
for i in range(x * y * z):
f.write(struct.pack(self.__struct4byte__, tmp[i]))
elif (depth > 8): # 16-bit
for i in range( x*y*z ):
elif depth > 8: # 16-bit
for i in range(x * y * z):
f.write(struct.pack(self.__struct2byte__, tmp[i]))
else:
for i in range( x*y*z ):
for i in range(x * y * z):
f.write(struct.pack(self.__struct1byte__, tmp[i]))
def importDF3(self, file, scale=1):
try:
f = open(file, 'rb');
f = open(file, 'rb')
size = os.stat(file)[stat.ST_SIZE]
except:
print("Could not open " + file + " for read");
print("Could not open " + file + " for read")
return []
(x, y, z) = struct.unpack(self.__struct2byte3__, f.read(6) )
(x, y, z) = struct.unpack(self.__struct2byte3__, f.read(6))
self.voxel = self.__create__(x, y, z)
self.maxX = x
self.maxY = y
self.maxZ = z
size = size-6
if (size == x*y*z): format = 8
elif (size == 2*x*y*z): format = 16
elif (size == 4*x*y*z): format = 32
size = size - 6
if size == x * y * z:
format = 8
elif size == 2 * x * y * z:
format = 16
elif size == 4 * x * y * z:
format = 32
if (format == 32):
for i in range(x*y*z):
self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4) )[0])
elif (format == 16):
for i in range(x*y*z):
self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2) )[0])
elif (format == 8):
for i in range(x*y*z):
self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1) )[0])
if format == 32:
for i in range(x * y * z):
self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4))[0])
elif format == 16:
for i in range(x * y * z):
self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2))[0])
elif format == 8:
for i in range(x * y * z):
self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1))[0])
return self
#### Local classes not intended for user use
def __rip__(self, minX, maxX, minY, maxY, minZ, maxZ):
sizeX = maxX-minX+1
sizeY = maxY-minY+1
sizeZ = maxZ-minZ+1
sizeX = maxX - minX + 1
sizeY = maxY - minY + 1
sizeZ = maxZ - minZ + 1
tmpV = self.__create__(sizeX, sizeY, sizeZ)
@ -286,95 +297,99 @@ class df3:
for y in range(sizeY):
for z in range(sizeZ):
# Check X
if ((minX + x) < 0):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
elif ((minX + x) > self.sizeX()-1):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
if (minX + x) < 0:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
elif (minX + x) > self.sizeX() - 1:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
# Check Y
elif ((minY + y) < 0):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
elif ((minY + y) > self.sizeY()-1):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
elif (minY + y) < 0:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
elif (minY + y) > self.sizeY() - 1:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
# Check Z
elif ((minZ + z) < 0):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
elif ((minZ + z) > self.sizeZ()-1):
tmpV[(z*sizeZ+y)*sizeY+x] = 0.0
elif (minZ + z) < 0:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
elif (minZ + z) > self.sizeZ() - 1:
tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
else:
tmpV[(z*sizeZ+y)*sizeY+x] = self.get(minX+x,minY+y,minZ+z)
tmpV[(z * sizeZ + y) * sizeY + x] = self.get(minX + x, minY + y, minZ + z)
return tmpV
def __samesize__(self, indf3):
if (self.sizeX() != indf3.sizeX()): return 0
if (self.sizeY() != indf3.sizeY()): return 0
if (self.sizeZ() != indf3.sizeZ()): return 0
if self.sizeX() != indf3.sizeX():
return 0
if self.sizeY() != indf3.sizeY():
return 0
if self.sizeZ() != indf3.sizeZ():
return 0
return 1
def __voxa__(self, x, y, z):
return ((z*self.sizeY()+y)*self.sizeX()+x)
return (z * self.sizeY() + y) * self.sizeX() + x
def __create__(self, x, y, z, atype='0', init=1):
if (atype == '0'):
if atype == '0':
tmp = self.__arraytype__
else:
tmp = atype
if (init == 1):
if tmp in ('f','d'):
voxel = array.array(tmp, [0.0 for i in range(x*y*z)])
if init == 1:
if tmp in ('f', 'd'):
voxel = array.array(tmp, [0.0 for i in range(x * y * z)])
else:
voxel = array.array(tmp, [0 for i in range(x*y*z)])
voxel = array.array(tmp, [0 for i in range(x * y * z)])
else:
voxel = array.array(tmp)
return voxel
def __toInteger__(self, scale, rescale=1):
if (scale < pow(2,8)): # 8-bit
if scale < pow(2, 8): # 8-bit
tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array1byte__)
elif (scale < pow(2,16)): # 16-bit
elif scale < pow(2, 16): # 16-bit
tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array2byte__)
else: # 32-bit
else: # 32-bit
tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array4byte__)
maxVal = self.max()
print(scale)
for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
if (rescale == 1):
tmp[i] = max(0,int(round(scale*self.voxel[i]/maxVal)))
for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
if rescale == 1:
tmp[i] = max(0, int(round(scale * self.voxel[i] / maxVal)))
else:
tmp[i] = max(0,min(scale,int(round(self.voxel[i]))))
tmp[i] = max(0, min(scale, int(round(self.voxel[i]))))
return tmp
# -=-=-=- End df3 Class -=-=-=-
##########DEFAULT EXAMPLES
# if __name__ == '__main__':
# localX = 80
# localY = 90
# localZ = 100
## Generate an output
# temp = df3(localX, localY, localZ)
# localX = 80
# localY = 90
# localZ = 100
## Generate an output
# temp = df3(localX, localY, localZ)
# for i in range(localX):
# for j in range(localY):
# for k in range(localZ):
# if (i >= (localX/2)):
# temp.set(i, j, k, 1.0)
# for i in range(localX):
# for j in range(localY):
# for k in range(localZ):
# if (i >= (localX/2)):
# temp.set(i, j, k, 1.0)
# temp.exportDF3('temp.df3', 16)
# temp.exportDF3('temp.df3', 16)
###############################################################################
## Import
# temp2 = df3().importDF3('temp.df3')
# temp2.mult(1/temp2.max())
## Import
# temp2 = df3().importDF3('temp.df3')
# temp2.mult(1/temp2.max())
## Compare
# print(temp2.size())
## Compare
# print(temp2.size())
# if (temp.compare(temp2) == 0): print("DF3's Do Not Match")
# if (temp.compare(temp2) == 0): print("DF3's Do Not Match")
###############################################################################
# ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,974 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# #**** END GPL LICENSE BLOCK #****
# <pep8 compliant>
"""Translate to POV the control point compounded geometries like polygon
meshes or curve based shapes.
"""
import bpy
from .shading import write_object_material
################################ LOFT, ETC.
def export_curves(ob, string_strip_hyphen, global_matrix, tab_write):
"""write all curves based POV primitives to exported file """
name_orig = "OB" + ob.name
dataname_orig = "DATA" + ob.data.name
name = string_strip_hyphen(bpy.path.clean_name(name_orig))
dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
matrix = global_matrix @ ob.matrix_world
bezier_sweep = False
if ob.pov.curveshape == 'sphere_sweep':
# inlined spheresweep macro, which itself calls Shapes.inc:
file.write(' #include "shapes.inc"\n')
file.write(
' #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n'
)
file.write(' //input adjusting and inspection\n')
file.write(' #if(_resolution <= 1)\n')
file.write(' #local res = 1;\n')
file.write(' #else\n')
file.write(' #local res = int(_resolution);\n')
file.write(' #end\n')
file.write(' #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n')
file.write(' #error ""\n')
file.write(
' #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n'
)
file.write(' #error ""\n')
file.write(
' #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n'
)
file.write(' #error ""\n')
file.write(' #else\n')
file.write(' #local n_of_seg = div(dimension_size(_points_array,1), 4);\n')
file.write(' #local ctrl_pts_array = array[n_of_seg]\n')
file.write(' #local ctrl_rs_array = array[n_of_seg]\n')
file.write(' #for(i, 0, n_of_seg-1)\n')
file.write(
' #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n'
)
file.write(
' #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n'
)
file.write(' #end\n')
file.write(' #end\n')
file.write(' //drawing\n')
file.write(' #local mockup1 =\n')
file.write(' #if(_merge_shape) merge{ #else union{ #end\n')
file.write(' #for(i, 0, n_of_seg-1)\n')
file.write(' #local has_head = true;\n')
file.write(' #if(i = 0)\n')
file.write(
' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n'
)
file.write(' #local has_head = false;\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(
' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n'
)
file.write(' #local has_head = false;\n')
file.write(' #end\n')
file.write(' #end\n')
file.write(' #if(has_head = true)\n')
file.write(' sphere{\n')
file.write(' ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n')
file.write(' }\n')
file.write(' #end\n')
file.write(' #local para_t = (1/2)/res;\n')
file.write(
' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
)
file.write(
' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
)
file.write(
' #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n'
)
file.write(' object{\n')
file.write(
' Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n'
)
file.write(' }\n')
file.write(' #end\n')
file.write(' sphere{\n')
file.write(' this_point, this_radius\n')
file.write(' }\n')
file.write(' #for(j, 1, res-1)\n')
file.write(' #local last_point = this_point;\n')
file.write(' #local last_radius = this_radius;\n')
file.write(' #local para_t = (1/2+j)/res;\n')
file.write(
' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
)
file.write(
' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
)
file.write(
' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
)
file.write(' object{\n')
file.write(
' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
)
file.write(' }\n')
file.write(' #end\n')
file.write(' sphere{\n')
file.write(' this_point, this_radius\n')
file.write(' }\n')
file.write(' #end\n')
file.write(' #local last_point = this_point;\n')
file.write(' #local last_radius = this_radius;\n')
file.write(' #local this_point = ctrl_pts_array[i][3];\n')
file.write(' #local this_radius = ctrl_rs_array[i][3];\n')
file.write(
' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
)
file.write(' object{\n')
file.write(
' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
)
file.write(' }\n')
file.write(' #end\n')
file.write(' sphere{\n')
file.write(' this_point, this_radius\n')
file.write(' }\n')
file.write(' #end\n')
file.write(' }\n')
file.write(' mockup1\n')
file.write(' #end\n')
for spl in ob.data.splines:
if spl.type == "BEZIER":
bezier_sweep = True
if ob.pov.curveshape in {'loft', 'birail'}:
n = 0
for spline in ob.data.splines:
n += 1
tab_write('#declare %s%s=spline {\n' % (dataname, n))
tab_write('cubic_spline\n')
lp = len(spline.points)
delta = 1 / (lp)
d = -delta
point = spline.points[lp - 1]
x, y, z, w = point.co[:]
tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
d += delta
for point in spline.points:
x, y, z, w = point.co[:]
tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
d += delta
for i in range(2):
point = spline.points[i]
x, y, z, w = point.co[:]
tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
d += delta
tab_write('}\n')
if ob.pov.curveshape in {'loft'}:
n = len(ob.data.splines)
tab_write('#declare %s = array[%s]{\n' % (dataname, (n + 3)))
tab_write('spline{%s%s},\n' % (dataname, n))
for i in range(n):
tab_write('spline{%s%s},\n' % (dataname, (i + 1)))
tab_write('spline{%s1},\n' % (dataname))
tab_write('spline{%s2}\n' % (dataname))
tab_write('}\n')
# Use some of the Meshmaker.inc macro, here inlined
file.write('#macro CheckFileName(FileName)\n')
file.write(' #local Len=strlen(FileName);\n')
file.write(' #if(Len>0)\n')
file.write(' #if(file_exists(FileName))\n')
file.write(' #if(Len>=4)\n')
file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n')
file.write(
' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
)
file.write(' #local Return=99;\n')
file.write(' #else\n')
file.write(' #local Return=0;\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(' #local Return=0;\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(' #if(Len>=4)\n')
file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n')
file.write(
' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
)
file.write(' #if (strcmp(Ext,".obj")=0)\n')
file.write(' #local Return=2;\n')
file.write(' #end\n')
file.write(' #if (strcmp(Ext,".pcm")=0)\n')
file.write(' #local Return=3;\n')
file.write(' #end\n')
file.write(' #if (strcmp(Ext,".arr")=0)\n')
file.write(' #local Return=4;\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(' #local Return=1;\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(' #local Return=1;\n')
file.write(' #end\n')
file.write(' #end\n')
file.write(' #else\n')
file.write(' #local Return=1;\n')
file.write(' #end\n')
file.write(' (Return)\n')
file.write('#end\n')
file.write('#macro BuildSpline(Arr, SplType)\n')
file.write(' #local Ds=dimension_size(Arr,1);\n')
file.write(' #local Asc=asc(strupr(SplType));\n')
file.write(' #if(Asc!=67 & Asc!=76 & Asc!=81) \n')
file.write(' #local Asc=76;\n')
file.write(
' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n'
)
file.write(' #end\n')
file.write(' spline {\n')
file.write(' #switch (Asc)\n')
file.write(' #case (67) //C cubic_spline\n')
file.write(' cubic_spline\n')
file.write(' #break\n')
file.write(' #case (76) //L linear_spline\n')
file.write(' linear_spline\n')
file.write(' #break\n')
file.write(' #case (78) //N linear_spline\n')
file.write(' natural_spline\n')
file.write(' #break\n')
file.write(' #case (81) //Q Quadratic_spline\n')
file.write(' quadratic_spline\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' #local Add=1/((Ds-2)-1);\n')
file.write(' #local J=0-Add;\n')
file.write(' #local I=0;\n')
file.write(' #while (I<Ds)\n')
file.write(' J\n')
file.write(' Arr[I]\n')
file.write(' #local I=I+1;\n')
file.write(' #local J=J+Add;\n')
file.write(' #end\n')
file.write(' }\n')
file.write('#end\n')
file.write('#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n')
# suppressed some file checking from original macro because no more separate files
file.write(' #local Write=0;\n')
file.write(' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n')
file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
file.write(' #switch (Write)\n')
file.write(' #case(1)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' " vertex_vectors {\\n",\n')
file.write(' " ", str(NumVertices,0,0),"\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' str(2*NumVertices,0,0),",\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' mesh2 {\n')
file.write(' vertex_vectors {\n')
file.write(' NumVertices\n')
file.write(' #local I=0;\n')
file.write(' #while (I<NumVertices)\n')
file.write(' VecArr[I]\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile, VecArr[I])\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(
' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n'
)
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile, VecArr[I])\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' #local I=I+1;\n')
file.write(' #if(Write=1 | Write=4)\n')
file.write(' #if(mod(I,3)=0)\n')
file.write(' #write(MeshFile,"\\n ")\n')
file.write(' #end\n')
file.write(' #end \n')
file.write(' #end\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile,"\\n }\\n")\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(MeshFile,"\\n")\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' // do nothing\n')
file.write(' #break\n')
file.write(' #case(4) \n')
file.write(' #write(MeshFile,"\\n}\\n")\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' }\n')
file.write(' #debug concat(" - normal_vectors\\n") \n')
file.write(' #local NumVertices=dimension_size(NormArr,1);\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' " normal_vectors {\\n",\n')
file.write(' " ", str(NumVertices,0,0),"\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "# Normals: ",str(NumVertices,0,0),"\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' // do nothing\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(
' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
)
file.write(' )\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' normal_vectors {\n')
file.write(' NumVertices\n')
file.write(' #local I=0;\n')
file.write(' #while (I<NumVertices)\n')
file.write(' NormArr[I]\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile NormArr[I])\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(
' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n'
)
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile NormArr[I])\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' #local I=I+1;\n')
file.write(' #if(Write=1 | Write=4) \n')
file.write(' #if(mod(I,3)=0)\n')
file.write(' #write(MeshFile,"\\n ")\n')
file.write(' #end\n')
file.write(' #end\n')
file.write(' #end\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile,"\\n }\\n")\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(MeshFile,"\\n")\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' //do nothing\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile,"\\n}\\n")\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' }\n')
file.write(' #debug concat(" - uv_vectors\\n") \n')
file.write(' #local NumVertices=dimension_size(UVArr,1);\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(\n')
file.write(' MeshFile, \n')
file.write(' " uv_vectors {\\n",\n')
file.write(' " ", str(NumVertices,0,0),"\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' // do nothing, *.pcm does not support uv-vectors\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' uv_vectors {\n')
file.write(' NumVertices\n')
file.write(' #local I=0;\n')
file.write(' #while (I<NumVertices)\n')
file.write(' UVArr[I]\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile UVArr[I])\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' //do nothing\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile UVArr[I])\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' #local I=I+1; \n')
file.write(' #if(Write=1 | Write=4)\n')
file.write(' #if(mod(I,3)=0)\n')
file.write(' #write(MeshFile,"\\n ")\n')
file.write(' #end \n')
file.write(' #end\n')
file.write(' #end \n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile,"\\n }\\n")\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(MeshFile,"\\n")\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' //do nothing\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile,"\\n}\\n")\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' }\n')
file.write('\n')
file.write(' #debug concat(" - face_indices\\n") \n')
file.write(' #declare NumFaces=U*V*2;\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' " face_indices {\\n"\n')
file.write(' " ", str(NumFaces,0,0),"\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write (\n')
file.write(' MeshFile,\n')
file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #write (\n')
file.write(' MeshFile,\n')
file.write(' "0,",str(NumFaces,0,0),",\\n"\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' face_indices {\n')
file.write(' NumFaces\n')
file.write(' #local I=0;\n')
file.write(' #local H=0;\n')
file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
file.write(' #while (I<V)\n')
file.write(' #local J=0;\n')
file.write(' #while (J<U)\n')
file.write(' #local Ind=(I*U)+I+J;\n')
file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(
' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n'
)
file.write(
' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n'
)
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(
' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
)
file.write(
' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
)
file.write(' )\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(\n')
file.write(' MeshFile,\n')
file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
file.write(' )\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' #local J=J+1;\n')
file.write(' #local H=H+1;\n')
file.write(' #if(Write=1 | Write=4)\n')
file.write(' #if(mod(H,3)=0)\n')
file.write(' #write(MeshFile,"\\n ")\n')
file.write(' #end \n')
file.write(' #end\n')
file.write(' #end\n')
file.write(' #local I=I+1;\n')
file.write(' #end\n')
file.write(' }\n')
file.write(' #switch(Write)\n')
file.write(' #case(1)\n')
file.write(' #write(MeshFile, "\\n }\\n}")\n')
file.write(' #fclose MeshFile\n')
file.write(' #debug concat(" Done writing\\n")\n')
file.write(' #break\n')
file.write(' #case(2)\n')
file.write(' #fclose MeshFile\n')
file.write(' #debug concat(" Done writing\\n")\n')
file.write(' #break\n')
file.write(' #case(3)\n')
file.write(' #fclose MeshFile\n')
file.write(' #debug concat(" Done writing\\n")\n')
file.write(' #break\n')
file.write(' #case(4)\n')
file.write(' #write(MeshFile, "\\n}\\n}")\n')
file.write(' #fclose MeshFile\n')
file.write(' #debug concat(" Done writing\\n")\n')
file.write(' #break\n')
file.write(' #end\n')
file.write(' }\n')
file.write('#end\n')
file.write('#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n')
file.write(' #declare Build=CheckFileName(FileName);\n')
file.write(' #if(Build=0)\n')
file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
file.write(' #include FileName\n')
file.write(' object{Surface}\n')
file.write(' #else\n')
file.write(' #local NumVertices=(SplRes+1)*(InterpRes+1);\n')
file.write(' #local NumFaces=SplRes*InterpRes*2;\n')
file.write(
' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n'
)
file.write(' #local VecArr=array[NumVertices]\n')
file.write(' #local NormArr=array[NumVertices]\n')
file.write(' #local UVArr=array[NumVertices]\n')
file.write(' #local N=dimension_size(SplineArray,1);\n')
file.write(' #local TempSplArr0=array[N];\n')
file.write(' #local TempSplArr1=array[N];\n')
file.write(' #local TempSplArr2=array[N];\n')
file.write(' #local PosStep=1/SplRes;\n')
file.write(' #local InterpStep=1/InterpRes;\n')
file.write(' #local Count=0;\n')
file.write(' #local Pos=0;\n')
file.write(' #while(Pos<=1)\n')
file.write(' #local I=0;\n')
file.write(' #if (Pos=0)\n')
file.write(' #while (I<N)\n')
file.write(' #local Spl=spline{SplineArray[I]}\n')
file.write(' #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n')
file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n')
file.write(' #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n')
file.write(' #local I=I+1;\n')
file.write(' #end\n')
file.write(' #local S0=BuildSpline(TempSplArr0, Interp_type)\n')
file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n')
file.write(' #local S2=BuildSpline(TempSplArr2, Interp_type)\n')
file.write(' #else\n')
file.write(' #while (I<N)\n')
file.write(' #local Spl=spline{SplineArray[I]}\n')
file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n')
file.write(' #local I=I+1;\n')
file.write(' #end\n')
file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n')
file.write(' #end\n')
file.write(' #local J=0;\n')
file.write(' #while (J<=1)\n')
file.write(' #local P0=<0,0,0>+S0(J);\n')
file.write(' #local P1=<0,0,0>+S1(J);\n')
file.write(' #local P2=<0,0,0>+S2(J);\n')
file.write(' #local P3=<0,0,0>+S0(J+InterpStep);\n')
file.write(' #local P4=<0,0,0>+S0(J-InterpStep);\n')
file.write(' #local B1=P4-P0;\n')
file.write(' #local B2=P2-P0;\n')
file.write(' #local B3=P3-P0;\n')
file.write(' #local B4=P1-P0;\n')
file.write(' #local N1=vcross(B1,B2);\n')
file.write(' #local N2=vcross(B2,B3);\n')
file.write(' #local N3=vcross(B3,B4);\n')
file.write(' #local N4=vcross(B4,B1);\n')
file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n')
file.write(' #local VecArr[Count]=P0;\n')
file.write(' #local NormArr[Count]=Norm;\n')
file.write(' #local UVArr[Count]=<J,Pos>;\n')
file.write(' #local J=J+InterpStep;\n')
file.write(' #local Count=Count+1;\n')
file.write(' #end\n')
file.write(' #local S2=spline{S0}\n')
file.write(' #local S0=spline{S1}\n')
file.write(
' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n'
)
file.write(' #local Pos=Pos+PosStep;\n')
file.write(' #end\n')
file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n')
file.write(' #end\n')
file.write('#end\n\n')
file.write('#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n')
file.write(' #declare Build=CheckFileName(FileName);\n')
file.write(' #if(Build=0)\n')
file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
file.write(' #include FileName\n')
file.write(' object{Surface}\n')
file.write(' #else\n')
file.write(' #local NumVertices=(Iter_U+1)*(Iter_V+1);\n')
file.write(' #local NumFaces=Iter_U*Iter_V*2;\n')
file.write(
' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n'
)
file.write(' #declare VecArr=array[NumVertices] \n')
file.write(' #declare NormArr=array[NumVertices] \n')
file.write(' #local UVArr=array[NumVertices] \n')
file.write(' #local Spl1_0=Spl1(0);\n')
file.write(' #local Spl2_0=Spl2(0);\n')
file.write(' #local Spl3_0=Spl3(0);\n')
file.write(' #local Spl4_0=Spl4(0);\n')
file.write(' #local UStep=1/Iter_U;\n')
file.write(' #local VStep=1/Iter_V;\n')
file.write(' #local Count=0;\n')
file.write(' #local I=0;\n')
file.write(' #while (I<=1)\n')
file.write(' #local Im=1-I;\n')
file.write(' #local J=0;\n')
file.write(' #while (J<=1)\n')
file.write(' #local Jm=1-J;\n')
file.write(
' #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n'
)
file.write(' #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n')
file.write(' LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n')
file.write(' #declare VecArr[Count]=P0;\n')
file.write(' #local UVArr[Count]=<J,I>;\n')
file.write(' #local J=J+UStep;\n')
file.write(' #local Count=Count+1;\n')
file.write(' #end\n')
file.write(' #debug concat(\n')
file.write(' "\r Done ", str(Count,0,0)," vertices : ",\n')
file.write(' str(100*Count/NumVertices,0,2)," %"\n')
file.write(' )\n')
file.write(' #local I=I+VStep;\n')
file.write(' #end\n')
file.write(' #debug "\r Normals "\n')
file.write(' #local Count=0;\n')
file.write(' #local I=0;\n')
file.write(' #while (I<=Iter_V)\n')
file.write(' #local J=0;\n')
file.write(' #while (J<=Iter_U)\n')
file.write(' #local Ind=(I*Iter_U)+I+J;\n')
file.write(' #local P0=VecArr[Ind];\n')
file.write(' #if(J=0)\n')
file.write(' #local P1=P0+(P0-VecArr[Ind+1]);\n')
file.write(' #else\n')
file.write(' #local P1=VecArr[Ind-1];\n')
file.write(' #end\n')
file.write(' #if (J=Iter_U)\n')
file.write(' #local P2=P0+(P0-VecArr[Ind-1]);\n')
file.write(' #else\n')
file.write(' #local P2=VecArr[Ind+1];\n')
file.write(' #end\n')
file.write(' #if (I=0)\n')
file.write(' #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n')
file.write(' #else\n')
file.write(' #local P3=VecArr[Ind-Iter_U-1];\n')
file.write(' #end\n')
file.write(' #if (I=Iter_V)\n')
file.write(' #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n')
file.write(' #else\n')
file.write(' #local P4=VecArr[Ind+Iter_U+1];\n')
file.write(' #end\n')
file.write(' #local B1=P4-P0;\n')
file.write(' #local B2=P2-P0;\n')
file.write(' #local B3=P3-P0;\n')
file.write(' #local B4=P1-P0;\n')
file.write(' #local N1=vcross(B1,B2);\n')
file.write(' #local N2=vcross(B2,B3);\n')
file.write(' #local N3=vcross(B3,B4);\n')
file.write(' #local N4=vcross(B4,B1);\n')
file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n')
file.write(' #declare NormArr[Count]=Norm;\n')
file.write(' #local J=J+1;\n')
file.write(' #local Count=Count+1;\n')
file.write(' #end\n')
file.write(
' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n'
)
file.write(' #local I=I+1;\n')
file.write(' #end\n')
file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n')
file.write(' #end\n')
file.write('#end\n\n')
# Empty curves
if len(ob.data.splines) == 0:
tab_write("\n//dummy sphere to represent empty curve location\n")
tab_write("#declare %s =\n" % dataname)
tab_write(
"sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
% (ob.location.x, ob.location.y, ob.location.z)
) # ob.name > povdataname)
# And non empty curves
else:
if not bezier_sweep:
tab_write("#declare %s =\n" % dataname)
if ob.pov.curveshape == 'sphere_sweep' and not bezier_sweep:
tab_write("union {\n")
for spl in ob.data.splines:
if spl.type != "BEZIER":
spl_type = "linear"
if spl.type == "NURBS":
spl_type = "cubic"
points = spl.points
num_points = len(points)
if spl.use_cyclic_u:
num_points += 3
tab_write("sphere_sweep { %s_spline %s,\n" % (spl_type, num_points))
if spl.use_cyclic_u:
pt1 = points[len(points) - 1]
wpt1 = pt1.co
tab_write(
"<%.4g,%.4g,%.4g>,%.4g\n"
% (wpt1[0], wpt1[1], wpt1[2], pt1.radius * ob.data.bevel_depth)
)
for pt in points:
wpt = pt.co
tab_write(
"<%.4g,%.4g,%.4g>,%.4g\n"
% (wpt[0], wpt[1], wpt[2], pt.radius * ob.data.bevel_depth)
)
if spl.use_cyclic_u:
for i in range(0, 2):
end_pt = points[i]
wpt = end_pt.co
tab_write(
"<%.4g,%.4g,%.4g>,%.4g\n"
% (wpt[0], wpt[1], wpt[2], end_pt.radius * ob.data.bevel_depth)
)
tab_write("}\n")
# below not used yet?
if ob.pov.curveshape == 'sor':
for spl in ob.data.splines:
if spl.type in {'POLY', 'NURBS'}:
points = spl.points
num_points = len(points)
tab_write("sor { %s,\n" % num_points)
for pt in points:
wpt = pt.co
tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
else:
tab_write("box { 0,0\n")
if ob.pov.curveshape in {'lathe', 'prism'}:
spl = ob.data.splines[0]
if spl.type == "BEZIER":
points = spl.bezier_points
len_cur = len(points) - 1
len_pts = len_cur * 4
ifprism = ''
if ob.pov.curveshape in {'prism'}:
height = ob.data.extrude
ifprism = '-%s, %s,' % (height, height)
len_cur += 1
len_pts += 4
tab_write("%s { bezier_spline %s %s,\n" % (ob.pov.curveshape, ifprism, len_pts))
for i in range(0, len_cur):
p1 = points[i].co
pR = points[i].handle_right
end = i + 1
if i == len_cur - 1 and ob.pov.curveshape in {'prism'}:
end = 0
pL = points[end].handle_left
p2 = points[end].co
line = "<%.4g,%.4g>" % (p1[0], p1[1])
line += "<%.4g,%.4g>" % (pR[0], pR[1])
line += "<%.4g,%.4g>" % (pL[0], pL[1])
line += "<%.4g,%.4g>" % (p2[0], p2[1])
tab_write("%s\n" % line)
else:
points = spl.points
len_cur = len(points)
len_pts = len_cur
ifprism = ''
if ob.pov.curveshape in {'prism'}:
height = ob.data.extrude
ifprism = '-%s, %s,' % (height, height)
len_pts += 3
spl_type = 'quadratic'
if spl.type == 'POLY':
spl_type = 'linear'
tab_write(
"%s { %s_spline %s %s,\n" % (ob.pov.curveshape, spl_type, ifprism, len_pts)
)
if ob.pov.curveshape in {'prism'}:
pt = points[len(points) - 1]
wpt = pt.co
tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
for pt in points:
wpt = pt.co
tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
if ob.pov.curveshape in {'prism'}:
for i in range(2):
pt = points[i]
wpt = pt.co
tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
if bezier_sweep:
for p in range(len(ob.data.splines)):
br = []
depth = ob.data.bevel_depth
spl = ob.data.splines[p]
points = spl.bezier_points
len_cur = len(points) - 1
num_points = len_cur * 4
if spl.use_cyclic_u:
len_cur += 1
num_points += 4
tab_write("#declare %s_points_%s = array[%s]{\n" % (dataname, p, num_points))
for i in range(len_cur):
p1 = points[i].co
pR = points[i].handle_right
end = i + 1
if spl.use_cyclic_u and i == (len_cur - 1):
end = 0
pL = points[end].handle_left
p2 = points[end].co
r3 = points[end].radius * depth
r0 = points[i].radius * depth
r1 = 2 / 3 * r0 + 1 / 3 * r3
r2 = 1 / 3 * r0 + 2 / 3 * r3
br.append((r0, r1, r2, r3))
line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2])
line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2])
line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2])
line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2])
tab_write("%s\n" % line)
tab_write("}\n")
tab_write("#declare %s_radii_%s = array[%s]{\n" % (dataname, p, len(br) * 4))
for rad_tuple in br:
tab_write(
'%.4f,%.4f,%.4f,%.4f\n'
% (rad_tuple[0], rad_tuple[1], rad_tuple[2], rad_tuple[3])
)
tab_write("}\n")
if len(ob.data.splines) == 1:
tab_write('#declare %s = object{\n' % dataname)
tab_write(
' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n'
% (ob.data.resolution_u, dataname, p, dataname, p)
)
else:
tab_write('#declare %s = union{\n' % dataname)
for p in range(len(ob.data.splines)):
tab_write(
' object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n'
% (ob.data.resolution_u, dataname, p, dataname, p)
)
# tab_write('#include "bezier_spheresweep.inc"\n') #now inlined
# tab_write('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth))
if ob.pov.curveshape in {'loft'}:
tab_write('object {MSM(%s,%s,"c",%s,"")\n' % (dataname, ob.pov.res_u, ob.pov.res_v))
if ob.pov.curveshape in {'birail'}:
splines = '%s1,%s2,%s3,%s4' % (dataname, dataname, dataname, dataname)
tab_write('object {Coons(%s, %s, %s, "")\n' % (splines, ob.pov.res_u, ob.pov.res_v))
pov_mat_name = "Default_texture"
if ob.active_material:
# pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
try:
material = ob.active_material
write_object_material(material, ob, tab_write)
except IndexError:
print(ob.data)
# tab_write("texture {%s}\n"%pov_mat_name)
if ob.pov.curveshape in {'prism'}:
tab_write("rotate <90,0,0>\n")
tab_write("scale y*-1\n")
tab_write("}\n")

731
render_povray/object_gui.py Executable file
View File

@ -0,0 +1,731 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""User interface for the POV tools"""
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import (
# Operator,
Menu,
Panel,
)
# Example of wrapping every class 'as is'
from bl_ui import properties_data_modifier
for member in dir(properties_data_modifier):
subclass = getattr(properties_data_modifier, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
del properties_data_modifier
from bl_ui import properties_data_mesh
# These panels are kept
properties_data_mesh.DATA_PT_custom_props_mesh.COMPAT_ENGINES.add('POVRAY_RENDER')
properties_data_mesh.DATA_PT_context_mesh.COMPAT_ENGINES.add('POVRAY_RENDER')
## make some native panels contextual to some object variable
## by recreating custom panels inheriting their properties
from .scripting_gui import VIEW_MT_POV_import
class ModifierButtonsPanel:
"""Use this class to define buttons from the modifier tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "modifier"
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
mods = context.object.modifiers
rd = context.scene.render
return mods and (rd.engine in cls.COMPAT_ENGINES)
class ObjectButtonsPanel:
"""Use this class to define buttons from the object tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
obj = context.object
rd = context.scene.render
return obj and (rd.engine in cls.COMPAT_ENGINES)
class PovDataButtonsPanel(properties_data_mesh.MeshButtonsPanel):
"""Use this class to define buttons from the edit data tab of
properties window."""
COMPAT_ENGINES = {'POVRAY_RENDER'}
POV_OBJECT_TYPES = {
'PLANE',
'BOX',
'SPHERE',
'CYLINDER',
'CONE',
'TORUS',
'BLOB',
'ISOSURFACE',
'SUPERELLIPSOID',
'SUPERTORUS',
'HEIGHT_FIELD',
'PARAMETRIC',
'POLYCIRCLE',
}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
# We use our parent class poll func too, avoids to re-define too much things...
return (
super(PovDataButtonsPanel, cls).poll(context)
and obj
and obj.pov.object_as not in cls.POV_OBJECT_TYPES
)
# We cannot inherit from RNA classes (like e.g. properties_data_mesh.DATA_PT_vertex_groups).
# Complex py/bpy/rna interactions (with metaclass and all) simply do not allow it to work.
# So we simply have to explicitly copy here the interesting bits. ;)
class DATA_PT_POV_normals(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_normals.bl_label
draw = properties_data_mesh.DATA_PT_normals.draw
class DATA_PT_POV_texture_space(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_texture_space.bl_label
bl_options = properties_data_mesh.DATA_PT_texture_space.bl_options
draw = properties_data_mesh.DATA_PT_texture_space.draw
class DATA_PT_POV_vertex_groups(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_vertex_groups.bl_label
draw = properties_data_mesh.DATA_PT_vertex_groups.draw
class DATA_PT_POV_shape_keys(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_shape_keys.bl_label
draw = properties_data_mesh.DATA_PT_shape_keys.draw
class DATA_PT_POV_uv_texture(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_uv_texture.bl_label
draw = properties_data_mesh.DATA_PT_uv_texture.draw
class DATA_PT_POV_vertex_colors(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_vertex_colors.bl_label
draw = properties_data_mesh.DATA_PT_vertex_colors.draw
class DATA_PT_POV_customdata(PovDataButtonsPanel, Panel):
bl_label = properties_data_mesh.DATA_PT_customdata.bl_label
bl_options = properties_data_mesh.DATA_PT_customdata.bl_options
draw = properties_data_mesh.DATA_PT_customdata.draw
del properties_data_mesh
class MODIFIERS_PT_POV_modifiers(ModifierButtonsPanel, Panel):
"""Use this class to define pov modifier buttons. (For booleans)"""
bl_label = "POV-Ray"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# def draw_header(self, context):
# scene = context.scene
# self.layout.prop(scene.pov, "boolean_mod", text="")
def draw(self, context):
# scene = context.scene
layout = self.layout
ob = context.object
mod = ob.modifiers
col = layout.column()
# Find Boolean Modifiers for displaying CSG option
onceCSG = 0
for mod in ob.modifiers:
if onceCSG == 0:
if mod:
if mod.type == 'BOOLEAN':
col.prop(ob.pov, "boolean_mod")
onceCSG = 1
if ob.pov.boolean_mod == "POV":
split = layout.split()
col = layout.column()
# Inside Vector for CSG
col.prop(ob.pov, "inside_vector")
class OBJECT_PT_POV_obj_parameters(ObjectButtonsPanel, Panel):
"""Use this class to define pov specific object level options buttons."""
bl_label = "POV"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
return engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
obj = context.object
split = layout.split()
col = split.column(align=True)
col.label(text="Radiosity:")
col.prop(obj.pov, "importance_value", text="Importance")
col.label(text="Photons:")
col.prop(obj.pov, "collect_photons", text="Receive Photon Caustics")
if obj.pov.collect_photons:
col.prop(obj.pov, "spacing_multiplier", text="Photons Spacing Multiplier")
split = layout.split()
col = split.column()
col.prop(obj.pov, "hollow")
col.prop(obj.pov, "double_illuminate")
if obj.type == 'META' or obj.pov.curveshape == 'lathe':
# if obj.pov.curveshape == 'sor'
col.prop(obj.pov, "sturm")
col.prop(obj.pov, "no_shadow")
col.prop(obj.pov, "no_image")
col.prop(obj.pov, "no_reflection")
col.prop(obj.pov, "no_radiosity")
col.prop(obj.pov, "inverse")
col.prop(obj.pov, "hierarchy")
# col.prop(obj.pov,"boundorclip",text="Bound / Clip")
# if obj.pov.boundorclip != "none":
# col.prop_search(obj.pov,"boundorclipob",context.blend_data,"objects",text="Object")
# text = "Clipped by"
# if obj.pov.boundorclip == "clipped_by":
# text = "Bounded by"
# col.prop(obj.pov,"addboundorclip",text=text)
class OBJECT_PT_POV_obj_sphere(PovDataButtonsPanel, Panel):
"""Use this class to define pov sphere primitive parameters buttons."""
bl_label = "POV Sphere"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'SPHERE' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'SPHERE':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Sphere radius: " + str(obj.pov.sphere_radius))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.sphere_update", text="Update", icon="SHADING_RENDERED")
# col.label(text="Parameters:")
col.prop(obj.pov, "sphere_radius", text="Radius of Sphere")
class OBJECT_PT_POV_obj_cylinder(PovDataButtonsPanel, Panel):
"""Use this class to define pov cylinder primitive parameters buttons."""
bl_label = "POV Cylinder"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'CYLINDER' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'CYLINDER':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Cylinder radius: " + str(obj.pov.cylinder_radius))
col.label(text="Cylinder cap location: " + str(obj.pov.cylinder_location_cap))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.cylinder_update", text="Update", icon="MESH_CYLINDER")
# col.label(text="Parameters:")
col.prop(obj.pov, "cylinder_radius")
col.prop(obj.pov, "cylinder_location_cap")
class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel):
"""Use this class to define pov cone primitive parameters buttons."""
bl_label = "POV Cone"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'CONE' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'CONE':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Cone base radius: " + str(obj.pov.cone_base_radius))
col.label(text="Cone cap radius: " + str(obj.pov.cone_cap_radius))
col.label(text="Cone proxy segments: " + str(obj.pov.cone_segments))
col.label(text="Cone height: " + str(obj.pov.cone_height))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.cone_update", text="Update", icon="MESH_CONE")
# col.label(text="Parameters:")
col.prop(obj.pov, "cone_base_radius", text="Radius of Cone Base")
col.prop(obj.pov, "cone_cap_radius", text="Radius of Cone Cap")
col.prop(obj.pov, "cone_segments", text="Segmentation of Cone proxy")
col.prop(obj.pov, "cone_height", text="Height of the cone")
class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel):
"""Use this class to define pov superellipsoid primitive parameters buttons."""
bl_label = "POV Superquadric ellipsoid"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'SUPERELLIPSOID' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'SUPERELLIPSOID':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Radial segmentation: " + str(obj.pov.se_u))
col.label(text="Lateral segmentation: " + str(obj.pov.se_v))
col.label(text="Ring shape: " + str(obj.pov.se_n1))
col.label(text="Cross-section shape: " + str(obj.pov.se_n2))
col.label(text="Fill up and down: " + str(obj.pov.se_edit))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.superellipsoid_update", text="Update", icon="MOD_SUBSURF")
# col.label(text="Parameters:")
col.prop(obj.pov, "se_u")
col.prop(obj.pov, "se_v")
col.prop(obj.pov, "se_n1")
col.prop(obj.pov, "se_n2")
col.prop(obj.pov, "se_edit")
class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel):
"""Use this class to define pov torus primitive parameters buttons."""
bl_label = "POV Torus"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'TORUS' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'TORUS':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Torus major radius: " + str(obj.pov.torus_major_radius))
col.label(text="Torus minor radius: " + str(obj.pov.torus_minor_radius))
col.label(text="Torus major segments: " + str(obj.pov.torus_major_segments))
col.label(text="Torus minor segments: " + str(obj.pov.torus_minor_segments))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.torus_update", text="Update", icon="MESH_TORUS")
# col.label(text="Parameters:")
col.prop(obj.pov, "torus_major_radius")
col.prop(obj.pov, "torus_minor_radius")
col.prop(obj.pov, "torus_major_segments")
col.prop(obj.pov, "torus_minor_segments")
class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel):
"""Use this class to define pov supertorus primitive parameters buttons."""
bl_label = "POV SuperTorus"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'SUPERTORUS' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'SUPERTORUS':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="SuperTorus major radius: " + str(obj.pov.st_major_radius))
col.label(text="SuperTorus minor radius: " + str(obj.pov.st_minor_radius))
col.label(text="SuperTorus major segments: " + str(obj.pov.st_u))
col.label(text="SuperTorus minor segments: " + str(obj.pov.st_v))
col.label(text="SuperTorus Ring Manipulator: " + str(obj.pov.st_ring))
col.label(text="SuperTorus Cross Manipulator: " + str(obj.pov.st_cross))
col.label(text="SuperTorus Internal And External radii: " + str(obj.pov.st_ie))
col.label(text="SuperTorus accuracy: " + str(obj.pov.st_accuracy))
col.label(text="SuperTorus max gradient: " + str(obj.pov.st_max_gradient))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.supertorus_update", text="Update", icon="MESH_TORUS")
# col.label(text="Parameters:")
col.prop(obj.pov, "st_major_radius")
col.prop(obj.pov, "st_minor_radius")
col.prop(obj.pov, "st_u")
col.prop(obj.pov, "st_v")
col.prop(obj.pov, "st_ring")
col.prop(obj.pov, "st_cross")
col.prop(obj.pov, "st_ie")
# col.prop(obj.pov, "st_edit") #?
col.prop(obj.pov, "st_accuracy")
col.prop(obj.pov, "st_max_gradient")
class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel):
"""Use this class to define pov parametric surface primitive parameters buttons."""
bl_label = "POV Parametric surface"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'PARAMETRIC' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'PARAMETRIC':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Minimum U: " + str(obj.pov.u_min))
col.label(text="Minimum V: " + str(obj.pov.v_min))
col.label(text="Maximum U: " + str(obj.pov.u_max))
col.label(text="Minimum V: " + str(obj.pov.v_min))
col.label(text="X Function: " + str(obj.pov.x_eq))
col.label(text="Y Function: " + str(obj.pov.y_eq))
col.label(text="Z Function: " + str(obj.pov.x_eq))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.parametric_update", text="Update", icon="SCRIPTPLUGINS")
col.prop(obj.pov, "u_min", text="Minimum U")
col.prop(obj.pov, "v_min", text="Minimum V")
col.prop(obj.pov, "u_max", text="Maximum U")
col.prop(obj.pov, "v_max", text="Minimum V")
col.prop(obj.pov, "x_eq", text="X Function")
col.prop(obj.pov, "y_eq", text="Y Function")
col.prop(obj.pov, "z_eq", text="Z Function")
class OBJECT_PT_povray_replacement_text(ObjectButtonsPanel, Panel):
"""Use this class to define pov object replacement field."""
bl_label = "Custom POV Code"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
col.label(text="Replace properties with:")
col.prop(obj.pov, "replacement_text", text="")
###############################################################################
# Add Povray Objects
###############################################################################
def check_add_mesh_extra_objects():
"""Test if Add mesh extra objects addon is activated
This addon is currently used to generate the proxy for POV parametric
surface which is almost the same priciple as its Math xyz surface
"""
if "add_mesh_extra_objects" in bpy.context.preferences.addons.keys():
return True
return False
def menu_func_add(self, context):
"""Append the POV primitives submenu to blender add objects menu"""
engine = context.scene.render.engine
if engine == 'POVRAY_RENDER':
self.layout.menu("VIEW_MT_POV_primitives_add", icon="PLUGIN")
class VIEW_MT_POV_primitives_add(Menu):
"""Define the primitives menu with presets"""
bl_idname = "VIEW_MT_POV_primitives_add"
bl_label = "Povray"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
return engine == 'POVRAY_RENDER'
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.menu(VIEW_MT_POV_Basic_Shapes.bl_idname, text="Primitives", icon="GROUP")
layout.menu(VIEW_MT_POV_import.bl_idname, text="Import", icon="IMPORT")
class VIEW_MT_POV_Basic_Shapes(Menu):
"""Use this class to sort simple primitives menu entries."""
bl_idname = "POVRAY_MT_basic_shape_tools"
bl_label = "Basic_shapes"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("pov.addplane", text="Infinite Plane", icon='MESH_PLANE')
layout.operator("pov.addbox", text="Box", icon='MESH_CUBE')
layout.operator("pov.addsphere", text="Sphere", icon='SHADING_RENDERED')
layout.operator("pov.addcylinder", text="Cylinder", icon="MESH_CYLINDER")
layout.operator("pov.cone_add", text="Cone", icon="MESH_CONE")
layout.operator("pov.addtorus", text="Torus", icon='MESH_TORUS')
layout.separator()
layout.operator("pov.addrainbow", text="Rainbow", icon="COLOR")
layout.operator("pov.addlathe", text="Lathe", icon='MOD_SCREW')
layout.operator("pov.addprism", text="Prism", icon='MOD_SOLIDIFY')
layout.operator("pov.addsuperellipsoid", text="Superquadric Ellipsoid", icon='MOD_SUBSURF')
layout.operator("pov.addheightfield", text="Height Field", icon="RNDCURVE")
layout.operator("pov.addspheresweep", text="Sphere Sweep", icon='FORCE_CURVE')
layout.separator()
layout.operator("pov.addblobsphere", text="Blob Sphere", icon='META_DATA')
layout.separator()
layout.label(text="Isosurfaces")
layout.operator("pov.addisosurfacebox", text="Isosurface Box", icon="META_CUBE")
layout.operator("pov.addisosurfacesphere", text="Isosurface Sphere", icon="META_BALL")
layout.operator("pov.addsupertorus", text="Supertorus", icon="SURFACE_NTORUS")
layout.separator()
layout.label(text="Macro based")
layout.operator(
"pov.addpolygontocircle", text="Polygon To Circle Blending", icon="MOD_CAST"
)
layout.operator("pov.addloft", text="Loft", icon="SURFACE_NSURFACE")
layout.separator()
# Warning if the Add Advanced Objects addon containing
# Add mesh extra objects is not enabled
if not check_add_mesh_extra_objects():
# col = box.column()
layout.label(text="Please enable Add Mesh: Extra Objects addon", icon="INFO")
# layout.separator()
layout.operator(
"preferences.addon_show",
text="Go to Add Mesh: Extra Objects addon",
icon="PREFERENCES",
).module = "add_mesh_extra_objects"
# layout.separator()
return
layout.operator("pov.addparametric", text="Parametric", icon='SCRIPTPLUGINS')
classes = (
# ObjectButtonsPanel,
# PovDataButtonsPanel,
DATA_PT_POV_normals,
DATA_PT_POV_texture_space,
DATA_PT_POV_vertex_groups,
DATA_PT_POV_shape_keys,
DATA_PT_POV_uv_texture,
DATA_PT_POV_vertex_colors,
DATA_PT_POV_customdata,
MODIFIERS_PT_POV_modifiers,
OBJECT_PT_POV_obj_parameters,
OBJECT_PT_POV_obj_sphere,
OBJECT_PT_POV_obj_cylinder,
OBJECT_PT_POV_obj_cone,
OBJECT_PT_POV_obj_superellipsoid,
OBJECT_PT_POV_obj_torus,
OBJECT_PT_POV_obj_supertorus,
OBJECT_PT_POV_obj_parametric,
OBJECT_PT_povray_replacement_text,
VIEW_MT_POV_primitives_add,
VIEW_MT_POV_Basic_Shapes,
)
def register():
# from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.VIEW3D_MT_add.prepend(menu_func_add)
# was used for parametric objects but made the other addon unreachable on
# unregister for other tools to use created a user action call instead
# addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True)
def unregister():
# addon_utils.disable("add_mesh_extra_objects", default_set=False)
bpy.types.VIEW3D_MT_add.remove(menu_func_add)
for cls in reversed(classes):
unregister_class(cls)

File diff suppressed because it is too large Load Diff

255
render_povray/object_particles.py Executable file
View File

@ -0,0 +1,255 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""Get some Blender particle objects translated to POV."""
import bpy
def export_hair(file, ob, p_sys, global_matrix, write_matrix):
"""Get Blender path particles (hair strands) objects translated to POV sphere_sweep unions."""
# tstart = time.time()
textured_hair = 0
if ob.material_slots[p_sys.settings.material - 1].material and ob.active_material is not None:
pmaterial = ob.material_slots[p_sys.settings.material - 1].material
# XXX Todo: replace by pov_(Particles?)_texture_slot
for th in pmaterial.pov_texture_slots:
povtex = th.texture # slot.name
tex = bpy.data.textures[povtex]
if th and th.use:
if (tex.type == 'IMAGE' and tex.image) or tex.type != 'IMAGE':
if th.use_map_color_diffuse:
textured_hair = 1
if pmaterial.strand.use_blender_units:
strand_start = pmaterial.strand.root_size
strand_end = pmaterial.strand.tip_size
strand_shape = pmaterial.strand.shape
else: # Blender unit conversion
strand_start = pmaterial.strand.root_size / 200.0
strand_end = pmaterial.strand.tip_size / 200.0
strand_shape = pmaterial.strand.shape
else:
pmaterial = "default" # No material assigned in blender, use default one
strand_start = 0.01
strand_end = 0.01
strand_shape = 0.0
# Set the number of particles to render count rather than 3d view display
# p_sys.set_resolution(scene, ob, 'RENDER') # DEPRECATED
# When you render, the entire dependency graph will be
# evaluated at render resolution, including the particles.
# In the viewport it will be at viewport resolution.
# So there is no need fo render engines to use this function anymore,
# it's automatic now.
steps = p_sys.settings.display_step
steps = 2 ** steps # or + 1 # Formerly : len(particle.hair_keys)
total_number_of_strands = p_sys.settings.count + p_sys.settings.rendered_child_count
# hairCounter = 0
file.write('#declare HairArray = array[%i] {\n' % total_number_of_strands)
for pindex in range(0, total_number_of_strands):
# if particle.is_exist and particle.is_visible:
# hairCounter += 1
# controlPointCounter = 0
# Each hair is represented as a separate sphere_sweep in POV-Ray.
file.write('sphere_sweep{')
if p_sys.settings.use_hair_bspline:
file.write('b_spline ')
file.write(
'%i,\n' % (steps + 2)
) # +2 because the first point needs tripling to be more than a handle in POV
else:
file.write('linear_spline ')
file.write('%i,\n' % (steps))
# changing world coordinates to object local coordinates by
# multiplying with inverted matrix
init_coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=0))
if (
ob.material_slots[p_sys.settings.material - 1].material
and ob.active_material is not None
):
pmaterial = ob.material_slots[p_sys.settings.material - 1].material
for th in pmaterial.pov_texture_slots:
if th and th.use and th.use_map_color_diffuse:
povtex = th.texture # slot.name
tex = bpy.data.textures[povtex]
# treat POV textures as bitmaps
if (
tex.type == 'IMAGE'
and tex.image
and th.texture_coords == 'UV'
and ob.data.uv_textures is not None
):
# or (
# tex.pov.tex_pattern_type != 'emulator'
# and th.texture_coords == 'UV'
# and ob.data.uv_textures is not None
# ):
image = tex.image
image_width = image.size[0]
image_height = image.size[1]
image_pixels = image.pixels[:]
uv_co = p_sys.uv_on_emitter(mod, p_sys.particles[pindex], pindex, 0)
x_co = round(uv_co[0] * (image_width - 1))
y_co = round(uv_co[1] * (image_height - 1))
pixelnumber = (image_width * y_co) + x_co
r = image_pixels[pixelnumber * 4]
g = image_pixels[pixelnumber * 4 + 1]
b = image_pixels[pixelnumber * 4 + 2]
a = image_pixels[pixelnumber * 4 + 3]
init_color = (r, g, b, a)
else:
# only overwrite variable for each competing texture for now
init_color = tex.evaluate((init_coord[0], init_coord[1], init_coord[2]))
for step in range(0, steps):
coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=step))
# for controlPoint in particle.hair_keys:
if p_sys.settings.clump_factor != 0:
hair_strand_diameter = p_sys.settings.clump_factor / 200.0 * random.uniform(0.5, 1)
elif step == 0:
hair_strand_diameter = strand_start
else:
hair_strand_diameter += (strand_end - strand_start) / (
p_sys.settings.display_step + 1
) # XXX +1 or not? # XXX use strand_shape in formula
if step == 0 and p_sys.settings.use_hair_bspline:
# Write three times the first point to compensate pov Bezier handling
file.write(
'<%.6g,%.6g,%.6g>,%.7g,\n'
% (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
)
file.write(
'<%.6g,%.6g,%.6g>,%.7g,\n'
% (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
)
# Useless because particle location is the tip, not the root:
# file.write(
# '<%.6g,%.6g,%.6g>,%.7g'
# % (
# particle.location[0],
# particle.location[1],
# particle.location[2],
# abs(hair_strand_diameter)
# )
# )
# file.write(',\n')
# controlPointCounter += 1
# total_number_of_strands += len(p_sys.particles)# len(particle.hair_keys)
# Each control point is written out, along with the radius of the
# hair at that point.
file.write(
'<%.6g,%.6g,%.6g>,%.7g' % (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
)
# All coordinates except the last need a following comma.
if step != steps - 1:
file.write(',\n')
else:
if textured_hair:
# Write pigment and alpha (between Pov and Blender,
# alpha 0 and 1 are reversed)
file.write(
'\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n'
% (init_color[0], init_color[1], init_color[2], 1.0 - init_color[3])
)
# End the sphere_sweep declaration for this hair
file.write('}\n')
# All but the final sphere_sweep (each array element) needs a terminating comma.
if pindex != total_number_of_strands:
file.write(',\n')
else:
file.write('\n')
# End the array declaration.
file.write('}\n')
file.write('\n')
if not textured_hair:
# Pick up the hair material diffuse color and create a default POV-Ray hair texture.
file.write('#ifndef (HairTexture)\n')
file.write(' #declare HairTexture = texture {\n')
file.write(
' pigment {srgbt <%s,%s,%s,%s>}\n'
% (
pmaterial.diffuse_color[0],
pmaterial.diffuse_color[1],
pmaterial.diffuse_color[2],
(pmaterial.strand.width_fade + 0.05),
)
)
file.write(' }\n')
file.write('#end\n')
file.write('\n')
# Dynamically create a union of the hairstrands (or a subset of them).
# By default use every hairstrand, commented line is for hand tweaking test renders.
file.write('//Increasing HairStep divides the amount of hair for test renders.\n')
file.write('#ifndef(HairStep) #declare HairStep = 1; #end\n')
file.write('union{\n')
file.write(' #local I = 0;\n')
file.write(' #while (I < %i)\n' % total_number_of_strands)
file.write(' object {HairArray[I]')
if not textured_hair:
file.write(' texture{HairTexture}\n')
else:
file.write('\n')
# Translucency of the hair:
file.write(' hollow\n')
file.write(' double_illuminate\n')
file.write(' interior {\n')
file.write(' ior 1.45\n')
file.write(' media {\n')
file.write(' scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n')
file.write(' absorption 10/<0.83, 0.75, 0.15>\n')
file.write(' samples 1\n')
file.write(' method 2\n')
file.write(' density {cylindrical\n')
file.write(' color_map {\n')
file.write(' [0.0 rgb <0.83, 0.45, 0.35>]\n')
file.write(' [0.5 rgb <0.8, 0.8, 0.4>]\n')
file.write(' [1.0 rgb <1,1,1>]\n')
file.write(' }\n')
file.write(' }\n')
file.write(' }\n')
file.write(' }\n')
file.write(' }\n')
file.write(' #local I = I + HairStep;\n')
file.write(' #end\n')
write_matrix(global_matrix @ ob.matrix_world)
file.write('}')
print('Totals hairstrands written: %i' % total_number_of_strands)
print('Number of tufts (particle systems)', len(ob.particle_systems))
# Set back the displayed number of particles to preview count
# p_sys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED
# When you render, the entire dependency graph will be
# evaluated at render resolution, including the particles.
# In the viewport it will be at viewport resolution.
# So there is no need fo render engines to use this function anymore,
# it's automatic now.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,670 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""Declare object level properties controllable in UI and translated to POV"""
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import PropertyGroup
from bpy.props import (
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
StringProperty,
EnumProperty,
PointerProperty,
)
###############################################################################
# Object POV properties.
###############################################################################
class RenderPovSettingsObject(PropertyGroup):
"""Declare object and primitives level properties controllable in UI and translated to POV."""
# Pov inside_vector used for CSG
inside_vector: FloatVectorProperty(
name="CSG Inside Vector",
description="Direction to shoot CSG inside test rays at",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.001, 0.001, 0.5),
options={"ANIMATABLE"},
subtype="XYZ",
)
# Importance sampling
importance_value: FloatProperty(
name="Radiosity Importance",
description="Priority value relative to other objects for sampling radiosity rays. "
"Increase to get more radiosity rays at comparatively small yet "
"bright objects",
min=0.01,
max=1.00,
default=0.50,
)
# Collect photons
collect_photons: BoolProperty(
name="Receive Photon Caustics",
description="Enable object to collect photons from other objects caustics. Turn "
"off for objects that don't really need to receive caustics (e.g. objects"
" that generate caustics often don't need to show any on themselves)",
default=True,
)
# Photons spacing_multiplier
spacing_multiplier: FloatProperty(
name="Photons Spacing Multiplier",
description="Multiplier value relative to global spacing of photons. "
"Decrease by half to get 4x more photons at surface of "
"this object (or 8x media photons than specified in the globals",
min=0.01,
max=1.00,
default=1.00,
)
##################################CustomPOV Code############################
# Only DUMMIES below for now:
replacement_text: StringProperty(
name="Declared name:",
description="Type the declared name in custom POV code or an external .inc "
"it points at. Any POV shape expected e.g: isosurface {}",
default="",
)
#############POV specific object properties.############################
object_as: StringProperty(maxlen=1024)
imported_loc: FloatVectorProperty(
name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
)
imported_loc_cap: FloatVectorProperty(
name="Imported Pov location", precision=6, default=(0.0, 0.0, 2.0)
)
unlock_parameters: BoolProperty(name="Lock", default=False)
# not in UI yet but used for sor (lathe) / prism... pov primitives
curveshape: EnumProperty(
name="Povray Shape Type",
items=(
("birail", "Birail", ""),
("cairo", "Cairo", ""),
("lathe", "Lathe", ""),
("loft", "Loft", ""),
("prism", "Prism", ""),
("sphere_sweep", "Sphere Sweep", ""),
),
default="sphere_sweep",
)
mesh_write_as: EnumProperty(
name="Mesh Write As",
items=(("blobgrid", "Blob Grid", ""), ("grid", "Grid", ""), ("mesh", "Mesh", "")),
default="mesh",
)
object_ior: FloatProperty(name="IOR", description="IOR", min=1.0, max=10.0, default=1.0)
# shape_as_light = StringProperty(name="Light",maxlen=1024)
# fake_caustics_power = FloatProperty(
# name="Power", description="Fake caustics power",
# min=0.0, max=10.0,default=0.0)
# target = BoolProperty(name="Target",description="",default=False)
# target_value = FloatProperty(
# name="Value", description="",
# min=0.0, max=1.0,default=1.0)
# refraction = BoolProperty(name="Refraction",description="",default=False)
# dispersion = BoolProperty(name="Dispersion",description="",default=False)
# dispersion_value = FloatProperty(
# name="Dispersion", description="Good values are 1.01 to 1.1. ",
# min=1.0, max=1.2,default=1.01)
# dispersion_samples = IntProperty(name="Samples",min=2, max=100,default=7)
# reflection = BoolProperty(name="Reflection",description="",default=False)
# pass_through = BoolProperty(name="Pass through",description="",default=False)
no_shadow: BoolProperty(name="No Shadow", default=False)
no_image: BoolProperty(name="No Image", default=False)
no_reflection: BoolProperty(name="No Reflection", default=False)
no_radiosity: BoolProperty(name="No Radiosity", default=False)
inverse: BoolProperty(name="Inverse", default=False)
sturm: BoolProperty(name="Sturm", default=False)
double_illuminate: BoolProperty(name="Double Illuminate", default=False)
hierarchy: BoolProperty(name="Hierarchy", default=False)
hollow: BoolProperty(name="Hollow", default=False)
boundorclip: EnumProperty(
name="Boundorclip",
items=(
("none", "None", ""),
("bounded_by", "Bounded_by", ""),
("clipped_by", "Clipped_by", ""),
),
default="none",
)
boundorclipob: StringProperty(maxlen=1024)
addboundorclip: BoolProperty(description="", default=False)
blob_threshold: FloatProperty(name="Threshold", min=0.00, max=10.0, default=0.6)
blob_strength: FloatProperty(name="Strength", min=-10.00, max=10.0, default=1.00)
res_u: IntProperty(name="U", min=100, max=1000, default=500)
res_v: IntProperty(name="V", min=100, max=1000, default=500)
contained_by: EnumProperty(
name="Contained by", items=(("box", "Box", ""), ("sphere", "Sphere", "")), default="box"
)
container_scale: FloatProperty(name="Container Scale", min=0.0, max=10.0, default=1.00)
threshold: FloatProperty(name="Threshold", min=0.0, max=10.0, default=0.00)
accuracy: FloatProperty(name="Accuracy", min=0.0001, max=0.1, default=0.001)
max_gradient: FloatProperty(name="Max Gradient", min=0.0, max=100.0, default=5.0)
all_intersections: BoolProperty(name="All Intersections", default=False)
max_trace: IntProperty(name="Max Trace", min=1, max=100, default=1)
###########Cylinder
def prop_update_cylinder(self, context):
"""Update POV cylinder primitive parameters not only at creation but anytime they are changed in UI."""
if bpy.ops.pov.cylinder_update.poll():
bpy.ops.pov.cylinder_update()
cylinder_radius: FloatProperty(
name="Cylinder R", min=0.00, max=10.0, default=0.04, update=prop_update_cylinder
)
cylinder_location_cap: FloatVectorProperty(
name="Cylinder Cap Location",
subtype="TRANSLATION",
description="The position of the 'other' end of the cylinder (relative to object location)",
default=(0.0, 0.0, 2.0),
update=prop_update_cylinder,
)
imported_cyl_loc: FloatVectorProperty(
name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
)
imported_cyl_loc_cap: FloatVectorProperty(
name="Imported Pov location", precision=6, default=(0.0, 0.0, 2.0)
)
###########Sphere
def prop_update_sphere(self, context):
"""Update POV sphere primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.sphere_update()
sphere_radius: FloatProperty(
name="Sphere radius", min=0.00, max=10.0, default=0.5, update=prop_update_sphere
)
###########Cone
def prop_update_cone(self, context):
"""Update POV cone primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.cone_update()
cone_base_radius: FloatProperty(
name="Base radius",
description="The first radius of the cone",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_cone,
)
cone_cap_radius: FloatProperty(
name="Cap radius",
description="The second radius of the cone",
default=0.3,
min=0.0,
max=100.0,
update=prop_update_cone,
)
cone_segments: IntProperty(
name="Segments",
description="Radial segmentation of proxy mesh",
default=16,
min=3,
max=265,
update=prop_update_cone,
)
cone_height: FloatProperty(
name="Height",
description="Height of the cone",
default=2.0,
min=0.01,
max=100.0,
update=prop_update_cone,
)
cone_base_z: FloatProperty()
cone_cap_z: FloatProperty()
###########Parametric
def prop_update_parametric(self, context):
"""Update POV parametric surface primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.parametric_update()
u_min: FloatProperty(name="U Min", description="", default=0.0, update=prop_update_parametric)
v_min: FloatProperty(name="V Min", description="", default=0.0, update=prop_update_parametric)
u_max: FloatProperty(name="U Max", description="", default=6.28, update=prop_update_parametric)
v_max: FloatProperty(name="V Max", description="", default=12.57, update=prop_update_parametric)
x_eq: StringProperty(
maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)", update=prop_update_parametric
)
y_eq: StringProperty(
maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5", update=prop_update_parametric
)
z_eq: StringProperty(
maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)", update=prop_update_parametric
)
###########Torus
def prop_update_torus(self, context):
"""Update POV torus primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.torus_update()
torus_major_segments: IntProperty(
name="Segments",
description="Radial segmentation of proxy mesh",
default=48,
min=3,
max=720,
update=prop_update_torus,
)
torus_minor_segments: IntProperty(
name="Segments",
description="Cross-section segmentation of proxy mesh",
default=12,
min=3,
max=720,
update=prop_update_torus,
)
torus_major_radius: FloatProperty(
name="Major radius",
description="Major radius",
min=0.00,
max=100.00,
default=1.0,
update=prop_update_torus,
)
torus_minor_radius: FloatProperty(
name="Minor radius",
description="Minor radius",
min=0.00,
max=100.00,
default=0.25,
update=prop_update_torus,
)
###########Rainbow
arc_angle: FloatProperty(
name="Arc angle",
description="The angle of the raynbow arc in degrees",
default=360,
min=0.01,
max=360.0,
)
falloff_angle: FloatProperty(
name="Falloff angle",
description="The angle after which rainbow dissolves into background",
default=360,
min=0.0,
max=360,
)
###########HeightFields
quality: IntProperty(name="Quality", description="", default=100, min=1, max=100)
hf_filename: StringProperty(maxlen=1024)
hf_gamma: FloatProperty(name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0)
hf_premultiplied: BoolProperty(name="Premultiplied", description="Premultiplied", default=True)
hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False)
hf_water: FloatProperty(
name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0
)
hf_hierarchy: BoolProperty(name="Hierarchy", description="Height field hierarchy", default=True)
##############Superellipsoid
def prop_update_superellipsoid(self, context):
"""Update POV superellipsoid primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.superellipsoid_update()
se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04)
se_param2: FloatProperty(name="Parameter 2", description="", min=0.00, max=10.0, default=0.04)
se_u: IntProperty(
name="U-segments",
description="radial segmentation",
default=20,
min=4,
max=265,
update=prop_update_superellipsoid,
)
se_v: IntProperty(
name="V-segments",
description="lateral segmentation",
default=20,
min=4,
max=265,
update=prop_update_superellipsoid,
)
se_n1: FloatProperty(
name="Ring manipulator",
description="Manipulates the shape of the Ring",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_superellipsoid,
)
se_n2: FloatProperty(
name="Cross manipulator",
description="Manipulates the shape of the cross-section",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_superellipsoid,
)
se_edit: EnumProperty(
items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")],
name="Fill up and down",
description="",
default="TRIANGLES",
update=prop_update_superellipsoid,
)
#############Used for loft but also Superellipsoid, etc.
curveshape: EnumProperty(
name="Povray Shape Type",
items=(
("birail", "Birail", ""),
("cairo", "Cairo", ""),
("lathe", "Lathe", ""),
("loft", "Loft", ""),
("prism", "Prism", ""),
("sphere_sweep", "Sphere Sweep", ""),
("sor", "Surface of Revolution", ""),
),
default="sphere_sweep",
)
#############Supertorus
def prop_update_supertorus(self, context):
"""Update POV supertorus primitive parameters not only at creation but anytime they are changed in UI."""
bpy.ops.pov.supertorus_update()
st_major_radius: FloatProperty(
name="Major radius",
description="Major radius",
min=0.00,
max=100.00,
default=1.0,
update=prop_update_supertorus,
)
st_minor_radius: FloatProperty(
name="Minor radius",
description="Minor radius",
min=0.00,
max=100.00,
default=0.25,
update=prop_update_supertorus,
)
st_ring: FloatProperty(
name="Ring",
description="Ring manipulator",
min=0.0001,
max=100.00,
default=1.00,
update=prop_update_supertorus,
)
st_cross: FloatProperty(
name="Cross",
description="Cross manipulator",
min=0.0001,
max=100.00,
default=1.00,
update=prop_update_supertorus,
)
st_accuracy: FloatProperty(
name="Accuracy", description="Supertorus accuracy", min=0.00001, max=1.00, default=0.001
)
st_max_gradient: FloatProperty(
name="Gradient",
description="Max gradient",
min=0.0001,
max=100.00,
default=10.00,
update=prop_update_supertorus,
)
st_R: FloatProperty(
name="big radius",
description="The radius inside the tube",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_supertorus,
)
st_r: FloatProperty(
name="small radius",
description="The radius of the tube",
default=0.3,
min=0.01,
max=100.0,
update=prop_update_supertorus,
)
st_u: IntProperty(
name="U-segments",
description="radial segmentation",
default=16,
min=3,
max=265,
update=prop_update_supertorus,
)
st_v: IntProperty(
name="V-segments",
description="lateral segmentation",
default=8,
min=3,
max=265,
update=prop_update_supertorus,
)
st_n1: FloatProperty(
name="Ring manipulator",
description="Manipulates the shape of the Ring",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_supertorus,
)
st_n2: FloatProperty(
name="Cross manipulator",
description="Manipulates the shape of the cross-section",
default=1.0,
min=0.01,
max=100.0,
update=prop_update_supertorus,
)
st_ie: BoolProperty(
name="Use Int.+Ext. radii",
description="Use internal and external radii",
default=False,
update=prop_update_supertorus,
)
st_edit: BoolProperty(
name="", description="", default=False, options={"HIDDEN"}, update=prop_update_supertorus
)
########################Loft
loft_n: IntProperty(
name="Segments", description="Vertical segments", default=16, min=3, max=720
)
loft_rings_bottom: IntProperty(
name="Bottom", description="Bottom rings", default=5, min=2, max=100
)
loft_rings_side: IntProperty(name="Side", description="Side rings", default=10, min=2, max=100)
loft_thick: FloatProperty(
name="Thickness",
description="Manipulates the shape of the Ring",
default=0.3,
min=0.01,
max=1.0,
)
loft_r: FloatProperty(name="Radius", description="Radius", default=1, min=0.01, max=10)
loft_height: FloatProperty(
name="Height",
description="Manipulates the shape of the Ring",
default=2,
min=0.01,
max=10.0,
)
###################Prism
prism_n: IntProperty(name="Sides", description="Number of sides", default=5, min=3, max=720)
prism_r: FloatProperty(name="Radius", description="Radius", default=1.0)
##################Isosurface
iso_function_text: StringProperty(
name="Function Text", maxlen=1024
) # ,update=iso_props_update_callback)
##################PolygonToCircle
polytocircle_resolution: IntProperty(
name="Resolution", description="", default=3, min=0, max=256
)
polytocircle_ngon: IntProperty(name="NGon", description="", min=3, max=64, default=5)
polytocircle_ngonR: FloatProperty(name="NGon Radius", description="", default=0.3)
polytocircle_circleR: FloatProperty(name="Circle Radius", description="", default=1.0)
###############################################################################
# Modifiers POV properties.
###############################################################################
# class RenderPovSettingsModifier(PropertyGroup):
boolean_mod: EnumProperty(
name="Operation",
description="Choose the type of calculation for Boolean modifier",
items=(
("BMESH", "Use the BMesh Boolean Solver", ""),
("CARVE", "Use the Carve Boolean Solver", ""),
("POV", "Use POV Constructive Solid Geometry", ""),
),
default="BMESH",
)
#################Avogadro
# filename_ext = ".png"
# filter_glob = StringProperty(
# default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF",
# options={'HIDDEN'},
# )
classes = (RenderPovSettingsObject,)
def register():
for cls in classes:
register_class(cls)
bpy.types.Object.pov = PointerProperty(type=RenderPovSettingsObject)
def unregister():
del bpy.types.Object.pov
for cls in reversed(classes):
unregister_class(cls)

5534
render_povray/render.py Normal file → Executable file

File diff suppressed because it is too large Load Diff

562
render_povray/render_gui.py Executable file
View File

@ -0,0 +1,562 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""User interface for rendering parameters"""
import bpy
from sys import platform # really import here, as in render.py?
# Or todo: handle this more crossplatform using QTpovray for Linux for instance
# from os.path import isfile
from bl_operators.presets import AddPresetBase
from bpy.utils import register_class, unregister_class
from bpy.props import EnumProperty
from bpy.types import Operator, Menu, Panel
# Example of wrapping every class 'as is'
from bl_ui import properties_output
for member in dir(properties_output):
subclass = getattr(properties_output, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_output
from bl_ui import properties_freestyle
for member in dir(properties_freestyle):
subclass = getattr(properties_freestyle, member)
try:
if not (subclass.bl_space_type == 'PROPERTIES' and subclass.bl_context == "render"):
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
# subclass.bl_parent_id = "RENDER_PT_POV_filter"
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_freestyle
from bl_ui import properties_view_layer
for member in dir(properties_view_layer):
subclass = getattr(properties_view_layer, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_view_layer
# Use some of the existing buttons.
from bl_ui import properties_render
# DEPRECATED#properties_render.RENDER_PT_render.COMPAT_ENGINES.add('POVRAY_RENDER')
# DEPRECATED#properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('POVRAY_RENDER')
# properties_render.RENDER_PT_antialiasing.COMPAT_ENGINES.add('POVRAY_RENDER')
# TORECREATE##DEPRECATED#properties_render.RENDER_PT_shading.COMPAT_ENGINES.add('POVRAY_RENDER')
# DEPRECATED#properties_render.RENDER_PT_output.COMPAT_ENGINES.add('POVRAY_RENDER')
del properties_render
def check_render_freestyle_svg():
"""Test if Freestyle SVG Exporter addon is activated
This addon is currently used to generate the SVG lines file
when Freestyle is enabled alongside POV
"""
if "render_freestyle_svg" in bpy.context.preferences.addons.keys():
return True
return False
class RenderButtonsPanel:
"""Use this class to define buttons from the render tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "render"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
rd = context.scene.render
return rd.engine in cls.COMPAT_ENGINES
class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel):
"""Use this class to define pov ini settingss buttons."""
bl_options = {'DEFAULT_CLOSED'}
bl_label = "Auto Start"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
scene = context.scene
if scene.pov.tempfiles_enable:
self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='AUTO')
else:
self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='CONSOLE')
def draw(self, context):
layout = self.layout
scene = context.scene
layout.active = scene.pov.max_trace_level != 0
split = layout.split()
col = split.column()
col.label(text="Command line options:")
col.prop(scene.pov, "command_line_switches", text="", icon='RIGHTARROW')
split = layout.split()
# layout.active = not scene.pov.tempfiles_enable
if not scene.pov.tempfiles_enable:
split.prop(scene.pov, "deletefiles_enable", text="Delete files")
split.prop(scene.pov, "pov_editor", text="POV Editor")
col = layout.column()
col.prop(scene.pov, "scene_name", text="Name")
col.prop(scene.pov, "scene_path", text="Path to files")
# col.prop(scene.pov, "scene_path", text="Path to POV-file")
# col.prop(scene.pov, "renderimage_path", text="Path to image")
split = layout.split()
split.prop(scene.pov, "indentation_character", text="Indent")
if scene.pov.indentation_character == 'SPACE':
split.prop(scene.pov, "indentation_spaces", text="Spaces")
row = layout.row()
row.prop(scene.pov, "comments_enable", text="Comments")
row.prop(scene.pov, "list_lf_enable", text="Line breaks in lists")
class RENDER_PT_POV_render_settings(RenderButtonsPanel, Panel):
"""Use this class to define pov render settings buttons."""
bl_label = "Global Settings"
bl_icon = 'SETTINGS'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
scene = context.scene
if scene.pov.global_settings_advanced:
self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='SETTINGS')
else:
self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='PREFERENCES')
def draw(self, context):
layout = self.layout
scene = context.scene
# rd = context.scene.render
# layout.active = (scene.pov.max_trace_level != 0)
if not platform.startswith('win'):
layout.prop(scene.pov, "sdl_window_enable", text="POV-Ray SDL Window")
col = layout.column()
col.label(text="Main Path Tracing:")
col.prop(scene.pov, "max_trace_level", text="Ray Depth")
align = True
layout.active = scene.pov.global_settings_advanced
row = layout.row(align=align)
row.prop(scene.pov, "adc_bailout")
row = layout.row(align=align)
row.prop(scene.pov, "ambient_light")
row = layout.row(align=align)
row.prop(scene.pov, "irid_wavelength")
row = layout.row(align=align)
row.prop(scene.pov, "number_of_waves")
row = layout.row(align=align)
row.prop(scene.pov, "noise_generator")
split = layout.split()
split.label(text="Shading:")
split = layout.split()
row = split.row(align=align)
row.prop(scene.pov, "use_shadows")
row.prop(scene.pov, "alpha_mode")
class RENDER_PT_POV_photons(RenderButtonsPanel, Panel):
"""Use this class to define pov photons buttons."""
bl_label = "Photons"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
# def draw_header(self, context):
# self.layout.label(icon='SETTINGS')
def draw_header(self, context):
scene = context.scene
if scene.pov.photon_enable:
self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER_ACT')
else:
self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER')
def draw(self, context):
scene = context.scene
layout = self.layout
layout.active = scene.pov.photon_enable
col = layout.column()
# col.label(text="Global Photons:")
col.prop(scene.pov, "photon_max_trace_level", text="Photon Depth")
split = layout.split()
col = split.column()
col.prop(scene.pov, "photon_spacing", text="Spacing")
col.prop(scene.pov, "photon_gather_min")
col = split.column()
col.prop(scene.pov, "photon_adc_bailout", text="Photon ADC")
col.prop(scene.pov, "photon_gather_max")
box = layout.box()
box.label(text='Photon Map File:')
row = box.row()
row.prop(scene.pov, "photon_map_file_save_load", expand=True)
if scene.pov.photon_map_file_save_load in {'save'}:
box.prop(scene.pov, "photon_map_dir")
box.prop(scene.pov, "photon_map_filename")
if scene.pov.photon_map_file_save_load in {'load'}:
box.prop(scene.pov, "photon_map_file")
# end main photons
class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel):
"""Use this class to define pov antialiasing buttons."""
bl_label = "Anti-Aliasing"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
scene = context.scene
if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2':
self.layout.prop(scene.pov, "antialias_enable", text="", icon='ERROR')
elif scene.pov.antialias_enable:
self.layout.prop(scene.pov, "antialias_enable", text="", icon='ANTIALIASED')
else:
self.layout.prop(scene.pov, "antialias_enable", text="", icon='ALIASED')
def draw(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
layout = self.layout
scene = context.scene
layout.active = scene.pov.antialias_enable
row = layout.row()
row.prop(scene.pov, "antialias_method", text="")
if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2':
col = layout.column()
col.alignment = 'CENTER'
col.label(text="Stochastic Anti Aliasing is")
col.label(text="Only Available with UberPOV")
col.label(text="Feature Set in User Preferences.")
col.label(text="Using Type 2 (recursive) instead")
else:
row.prop(scene.pov, "jitter_enable", text="Jitter")
split = layout.split()
col = split.column()
col.prop(scene.pov, "antialias_depth", text="AA Depth")
sub = split.column()
sub.prop(scene.pov, "jitter_amount", text="Jitter Amount")
if scene.pov.jitter_enable:
sub.enabled = True
else:
sub.enabled = False
row = layout.row()
row.prop(scene.pov, "antialias_threshold", text="AA Threshold")
row.prop(scene.pov, "antialias_gamma", text="AA Gamma")
if prefs.branch_feature_set_povray == 'uberpov':
row = layout.row()
row.prop(scene.pov, "antialias_confidence", text="AA Confidence")
if scene.pov.antialias_method == '2':
row.enabled = True
else:
row.enabled = False
class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel):
"""Use this class to define pov radiosity buttons."""
bl_label = "Diffuse Radiosity"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
scene = context.scene
if scene.pov.radio_enable:
self.layout.prop(scene.pov, "radio_enable", text="", icon='OUTLINER_OB_LIGHTPROBE')
else:
self.layout.prop(scene.pov, "radio_enable", text="", icon='LIGHTPROBE_CUBEMAP')
def draw(self, context):
layout = self.layout
scene = context.scene
layout.active = scene.pov.radio_enable
split = layout.split()
col = split.column()
col.prop(scene.pov, "radio_count", text="Rays")
col.prop(scene.pov, "radio_recursion_limit", text="Recursions")
split.prop(scene.pov, "radio_error_bound", text="Error Bound")
layout.prop(scene.pov, "radio_display_advanced")
if scene.pov.radio_display_advanced:
split = layout.split()
col = split.column()
col.prop(scene.pov, "radio_adc_bailout", slider=True)
col.prop(scene.pov, "radio_minimum_reuse", text="Min Reuse")
col.prop(scene.pov, "radio_gray_threshold", slider=True)
col.prop(scene.pov, "radio_pretrace_start", slider=True)
col.prop(scene.pov, "radio_low_error_factor", slider=True)
col = split.column()
col.prop(scene.pov, "radio_brightness")
col.prop(scene.pov, "radio_maximum_reuse", text="Max Reuse")
col.prop(scene.pov, "radio_nearest_count")
col.prop(scene.pov, "radio_pretrace_end", slider=True)
col = layout.column()
col.label(text="Estimation Influence:")
col.prop(scene.pov, "radio_always_sample")
col.prop(scene.pov, "radio_normal")
col.prop(scene.pov, "radio_media")
col.prop(scene.pov, "radio_subsurface")
class POV_RADIOSITY_MT_presets(Menu):
"""Use this class to define pov radiosity presets menu."""
bl_label = "Radiosity Presets"
preset_subdir = "pov/radiosity"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
class RENDER_OT_POV_radiosity_add_preset(AddPresetBase, Operator):
"""Use this class to define pov radiosity add presets button"""
'''Add a Radiosity Preset'''
bl_idname = "scene.radiosity_preset_add"
bl_label = "Add Radiosity Preset"
preset_menu = "POV_RADIOSITY_MT_presets"
# variable used for all preset values
preset_defines = ["scene = bpy.context.scene"]
# properties to store in the preset
preset_values = [
"scene.pov.radio_display_advanced",
"scene.pov.radio_adc_bailout",
"scene.pov.radio_always_sample",
"scene.pov.radio_brightness",
"scene.pov.radio_count",
"scene.pov.radio_error_bound",
"scene.pov.radio_gray_threshold",
"scene.pov.radio_low_error_factor",
"scene.pov.radio_media",
"scene.pov.radio_subsurface",
"scene.pov.radio_minimum_reuse",
"scene.pov.radio_maximum_reuse",
"scene.pov.radio_nearest_count",
"scene.pov.radio_normal",
"scene.pov.radio_recursion_limit",
"scene.pov.radio_pretrace_start",
"scene.pov.radio_pretrace_end",
]
# where to store the preset
preset_subdir = "pov/radiosity"
# Draw into an existing panel
def rad_panel_func(self, context):
"""Display radiosity presets rolldown menu"""
layout = self.layout
row = layout.row(align=True)
row.menu(POV_RADIOSITY_MT_presets.__name__, text=POV_RADIOSITY_MT_presets.bl_label)
row.operator(RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='ADD')
row.operator(
RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='REMOVE'
).remove_active = True
###############################################################################
# Freestyle
###############################################################################
# import addon_utils
# addon_utils.paths()[0]
# addon_utils.modules()
# mod.bl_info['name'] == 'Freestyle SVG Exporter':
bpy.utils.script_paths("addons")
# render_freestyle_svg = os.path.join(bpy.utils.script_paths("addons"), "render_freestyle_svg.py")
render_freestyle_svg = bpy.context.preferences.addons.get('render_freestyle_svg')
# mpath=addon_utils.paths()[0].render_freestyle_svg
# import mpath
# from mpath import render_freestyle_svg #= addon_utils.modules(['Freestyle SVG Exporter'])
# from scripts\\addons import render_freestyle_svg
if check_render_freestyle_svg():
'''
snippetsWIP
import myscript
import importlib
importlib.reload(myscript)
myscript.main()
'''
for member in dir(render_freestyle_svg):
subclass = getattr(render_freestyle_svg, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
if subclass.bl_idname == "RENDER_PT_SVGExporterPanel":
subclass.bl_parent_id = "RENDER_PT_POV_filter"
subclass.bl_options = {'HIDE_HEADER'}
# subclass.bl_order = 11
print(subclass.bl_info)
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
# del render_freestyle_svg.RENDER_PT_SVGExporterPanel.bl_parent_id
class RENDER_PT_POV_filter(RenderButtonsPanel, Panel):
"""Use this class to invoke stuff like Freestyle UI."""
bl_label = "Freestyle"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
with_freestyle = bpy.app.build_options.freestyle
engine = context.scene.render.engine
return with_freestyle and engine == 'POVRAY_RENDER'
def draw_header(self, context):
# scene = context.scene
rd = context.scene.render
layout = self.layout
if rd.use_freestyle:
layout.prop(rd, "use_freestyle", text="", icon='LINE_DATA')
else:
layout.prop(rd, "use_freestyle", text="", icon='OUTLINER_OB_IMAGE')
def draw(self, context):
rd = context.scene.render
layout = self.layout
layout.active = rd.use_freestyle
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
flow = layout.grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=True
)
flow.prop(rd, "line_thickness_mode", expand=True)
if rd.line_thickness_mode == 'ABSOLUTE':
flow.prop(rd, "line_thickness")
# Warning if the Freestyle SVG Exporter addon is not enabled
if not check_render_freestyle_svg():
# col = box.column()
layout.label(text="Please enable Freestyle SVG Exporter addon", icon="INFO")
# layout.separator()
layout.operator(
"preferences.addon_show",
text="Go to Render: Freestyle SVG Exporter addon",
icon="PREFERENCES",
).module = "render_freestyle_svg"
##class RENDER_PT_povray_baking(RenderButtonsPanel, Panel):
## bl_label = "Baking"
## COMPAT_ENGINES = {'POVRAY_RENDER'}
##
## def draw_header(self, context):
## scene = context.scene
##
## self.layout.prop(scene.pov, "baking_enable", text="")
##
## def draw(self, context):
## layout = self.layout
##
## scene = context.scene
## rd = scene.render
##
## layout.active = scene.pov.baking_enable
classes = (
RENDER_PT_POV_export_settings,
RENDER_PT_POV_render_settings,
RENDER_PT_POV_photons,
RENDER_PT_POV_antialias,
RENDER_PT_POV_radiosity,
RENDER_PT_POV_filter,
# RENDER_PT_povray_baking,
POV_RADIOSITY_MT_presets,
RENDER_OT_POV_radiosity_add_preset,
)
def register():
for cls in classes:
register_class(cls)
bpy.types.RENDER_PT_POV_radiosity.prepend(rad_panel_func)
def unregister():
bpy.types.RENDER_PT_POV_radiosity.remove(rad_panel_func)
for cls in reversed(classes):
unregister_class(cls)

View File

@ -0,0 +1,687 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""Declare rendering properties controllable in UI"""
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import PropertyGroup
from bpy.props import (
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
StringProperty,
EnumProperty,
PointerProperty,
)
###############################################################################
# Scene POV properties.
###############################################################################
class RenderPovSettingsScene(PropertyGroup):
"""Declare scene level properties controllable in UI and translated to POV"""
# Linux SDL-window enable
sdl_window_enable: BoolProperty(
name="Enable SDL window", description="Enable the SDL window in Linux OS", default=True
)
# File Options
text_block: StringProperty(
name="Text Scene Name",
description="Name of POV scene to use. "
"Set when clicking Run to render current text only",
maxlen=1024,
)
tempfiles_enable: BoolProperty(
name="Enable Tempfiles",
description="Enable the OS-Tempfiles. Otherwise set the path where to save the files",
default=True,
)
pov_editor: BoolProperty(
name="POV editor",
description="Don't Close POV editor after rendering (Overridden by /EXIT command)",
default=False,
)
deletefiles_enable: BoolProperty(
name="Delete files",
description="Delete files after rendering. Doesn't work with the image",
default=True,
)
scene_name: StringProperty(
name="Scene Name",
description="Name of POV scene to create. Empty name will use "
"the name of the blend file",
maxlen=1024,
)
scene_path: StringProperty(
name="Export scene path",
# Bug in POV-Ray RC3
# description="Path to directory where the exported scene "
# "(POV and INI) is created",
description="Path to directory where the files are created",
maxlen=1024,
subtype="DIR_PATH",
)
renderimage_path: StringProperty(
name="Rendered image path",
description="Full path to directory where the rendered image is saved",
maxlen=1024,
subtype="DIR_PATH",
)
list_lf_enable: BoolProperty(
name="LF in lists",
description="Enable line breaks in lists (vectors and indices). "
"Disabled: lists are exported in one line",
default=False,
)
# Not a real pov option, just to know if we should write
radio_enable: BoolProperty(
name="Enable Radiosity", description="Enable POV radiosity calculation", default=True
)
radio_display_advanced: BoolProperty(
name="Advanced Options", description="Show advanced options", default=False
)
media_enable: BoolProperty(
name="Enable Media", description="Enable POV atmospheric media", default=False
)
media_samples: IntProperty(
name="Samples",
description="Number of samples taken from camera to first object "
"encountered along ray path for media calculation",
min=1,
max=100,
default=35,
)
media_scattering_type: EnumProperty(
name="Scattering Type",
description="Scattering model",
items=(
(
"1",
"1 Isotropic",
"The simplest form of scattering because it is independent of direction.",
),
(
"2",
"2 Mie haze ",
"For relatively small particles such as "
"minuscule water droplets of fog, cloud "
"particles, and particles responsible "
"for the polluted sky. In this model the"
" scattering is extremely directional in"
" the forward direction i.e. the amount "
"of scattered light is largest when the "
"incident light is anti-parallel to the "
"viewing direction (the light goes "
"directly to the viewer). It is smallest"
" when the incident light is parallel to"
" the viewing direction. ",
),
("3", "3 Mie murky", "Like haze but much more directional"),
(
"4",
"4 Rayleigh",
"For extremely small particles such as "
"molecules of the air. The amount of "
"scattered light depends on the incident"
" light angle. It is largest when the "
"incident light is parallel or "
"anti-parallel to the viewing direction "
"and smallest when the incident light is "
"perpendicular to viewing direction.",
),
(
"5",
"5 Henyey-Greenstein",
"The default eccentricity value "
"of zero defines isotropic "
"scattering while positive "
"values lead to scattering in "
"the direction of the light and "
"negative values lead to "
"scattering in the opposite "
"direction of the light. Larger "
"values of e (or smaller values "
"in the negative case) increase "
"the directional property of the"
" scattering.",
),
),
default="1",
)
media_diffusion_scale: FloatProperty(
name="Scale",
description="Scale factor of Media Diffusion Color",
precision=6,
step=0.00000001,
min=0.000000001,
max=1.0,
default=(1.0),
)
media_diffusion_color: FloatVectorProperty(
name="Media Diffusion Color",
description="The atmospheric media color",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.001, 0.001, 0.001),
options={"ANIMATABLE"},
subtype="COLOR",
)
media_absorption_scale: FloatProperty(
name="Scale",
description="Scale factor of Media Absorption Color. "
"use 1/depth of media volume in meters",
precision=6,
step=0.000001,
min=0.000000001,
max=1.0,
default=(0.00002),
)
media_absorption_color: FloatVectorProperty(
name="Media Absorption Color",
description="The atmospheric media absorption color",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.0, 0.0, 0.0),
options={"ANIMATABLE"},
subtype="COLOR",
)
media_eccentricity: FloatProperty(
name="Media Eccenticity Factor",
description="Positive values lead"
" to scattering in the direction of the light and negative "
"values lead to scattering in the opposite direction of the "
"light. Larger values of e (or smaller values in the negative"
" case) increase the directional property of the scattering",
precision=2,
step=0.01,
min=-1.0,
max=1.0,
default=(0.0),
options={"ANIMATABLE"},
)
baking_enable: BoolProperty(
name="Enable Baking", description="Enable POV texture baking", default=False
)
indentation_character: EnumProperty(
name="Indentation",
description="Select the indentation type",
items=(
("NONE", "None", "No indentation"),
("TAB", "Tabs", "Indentation with tabs"),
("SPACE", "Spaces", "Indentation with spaces"),
),
default="SPACE",
)
indentation_spaces: IntProperty(
name="Quantity of spaces",
description="The number of spaces for indentation",
min=1,
max=10,
default=4,
)
comments_enable: BoolProperty(
name="Enable Comments", description="Add comments to pov file", default=True
)
# Real pov options
command_line_switches: StringProperty(
name="Command Line Switches",
description="Command line switches consist of a + (plus) or - "
"(minus) sign, followed by one or more alphabetic "
"characters and possibly a numeric value",
maxlen=500,
)
antialias_enable: BoolProperty(
name="Anti-Alias", description="Enable Anti-Aliasing", default=True
)
antialias_method: EnumProperty(
name="Method",
description="AA-sampling method. Type 1 is an adaptive, "
"non-recursive, super-sampling (as in the plain old render "
"bigger and scale down trick. Type 2 is a slightly "
"more efficient adaptive and recursive super-sampling. "
"Type 3 is a stochastic halton based super-sampling method so "
"rather artifact free and sampling rays so depth of field can "
"use them at no additional cost, as do area lights and "
"subsurface scattering materials, making it the best "
"quality / time trade-off in complex scenes",
items=(
("0", "non-recursive AA", "Type 1 Sampling in POV"),
("1", "recursive AA", "Type 2 Sampling in POV"),
("2", "stochastic AA", "Type 3 Sampling in POV"),
),
default="1",
)
antialias_confidence: FloatProperty(
name="Antialias Confidence",
description="how surely the computed color "
"of a given pixel is indeed"
"within the threshold error margin",
min=0.0001,
max=1.0000,
default=0.9900,
precision=4,
)
antialias_depth: IntProperty(
name="Antialias Depth", description="Depth of pixel for sampling", min=1, max=9, default=2
)
antialias_threshold: FloatProperty(
name="Antialias Threshold",
description="Tolerance for sub-pixels",
min=0.0,
max=1.0,
soft_min=0.05,
soft_max=0.5,
default=0.03,
)
jitter_enable: BoolProperty(
name="Jitter",
description="Enable Jittering. Adds noise into the sampling "
"process (it should be avoided to use jitter in "
"animation)",
default=False,
)
jitter_amount: FloatProperty(
name="Jitter Amount",
description="Amount of jittering",
min=0.0,
max=1.0,
soft_min=0.01,
soft_max=1.0,
default=1.0,
)
antialias_gamma: FloatProperty(
name="Antialias Gamma",
description="POV-Ray compares gamma-adjusted values for super "
"sampling. Antialias Gamma sets the Gamma before "
"comparison",
min=0.0,
max=5.0,
soft_min=0.01,
soft_max=2.5,
default=2.5,
)
alpha_mode: EnumProperty(
name="Alpha",
description="Representation of alpha information in the RGBA pixels",
items=(
("SKY", "Sky", "Transparent pixels are filled with sky color"),
(
"TRANSPARENT",
"Transparent",
"Transparent, World background is transparent with premultiplied alpha",
),
),
default="SKY",
)
use_shadows: BoolProperty(
name="Shadows", description="Calculate shadows while rendering", default=True
)
max_trace_level: IntProperty(
name="Max Trace Level",
description="Number of reflections/refractions allowed on ray " "path",
min=1,
max=256,
default=5,
)
adc_bailout_enable: BoolProperty(name="Enable", description="", default=False)
adc_bailout: FloatProperty(
name="ADC Bailout",
description="Adaptive Depth Control (ADC) to stop computing additional"
"reflected or refracted rays when their contribution is insignificant."
"The default value is 1/255, or approximately 0.0039, since a change "
"smaller than that could not be visible in a 24 bit image. Generally "
"this value is fine and should be left alone."
"Setting adc_bailout to 0 will disable ADC, relying completely on "
"max_trace_level to set an upper limit on the number of rays spawned. ",
min=0.0,
max=1000.0,
default=0.00392156862745,
precision=3,
)
ambient_light_enable: BoolProperty(name="Enable", description="", default=False)
ambient_light: FloatVectorProperty(
name="Ambient Light",
description="Ambient light is used to simulate the effect of inter-diffuse reflection",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(1, 1, 1),
options={"ANIMATABLE"},
subtype="COLOR",
)
global_settings_advanced: BoolProperty(name="Advanced", description="", default=False)
irid_wavelength_enable: BoolProperty(name="Enable", description="", default=False)
irid_wavelength: FloatVectorProperty(
name="Irid Wavelength",
description=(
"Iridescence calculations depend upon the dominant "
"wavelengths of the primary colors of red, green and blue light"
),
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.25, 0.18, 0.14),
options={"ANIMATABLE"},
subtype="COLOR",
)
number_of_waves_enable: BoolProperty(name="Enable", description="", default=False)
number_of_waves: IntProperty(
name="Number Waves",
description=(
"The waves and ripples patterns are generated by summing a series of waves, "
"each with a slightly different center and size"
),
min=1,
max=10,
default=1000,
)
noise_generator_enable: BoolProperty(name="Enable", description="", default=False)
noise_generator: IntProperty(
name="Noise Generator",
description="There are three noise generators implemented",
min=1,
max=3,
default=2,
)
########################### PHOTONS #######################################
photon_enable: BoolProperty(name="Photons", description="Enable global photons", default=False)
photon_enable_count: BoolProperty(
name="Spacing / Count", description="Enable count photons", default=False
)
photon_count: IntProperty(
name="Count", description="Photons count", min=1, max=100000000, default=20000
)
photon_spacing: FloatProperty(
name="Spacing",
description="Average distance between photons on surfaces. half "
"this get four times as many surface photons",
min=0.001,
max=1.000,
soft_min=0.001,
soft_max=1.000,
precision=3,
default=0.005,
)
photon_max_trace_level: IntProperty(
name="Max Trace Level",
description="Number of reflections/refractions allowed on ray " "path",
min=1,
max=256,
default=5,
)
photon_adc_bailout: FloatProperty(
name="ADC Bailout",
description="The adc_bailout for photons. Use adc_bailout = "
"0.01 / brightest_ambient_object for good results",
min=0.0,
max=1000.0,
soft_min=0.0,
soft_max=1.0,
precision=3,
default=0.1,
)
photon_gather_min: IntProperty(
name="Gather Min",
description="Minimum number of photons gathered" "for each point",
min=1,
max=256,
default=20,
)
photon_gather_max: IntProperty(
name="Gather Max",
description="Maximum number of photons gathered for each point",
min=1,
max=256,
default=100,
)
photon_map_file_save_load: EnumProperty(
name="Operation",
description="Load or Save photon map file",
items=(("NONE", "None", ""), ("save", "Save", ""), ("load", "Load", "")),
default="NONE",
)
photon_map_filename: StringProperty(name="Filename", description="", maxlen=1024)
photon_map_dir: StringProperty(
name="Directory", description="", maxlen=1024, subtype="DIR_PATH"
)
photon_map_file: StringProperty(name="File", description="", maxlen=1024, subtype="FILE_PATH")
#########RADIOSITY########
radio_adc_bailout: FloatProperty(
name="ADC Bailout",
description="The adc_bailout for radiosity rays. Use "
"adc_bailout = 0.01 / brightest_ambient_object for good results",
min=0.0,
max=1000.0,
soft_min=0.0,
soft_max=1.0,
default=0.0039,
precision=4,
)
radio_always_sample: BoolProperty(
name="Always Sample",
description="Only use the data from the pretrace step and not gather "
"any new samples during the final radiosity pass",
default=False,
)
radio_brightness: FloatProperty(
name="Brightness",
description="Amount objects are brightened before being returned "
"upwards to the rest of the system",
min=0.0,
max=1000.0,
soft_min=0.0,
soft_max=10.0,
default=1.0,
)
radio_count: IntProperty(
name="Ray Count",
description="Number of rays for each new radiosity value to be calculated "
"(halton sequence over 1600)",
min=1,
max=10000,
soft_max=1600,
default=35,
)
radio_error_bound: FloatProperty(
name="Error Bound",
description="One of the two main speed/quality tuning values, "
"lower values are more accurate",
min=0.0,
max=1000.0,
soft_min=0.1,
soft_max=10.0,
default=10.0,
)
radio_gray_threshold: FloatProperty(
name="Gray Threshold",
description="One of the two main speed/quality tuning values, "
"lower values are more accurate",
min=0.0,
max=1.0,
soft_min=0,
soft_max=1,
default=0.0,
)
radio_low_error_factor: FloatProperty(
name="Low Error Factor",
description="Just enough samples is slightly blotchy. Low error changes error "
"tolerance for less critical last refining pass",
min=0.000001,
max=1.0,
soft_min=0.000001,
soft_max=1.0,
default=0.5,
)
radio_media: BoolProperty(
name="Media", description="Radiosity estimation can be affected by media", default=True
)
radio_subsurface: BoolProperty(
name="Subsurface",
description="Radiosity estimation can be affected by Subsurface Light Transport",
default=False,
)
radio_minimum_reuse: FloatProperty(
name="Minimum Reuse",
description="Fraction of the screen width which sets the minimum radius of reuse "
"for each sample point (At values higher than 2% expect errors)",
min=0.0,
max=1.0,
soft_min=0.1,
soft_max=0.1,
default=0.015,
precision=3,
)
radio_maximum_reuse: FloatProperty(
name="Maximum Reuse",
description="The maximum reuse parameter works in conjunction with, and is similar to that of minimum reuse, "
"the only difference being that it is an upper bound rather than a lower one",
min=0.0,
max=1.0,
default=0.2,
precision=3,
)
radio_nearest_count: IntProperty(
name="Nearest Count",
description="Number of old ambient values blended together to "
"create a new interpolated value",
min=1,
max=20,
default=1,
)
radio_normal: BoolProperty(
name="Normals", description="Radiosity estimation can be affected by normals", default=False
)
radio_recursion_limit: IntProperty(
name="Recursion Limit",
description="how many recursion levels are used to calculate "
"the diffuse inter-reflection",
min=1,
max=20,
default=1,
)
radio_pretrace_start: FloatProperty(
name="Pretrace Start",
description="Fraction of the screen width which sets the size of the "
"blocks in the mosaic preview first pass",
min=0.005,
max=1.00,
soft_min=0.02,
soft_max=1.0,
default=0.04,
)
# XXX TODO set automatically to pretrace_end = 8 / max (image_width, image_height)
# for non advanced mode
radio_pretrace_end: FloatProperty(
name="Pretrace End",
description="Fraction of the screen width which sets the size of the blocks "
"in the mosaic preview last pass",
min=0.000925,
max=1.00,
soft_min=0.01,
soft_max=1.00,
default=0.004,
precision=3,
)
classes = (RenderPovSettingsScene,)
def register():
for cls in classes:
register_class(cls)
bpy.types.Scene.pov = PointerProperty(type=RenderPovSettingsScene)
def unregister():
del bpy.types.Scene.pov
for cls in reversed(classes):
unregister_class(cls)

847
render_povray/scenography.py Executable file
View File

@ -0,0 +1,847 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# #**** END GPL LICENSE BLOCK #****
# <pep8 compliant>
"""With respect to camera frame and optics distortions, also export environment
with world, sky, atmospheric effects such as rainbows or smoke """
import bpy
from bpy.utils import register_class, unregister_class
import os
from imghdr import what # imghdr is a python lib to identify image file types
from math import atan, pi, sqrt, degrees
from . import df3_library # for smoke rendering
from .object_primitives import write_object_modifiers
##############find image texture # used for export_world
def image_format(imgF):
"""Identify input image filetypes to transmit to POV."""
# First use the below explicit extensions to identify image file prospects
ext = {
'JPG': "jpeg",
'JPEG': "jpeg",
'GIF': "gif",
'TGA': "tga",
'IFF': "iff",
'PPM': "ppm",
'PNG': "png",
'SYS': "sys",
'TIFF': "tiff",
'TIF': "tiff",
'EXR': "exr",
'HDR': "hdr",
}.get(os.path.splitext(imgF)[-1].upper(), "")
# Then, use imghdr to really identify the filetype as it can be different
if not ext:
# maybe add a check for if path exists here?
print(" WARNING: texture image has no extension") # too verbose
ext = what(imgF) # imghdr is a python lib to identify image file types
return ext
def img_map(ts):
"""Translate mapping type from Blender UI to POV syntax and return that string."""
image_map = ""
texdata = bpy.data.textures[ts.texture]
if ts.mapping == 'FLAT':
image_map = "map_type 0 "
elif ts.mapping == 'SPHERE':
image_map = "map_type 1 "
elif ts.mapping == 'TUBE':
image_map = "map_type 2 "
## map_type 3 and 4 in development (?) (ENV in pov 3.8)
## for POV-Ray, currently they just seem to default back to Flat (type 0)
# elif ts.mapping=="?":
# image_map = " map_type 3 "
# elif ts.mapping=="?":
# image_map = " map_type 4 "
if ts.use_interpolation: # Available if image sampling class reactivated?
image_map += " interpolate 2 "
if texdata.extension == 'CLIP':
image_map += " once "
# image_map += "}"
# if ts.mapping=='CUBE':
# image_map+= "warp { cubic } rotate <-90,0,180>"
# no direct cube type mapping. Though this should work in POV 3.7
# it doesn't give that good results(best suited to environment maps?)
# if image_map == "":
# print(" No texture image found ")
return image_map
def img_map_transforms(ts):
"""Translate mapping transformations from Blender UI to POV syntax and return that string."""
# XXX TODO: unchecked textures give error of variable referenced before assignment XXX
# POV-Ray "scale" is not a number of repetitions factor, but ,its
# inverse, a standard scale factor.
# 0.5 Offset is needed relatively to scale because center of the
# scale is 0.5,0.5 in blender and 0,0 in POV
# Strange that the translation factor for scale is not the same as for
# translate.
# TODO: verify both matches with other blender renderers / internal in previous versions.
image_map_transforms = ""
image_map_transforms = "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % (
ts.scale[0],
ts.scale[1],
ts.scale[2],
ts.offset[0],
ts.offset[1],
ts.offset[2],
)
# image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \
# ( 1.0 / ts.scale.x,
# 1.0 / ts.scale.y,
# 1.0 / ts.scale.z,
# (0.5 / ts.scale.x) + ts.offset.x,
# (0.5 / ts.scale.y) + ts.offset.y,
# ts.offset.z))
# image_map_transforms = (
# "translate <-0.5,-0.5,0> "
# "scale <-1,-1,1> * <%.4g,%.4g,%.4g> "
# "translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \
# (1.0 / ts.scale.x,
# 1.0 / ts.scale.y,
# 1.0 / ts.scale.z,
# ts.offset.x,
# ts.offset.y,
# ts.offset.z)
# )
return image_map_transforms
def img_map_bg(wts):
"""Translate world mapping from Blender UI to POV syntax and return that string."""
tex = bpy.data.textures[wts.texture]
image_mapBG = ""
# texture_coords refers to the mapping of world textures:
if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL':
image_mapBG = " map_type 0 "
elif wts.texture_coords == 'ANGMAP':
image_mapBG = " map_type 1 "
elif wts.texture_coords == 'TUBE':
image_mapBG = " map_type 2 "
if tex.use_interpolation:
image_mapBG += " interpolate 2 "
if tex.extension == 'CLIP':
image_mapBG += " once "
# image_mapBG += "}"
# if wts.mapping == 'CUBE':
# image_mapBG += "warp { cubic } rotate <-90,0,180>"
# no direct cube type mapping. Though this should work in POV 3.7
# it doesn't give that good results(best suited to environment maps?)
# if image_mapBG == "":
# print(" No background texture image found ")
return image_mapBG
def path_image(image):
"""Conform a path string to POV syntax to avoid POV errors."""
return bpy.path.abspath(image.filepath, library=image.library).replace("\\", "/")
# .replace("\\","/") to get only forward slashes as it's what POV prefers,
# even on windows
# end find image texture
# -----------------------------------------------------------------------------
def export_camera(scene, global_matrix, render, tab_write):
"""Translate camera from Blender UI to POV syntax and write to exported file."""
camera = scene.camera
# DH disabled for now, this isn't the correct context
active_object = None # bpy.context.active_object # does not always work MR
matrix = global_matrix @ camera.matrix_world
focal_point = camera.data.dof.focus_distance
# compute resolution
q_size = render.resolution_x / render.resolution_y
tab_write("#declare camLocation = <%.6f, %.6f, %.6f>;\n" % matrix.translation[:])
tab_write(
"#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
% tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
)
tab_write("camera {\n")
if scene.pov.baking_enable and active_object and active_object.type == 'MESH':
tab_write("mesh_camera{ 1 3\n") # distribution 3 is what we want here
tab_write("mesh{%s}\n" % active_object.name)
tab_write("}\n")
tab_write("location <0,0,.01>")
tab_write("direction <0,0,-1>")
else:
if camera.data.type == 'ORTHO':
# todo: track when SensorHeightRatio was added to see if needed (not used)
sensor_height_ratio = (
render.resolution_x * camera.data.ortho_scale / render.resolution_y
)
tab_write("orthographic\n")
# Blender angle is radian so should be converted to degrees:
# % (camera.data.angle * (180.0 / pi) )
# but actually argument is not compulsory after angle in pov ortho mode
tab_write("angle\n")
tab_write("right <%6f, 0, 0>\n" % -camera.data.ortho_scale)
tab_write("location <0, 0, 0>\n")
tab_write("look_at <0, 0, -1>\n")
tab_write("up <0, %6f, 0>\n" % (camera.data.ortho_scale / q_size))
elif camera.data.type == 'PANO':
tab_write("panoramic\n")
tab_write("location <0, 0, 0>\n")
tab_write("look_at <0, 0, -1>\n")
tab_write("right <%s, 0, 0>\n" % -q_size)
tab_write("up <0, 1, 0>\n")
tab_write("angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi))
elif camera.data.type == 'PERSP':
# Standard camera otherwise would be default in pov
tab_write("location <0, 0, 0>\n")
tab_write("look_at <0, 0, -1>\n")
tab_write("right <%s, 0, 0>\n" % -q_size)
tab_write("up <0, 1, 0>\n")
tab_write(
"angle %f\n"
% (2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi)
)
tab_write(
"rotate <%.6f, %.6f, %.6f>\n" % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
)
tab_write("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
if camera.data.dof.use_dof and (focal_point != 0 or camera.data.dof.focus_object):
tab_write("aperture %.3g\n" % (1 / (camera.data.dof.aperture_fstop * 10000) * 1000))
tab_write(
"blur_samples %d %d\n"
% (camera.data.pov.dof_samples_min, camera.data.pov.dof_samples_max)
)
tab_write("variance 1/%d\n" % camera.data.pov.dof_variance)
tab_write("confidence %.3g\n" % camera.data.pov.dof_confidence)
if camera.data.dof.focus_object:
focal_ob = scene.objects[camera.data.dof.focus_object.name]
matrix_blur = global_matrix @ focal_ob.matrix_world
tab_write("focal_point <%.4f,%.4f,%.4f>\n" % matrix_blur.translation[:])
else:
tab_write("focal_point <0, 0, %f>\n" % focal_point)
if camera.data.pov.normal_enable:
tab_write(
"normal {%s %.4f turbulence %.4f scale %.4f}\n"
% (
camera.data.pov.normal_patterns,
camera.data.pov.cam_normal,
camera.data.pov.turbulence,
camera.data.pov.scale,
)
)
tab_write("}\n")
exported_lights_count = 0
def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write):
"""Translate lights from Blender UI to POV syntax and write to exported file."""
# Incremented after each lamp export to declare its target
# currently used for Fresnel diffuse shader as their slope vector:
global exported_lights_count
exported_lights_count = 0
# Get all lamps
for ob in lamps:
lamp = ob.data
matrix = global_matrix @ ob.matrix_world
# Color is no longer modified by energy
# any way to directly get bpy_prop_array as tuple?
color = tuple(lamp.color)
tab_write("light_source {\n")
tab_write("< 0,0,0 >\n")
tab_write("color srgb<%.3g, %.3g, %.3g>\n" % color)
if lamp.type == 'POINT':
pass
elif lamp.type == 'SPOT':
tab_write("spotlight\n")
# Falloff is the main radius from the centre line
tab_write("falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)) # 1 TO 179 FOR BOTH
tab_write("radius %.6f\n" % ((degrees(lamp.spot_size) / 2.0) * (1.0 - lamp.spot_blend)))
# Blender does not have a tightness equivalent, 0 is most like blender default.
tab_write("tightness 0\n") # 0:10f
tab_write("point_at <0, 0, -1>\n")
if lamp.pov.use_halo:
tab_write("looks_like{\n")
tab_write("sphere{<0,0,0>,%.6f\n" % lamp.distance)
tab_write("hollow\n")
tab_write("material{\n")
tab_write("texture{\n")
tab_write("pigment{rgbf<1,1,1,%.4f>}\n" % (lamp.pov.halo_intensity * 5.0))
tab_write("}\n")
tab_write("interior{\n")
tab_write("media{\n")
tab_write("emission 1\n")
tab_write("scattering {1, 0.5}\n")
tab_write("density{\n")
tab_write("spherical\n")
tab_write("color_map{\n")
tab_write("[0.0 rgb <0,0,0>]\n")
tab_write("[0.5 rgb <1,1,1>]\n")
tab_write("[1.0 rgb <1,1,1>]\n")
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
elif lamp.type == 'SUN':
tab_write("parallel\n")
tab_write("point_at <0, 0, -1>\n") # *must* be after 'parallel'
elif lamp.type == 'AREA':
tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
# Area lights have no falloff type, so always use blenders lamp quad equivalent
# for those?
tab_write("fade_power %d\n" % 2)
size_x = lamp.size
samples_x = lamp.pov.shadow_ray_samples_x
if lamp.shape == 'SQUARE':
size_y = size_x
samples_y = samples_x
else:
size_y = lamp.size_y
samples_y = lamp.pov.shadow_ray_samples_y
tab_write(
"area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n" % (size_x, size_y, samples_x, samples_y)
)
tab_write("area_illumination\n")
if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED':
if lamp.pov.use_jitter:
tab_write("jitter\n")
else:
tab_write("adaptive 1\n")
tab_write("jitter\n")
# No shadow checked either at global or light level:
if not scene.pov.use_shadows or (lamp.pov.shadow_method == 'NOSHADOW'):
tab_write("shadowless\n")
# Sun shouldn't be attenuated. Area lights have no falloff attribute so they
# are put to type 2 attenuation a little higher above.
if lamp.type not in {'SUN', 'AREA'}:
if lamp.falloff_type == 'INVERSE_SQUARE':
tab_write("fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0)))
tab_write("fade_power %d\n" % 2) # Use blenders lamp quad equivalent
elif lamp.falloff_type == 'INVERSE_LINEAR':
tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
tab_write("fade_power %d\n" % 1) # Use blenders lamp linear
elif lamp.falloff_type == 'CONSTANT':
tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
tab_write("fade_power %d\n" % 3)
# Use blenders lamp constant equivalent no attenuation.
# Using Custom curve for fade power 3 for now.
elif lamp.falloff_type == 'CUSTOM_CURVE':
tab_write("fade_power %d\n" % 4)
write_matrix(matrix)
tab_write("}\n")
exported_lights_count += 1
# v(A,B) rotates vector A about origin by vector B.
file.write(
"#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n"
% (
exported_lights_count,
-(ob.location.x),
-(ob.location.y),
-(ob.location.z),
ob.rotation_euler.x,
ob.rotation_euler.y,
ob.rotation_euler.z,
)
)
def export_world(world, scene, global_matrix, tab_write):
"""write world as POV backgrounbd and sky_sphere to exported file """
render = scene.pov
camera = scene.camera
matrix = global_matrix @ camera.matrix_world # view dependant for later use
if not world:
return
#############Maurice####################################
# These lines added to get sky gradient (visible with PNG output)
if world:
# For simple flat background:
if not world.pov.use_sky_blend:
# Non fully transparent background could premultiply alpha and avoid anti-aliasing
# display issue:
if render.alpha_mode == 'TRANSPARENT':
tab_write(
"background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n" % (world.pov.horizon_color[:])
)
# Currently using no alpha with Sky option:
elif render.alpha_mode == 'SKY':
tab_write("background {rgbt<%.3g, %.3g, %.3g, 0>}\n" % (world.pov.horizon_color[:]))
# StraightAlpha:
# XXX Does not exists anymore
# else:
# tab_write("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:]))
world_tex_count = 0
# For Background image textures
for t in world.pov_texture_slots: # risk to write several sky_spheres but maybe ok.
if t:
tex = bpy.data.textures[t.texture]
if tex.type is not None:
world_tex_count += 1
# XXX No enable checkbox for world textures yet (report it?)
# if t and tex.type == 'IMAGE' and t.use:
if tex.type == 'IMAGE':
image_filename = path_image(tex.image)
if tex.image.filepath != image_filename:
tex.image.filepath = image_filename
if image_filename != "" and t.use_map_blend:
textures_blend = image_filename
# colvalue = t.default_value
t_blend = t
# Commented below was an idea to make the Background image oriented as camera
# taken here:
# http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/
# Replace 4/3 by the ratio of each image found by some custom or existing
# function
# mapping_blend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \
# "(atan((camLocation - camLookAt).x/(camLocation - " \
# "camLookAt).y)) rotate x*degrees(atan((camLocation - " \
# "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \
# "degrees(atan((camLocation - camLookAt).z/(camLocation - " \
# "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \
# (t_blend.offset.x / 10 , t_blend.offset.y / 10 ,
# t_blend.offset.z / 10, t_blend.scale.x ,
# t_blend.scale.y , t_blend.scale.z))
# using camera rotation valuesdirectly from blender seems much easier
if t_blend.texture_coords == 'ANGMAP':
mapping_blend = ""
else:
# POV-Ray "scale" is not a number of repetitions factor, but its
# inverse, a standard scale factor.
# 0.5 Offset is needed relatively to scale because center of the
# UV scale is 0.5,0.5 in blender and 0,0 in POV
# Further Scale by 2 and translate by -1 are
# required for the sky_sphere not to repeat
mapping_blend = (
"scale 2 scale <%.4g,%.4g,%.4g> translate -1 "
"translate <%.4g,%.4g,%.4g> rotate<0,0,0> "
% (
(1.0 / t_blend.scale.x),
(1.0 / t_blend.scale.y),
(1.0 / t_blend.scale.z),
0.5 - (0.5 / t_blend.scale.x) - t_blend.offset.x,
0.5 - (0.5 / t_blend.scale.y) - t_blend.offset.y,
t_blend.offset.z,
)
)
# The initial position and rotation of the pov camera is probably creating
# the rotation offset should look into it someday but at least background
# won't rotate with the camera now.
# Putting the map on a plane would not introduce the skysphere distortion and
# allow for better image scale matching but also some waay to chose depth and
# size of the plane relative to camera.
tab_write("sky_sphere {\n")
tab_write("pigment {\n")
tab_write(
"image_map{%s \"%s\" %s}\n"
% (image_format(textures_blend), textures_blend, img_map_bg(t_blend))
)
tab_write("}\n")
tab_write("%s\n" % (mapping_blend))
# The following layered pigment opacifies to black over the texture for
# transmit below 1 or otherwise adds to itself
tab_write("pigment {rgb 0 transmit %s}\n" % (tex.intensity))
tab_write("}\n")
# tab_write("scale 2\n")
# tab_write("translate -1\n")
# For only Background gradient
if world_tex_count == 0:
if world.pov.use_sky_blend:
tab_write("sky_sphere {\n")
tab_write("pigment {\n")
# maybe Should follow the advice of POV doc about replacing gradient
# for skysphere..5.5
tab_write("gradient y\n")
tab_write("color_map {\n")
# XXX Does not exists anymore
# if render.alpha_mode == 'STRAIGHT':
# tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.horizon_color[:]))
# tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.zenith_color[:]))
if render.alpha_mode == 'TRANSPARENT':
tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.horizon_color[:]))
# aa premult not solved with transmit 1
tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.zenith_color[:]))
else:
tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.horizon_color[:]))
tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.zenith_color[:]))
tab_write("}\n")
tab_write("}\n")
tab_write("}\n")
# Sky_sphere alpha (transmit) is not translating into image alpha the same
# way as 'background'
# if world.pov.light_settings.use_indirect_light:
# scene.pov.radio_enable=1
# Maybe change the above to a function copyInternalRenderer settings when
# user pushes a button, then:
# scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
# and other such translations but maybe this would not be allowed either?
###############################################################
mist = world.mist_settings
if mist.use_mist:
tab_write("fog {\n")
if mist.falloff == 'LINEAR':
tab_write("distance %.6f\n" % ((mist.start + mist.depth) * 0.368))
elif mist.falloff == 'QUADRATIC': # n**2 or squrt(n)?
tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368))
elif mist.falloff == 'INVERSE_QUADRATIC': # n**2 or squrt(n)?
tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368))
tab_write(
"color rgbt<%.3g, %.3g, %.3g, %.3g>\n"
% (*world.pov.horizon_color, 1.0 - mist.intensity)
)
# tab_write("fog_offset %.6f\n" % mist.start) #create a pov property to prepend
# tab_write("fog_alt %.6f\n" % mist.height) #XXX right?
# tab_write("turbulence 0.2\n")
# tab_write("turb_depth 0.3\n")
tab_write("fog_type 1\n") # type2 for height
tab_write("}\n")
if scene.pov.media_enable:
tab_write("media {\n")
tab_write(
"scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n"
% (
int(scene.pov.media_scattering_type),
(scene.pov.media_diffusion_scale),
*(scene.pov.media_diffusion_color[:]),
)
)
if scene.pov.media_scattering_type == '5':
tab_write("eccentricity %.3g\n" % scene.pov.media_eccentricity)
tab_write("}\n")
tab_write(
"absorption %.12f*<%.4g, %.4g, %.4g>\n"
% (scene.pov.media_absorption_scale, *(scene.pov.media_absorption_color[:]))
)
tab_write("\n")
tab_write("samples %.d\n" % scene.pov.media_samples)
tab_write("}\n")
####################################################################################################
def export_rainbows(rainbows, file, scene, global_matrix, write_matrix, tab_write):
"""write all POV rainbows primitives to exported file """
for ob in rainbows:
povdataname = ob.data.name # enough? XXX not used nor matrix fn?
angle = degrees(ob.data.spot_size / 2.5) # radians in blender (2
width = ob.data.spot_blend * 10
distance = ob.data.shadow_buffer_clip_start
# eps=0.0000001
# angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero
# width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height
# formerly:
# cz-bz # let's say width of the rainbow is height of the cone (interfacing choice
# v(A,B) rotates vector A about origin by vector B.
# and avoid a 0 length vector by adding 1
# file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \
# (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1),
# ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z))
direction = ( # XXX currently not used (replaced by track to?)
ob.location.x,
ob.location.y,
ob.location.z,
) # not taking matrix into account
rmatrix = global_matrix @ ob.matrix_world
# ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1))
# XXX Is result of the below offset by 90 degrees?
up = ob.matrix_world.to_3x3()[1].xyz # * global_matrix
# XXX TO CHANGE:
# formerly:
# tab_write("#declare %s = rainbow {\n"%povdataname)
# clumsy for now but remove the rainbow from instancing
# system because not an object. use lamps later instead of meshes
# del data_ref[dataname]
tab_write("rainbow {\n")
tab_write("angle %.4f\n" % angle)
tab_write("width %.4f\n" % width)
tab_write("distance %.4f\n" % distance)
tab_write("arc_angle %.4f\n" % ob.pov.arc_angle)
tab_write("falloff_angle %.4f\n" % ob.pov.falloff_angle)
tab_write("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
tab_write("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
tab_write("color_map {\n")
tab_write("[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
tab_write("[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
tab_write("[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
tab_write("[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
tab_write("[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
tab_write("[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
tab_write("[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
tab_write("[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
tab_write("[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
tab_write("}\n")
pov_mat_name = "Default_texture"
# tab_write("texture {%s}\n"%pov_mat_name)
write_object_modifiers(scene, ob, file)
# tab_write("rotate x*90\n")
# matrix = global_matrix @ ob.matrix_world
# write_matrix(matrix)
tab_write("}\n")
# continue #Don't render proxy mesh, skip to next object
def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, write_matrix):
"""export Blender smoke type fluids to pov media using df3 library"""
flowtype = -1 # XXX todo: not used yet? should trigger emissive for fire type
depsgraph = bpy.context.evaluated_depsgraph_get()
smoke_obj = bpy.data.objects[smoke_obj_name].evaluated_get(depsgraph)
domain = None
smoke_modifier = None
# Search smoke domain target for smoke modifiers
for mod in smoke_obj.modifiers:
if mod.type == 'FLUID':
if mod.fluid_type == 'FLOW':
if mod.flow_settings.flow_type == 'BOTH':
flowtype = 2
else:
if mod.flow_settings.smoke_flow_type == 'SMOKE':
flowtype = 0
else:
if mod.flow_settings.smoke_flow_type == 'FIRE':
flowtype = 1
if mod.fluid_type == 'DOMAIN':
domain = smoke_obj
smoke_modifier = mod
eps = 0.000001 # XXX not used currently. restore from corner case ... zero div?
if domain is not None:
mod_set = smoke_modifier.domain_settings
channeldata = []
for v in mod_set.density_grid:
channeldata.append(v.real)
print(v.real)
## Usage en voxel texture:
# channeldata = []
# if channel == 'density':
# for v in mod_set.density_grid:
# channeldata.append(v.real)
# if channel == 'fire':
# for v in mod_set.flame_grid:
# channeldata.append(v.real)
resolution = mod_set.resolution_max
big_res = []
big_res.append(mod_set.domain_resolution[0])
big_res.append(mod_set.domain_resolution[1])
big_res.append(mod_set.domain_resolution[2])
if mod_set.use_noise:
big_res[0] = big_res[0] * (mod_set.noise_scale + 1)
big_res[1] = big_res[1] * (mod_set.noise_scale + 1)
big_res[2] = big_res[2] * (mod_set.noise_scale + 1)
# else:
# p = []
##gather smoke domain settings
# BBox = domain.bound_box
# p.append([BBox[0][0], BBox[0][1], BBox[0][2]])
# p.append([BBox[6][0], BBox[6][1], BBox[6][2]])
# mod_set = smoke_modifier.domain_settings
# resolution = mod_set.resolution_max
# smokecache = mod_set.point_cache
# ret = read_cache(smokecache, mod_set.use_noise, mod_set.noise_scale + 1, flowtype)
# res_x = ret[0]
# res_y = ret[1]
# res_z = ret[2]
# density = ret[3]
# fire = ret[4]
# if res_x * res_y * res_z > 0:
##new cache format
# big_res = []
# big_res.append(res_x)
# big_res.append(res_y)
# big_res.append(res_z)
# else:
# max = domain.dimensions[0]
# if (max - domain.dimensions[1]) < -eps:
# max = domain.dimensions[1]
# if (max - domain.dimensions[2]) < -eps:
# max = domain.dimensions[2]
# big_res = [int(round(resolution * domain.dimensions[0] / max, 0)),
# int(round(resolution * domain.dimensions[1] / max, 0)),
# int(round(resolution * domain.dimensions[2] / max, 0))]
# if mod_set.use_noise:
# big_res = [big_res[0] * (mod_set.noise_scale + 1),
# big_res[1] * (mod_set.noise_scale + 1),
# big_res[2] * (mod_set.noise_scale + 1)]
# if channel == 'density':
# channeldata = density
# if channel == 'fire':
# channeldata = fire
# sc_fr = '%s/%s/%s/%05d' % (
# efutil.export_path,
# efutil.scene_filename(),
# bpy.context.scene.name,
# bpy.context.scene.frame_current
# )
# if not os.path.exists( sc_fr ):
# os.makedirs(sc_fr)
#
# smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name)
# smoke_path = '/'.join([sc_fr, smoke_filename])
#
# with open(smoke_path, 'wb') as smoke_file:
# # Binary densitygrid file format
# #
# # File header
# smoke_file.write(b'SMOKE') #magic number
# smoke_file.write(struct.pack('<I', big_res[0]))
# smoke_file.write(struct.pack('<I', big_res[1]))
# smoke_file.write(struct.pack('<I', big_res[2]))
# Density data
# smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata))
#
# LuxLog('Binary SMOKE file written: %s' % (smoke_path))
# return big_res[0], big_res[1], big_res[2], channeldata
mydf3 = df3_library.df3(big_res[0], big_res[1], big_res[2])
sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size()
for x in range(sim_sizeX):
for y in range(sim_sizeY):
for z in range(sim_sizeZ):
mydf3.set(x, y, z, channeldata[((z * sim_sizeY + y) * sim_sizeX + x)])
mydf3.exportDF3(smoke_path)
print('Binary smoke.df3 file written in preview directory')
if comments:
file.write("\n//--Smoke--\n\n")
# Note: We start with a default unit cube.
# This is mandatory to read correctly df3 data - otherwise we could just directly use bbox
# coordinates from the start, and avoid scale/translate operations at the end...
file.write("box{<0,0,0>, <1,1,1>\n")
file.write(" pigment{ rgbt 1 }\n")
file.write(" hollow\n")
file.write(" interior{ //---------------------\n")
file.write(" media{ method 3\n")
file.write(" emission <1,1,1>*1\n") # 0>1 for dark smoke to white vapour
file.write(" scattering{ 1, // Type\n")
file.write(" <1,1,1>*0.1\n")
file.write(" } // end scattering\n")
file.write(" density{density_file df3 \"%s\"\n" % (smoke_path))
file.write(" color_map {\n")
file.write(" [0.00 rgb 0]\n")
file.write(" [0.05 rgb 0]\n")
file.write(" [0.20 rgb 0.2]\n")
file.write(" [0.30 rgb 0.6]\n")
file.write(" [0.40 rgb 1]\n")
file.write(" [1.00 rgb 1]\n")
file.write(" } // end color_map\n")
file.write(" } // end of density\n")
file.write(" samples %i // higher = more precise\n" % resolution)
file.write(" } // end of media --------------------------\n")
file.write(" } // end of interior\n")
# START OF TRANSFORMATIONS
# Size to consider here are bbox dimensions (i.e. still in object space, *before* applying
# loc/rot/scale and other transformations (like parent stuff), aka matrix_world).
bbox = smoke_obj.bound_box
dim = [
abs(bbox[6][0] - bbox[0][0]),
abs(bbox[6][1] - bbox[0][1]),
abs(bbox[6][2] - bbox[0][2]),
]
# We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox).
file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2]))
# We offset our cube such that (0,0,0) coordinate matches Blender's object center.
file.write("translate<%.6g,%.6g,%.6g>\n" % (bbox[0][0], bbox[0][1], bbox[0][2]))
# We apply object's transformations to get final loc/rot/size in world space!
# Note: we could combine the two previous transformations with this matrix directly...
write_matrix(global_matrix @ smoke_obj.matrix_world)
# END OF TRANSFORMATIONS
file.write("}\n")
# file.write(" interpolate 1\n")
# file.write(" frequency 0\n")
# file.write(" }\n")
# file.write("}\n")
classes = ()
def register():
for cls in classes:
register_class(cls)
def unregister():
for cls in classes:
unregister_class(cls)

800
render_povray/scenography_gui.py Executable file
View File

@ -0,0 +1,800 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""User interface to camera frame, optics distortions, and environment
with world, sky, atmospheric effects such as rainbows or smoke """
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import Operator, Menu, Panel
from bl_operators.presets import AddPresetBase
from bl_ui import properties_data_camera
for member in dir(properties_data_camera):
subclass = getattr(properties_data_camera, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_data_camera
# ##################################
# # Use only a subset of the world panels
# from bl_ui import properties_world
# # TORECREATE##DEPRECATED#properties_world.WORLD_PT_preview.COMPAT_ENGINES.add('POVRAY_RENDER')
# properties_world.WORLD_PT_context_world.COMPAT_ENGINES.add('POVRAY_RENDER')
# # TORECREATE##DEPRECATED#properties_world.WORLD_PT_world.COMPAT_ENGINES.add('POVRAY_RENDER')
# del properties_world
##################################
# Physics Main wrapping every class 'as is'
from bl_ui import properties_physics_common
for member in dir(properties_physics_common):
subclass = getattr(properties_physics_common, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_common
# Physics Rigid Bodies wrapping every class 'as is'
from bl_ui import properties_physics_rigidbody
for member in dir(properties_physics_rigidbody):
subclass = getattr(properties_physics_rigidbody, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_rigidbody
# Physics Rigid Body Constraint wrapping every class 'as is'
from bl_ui import properties_physics_rigidbody_constraint
for member in dir(properties_physics_rigidbody_constraint):
subclass = getattr(properties_physics_rigidbody_constraint, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_rigidbody_constraint
# Physics Smoke and fluids wrapping every class 'as is'
from bl_ui import properties_physics_fluid
for member in dir(properties_physics_fluid):
subclass = getattr(properties_physics_fluid, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_fluid
# Physics softbody wrapping every class 'as is'
from bl_ui import properties_physics_softbody
for member in dir(properties_physics_softbody):
subclass = getattr(properties_physics_softbody, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_softbody
# Physics Field wrapping every class 'as is'
from bl_ui import properties_physics_field
for member in dir(properties_physics_field):
subclass = getattr(properties_physics_field, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_field
# Physics Cloth wrapping every class 'as is'
from bl_ui import properties_physics_cloth
for member in dir(properties_physics_cloth):
subclass = getattr(properties_physics_cloth, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_cloth
# Physics Dynamic Paint wrapping every class 'as is'
from bl_ui import properties_physics_dynamicpaint
for member in dir(properties_physics_dynamicpaint):
subclass = getattr(properties_physics_dynamicpaint, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_physics_dynamicpaint
from bl_ui import properties_particle
for member in dir(properties_particle): # add all "particle" panels from blender
subclass = getattr(properties_particle, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_particle
class CameraDataButtonsPanel:
"""Use this class to define buttons from the camera data tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
cam = context.camera
rd = context.scene.render
return cam and (rd.engine in cls.COMPAT_ENGINES)
class WorldButtonsPanel:
"""Use this class to define buttons from the world tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "world"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
wld = context.world
rd = context.scene.render
return wld and (rd.engine in cls.COMPAT_ENGINES)
###############################################################################
# Camera Settings
###############################################################################
class CAMERA_PT_POV_cam_dof(CameraDataButtonsPanel, Panel):
"""Use this class for camera depth of field focal blur buttons."""
bl_label = "POV Aperture"
COMPAT_ENGINES = {'POVRAY_RENDER'}
bl_parent_id = "DATA_PT_camera_dof_aperture"
bl_options = {'HIDE_HEADER'}
# def draw_header(self, context):
# cam = context.camera
# self.layout.prop(cam.pov, "dof_enable", text="")
def draw(self, context):
layout = self.layout
cam = context.camera
layout.active = cam.dof.use_dof
layout.use_property_split = True # Active single-column layout
flow = layout.grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=False
)
col = flow.column()
col.label(text="F-Stop value will export as")
col.label(text="POV aperture : " + "%.3f" % (1 / cam.dof.aperture_fstop * 1000))
col = flow.column()
col.prop(cam.pov, "dof_samples_min")
col.prop(cam.pov, "dof_samples_max")
col.prop(cam.pov, "dof_variance")
col.prop(cam.pov, "dof_confidence")
class CAMERA_PT_POV_cam_nor(CameraDataButtonsPanel, Panel):
"""Use this class for camera normal perturbation buttons."""
bl_label = "POV Perturbation"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
cam = context.camera
self.layout.prop(cam.pov, "normal_enable", text="")
def draw(self, context):
layout = self.layout
cam = context.camera
layout.active = cam.pov.normal_enable
layout.prop(cam.pov, "normal_patterns")
layout.prop(cam.pov, "cam_normal")
layout.prop(cam.pov, "turbulence")
layout.prop(cam.pov, "scale")
class CAMERA_PT_POV_replacement_text(CameraDataButtonsPanel, Panel):
"""Use this class for camera text replacement field."""
bl_label = "Custom POV Code"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
cam = context.camera
col = layout.column()
col.label(text="Replace properties with:")
col.prop(cam.pov, "replacement_text", text="")
###############################################################################
# World background and sky sphere Settings
###############################################################################
class WORLD_PT_POV_world(WorldButtonsPanel, Panel):
"""Use this class to define pov world buttons"""
bl_label = "World"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
world = context.world.pov
row = layout.row(align=True)
row.menu(WORLD_MT_POV_presets.__name__, text=WORLD_MT_POV_presets.bl_label)
row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='ADD')
row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True
row = layout.row()
row.prop(world, "use_sky_paper")
row.prop(world, "use_sky_blend")
row.prop(world, "use_sky_real")
row = layout.row()
row.column().prop(world, "horizon_color")
col = row.column()
col.prop(world, "zenith_color")
col.active = world.use_sky_blend
row.column().prop(world, "ambient_color")
# row = layout.row()
# row.prop(world, "exposure") #Re-implement later as a light multiplier
# row.prop(world, "color_range")
class WORLD_PT_POV_mist(WorldButtonsPanel, Panel):
"""Use this class to define pov mist buttons."""
bl_label = "Mist"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
world = context.world
self.layout.prop(world.mist_settings, "use_mist", text="")
def draw(self, context):
layout = self.layout
world = context.world
layout.active = world.mist_settings.use_mist
split = layout.split()
col = split.column()
col.prop(world.mist_settings, "intensity")
col.prop(world.mist_settings, "start")
col = split.column()
col.prop(world.mist_settings, "depth")
col.prop(world.mist_settings, "height")
layout.prop(world.mist_settings, "falloff")
class WORLD_MT_POV_presets(Menu):
"""Apply world preset to all concerned properties"""
bl_label = "World Presets"
preset_subdir = "pov/world"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
class WORLD_OT_POV_add_preset(AddPresetBase, Operator):
"""Add a World Preset recording current values"""
bl_idname = "object.world_preset_add"
bl_label = "Add World Preset"
preset_menu = "WORLD_MT_POV_presets"
# variable used for all preset values
preset_defines = ["scene = bpy.context.scene"]
# properties to store in the preset
preset_values = [
"scene.world.use_sky_blend",
"scene.world.horizon_color",
"scene.world.zenith_color",
"scene.world.ambient_color",
"scene.world.mist_settings.use_mist",
"scene.world.mist_settings.intensity",
"scene.world.mist_settings.depth",
"scene.world.mist_settings.start",
"scene.pov.media_enable",
"scene.pov.media_scattering_type",
"scene.pov.media_samples",
"scene.pov.media_diffusion_scale",
"scene.pov.media_diffusion_color",
"scene.pov.media_absorption_scale",
"scene.pov.media_absorption_color",
"scene.pov.media_eccentricity",
]
# where to store the preset
preset_subdir = "pov/world"
class RENDER_PT_POV_media(WorldButtonsPanel, Panel):
"""Use this class to define a pov global atmospheric media buttons."""
bl_label = "Atmosphere Media"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw_header(self, context):
scene = context.scene
self.layout.prop(scene.pov, "media_enable", text="")
def draw(self, context):
layout = self.layout
scene = context.scene
layout.active = scene.pov.media_enable
col = layout.column()
col.prop(scene.pov, "media_scattering_type", text="")
col = layout.column()
col.prop(scene.pov, "media_samples", text="Samples")
split = layout.split()
col = split.column(align=True)
col.label(text="Scattering:")
col.prop(scene.pov, "media_diffusion_scale")
col.prop(scene.pov, "media_diffusion_color", text="")
col = split.column(align=True)
col.label(text="Absorption:")
col.prop(scene.pov, "media_absorption_scale")
col.prop(scene.pov, "media_absorption_color", text="")
if scene.pov.media_scattering_type == '5':
col = layout.column()
col.prop(scene.pov, "media_eccentricity", text="Eccentricity")
###############################################################################
# Lights settings
###############################################################################
################################################################################
# from bl_ui import properties_data_light
# for member in dir(properties_data_light):
# subclass = getattr(properties_data_light, member)
# try:
# subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
# except BaseException as e:
# print e.__doc__
# print('An exception occurred: {}'.format(e))
# pass
# del properties_data_light
#########################LIGHTS################################
from bl_ui import properties_data_light
# # These panels are kept
# properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER')
# properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER')
## make some native panels contextual to some object variable
## by recreating custom panels inheriting their properties
class PovLightButtonsPanel(properties_data_light.DataButtonsPanel):
"""Use this class to define buttons from the light data tab of
properties window."""
COMPAT_ENGINES = {'POVRAY_RENDER'}
POV_OBJECT_TYPES = {'RAINBOW'}
@classmethod
def poll(cls, context):
obj = context.object
# We use our parent class poll func too, avoids to re-define too much things...
return (
super(PovLightButtonsPanel, cls).poll(context)
and obj
and obj.pov.object_as not in cls.POV_OBJECT_TYPES
)
# We cannot inherit from RNA classes (like e.g. properties_data_mesh.DATA_PT_vertex_groups).
# Complex py/bpy/rna interactions (with metaclass and all) simply do not allow it to work.
# So we simply have to explicitly copy here the interesting bits. ;)
from bl_ui import properties_data_light
# for member in dir(properties_data_light):
# subclass = getattr(properties_data_light, member)
# try:
# subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
# except BaseException as e:
# print(e.__doc__)
# print('An exception occurred: {}'.format(e))
# pass
# Now only These panels are kept
properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER')
properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER')
class LIGHT_PT_POV_preview(PovLightButtonsPanel, Panel):
# XXX Needs update and docstring
bl_label = properties_data_light.DATA_PT_preview.bl_label
draw = properties_data_light.DATA_PT_preview.draw
class LIGHT_PT_POV_light(PovLightButtonsPanel, Panel):
"""UI panel to main pov light parameters"""
# bl_label = properties_data_light.DATA_PT_light.bl_label
# draw = properties_data_light.DATA_PT_light.draw
# class DATA_PT_POV_light(DataButtonsPanel, Panel):
bl_label = "Light"
# COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
light = context.light
layout.row().prop(light, "type", expand=True)
split = layout.split()
col = split.column()
sub = col.column()
sub.prop(light, "color", text="")
sub.prop(light, "energy")
if light.type in {'POINT', 'SPOT'}:
sub.label(text="Falloff:")
sub.prop(light, "falloff_type", text="")
sub.prop(light, "distance")
if light.falloff_type == 'LINEAR_QUADRATIC_WEIGHTED':
col.label(text="Attenuation Factors:")
sub = col.column(align=True)
sub.prop(light, "linear_attenuation", slider=True, text="Linear")
sub.prop(light, "quadratic_attenuation", slider=True, text="Quadratic")
elif light.falloff_type == 'INVERSE_COEFFICIENTS':
col.label(text="Inverse Coefficients:")
sub = col.column(align=True)
sub.prop(light, "constant_coefficient", text="Constant")
sub.prop(light, "linear_coefficient", text="Linear")
sub.prop(light, "quadratic_coefficient", text="Quadratic")
if light.type == 'AREA':
col.prop(light, "distance")
# restore later as interface to POV light groups ?
# col = split.column()
# col.prop(light, "use_own_layer", text="This Layer Only")
class LIGHT_MT_POV_presets(Menu):
"""Use this class to define preset menu for pov lights."""
bl_label = "Lamp Presets"
preset_subdir = "pov/light"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
class LIGHT_OT_POV_add_preset(AddPresetBase, Operator):
"""Operator to add a Light Preset"""
bl_idname = "object.light_preset_add"
bl_label = "Add Light Preset"
preset_menu = "LIGHT_MT_POV_presets"
# variable used for all preset values
preset_defines = ["lightdata = bpy.context.object.data"]
# properties to store in the preset
preset_values = ["lightdata.type", "lightdata.color"]
# where to store the preset
preset_subdir = "pov/light"
# Draw into the existing light panel
def light_panel_func(self, context):
"""Menu to browse and add light preset"""
layout = self.layout
row = layout.row(align=True)
row.menu(LIGHT_MT_POV_presets.__name__, text=LIGHT_MT_POV_presets.bl_label)
row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='ADD')
row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True
'''#TORECREATE##DEPRECATED#
class LIGHT_PT_POV_sunsky(PovLightButtonsPanel, Panel):
bl_label = properties_data_light.DATA_PT_sunsky.bl_label
@classmethod
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
return (lamp and lamp.type == 'SUN') and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_sunsky.draw
'''
class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
# Todo : update and docstring
bl_label = "Shadow"
@classmethod
def poll(cls, context):
light = context.light
engine = context.scene.render.engine
return light and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
light = context.light
layout.row().prop(light.pov, "shadow_method", expand=True)
split = layout.split()
col = split.column()
col.prop(light.pov, "use_halo")
sub = col.column(align=True)
sub.active = light.pov.use_halo
sub.prop(light.pov, "halo_intensity", text="Intensity")
if light.pov.shadow_method == 'NOSHADOW' and light.type == 'AREA':
split = layout.split()
col = split.column()
col.label(text="Form factor sampling:")
sub = col.row(align=True)
if light.shape == 'SQUARE':
sub.prop(light, "shadow_ray_samples_x", text="Samples")
elif light.shape == 'RECTANGLE':
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
if light.pov.shadow_method != 'NOSHADOW':
split = layout.split()
col = split.column()
col.prop(light, "shadow_color", text="")
# col = split.column()
# col.prop(light.pov, "use_shadow_layer", text="This Layer Only")
# col.prop(light.pov, "use_only_shadow")
if light.pov.shadow_method == 'RAY_SHADOW':
split = layout.split()
col = split.column()
col.label(text="Sampling:")
if light.type in {'POINT', 'SUN', 'SPOT'}:
sub = col.row()
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
# any equivalent in pov?
# sub.prop(light, "shadow_soft_size", text="Soft Size")
elif light.type == 'AREA':
sub = col.row(align=True)
if light.shape == 'SQUARE':
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
elif light.shape == 'RECTANGLE':
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
class LIGHT_PT_POV_area(PovLightButtonsPanel, Panel):
"""Area light UI panel"""
bl_label = properties_data_light.DATA_PT_area.bl_label
bl_parent_id = "LIGHT_PT_POV_light"
bl_context = "data"
@classmethod
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_area.draw
class LIGHT_PT_POV_spot(PovLightButtonsPanel, Panel):
bl_label = properties_data_light.DATA_PT_spot.bl_label
bl_parent_id = "LIGHT_PT_POV_light"
bl_context = "data"
@classmethod
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
return (lamp and lamp.type == 'SPOT') and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_spot.draw
class LIGHT_PT_POV_falloff_curve(PovLightButtonsPanel, Panel):
bl_label = properties_data_light.DATA_PT_falloff_curve.bl_label
bl_options = properties_data_light.DATA_PT_falloff_curve.bl_options
@classmethod
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
return (
lamp and lamp.type in {'POINT', 'SPOT'} and lamp.falloff_type == 'CUSTOM_CURVE'
) and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_falloff_curve.draw
class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel):
"""Use this class to define buttons from the rainbow panel of
properties window. inheriting lamp buttons panel class"""
bl_label = "POV-Ray Rainbow"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
return obj and obj.pov.object_as == 'RAINBOW' and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
obj = context.object
col = layout.column()
if obj.pov.object_as == 'RAINBOW':
if not obj.pov.unlock_parameters:
col.prop(
obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
)
col.label(text="Rainbow projection angle: " + str(obj.data.spot_size))
col.label(text="Rainbow width: " + str(obj.data.spot_blend))
col.label(text="Rainbow distance: " + str(obj.data.shadow_buffer_clip_start))
col.label(text="Rainbow arc angle: " + str(obj.pov.arc_angle))
col.label(text="Rainbow falloff angle: " + str(obj.pov.falloff_angle))
else:
col.prop(
obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
layout.operator("pov.cone_update", text="Update", icon="MESH_CONE")
# col.label(text="Parameters:")
col.prop(obj.data, "spot_size", text="Rainbow Projection Angle")
col.prop(obj.data, "spot_blend", text="Rainbow width")
col.prop(obj.data, "shadow_buffer_clip_start", text="Visibility distance")
col.prop(obj.pov, "arc_angle")
col.prop(obj.pov, "falloff_angle")
del properties_data_light
classes = (
WORLD_PT_POV_world,
WORLD_MT_POV_presets,
WORLD_OT_POV_add_preset,
WORLD_PT_POV_mist,
RENDER_PT_POV_media,
LIGHT_PT_POV_preview,
LIGHT_PT_POV_light,
LIGHT_PT_POV_shadow,
LIGHT_PT_POV_spot,
LIGHT_PT_POV_area,
LIGHT_MT_POV_presets,
LIGHT_OT_POV_add_preset,
OBJECT_PT_POV_rainbow,
CAMERA_PT_POV_cam_dof,
CAMERA_PT_POV_cam_nor,
CAMERA_PT_POV_replacement_text,
)
def register():
for cls in classes:
register_class(cls)
bpy.types.LIGHT_PT_POV_light.prepend(light_panel_func)
def unregister():
for cls in reversed(classes):
unregister_class(cls)
bpy.types.LIGHT_PT_POV_light.remove(light_panel_func)

View File

@ -0,0 +1,514 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""Declare stage set and surrounding (camera, lights, environment) properties controllable in UI"""
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import PropertyGroup
from bpy.props import (
FloatVectorProperty,
StringProperty,
BoolProperty,
IntProperty,
FloatProperty,
EnumProperty,
PointerProperty,
CollectionProperty,
)
from .shading_properties import (
active_texture_name_from_uilist,
active_texture_name_from_search,
brush_texture_update,
)
###############################################################################
# Camera POV properties.
###############################################################################
class RenderPovSettingsCamera(PropertyGroup):
"""Declare camera properties controllable in UI and translated to POV."""
# DOF Toggle
dof_enable: BoolProperty(
name="Depth Of Field", description="Enable POV Depth Of Field ", default=False
)
# Aperture (Intensity of the Blur)
dof_aperture: FloatProperty(
name="Aperture",
description="Similar to a real camera's aperture effect over focal blur (though not "
"in physical units and independent of focal length). "
"Increase to get more blur",
min=0.01,
max=1.00,
default=0.50,
)
# Aperture adaptive sampling
dof_samples_min: IntProperty(
name="Samples Min",
description="Minimum number of rays to use for each pixel",
min=1,
max=128,
default=3,
)
dof_samples_max: IntProperty(
name="Samples Max",
description="Maximum number of rays to use for each pixel",
min=1,
max=128,
default=9,
)
dof_variance: IntProperty(
name="Variance",
description="Minimum threshold (fractional value) for adaptive DOF sampling (up "
"increases quality and render time). The value for the variance should "
"be in the range of the smallest displayable color difference",
min=1,
max=100000,
soft_max=10000,
default=8192,
)
dof_confidence: FloatProperty(
name="Confidence",
description="Probability to reach the real color value. Larger confidence values "
"will lead to more samples, slower traces and better images",
min=0.01,
max=0.99,
default=0.20,
)
normal_enable: BoolProperty(name="Perturbated Camera", default=False)
cam_normal: FloatProperty(name="Normal Strength", min=0.0, max=1.0, default=0.001)
normal_patterns: EnumProperty(
name="Pattern",
description="",
items=(
("agate", "Agate", ""),
("boxed", "Boxed", ""),
("bumps", "Bumps", ""),
("cells", "Cells", ""),
("crackle", "Crackle", ""),
("dents", "Dents", ""),
("granite", "Granite", ""),
("leopard", "Leopard", ""),
("marble", "Marble", ""),
("onion", "Onion", ""),
("pavement", "Pavement", ""),
("planar", "Planar", ""),
("quilted", "Quilted", ""),
("ripples", "Ripples", ""),
("radial", "Radial", ""),
("spherical", "Spherical", ""),
("spiral1", "Spiral1", ""),
("spiral2", "Spiral2", ""),
("spotted", "Spotted", ""),
("square", "Square", ""),
("tiling", "Tiling", ""),
("waves", "Waves", ""),
("wood", "Wood", ""),
("wrinkles", "Wrinkles", ""),
),
default="agate",
)
turbulence: FloatProperty(name="Turbulence", min=0.0, max=100.0, default=0.1)
scale: FloatProperty(name="Scale", min=0.0, default=1.0)
##################################CustomPOV Code############################
# Only DUMMIES below for now:
replacement_text: StringProperty(
name="Texts in blend file",
description="Type the declared name in custom POV code or an external .inc "
"it points at. camera {} expected",
default="",
)
###############################################################################
# Light POV properties.
###############################################################################
class RenderPovSettingsLight(PropertyGroup):
"""Declare light properties controllable in UI and translated to POV."""
# former Space properties from removed Blender Internal
use_limited_texture_context: BoolProperty(
name="",
description="Use the limited version of texture user (for old shading mode)",
default=True,
)
texture_context: EnumProperty(
name="Texture context",
description="Type of texture data to display and edit",
items=(
("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures"
("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures"
("LAMP", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures"
(
"PARTICLES",
"",
"Show particles textures",
"PARTICLES",
3,
), # "Show particles textures"
(
"LINESTYLE",
"",
"Show linestyle textures",
"LINE_DATA",
4,
), # "Show linestyle textures"
(
"OTHER",
"",
"Show other data textures",
"TEXTURE_DATA",
5,
), # "Show other data textures"
),
default="MATERIAL",
)
shadow_method: EnumProperty(
name="Shadow",
description="",
items=(
("NOSHADOW", "No Shadow", "No Shadow"),
("RAY_SHADOW", "Ray Shadow", "Ray Shadow, Use ray tracing for shadow"),
),
default="RAY_SHADOW",
)
active_texture_index: IntProperty(name="Index for texture_slots", default=0)
use_halo: BoolProperty(
name="Halo", description="Render spotlight with a volumetric halo", default=False
)
halo_intensity: FloatProperty(
name="Halo intensity",
description="Brightness of the spotlight halo cone",
soft_min=0.0,
soft_max=1.0,
default=1.0,
)
shadow_ray_samples_x: IntProperty(
name="Number of samples taken extra (samples x samples)", min=1, soft_max=64, default=1
)
shadow_ray_samples_y: IntProperty(
name="Number of samples taken extra (samples x samples)", min=1, soft_max=64, default=1
)
shadow_ray_sample_method: EnumProperty(
name="",
description="Method for generating shadow samples: Adaptive QMC is fastest,"
"Constant QMC is less noisy but slower",
items=(
("ADAPTIVE_QMC", "", "Halton samples distribution", "", 0),
("CONSTANT_QMC", "", "QMC samples distribution", "", 1),
(
"CONSTANT_JITTERED",
"",
"Uses POV jitter keyword",
"",
2,
), # "Show other data textures"
),
default="CONSTANT_JITTERED",
)
use_jitter: BoolProperty(
name="Jitter",
description="Use noise for sampling (Constant Jittered sampling)",
default=False,
)
###############################################################################
# World POV properties.
###############################################################################
class RenderPovSettingsWorld(PropertyGroup):
"""Declare world properties controllable in UI and translated to POV."""
# former Space properties from removed Blender Internal
use_limited_texture_context: BoolProperty(
name="",
description="Use the limited version of texture user (for old shading mode)",
default=True,
)
texture_context: EnumProperty(
name="Texture context",
description="Type of texture data to display and edit",
items=(
("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures"
("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures"
("LIGHT", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures"
(
"PARTICLES",
"",
"Show particles textures",
"PARTICLES",
3,
), # "Show particles textures"
(
"LINESTYLE",
"",
"Show linestyle textures",
"LINE_DATA",
4,
), # "Show linestyle textures"
(
"OTHER",
"",
"Show other data textures",
"TEXTURE_DATA",
5,
), # "Show other data textures"
),
default="MATERIAL",
)
use_sky_blend: BoolProperty(
name="Blend Sky",
description="Render background with natural progression from horizon to zenith",
default=False,
)
use_sky_paper: BoolProperty(
name="Paper Sky", description="Flatten blend or texture coordinates", default=False
)
use_sky_real: BoolProperty(
name="Real Sky",
description="Render background with a real horizon, relative to the camera angle",
default=False,
)
horizon_color: FloatVectorProperty(
name="Horizon Color",
description="Color at the horizon",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.050876, 0.050876, 0.050876),
options={"ANIMATABLE"},
subtype="COLOR",
)
zenith_color: FloatVectorProperty(
name="Zenith Color",
description="Color at the zenith",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.0, 0.0, 0.0),
options={"ANIMATABLE"},
subtype="COLOR",
)
ambient_color: FloatVectorProperty(
name="Ambient Color",
description="Ambient color of the world",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.0, 0.0, 0.0),
options={"ANIMATABLE"},
subtype="COLOR",
)
active_texture_index: IntProperty(
name="Index for texture_slots", default=0, update=brush_texture_update
)
class WorldTextureSlot(PropertyGroup):
"""Declare world texture slot level properties for UI and translated to POV."""
bl_idname = ("pov_texture_slots",)
bl_description = ("Texture_slots from Blender-2.79",)
# Adding a "real" texture datablock as property is not possible
# (or at least not easy through a dynamically populated EnumProperty).
# That's why we'll use a prop_search() UILayout function in texturing_gui.py.
# So we'll assign the name of the needed texture datablock to the below StringProperty.
texture: StringProperty(update=active_texture_name_from_uilist)
# and use another temporary StringProperty to change the linked data
texture_search: StringProperty(
name="", update=active_texture_name_from_search, description="Browse Texture to be linked"
)
blend_factor: FloatProperty(
name="Blend",
description="Amount texture affects color progression of the " "background",
soft_min=0.0,
soft_max=1.0,
default=1.0,
)
horizon_factor: FloatProperty(
name="Horizon",
description="Amount texture affects color of the horizon",
soft_min=0.0,
soft_max=1.0,
default=1.0,
)
object: StringProperty(
name="Object",
description="Object to use for mapping with Object texture coordinates",
default="",
)
offset: FloatVectorProperty(
name="Offset",
description=("Fine tune of the texture mapping X, Y and Z locations "),
precision=4,
step=0.1,
soft_min=-100.0,
soft_max=100.0,
default=(0.0, 0.0, 0.0),
options={"ANIMATABLE"},
subtype="TRANSLATION",
)
scale: FloatVectorProperty(
name="Size",
subtype="XYZ",
size=3,
description="Set scaling for the textures X, Y and Z sizes ",
precision=4,
step=0.1,
soft_min=-100.0,
soft_max=100.0,
default=(1.0, 1.0, 1.0),
options={"ANIMATABLE"},
)
texture_coords: EnumProperty(
name="Coordinates",
description="Texture coordinates used to map the texture onto the background",
items=(
("VIEW", "View", "Use view vector for the texture coordinates"),
(
"GLOBAL",
"Global",
"Use global coordinates for the texture coordinates (interior mist)",
),
(
"ANGMAP",
"AngMap",
"Use 360 degree angular coordinates, e.g. for spherical light probes",
),
("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"),
("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"),
("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"),
("OBJECT", "Object", "Use linked objects coordinates for texture coordinates"),
),
default="VIEW",
)
use_map_blend: BoolProperty(
name="Blend Map", description="Affect the color progression of the background", default=True
)
use_map_horizon: BoolProperty(
name="Horizon Map", description="Affect the color of the horizon", default=False
)
use_map_zenith_down: BoolProperty(
name="", description="Affect the color of the zenith below", default=False
)
use_map_zenith_up: BoolProperty(
name="Zenith Up Map", description="Affect the color of the zenith above", default=False
)
zenith_down_factor: FloatProperty(
name="Zenith Down",
description="Amount texture affects color of the zenith below",
soft_min=0.0,
soft_max=1.0,
default=1.0,
)
zenith_up_factor: FloatProperty(
name="Zenith Up",
description="Amount texture affects color of the zenith above",
soft_min=0.0,
soft_max=1.0,
default=1.0,
)
"""
# class WORLD_TEXTURE_SLOTS_UL_layerlist(bpy.types.UIList):
# texture_slots:
class WorldTextureSlots(bpy.props.PropertyGroup):
index = bpy.prop.PropertyInt(name='index')
# foo = random prop
bpy.types.World.texture_slots = bpy.props.CollectionProperty(type=PropertyGroup)
for i in range(18): # length of world texture slots
world.texture_slots.add()
"""
classes = (
RenderPovSettingsCamera,
RenderPovSettingsLight,
RenderPovSettingsWorld,
WorldTextureSlot,
)
def register():
for cls in classes:
register_class(cls)
bpy.types.Camera.pov = PointerProperty(type=RenderPovSettingsCamera)
bpy.types.Light.pov = PointerProperty(type=RenderPovSettingsLight)
bpy.types.World.pov = PointerProperty(type=RenderPovSettingsWorld)
bpy.types.World.pov_texture_slots = CollectionProperty(type=WorldTextureSlot)
def unregister():
del bpy.types.Camera.pov
del bpy.types.Light.pov
del bpy.types.World.pov
del bpy.types.World.pov_texture_slots
for cls in reversed(classes):
unregister_class(cls)

529
render_povray/scripting.py Executable file
View File

@ -0,0 +1,529 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# #**** END GPL LICENSE BLOCK #****
# <pep8 compliant>
"""Support POV Scene Description Language snippets or full includes: import,
load, create or edit"""
import bpy
from bpy.props import StringProperty, BoolProperty, CollectionProperty
from bpy_extras.io_utils import ImportHelper
from mathutils import Vector
from math import pi
def export_custom_code(file):
"""write all POV user defined custom code to exported file """
# Write CurrentAnimation Frame for use in Custom POV Code
file.write("#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current)
# Change path and uncomment to add an animated include file by hand:
file.write("//#include \"/home/user/directory/animation_include_file.inc\"\n")
for txt in bpy.data.texts:
if txt.pov.custom_code == 'both':
# Why are the newlines needed?
file.write("\n")
file.write(txt.as_string())
file.write("\n")
#############################IMPORT
class ImportPOV(bpy.types.Operator, ImportHelper):
"""Load Povray files"""
bl_idname = "import_scene.pov"
bl_label = "POV-Ray files (.pov/.inc)"
bl_options = {'PRESET', 'UNDO'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
# -----------
# File props.
files: CollectionProperty(
type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}
)
directory: StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
filename_ext = {".pov", ".inc"}
filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'})
import_at_cur: BoolProperty(
name="Import at Cursor Location", description="Ignore Object Matrix", default=False
)
def execute(self, context):
from mathutils import Matrix
verts = []
faces = []
materials = []
blend_mats = [] ##############
pov_mats = [] ##############
colors = []
mat_names = []
lenverts = None
lenfaces = None
suffix = -1
name = 'Mesh2_%s' % suffix
name_search = False
verts_search = False
faces_search = False
plane_search = False
box_search = False
cylinder_search = False
sphere_search = False
cone_search = False
tex_search = False ##################
cache = []
matrixes = {}
write_matrix = False
index = None
value = None
# file_pov = bpy.path.abspath(self.filepath) #was used for single files
def mat_search(cache):
r = g = b = 0.5
f = t = 0
color = None
for item, value in enumerate(cache):
if value == 'texture':
pass
if value == 'pigment':
if cache[item + 2] in {'rgb', 'srgb'}:
pass
elif cache[item + 2] in {'rgbf', 'srgbf'}:
pass
elif cache[item + 2] in {'rgbt', 'srgbt'}:
try:
r, g, b, t = (
float(cache[item + 3]),
float(cache[item + 4]),
float(cache[item + 5]),
float(cache[item + 6]),
)
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
r = g = b = t = float(cache[item + 2])
color = (r, g, b, t)
elif cache[item + 2] in {'rgbft', 'srgbft'}:
pass
else:
pass
if colors == [] or (colors != [] and color not in colors):
colors.append(color)
name = ob.name + "_mat"
mat_names.append(name)
mat = bpy.data.materials.new(name)
mat.diffuse_color = (r, g, b)
mat.alpha = 1 - t
if mat.alpha != 1:
mat.use_transparency = True
ob.data.materials.append(mat)
else:
for i, value in enumerate(colors):
if color == value:
ob.data.materials.append(bpy.data.materials[mat_names[i]])
for file in self.files:
print("Importing file: " + file.name)
file_pov = self.directory + file.name
for line in open(file_pov):
string = line.replace("{", " ")
string = string.replace("}", " ")
string = string.replace("<", " ")
string = string.replace(">", " ")
string = string.replace(",", " ")
lw = string.split()
# lenwords = len(lw) # Not used... why written?
if lw:
if lw[0] == "object":
write_matrix = True
if write_matrix:
if lw[0] not in {"object", "matrix"}:
index = lw[0]
if lw[0] in {"matrix"}:
value = [
float(lw[1]),
float(lw[2]),
float(lw[3]),
float(lw[4]),
float(lw[5]),
float(lw[6]),
float(lw[7]),
float(lw[8]),
float(lw[9]),
float(lw[10]),
float(lw[11]),
float(lw[12]),
]
matrixes[index] = value
write_matrix = False
for line in open(file_pov):
S = line.replace("{", " { ")
S = S.replace("}", " } ")
S = S.replace(",", " ")
S = S.replace("<", "")
S = S.replace(">", " ")
S = S.replace("=", " = ")
S = S.replace(";", " ; ")
S = S.split()
# lenS = len(S) # Not used... why written?
for i, word in enumerate(S):
##################Primitives Import##################
if word == 'cone':
cone_search = True
name_search = False
if cone_search:
cache.append(word)
if cache[-1] == '}':
try:
x0 = float(cache[2])
y0 = float(cache[3])
z0 = float(cache[4])
r0 = float(cache[5])
x1 = float(cache[6])
y1 = float(cache[7])
z1 = float(cache[8])
r1 = float(cache[9])
# Y is height in most pov files, not z
bpy.ops.pov.cone_add(base=r0, cap=r1, height=(y1 - y0))
ob = context.object
ob.location = (x0, y0, z0)
# ob.scale = (r,r,r)
mat_search(cache)
except ValueError:
pass
cache = []
cone_search = False
if word == 'plane':
plane_search = True
name_search = False
if plane_search:
cache.append(word)
if cache[-1] == '}':
try:
bpy.ops.pov.addplane()
ob = context.object
mat_search(cache)
except ValueError:
pass
cache = []
plane_search = False
if word == 'box':
box_search = True
name_search = False
if box_search:
cache.append(word)
if cache[-1] == '}':
try:
x0 = float(cache[2])
y0 = float(cache[3])
z0 = float(cache[4])
x1 = float(cache[5])
y1 = float(cache[6])
z1 = float(cache[7])
# imported_corner_1=(x0, y0, z0)
# imported_corner_2 =(x1, y1, z1)
center = ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2)
bpy.ops.pov.addbox()
ob = context.object
ob.location = center
mat_search(cache)
except ValueError:
pass
cache = []
box_search = False
if word == 'cylinder':
cylinder_search = True
name_search = False
if cylinder_search:
cache.append(word)
if cache[-1] == '}':
try:
x0 = float(cache[2])
y0 = float(cache[3])
z0 = float(cache[4])
x1 = float(cache[5])
y1 = float(cache[6])
z1 = float(cache[7])
imported_cyl_loc = (x0, y0, z0)
imported_cyl_loc_cap = (x1, y1, z1)
r = float(cache[8])
vec = Vector(imported_cyl_loc_cap) - Vector(imported_cyl_loc)
depth = vec.length
rot = Vector((0, 0, 1)).rotation_difference(
vec
) # Rotation from Z axis.
trans = rot @ Vector( # XXX Not used, why written?
(0, 0, depth / 2)
) # Such that origin is at center of the base of the cylinder.
# center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
scale_z = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2) / 2
bpy.ops.pov.addcylinder(
R=r,
imported_cyl_loc=imported_cyl_loc,
imported_cyl_loc_cap=imported_cyl_loc_cap,
)
ob = context.object
ob.location = (x0, y0, z0)
ob.rotation_euler = rot.to_euler()
ob.scale = (1, 1, scale_z)
# scale data rather than obj?
# bpy.ops.object.mode_set(mode='EDIT')
# bpy.ops.mesh.reveal()
# bpy.ops.mesh.select_all(action='SELECT')
# bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
# bpy.ops.mesh.hide(unselected=False)
# bpy.ops.object.mode_set(mode='OBJECT')
mat_search(cache)
except ValueError:
pass
cache = []
cylinder_search = False
if word == 'sphere':
sphere_search = True
name_search = False
if sphere_search:
cache.append(word)
if cache[-1] == '}':
x = y = z = r = 0
try:
x = float(cache[2])
y = float(cache[3])
z = float(cache[4])
r = float(cache[5])
except ValueError:
pass
except:
x = y = z = float(cache[2])
r = float(cache[3])
bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z))
ob = context.object
ob.location = (x, y, z)
ob.scale = (r, r, r)
mat_search(cache)
cache = []
sphere_search = False
##################End Primitives Import##################
if word == '#declare':
name_search = True
if name_search:
cache.append(word)
if word == 'mesh2':
name_search = False
if cache[-2] == '=':
name = cache[-3]
else:
suffix += 1
cache = []
if word in {'texture', ';'}:
name_search = False
cache = []
if word == 'vertex_vectors':
verts_search = True
if verts_search:
cache.append(word)
if word == '}':
verts_search = False
lenverts = cache[2]
cache.pop()
cache.pop(0)
cache.pop(0)
cache.pop(0)
for j in range(int(lenverts)):
x = j * 3
y = (j * 3) + 1
z = (j * 3) + 2
verts.append((float(cache[x]), float(cache[y]), float(cache[z])))
cache = []
# if word == 'face_indices':
# faces_search = True
if word == 'texture_list': ########
tex_search = True #######
if tex_search: #########
if (
word not in {'texture_list', 'texture', '{', '}', 'face_indices'}
and not word.isdigit()
): ##############
pov_mats.append(word) #################
if word == 'face_indices':
tex_search = False ################
faces_search = True
if faces_search:
cache.append(word)
if word == '}':
faces_search = False
lenfaces = cache[2]
cache.pop()
cache.pop(0)
cache.pop(0)
cache.pop(0)
lf = int(lenfaces)
var = int(len(cache) / lf)
for k in range(lf):
if var == 3:
v0 = k * 3
v1 = k * 3 + 1
v2 = k * 3 + 2
faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
if var == 4:
v0 = k * 4
v1 = k * 4 + 1
v2 = k * 4 + 2
m = k * 4 + 3
materials.append((int(cache[m])))
faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
if var == 6:
v0 = k * 6
v1 = k * 6 + 1
v2 = k * 6 + 2
m0 = k * 6 + 3
m1 = k * 6 + 4
m2 = k * 6 + 5
materials.append(
(int(cache[m0]), int(cache[m1]), int(cache[m2]))
)
faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
# mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
# ob = object_utils.object_data_add(context, mesh, operator=None)
me = bpy.data.meshes.new(name) ########
ob = bpy.data.objects.new(name, me) ##########
bpy.context.collection.objects.link(ob) #########
me.from_pydata(verts, [], faces) ############
for mat in bpy.data.materials: ##############
blend_mats.append(mat.name) #############
for m_name in pov_mats: #####################
if m_name not in blend_mats: ###########
povMat = bpy.data.materials.new(m_name) #################
mat_search(cache)
ob.data.materials.append(
bpy.data.materials[m_name]
) ###################
if materials: ##################
for l, val in enumerate(materials): ####################
try: ###################
ob.data.polygons[
l
].material_index = val ####################
except TypeError: ###################
ob.data.polygons[l].material_index = int(
val[0]
) ##################
blend_mats = [] #########################
pov_mats = [] #########################
materials = [] #########################
cache = []
name_search = True
if name in matrixes and not self.import_at_cur:
global_matrix = Matrix.Rotation(pi / 2.0, 4, 'X')
ob = bpy.context.object
matrix = ob.matrix_world
v = matrixes[name]
matrix[0][0] = v[0]
matrix[1][0] = v[1]
matrix[2][0] = v[2]
matrix[0][1] = v[3]
matrix[1][1] = v[4]
matrix[2][1] = v[5]
matrix[0][2] = v[6]
matrix[1][2] = v[7]
matrix[2][2] = v[8]
matrix[0][3] = v[9]
matrix[1][3] = v[10]
matrix[2][3] = v[11]
matrix = global_matrix * ob.matrix_world
ob.matrix_world = matrix
verts = []
faces = []
# if word == 'pigment':
# try:
# #all indices have been incremented once to fit a bad test file
# r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
# color = (r,g,b,t)
# except IndexError:
# #all indices have been incremented once to fit alternate test file
# r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
# color = (r,g,b,t)
# except UnboundLocalError:
# # In case no transmit is specified ? put it to 0
# r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
# color = (r,g,b,t)
# except ValueError:
# color = (0.8,0.8,0.8,0)
# pass
# if colors == [] or (colors != [] and color not in colors):
# colors.append(color)
# name = ob.name+"_mat"
# mat_names.append(name)
# mat = bpy.data.materials.new(name)
# mat.diffuse_color = (r,g,b)
# mat.alpha = 1-t
# if mat.alpha != 1:
# mat.use_transparency=True
# ob.data.materials.append(mat)
# print (colors)
# else:
# for m in range(len(colors)):
# if color == colors[m]:
# ob.data.materials.append(bpy.data.materials[mat_names[m]])
##To keep Avogadro Camera angle:
# for obj in bpy.context.view_layer.objects:
# if obj.type == "CAMERA":
# track = obj.constraints.new(type = "TRACK_TO")
# track.target = ob
# track.track_axis ="TRACK_NEGATIVE_Z"
# track.up_axis = "UP_Y"
# obj.location = (0,0,0)
return {'FINISHED'}
def register():
bpy.utils.register_class(ImportPOV)
def unregister():
bpy.utils.unregister_class(ImportPOV)

268
render_povray/scripting_gui.py Executable file
View File

@ -0,0 +1,268 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""User interface to POV Scene Description Language snippets or full includes:
import, load, create or edit """
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import Operator, Menu, Panel
from sys import platform # really import here, as in ui.py and in render.py?
import os # really import here and in render.py?
from os.path import isfile
def locate_docpath():
"""POV can be installed with some include files.
Get their path as defined in user preferences or registry keys for
the user to be able to invoke them."""
addon_prefs = bpy.context.preferences.addons[__package__].preferences
# Use the system preference if its set.
pov_documents = addon_prefs.docpath_povray
if pov_documents:
if os.path.exists(pov_documents):
return pov_documents
# Implicit else, as here return was still not triggered:
print(
"User Preferences path to povray documents %r NOT FOUND, checking $PATH" % pov_documents
)
# Windows Only
if platform.startswith('win'):
import winreg
try:
win_reg_key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows"
)
win_docpath = winreg.QueryValueEx(win_reg_key, "DocPath")[0]
pov_documents = os.path.join(win_docpath, "Insert Menu")
if os.path.exists(pov_documents):
return pov_documents
except FileNotFoundError:
return ""
# search the path all os's
pov_documents_default = "include"
os_path_ls = os.getenv("PATH").split(':') + [""]
for dir_name in os_path_ls:
pov_documents = os.path.join(dir_name, pov_documents_default)
if os.path.exists(pov_documents):
return pov_documents
return ""
################################################################################
class TextButtonsPanel:
"""Use this class to define buttons from the side tab of
text window."""
bl_space_type = 'TEXT_EDITOR'
bl_region_type = 'UI'
bl_label = "POV-Ray"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
text = context.space_data
rd = context.scene.render
return text and (rd.engine in cls.COMPAT_ENGINES)
###############################################################################
# Text Povray Settings
###############################################################################
class TEXT_OT_POV_insert(Operator):
"""Use this class to create blender text editor operator to insert pov snippets like other pov IDEs"""
bl_idname = "text.povray_insert"
bl_label = "Insert"
filepath: bpy.props.StringProperty(name="Filepath", subtype='FILE_PATH')
@classmethod
def poll(cls, context):
text = context.space_data.text
return context.area.type == 'TEXT_EDITOR' and text is not None
# return bpy.ops.text.insert.poll() this Bpy op has no poll()
def execute(self, context):
if self.filepath and isfile(self.filepath):
file = open(self.filepath, "r")
bpy.ops.text.insert(text=file.read())
# places the cursor at the end without scrolling -.-
# context.space_data.text.write(file.read())
file.close()
return {'FINISHED'}
def validinsert(ext):
"""Since preview images could be in same folder, filter only insertable text"""
return ext in {".txt", ".inc", ".pov"}
class TEXT_MT_POV_insert(Menu):
"""Use this class to create a menu launcher in text editor for the TEXT_OT_POV_insert operator ."""
bl_label = "Insert"
bl_idname = "TEXT_MT_POV_insert"
def draw(self, context):
pov_documents = locate_docpath()
prop = self.layout.operator("wm.path_open", text="Open folder", icon='FILE_FOLDER')
prop.filepath = pov_documents
self.layout.separator()
pov_insert_items_list = []
for root, dirs, files in os.walk(pov_documents): # todo: structure submenus by dir
pov_insert_items_list.append(root)
print(pov_insert_items_list)
self.path_menu(
pov_insert_items_list,
"text.povray_insert",
# {"internal": True},
filter_ext=validinsert,
)
class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel):
"""Use this class to create a panel in text editor for the user to decide if he renders text
only or adds to 3d scene."""
bl_label = "POV"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
text = context.space_data.text
pov_documents = locate_docpath()
if not pov_documents:
layout.label(text="Please configure ", icon="INFO")
layout.label(text="default pov include path ")
layout.label(text="in addon preferences")
# layout.separator()
layout.operator(
"preferences.addon_show",
text="Go to Render: Persistence of Vision addon",
icon="PREFERENCES",
).module = "render_povray"
# layout.separator()
else:
# print(pov_documents)
layout.menu(TEXT_MT_POV_insert.bl_idname)
if text:
box = layout.box()
box.label(text='Source to render:', icon='RENDER_STILL')
row = box.row()
row.prop(text.pov, "custom_code", expand=True)
if text.pov.custom_code in {'3dview'}:
box.operator("render.render", icon='OUTLINER_DATA_ARMATURE')
if text.pov.custom_code in {'text'}:
rtext = bpy.context.space_data.text # is r a typo ? or why written, not used
box.operator("text.run", icon='ARMATURE_DATA')
# layout.prop(text.pov, "custom_code")
elif text.pov.custom_code in {'both'}:
box.operator("render.render", icon='POSE_HLT')
layout.label(text="Please specify declared", icon="INFO")
layout.label(text="items in properties ")
# layout.label(text="")
layout.label(text="replacement fields")
###############################################
# Text editor templates from header menu
class TEXT_MT_POV_templates(Menu):
"""Use this class to create a menu for the same pov templates scenes as other pov IDEs."""
bl_label = "POV"
# We list templates on file evaluation, we can assume they are static data,
# and better avoid running this on every draw call.
template_paths = [os.path.join(os.path.dirname(__file__), "templates_pov")]
def draw(self, context):
self.path_menu(self.template_paths, "text.open", props_default={"internal": True})
def menu_func_templates(self, context):
"""Add POV files to the text editor templates menu"""
# Do not depend on POV being active renderer here...
self.layout.menu("TEXT_MT_POV_templates")
###############################################
# POV Import menu
class VIEW_MT_POV_import(Menu):
"""Use this class for the import menu."""
bl_idname = "POVRAY_MT_import_tools"
bl_label = "Import"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
def menu_func_import(self, context):
"""Add the import operator to menu"""
engine = context.scene.render.engine
if engine == 'POVRAY_RENDER':
self.layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
classes = (
VIEW_MT_POV_import,
TEXT_OT_POV_insert,
TEXT_MT_POV_insert,
TEXT_PT_POV_custom_code,
TEXT_MT_POV_templates,
)
def register():
for cls in classes:
register_class(cls)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.TEXT_MT_templates.append(menu_func_templates)
def unregister():
bpy.types.TEXT_MT_templates.remove(menu_func_templates)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
for cls in reversed(classes):
unregister_class(cls)

View File

@ -0,0 +1,57 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
"""Declare pov native file syntax properties controllable in UI hooks and text blocks"""
from bpy.utils import register_class, unregister_class
from bpy.types import PropertyGroup
from bpy.props import EnumProperty, PointerProperty
###############################################################################
# Text POV properties.
###############################################################################
class RenderPovSettingsText(PropertyGroup):
"""Declare text properties to use UI as an IDE or render text snippets to POV."""
custom_code: EnumProperty(
name="Custom Code",
description="rendered source: Both adds text at the " "top of the exported POV file",
items=(("3dview", "View", ""), ("text", "Text", ""), ("both", "Both", "")),
default="text",
)
classes = (RenderPovSettingsText,)
def register():
for cls in classes:
register_class(cls)
bpy.types.Text.pov = PointerProperty(type=RenderPovSettingsText)
def unregister():
del bpy.types.Text.pov
for cls in reversed(classes):
unregister_class(cls)

2691
render_povray/shading.py Normal file → Executable file

File diff suppressed because it is too large Load Diff

688
render_povray/shading_gui.py Executable file
View File

@ -0,0 +1,688 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
""""User interface for shaders exported to POV textures."""
import bpy
from bpy.utils import register_class, unregister_class
from bpy.types import Operator, Menu, Panel
from bl_operators.presets import AddPresetBase
# Example of wrapping every class 'as is' except some
from bl_ui import properties_material
for member in dir(properties_material):
subclass = getattr(properties_material, member)
try:
# mat=bpy.context.active_object.active_material
# if (mat and mat.pov.type == "SURFACE"
# and not (mat.pov.material_use_nodes or mat.use_nodes)):
# and (engine in cls.COMPAT_ENGINES)) if subclasses were sorted
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
pass
del properties_material
from .shading_properties import check_material
def simple_material(mat):
"""Test if a material uses nodes"""
if (mat is not None) and (not mat.use_nodes):
return True
return False
class MaterialButtonsPanel:
"""Use this class to define buttons from the material tab of
properties window."""
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "material"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
rd = context.scene.render
return mat and (rd.engine in cls.COMPAT_ENGINES)
class MATERIAL_MT_POV_sss_presets(Menu):
"""Use this class to define pov sss preset menu."""
bl_label = "SSS Presets"
preset_subdir = "pov/material/sss"
preset_operator = "script.execute_preset"
draw = bpy.types.Menu.draw_preset
class MATERIAL_OT_POV_sss_add_preset(AddPresetBase, Operator):
"""Add an SSS Preset"""
bl_idname = "material.sss_preset_add"
bl_label = "Add SSS Preset"
preset_menu = "MATERIAL_MT_POV_sss_presets"
# variable used for all preset values
preset_defines = ["material = bpy.context.material"]
# properties to store in the preset
preset_values = [
"material.pov_subsurface_scattering.radius",
"material.pov_subsurface_scattering.color",
]
# where to store the preset
preset_subdir = "pov/material/sss"
class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel):
"""Use this class to define pov sss buttons panel."""
bl_label = "Subsurface Scattering"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
check_material(mat)
and (mat.pov.type in {'SURFACE', 'WIRE'})
and (engine in cls.COMPAT_ENGINES)
)
def draw_header(self, context):
mat = context.material # FORMERLY : #active_node_mat(context.material)
sss = mat.pov_subsurface_scattering
self.layout.active = not mat.pov.use_shadeless
self.layout.prop(sss, "use", text="")
def draw(self, context):
layout = self.layout
mat = context.material # FORMERLY : #active_node_mat(context.material)
sss = mat.pov_subsurface_scattering
layout.active = (sss.use) and (not mat.pov.use_shadeless)
row = layout.row().split()
sub = row.row(align=True).split(align=True, factor=0.75)
sub.menu(MATERIAL_MT_POV_sss_presets.__name__, text=MATERIAL_MT_POV_sss_presets.bl_label)
sub.operator(MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='ADD')
sub.operator(
MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='REMOVE'
).remove_active = True
split = layout.split()
col = split.column()
col.prop(sss, "ior")
col.prop(sss, "scale")
col.prop(sss, "color", text="")
col.prop(sss, "radius", text="RGB Radius", expand=True)
col = split.column()
sub = col.column(align=True)
sub.label(text="Blend:")
sub.prop(sss, "color_factor", text="Color")
sub.prop(sss, "texture_factor", text="Texture")
sub.label(text="Scattering Weight:")
sub.prop(sss, "front")
sub.prop(sss, "back")
col.separator()
col.prop(sss, "error_threshold", text="Error")
class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel):
"""Use this class to define an activate pov nodes button."""
bl_label = "Activate Node Settings"
bl_context = "material"
bl_options = {'HIDE_HEADER'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat = context.material
return (
mat
and mat.pov.type == "SURFACE"
and (engine in cls.COMPAT_ENGINES)
and not (mat.pov.material_use_nodes or mat.use_nodes)
)
def draw(self, context):
layout = self.layout
# layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
# the above replaced with a context hook below:
layout.operator(
"WM_OT_context_toggle", text="Use POV-Ray Nodes", icon='NODETREE'
).data_path = "material.pov.material_use_nodes"
class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel):
"""Use this class to show pov active node properties buttons."""
bl_label = "Active Node Settings"
bl_context = "material"
bl_options = {'HIDE_HEADER'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat = context.material
return (
mat
and mat.pov.type == "SURFACE"
and (engine in cls.COMPAT_ENGINES)
and mat.pov.material_use_nodes
)
def draw(self, context):
layout = self.layout
mat = context.material
node_tree = mat.node_tree
if node_tree:
node = node_tree.nodes.active
if mat.use_nodes:
if node:
layout.prop(mat.pov, "material_active_node")
if node.bl_idname == "PovrayMaterialNode":
layout.context_pointer_set("node", node)
if hasattr(node, "draw_buttons_ext"):
node.draw_buttons_ext(context, layout)
elif hasattr(node, "draw_buttons"):
node.draw_buttons(context, layout)
value_inputs = [
socket
for socket in node.inputs
if socket.enabled and not socket.is_linked
]
if value_inputs:
layout.separator()
layout.label(text="Inputs:")
for socket in value_inputs:
row = layout.row()
socket.draw(context, row, node, socket.name)
else:
layout.context_pointer_set("node", node)
if hasattr(node, "draw_buttons_ext"):
node.draw_buttons_ext(context, layout)
elif hasattr(node, "draw_buttons"):
node.draw_buttons(context, layout)
value_inputs = [
socket
for socket in node.inputs
if socket.enabled and not socket.is_linked
]
if value_inputs:
layout.separator()
layout.label(text="Inputs:")
for socket in value_inputs:
row = layout.row()
socket.draw(context, row, node, socket.name)
else:
layout.label(text="No active nodes!")
class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel):
"""Use this class to define standard material specularity (highlights) buttons."""
bl_label = "Specular"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
check_material(mat)
and (mat.pov.type in {'SURFACE', 'WIRE'})
and (engine in cls.COMPAT_ENGINES)
)
def draw(self, context):
layout = self.layout
mat = context.material.pov
layout.active = not mat.use_shadeless
split = layout.split()
col = split.column()
col.prop(mat, "specular_color", text="")
col.prop(mat, "specular_intensity", text="Intensity")
col = split.column()
col.prop(mat, "specular_shader", text="")
col.prop(mat, "use_specular_ramp", text="Ramp")
col = layout.column()
if mat.specular_shader in {'COOKTORR', 'PHONG'}:
col.prop(mat, "specular_hardness", text="Hardness")
elif mat.specular_shader == 'BLINN':
row = col.row()
row.prop(mat, "specular_hardness", text="Hardness")
row.prop(mat, "specular_ior", text="IOR")
elif mat.specular_shader == 'WARDISO':
col.prop(mat, "specular_slope", text="Slope")
elif mat.specular_shader == 'TOON':
row = col.row()
row.prop(mat, "specular_toon_size", text="Size")
row.prop(mat, "specular_toon_smooth", text="Smooth")
if mat.use_specular_ramp:
layout.separator()
layout.template_color_ramp(mat, "specular_ramp", expand=True)
layout.separator()
row = layout.row()
row.prop(mat, "specular_ramp_input", text="Input")
row.prop(mat, "specular_ramp_blend", text="Blend")
layout.prop(mat, "specular_ramp_factor", text="Factor")
class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel):
"""Use this class to define standard material reflectivity (mirror) buttons."""
bl_label = "Mirror"
bl_options = {'DEFAULT_CLOSED'}
bl_idname = "MATERIAL_PT_POV_raytrace_mirror"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
check_material(mat)
and (mat.pov.type in {'SURFACE', 'WIRE'})
and (engine in cls.COMPAT_ENGINES)
)
def draw_header(self, context):
mat = context.material
raym = mat.pov_raytrace_mirror
self.layout.prop(raym, "use", text="")
def draw(self, context):
layout = self.layout
mat = context.material # Formerly : #mat = active_node_mat(context.material)
raym = mat.pov_raytrace_mirror
layout.active = raym.use
split = layout.split()
col = split.column()
col.prop(raym, "reflect_factor")
col.prop(raym, "mirror_color", text="")
col = split.column()
col.prop(raym, "fresnel")
sub = col.column()
sub.active = raym.fresnel > 0.0
sub.prop(raym, "fresnel_factor", text="Blend")
split = layout.split()
col = split.column()
col.separator()
col.prop(raym, "depth")
col.prop(raym, "distance", text="Max Dist")
col.separator()
sub = col.split(factor=0.4)
sub.active = raym.distance > 0.0
sub.label(text="Fade To:")
sub.prop(raym, "fade_to", text="")
col = split.column()
col.label(text="Gloss:")
col.prop(raym, "gloss_factor", text="Amount")
sub = col.column()
sub.active = raym.gloss_factor < 1.0
sub.prop(raym, "gloss_threshold", text="Threshold")
sub.prop(raym, "gloss_samples", text="Noise")
sub.prop(raym, "gloss_anisotropic", text="Anisotropic")
class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
"""Use this class to define pov material transparency (alpha) buttons."""
bl_label = "Transparency"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
check_material(mat)
and (mat.pov.type in {'SURFACE', 'WIRE'})
and (engine in cls.COMPAT_ENGINES)
)
def draw_header(self, context):
mat = context.material
if simple_material(mat):
self.layout.prop(mat.pov, "use_transparency", text="")
def draw(self, context):
layout = self.layout
base_mat = context.material
mat = context.material # FORMERLY active_node_mat(context.material)
rayt = mat.pov_raytrace_transparency
if simple_material(base_mat):
row = layout.row()
row.active = mat.pov.use_transparency
row.prop(mat.pov, "transparency_method", expand=True)
split = layout.split()
split.active = base_mat.pov.use_transparency
col = split.column()
col.prop(mat.pov, "alpha")
row = col.row()
row.active = (base_mat.pov.transparency_method != 'MASK') and (not mat.pov.use_shadeless)
row.prop(mat.pov, "specular_alpha", text="Specular")
col = split.column()
col.active = not mat.pov.use_shadeless
col.prop(rayt, "fresnel")
sub = col.column()
sub.active = rayt.fresnel > 0.0
sub.prop(rayt, "fresnel_factor", text="Blend")
if base_mat.pov.transparency_method == 'RAYTRACE':
layout.separator()
split = layout.split()
split.active = base_mat.pov.use_transparency
col = split.column()
col.prop(rayt, "ior")
col.prop(rayt, "filter")
col.prop(rayt, "falloff")
col.prop(rayt, "depth_max")
col.prop(rayt, "depth")
col = split.column()
col.label(text="Gloss:")
col.prop(rayt, "gloss_factor", text="Amount")
sub = col.column()
sub.active = rayt.gloss_factor < 1.0
sub.prop(rayt, "gloss_threshold", text="Threshold")
sub.prop(rayt, "gloss_samples", text="Samples")
class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel):
"""Use this class to define more pov specific reflectivity buttons."""
bl_label = "POV-Ray Reflection"
bl_parent_id = "MATERIAL_PT_POV_raytrace_mirror"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat = context.material
return (
mat
and mat.pov.type == "SURFACE"
and (engine in cls.COMPAT_ENGINES)
and not (mat.pov.material_use_nodes or mat.use_nodes)
)
def draw(self, context):
layout = self.layout
mat = context.material
col = layout.column()
col.prop(mat.pov, "irid_enable")
if mat.pov.irid_enable:
col = layout.column()
col.prop(mat.pov, "irid_amount", slider=True)
col.prop(mat.pov, "irid_thickness", slider=True)
col.prop(mat.pov, "irid_turbulence", slider=True)
col.prop(mat.pov, "conserve_energy")
col2 = col.split().column()
if not mat.pov_raytrace_mirror.use:
col2.label(text="Please Check Mirror settings :")
col2.active = mat.pov_raytrace_mirror.use
col2.prop(mat.pov, "mirror_use_IOR")
if mat.pov.mirror_use_IOR:
col2.alignment = 'CENTER'
col2.label(text="The current Raytrace ")
col2.label(text="Transparency IOR is: " + str(mat.pov_raytrace_transparency.ior))
col2.prop(mat.pov, "mirror_metallic")
'''
#group some native Blender (SSS) and POV (Fade)settings under such a parent panel?
class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel):
bl_label = "POV-Ray Interior"
bl_idname = "material.pov_interior"
#bl_parent_id = "material.absorption"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat=context.material
return mat and mat.pov.type == "SURFACE" and (engine in cls.COMPAT_ENGINES) and not (mat.pov.material_use_nodes or mat.use_nodes)
def draw_header(self, context):
mat = context.material
'''
class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel, Panel):
"""Use this class to define pov fading (absorption) color buttons."""
bl_label = "POV-Ray Absorption"
COMPAT_ENGINES = {'POVRAY_RENDER'}
# bl_parent_id = "material.pov_interior"
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat = context.material
return (
mat
and mat.pov.type == "SURFACE"
and (engine in cls.COMPAT_ENGINES)
and not (mat.pov.material_use_nodes or mat.use_nodes)
)
def draw_header(self, context):
mat = context.material
self.layout.prop(mat.pov, "interior_fade_color", text="")
def draw(self, context):
layout = self.layout
mat = context.material
# layout.active = mat.pov.interior_fade_color
if mat.pov.interior_fade_color != (0.0, 0.0, 0.0):
layout.label(text="Raytrace transparency")
layout.label(text="depth max Limit needs")
layout.label(text="to be non zero to fade")
class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel):
"""Use this class to define pov caustics buttons."""
bl_label = "Caustics"
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
mat = context.material
return (
mat
and mat.pov.type == "SURFACE"
and (engine in cls.COMPAT_ENGINES)
and not (mat.pov.material_use_nodes or mat.use_nodes)
)
def draw_header(self, context):
mat = context.material
if mat.pov.caustics_enable:
self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER_SEL")
else:
self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER")
def draw(self, context):
layout = self.layout
mat = context.material
layout.active = mat.pov.caustics_enable
col = layout.column()
if mat.pov.caustics_enable:
col.prop(mat.pov, "refraction_caustics")
if mat.pov.refraction_caustics:
col.prop(mat.pov, "refraction_type", text="")
if mat.pov.refraction_type == "1":
col.prop(mat.pov, "fake_caustics_power", slider=True)
elif mat.pov.refraction_type == "2":
col.prop(mat.pov, "photons_dispersion", slider=True)
col.prop(mat.pov, "photons_dispersion_samples", slider=True)
col.prop(mat.pov, "photons_reflection")
if not mat.pov.refraction_caustics and not mat.pov.photons_reflection:
col = layout.column()
col.alignment = 'CENTER'
col.label(text="Caustics override is on, ")
col.label(text="but you didn't chose any !")
class MATERIAL_PT_strand(MaterialButtonsPanel, Panel):
"""Use this class to define Blender strand antialiasing buttons."""
bl_label = "Strand"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'POVRAY_RENDER'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
mat and (mat.pov.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES)
)
def draw(self, context):
layout = self.layout
mat = context.material # don't use node material
tan = mat.strand
split = layout.split()
col = split.column()
sub = col.column(align=True)
sub.label(text="Size:")
sub.prop(tan, "root_size", text="Root")
sub.prop(tan, "tip_size", text="Tip")
sub.prop(tan, "size_min", text="Minimum")
sub.prop(tan, "use_blender_units")
sub = col.column()
sub.active = not mat.pov.use_shadeless
sub.prop(tan, "use_tangent_shading")
col.prop(tan, "shape")
col = split.column()
col.label(text="Shading:")
col.prop(tan, "width_fade")
ob = context.object
if ob and ob.type == 'MESH':
col.prop_search(tan, "uv_layer", ob.data, "uv_layers", text="")
else:
col.prop(tan, "uv_layer", text="")
col.separator()
sub = col.column()
sub.active = not mat.pov.use_shadeless
sub.label(text="Surface diffuse:")
sub = col.column()
sub.prop(tan, "blend_distance", text="Distance")
class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel):
"""Use this class to define pov custom code declared name field."""
bl_label = "Custom POV Code"
COMPAT_ENGINES = {'POVRAY_RENDER'}
def draw(self, context):
layout = self.layout
mat = context.material
col = layout.column()
col.alignment = 'RIGHT'
col.label(text="Override properties with this")
col.label(text="text editor {pov code} block")
layout.prop(mat.pov, "replacement_text", text="#declare name", icon='SYNTAX_ON')
classes = (
MATERIAL_PT_POV_sss,
MATERIAL_MT_POV_sss_presets,
MATERIAL_OT_POV_sss_add_preset,
MATERIAL_PT_strand,
MATERIAL_PT_POV_activate_node,
MATERIAL_PT_POV_active_node,
MATERIAL_PT_POV_specular,
MATERIAL_PT_POV_mirror,
MATERIAL_PT_POV_transp,
MATERIAL_PT_POV_reflection,
## MATERIAL_PT_POV_interior,
MATERIAL_PT_POV_fade_color,
MATERIAL_PT_POV_caustics,
MATERIAL_PT_POV_replacement_text,
)
def register():
for cls in classes:
register_class(cls)
def unregister():
for cls in reversed(classes):
unregister_class(cls)

2011
render_povray/shading_nodes.py Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

902
render_povray/texturing.py Executable file
View File

@ -0,0 +1,902 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# #**** END GPL LICENSE BLOCK #****
# <pep8 compliant>
"""Translate blender texture influences into POV."""
import os
import bpy
def write_texture_influence(
using_uberpov,
mater,
material_names_dictionary,
local_material_names,
path_image,
exported_lights_count,
image_format,
img_map,
img_map_transforms,
tab_write,
comments,
string_strip_hyphen,
safety,
col,
preview_dir,
unpacked_images,
):
"""Translate Blender texture influences to various POV texture tricks and write to pov file."""
material_finish = material_names_dictionary[mater.name]
if mater.pov.use_transparency:
trans = 1.0 - mater.pov.alpha
else:
trans = 0.0
if (mater.pov.specular_color.s == 0.0) or (mater.pov.diffuse_shader == "MINNAERT"):
# No layered texture because of aoi pattern used for minnaert and pov can't layer patterned
colored_specular_found = False
else:
colored_specular_found = True
if mater.pov.use_transparency and mater.pov.transparency_method == "RAYTRACE":
pov_filter = mater.pov_raytrace_transparency.filter * (1.0 - mater.pov.alpha)
trans = (1.0 - mater.pov.alpha) - pov_filter
else:
pov_filter = 0.0
##############SF
texture_dif = ""
texture_spec = ""
texture_norm = ""
texture_alpha = ""
# procedural_flag=False
tmpidx = -1
for t in mater.pov_texture_slots:
tmpidx += 1
# index = mater.pov.active_texture_index
slot = mater.pov_texture_slots[tmpidx] # [index]
povtex = slot.texture # slot.name
tex = bpy.data.textures[povtex]
if t and (t.use and (tex is not None)):
# 'NONE' ('NONE' type texture is different from no texture covered above)
if tex.type == "NONE" and tex.pov.tex_pattern_type == "emulator":
continue # move to next slot
# Implicit else-if (as not skipped by previous "continue")
# PROCEDURAL
if tex.type != "IMAGE" and tex.type != "NONE":
procedural_flag = True
image_filename = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name))
if image_filename:
if t.use_map_color_diffuse:
texture_dif = image_filename
# colvalue = t.default_value # UNUSED
t_dif = t
if t_dif.texture.pov.tex_gamma_enable:
img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
if t.use_map_specular or t.use_map_raymir:
texture_spec = image_filename
# colvalue = t.default_value # UNUSED
t_spec = t
if t.use_map_normal:
texture_norm = image_filename
# colvalue = t.normal_factor/10 # UNUSED
# textNormName=tex.image.name + ".normal"
# was the above used? --MR
t_nor = t
if t.use_map_alpha:
texture_alpha = image_filename
# colvalue = t.alpha_factor * 10.0 # UNUSED
# textDispName=tex.image.name + ".displ"
# was the above used? --MR
t_alpha = t
# RASTER IMAGE
elif tex.type == "IMAGE" and tex.image and tex.pov.tex_pattern_type == "emulator":
procedural_flag = False
# PACKED
if tex.image.packed_file:
orig_image_filename = tex.image.filepath_raw
unpackedfilename = os.path.join(
preview_dir,
("unpacked_img_" + (string_strip_hyphen(bpy.path.clean_name(tex.name)))),
)
if not os.path.exists(unpackedfilename):
# record which images that were newly copied and can be safely
# cleaned up
unpacked_images.append(unpackedfilename)
tex.image.filepath_raw = unpackedfilename
tex.image.save()
image_filename = unpackedfilename.replace("\\", "/")
# .replace("\\","/") to get only forward slashes as it's what POV prefers,
# even on windows
tex.image.filepath_raw = orig_image_filename
# FILE
else:
image_filename = path_image(tex.image)
# IMAGE SEQUENCE BEGINS
if image_filename:
if bpy.data.images[tex.image.name].source == "SEQUENCE":
korvaa = "." + str(tex.image_user.frame_offset + 1).zfill(3) + "."
image_filename = image_filename.replace(".001.", korvaa)
print(" seq debug ")
print(image_filename)
# IMAGE SEQUENCE ENDS
img_gamma = ""
if image_filename:
texdata = bpy.data.textures[t.texture]
if t.use_map_color_diffuse:
texture_dif = image_filename
# colvalue = t.default_value # UNUSED
t_dif = t
print(texdata)
if texdata.pov.tex_gamma_enable:
img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
if t.use_map_specular or t.use_map_raymir:
texture_spec = image_filename
# colvalue = t.default_value # UNUSED
t_spec = t
if t.use_map_normal:
texture_norm = image_filename
# colvalue = t.normal_factor/10 # UNUSED
# textNormName=tex.image.name + ".normal"
# was the above used? --MR
t_nor = t
if t.use_map_alpha:
texture_alpha = image_filename
# colvalue = t.alpha_factor * 10.0 # UNUSED
# textDispName=tex.image.name + ".displ"
# was the above used? --MR
t_alpha = t
####################################################################################
tab_write("\n")
# THIS AREA NEEDS TO LEAVE THE TEXTURE OPEN UNTIL ALL MAPS ARE WRITTEN DOWN.
# --MR
current_material_name = string_strip_hyphen(material_names_dictionary[mater.name])
local_material_names.append(current_material_name)
tab_write("\n#declare MAT_%s = \ntexture{\n" % current_material_name)
################################################################################
if mater.pov.replacement_text != "":
tab_write("%s\n" % mater.pov.replacement_text)
#################################################################################
# XXX TODO: replace by new POV MINNAERT rather than aoi
if mater.pov.diffuse_shader == "MINNAERT":
tab_write("\n")
tab_write("aoi\n")
tab_write("texture_map {\n")
tab_write("[%.3g finish {diffuse %.3g}]\n" % (mater.darkness / 2.0, 2.0 - mater.darkness))
tab_write("[%.3g\n" % (1.0 - (mater.darkness / 2.0)))
if mater.pov.diffuse_shader == "FRESNEL":
# For FRESNEL diffuse in POV, we'll layer slope patterned textures
# with lamp vector as the slope vector and nest one slope per lamp
# into each texture map's entry.
c = 1
while c <= exported_lights_count:
tab_write("slope { lampTarget%s }\n" % (c))
tab_write("texture_map {\n")
# Diffuse Fresnel value and factor go up to five,
# other kind of values needed: used the number 5 below to remap
tab_write(
"[%.3g finish {diffuse %.3g}]\n"
% (
(5.0 - mater.diffuse_fresnel) / 5,
(mater.diffuse_intensity * ((5.0 - mater.diffuse_fresnel_factor) / 5)),
)
)
tab_write(
"[%.3g\n" % ((mater.diffuse_fresnel_factor / 5) * (mater.diffuse_fresnel / 5.0))
)
c += 1
# if shader is a 'FRESNEL' or 'MINNAERT': slope pigment pattern or aoi
# and texture map above, the rest below as one of its entry
if texture_spec != "" or texture_alpha != "":
if texture_spec != "":
# tab_write("\n")
tab_write("pigment_pattern {\n")
mapping_spec = img_map_transforms(t_spec)
if texture_spec and texture_spec.startswith("PAT_"):
tab_write("function{f%s(x,y,z).grey}\n" % texture_spec)
tab_write("%s\n" % mapping_spec)
else:
tab_write(
'uv_mapping image_map{%s "%s" %s}\n'
% (image_format(texture_spec), texture_spec, img_map(t_spec))
)
tab_write("%s\n" % mapping_spec)
tab_write("}\n")
tab_write("texture_map {\n")
tab_write("[0 \n")
if texture_dif == "":
if texture_alpha != "":
tab_write("\n")
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha))
else:
tab_write(
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s'
% (
image_format(texture_alpha),
texture_alpha,
img_map(t_alpha),
mapping_alpha,
)
)
tab_write("}\n")
tab_write("pigment_map {\n")
tab_write("[0 color rgbft<0,0,0,1,1>]\n")
tab_write(
"[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n"
% (col[0], col[1], col[2], pov_filter, trans)
)
tab_write("}\n")
tab_write("}\n")
else:
tab_write(
"pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n"
% (col[0], col[1], col[2], pov_filter, trans)
)
if texture_spec != "":
# ref_level_bound 1 is no specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
else:
# ref_level_bound 2 is translated spec
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
else:
mapping_dif = img_map_transforms(t_dif)
if texture_alpha != "":
mapping_alpha = img_map_transforms(t_alpha)
tab_write("pigment {\n")
tab_write("pigment_pattern {\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha))
else:
tab_write(
'uv_mapping image_map{%s "%s" %s}%s}\n'
% (
image_format(texture_alpha),
texture_alpha,
img_map(t_alpha),
mapping_alpha,
)
)
tab_write("pigment_map {\n")
tab_write("[0 color rgbft<0,0,0,1,1>]\n")
# if texture_alpha and texture_alpha.startswith("PAT_"):
# tab_write("[1 pigment{%s}]\n" %texture_dif)
if texture_dif and not texture_dif.startswith("PAT_"):
tab_write(
'[1 uv_mapping image_map {%s "%s" %s} %s]\n'
% (
image_format(texture_dif),
texture_dif,
(img_gamma + img_map(t_dif)),
mapping_dif,
)
)
elif texture_dif and texture_dif.startswith("PAT_"):
tab_write("[1 %s]\n" % texture_dif)
tab_write("}\n")
tab_write("}\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write("}\n")
else:
if texture_dif and texture_dif.startswith("PAT_"):
tab_write("pigment{%s}\n" % texture_dif)
else:
tab_write(
'pigment {uv_mapping image_map {%s "%s" %s}%s}\n'
% (
image_format(texture_dif),
texture_dif,
(img_gamma + img_map(t_dif)),
mapping_dif,
)
)
if texture_spec != "":
# ref_level_bound 1 is no specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
else:
# ref_level_bound 2 is translated specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
## scale 1 rotate y*0
# imageMap = ("{image_map {%s \"%s\" %s }\n" % \
# (image_format(textures),textures,img_map(t_dif)))
# tab_write("uv_mapping pigment %s} %s finish {%s}\n" % \
# (imageMap,mapping,safety(material_finish)))
# tab_write("pigment {uv_mapping image_map {%s \"%s\" %s}%s} " \
# "finish {%s}\n" % \
# (image_format(texture_dif), texture_dif, img_map(t_dif),
# mapping_dif, safety(material_finish)))
if texture_norm != "":
## scale 1 rotate y*0
mapping_normal = img_map_transforms(t_nor)
if texture_norm and texture_norm.startswith("PAT_"):
tab_write(
"normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n"
% (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal)
)
else:
tab_write("normal {\n")
# XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("average\n")
tab_write("normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write("[1.0 ") # Proceed with user bump...
tab_write(
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s'
% (
image_format(texture_norm),
texture_norm,
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
)
)
# ...Then close its last entry and the the normal_map itself
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("]}}\n")
else:
tab_write("]}\n")
if texture_spec != "":
tab_write("]\n")
##################Second index for mapping specular max value###############
tab_write("[1 \n")
if texture_dif == "" and mater.pov.replacement_text == "":
if texture_alpha != "":
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write("function{f%s(x,y,z).transmit %s}\n" % (texture_alpha, mapping_alpha))
else:
tab_write(
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s}\n'
% (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha)
)
tab_write("pigment_map {\n")
tab_write("[0 color rgbft<0,0,0,1,1>]\n")
tab_write(
"[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n"
% (col[0], col[1], col[2], pov_filter, trans)
)
tab_write("}\n")
tab_write("}\n")
else:
tab_write(
"pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n"
% (col[0], col[1], col[2], pov_filter, trans)
)
if texture_spec != "":
# ref_level_bound 3 is full specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("normal {\n")
tab_write("average\n")
tab_write("normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
# XXX IF USER BUMP_MAP
if texture_norm != "":
tab_write(
"[1.0 "
) # Blurry reflection or not Proceed with user bump in either case...
tab_write(
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s]\n'
% (
image_format(texture_norm),
texture_norm,
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
)
)
# ...Then close the normal_map itself if blurry reflection
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("}}\n")
else:
tab_write("}\n")
elif colored_specular_found:
# ref_level_bound 1 is no specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
else:
# ref_level_bound 2 is translated specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
elif mater.pov.replacement_text == "":
mapping_dif = img_map_transforms(t_dif)
if texture_alpha != "":
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write(
"pigment{pigment_pattern {function{f%s(x,y,z).transmit}%s}\n"
% (texture_alpha, mapping_alpha)
)
else:
tab_write(
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s}\n'
% (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha)
)
tab_write("pigment_map {\n")
tab_write("[0 color rgbft<0,0,0,1,1>]\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write("[1 function{f%s(x,y,z).transmit}%s]\n" % (texture_alpha, mapping_alpha))
elif texture_dif and not texture_dif.startswith("PAT_"):
tab_write(
'[1 uv_mapping image_map {%s "%s" %s} %s]\n'
% (
image_format(texture_dif),
texture_dif,
(img_map(t_dif) + img_gamma),
mapping_dif,
)
)
elif texture_dif and texture_dif.startswith("PAT_"):
tab_write("[1 %s %s]\n" % (texture_dif, mapping_dif))
tab_write("}\n")
tab_write("}\n")
else:
if texture_dif and texture_dif.startswith("PAT_"):
tab_write("pigment{%s %s}\n" % (texture_dif, mapping_dif))
else:
tab_write("pigment {\n")
tab_write("uv_mapping image_map {\n")
# tab_write("%s \"%s\" %s}%s\n" % \
# (image_format(texture_dif), texture_dif,
# (img_gamma + img_map(t_dif)),mapping_dif))
tab_write('%s "%s" \n' % (image_format(texture_dif), texture_dif))
tab_write("%s\n" % (img_gamma + img_map(t_dif)))
tab_write("}\n")
tab_write("%s\n" % mapping_dif)
tab_write("}\n")
if texture_spec != "":
# ref_level_bound 3 is full specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
else:
# ref_level_bound 2 is translated specular
tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
## scale 1 rotate y*0
# imageMap = ("{image_map {%s \"%s\" %s }" % \
# (image_format(textures), textures,img_map(t_dif)))
# tab_write("\n\t\t\tuv_mapping pigment %s} %s finish {%s}" % \
# (imageMap, mapping, safety(material_finish)))
# tab_write("\n\t\t\tpigment {uv_mapping image_map " \
# "{%s \"%s\" %s}%s} finish {%s}" % \
# (image_format(texture_dif), texture_dif,img_map(t_dif),
# mapping_dif, safety(material_finish)))
if texture_norm != "" and mater.pov.replacement_text == "":
mapping_normal = img_map_transforms(t_nor)
if texture_norm and texture_norm.startswith("PAT_"):
tab_write(
"normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n"
% (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal)
)
else:
tab_write("normal {\n")
# XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("average\n")
tab_write("normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
)
) # micronormals blurring
tab_write(
"[1.0 "
) # Blurry reflection or not Proceed with user bump in either case...
tab_write(
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s]\n'
% (
image_format(texture_norm),
texture_norm,
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
)
)
# ...Then close the normal_map itself if blurry reflection
if (
mater.pov_raytrace_mirror.use
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
tab_write("}}\n")
else:
tab_write("}\n")
if texture_spec != "" and mater.pov.replacement_text == "":
tab_write("]\n")
tab_write("}\n")
# End of slope/ior texture_map
if mater.pov.diffuse_shader == "MINNAERT" and mater.pov.replacement_text == "":
tab_write("]\n")
tab_write("}\n")
if mater.pov.diffuse_shader == "FRESNEL" and mater.pov.replacement_text == "":
c = 1
while c <= exported_lights_count:
tab_write("]\n")
tab_write("}\n")
c += 1
# Close first layer of POV "texture" (Blender material)
tab_write("}\n")
colored_specular_found = bool(
(mater.pov.specular_color.s > 0.0) and (mater.pov.diffuse_shader != "MINNAERT")
)
# Write another layered texture using invisible diffuse and metallic trick
# to emulate colored specular highlights
special_texture_found = False
tmpidx = -1
for t in mater.pov_texture_slots:
tmpidx += 1
# index = mater.pov.active_texture_index
slot = mater.pov_texture_slots[tmpidx] # [index]
povtex = slot.texture # slot.name
tex = bpy.data.textures[povtex]
# Specular mapped textures would conflict with colored specular
# because POV can't layer over or under pigment patterned textures
special_texture_found = bool(
t
and t.use
and ((tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE")
and (t.use_map_specular or t.use_map_raymir)
)
if colored_specular_found and not special_texture_found:
if comments:
tab_write(" // colored highlights with a stransparent metallic layer\n")
else:
tab_write("\n")
tab_write("texture {\n")
tab_write(
"pigment {rgbft<%.3g, %.3g, %.3g, 0, 1>}\n"
% (
mater.pov.specular_color[0],
mater.pov.specular_color[1],
mater.pov.specular_color[2],
)
)
tab_write(
"finish {%s}\n" % (safety(material_finish, ref_level_bound=2))
) # ref_level_bound 2 is translated spec
texture_norm = ""
for t in mater.pov_texture_slots:
if t and tex.pov.tex_pattern_type != "emulator":
procedural_flag = True
image_filename = string_strip_hyphen(bpy.path.clean_name(tex.name))
if (
t
and tex.type == "IMAGE"
and t.use
and tex.image
and tex.pov.tex_pattern_type == "emulator"
):
procedural_flag = False
image_filename = path_image(tex.image)
img_gamma = ""
if image_filename:
if t.use_map_normal:
texture_norm = image_filename
# colvalue = t.normal_factor/10 # UNUSED XXX *-9.5 !
# textNormName=tex.image.name + ".normal"
# was the above used? --MR
t_nor = t
if procedural_flag:
tab_write(
"normal{function"
"{f%s(x,y,z).grey} bump_size %.4g}\n"
% (texture_norm, (-t_nor.normal_factor * 9.5))
)
else:
tab_write(
"normal {uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s}\n'
% (
image_format(texture_norm),
texture_norm,
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
)
)
tab_write("}\n") # THEN IT CAN CLOSE LAST LAYER OF TEXTURE

1255
render_povray/texturing_gui.py Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

196
render_povray/update_files.py Normal file → Executable file
View File

@ -33,8 +33,22 @@ from bpy.props import (
EnumProperty,
)
# Todo:
# *update this file to just cover 2.79 to 3.xx and ui it from a Blender internal to pov menu
# *as well as update from older pov > switch to QMC when pov 3.8 is out ?
# *filter if possible files built in pre 2.79 versions. tell user their file is too old and may
# be salvaged from older vesion of this operator from within latest stable blender 2.79 version.
# else if bpy.app.version[0] == 2 and bpy.app.version[1] <= 92 and and bpy.app.version[1] >= 79:
# warn users to update blender to 3.xx for creating their newer files then try to salvage
# using this script
#
# if bpy.app.version[0] >= 3: # just test file created a)there or b)before, a) do nothing
# "your version is relatively futureproof" > doing nothing
# b)"use this operator to salvage your blends from latest stable 2.79"
def update2_0_0_9():
"""Update properties from older Blender versions. The render API changed a lot up to 2.79."""
# Temporally register old props, so we can access their values.
register()
@ -109,26 +123,16 @@ def update2_0_0_9():
"pov_refraction_type",
"pov_replacement_text",
]:
old_mat_props[k] = getattr(bpy.types.Material, k)[1].get(
'default', None
)
old_mat_props[k] = getattr(bpy.types.Material, k)[1].get('default', None)
# Get default values of pov texture props.
old_tex_props = {}
for k in [
"pov_tex_gamma_enable",
"pov_tex_gamma_value",
"pov_replacement_text",
]:
for k in ["pov_tex_gamma_enable", "pov_tex_gamma_value", "pov_replacement_text"]:
old_tex_props[k] = getattr(bpy.types.Texture, k)[1].get('default', None)
# Get default values of pov object props.
old_obj_props = {}
for k in [
"pov_importance_value",
"pov_collect_photons",
"pov_replacement_text",
]:
for k in ["pov_importance_value", "pov_collect_photons", "pov_replacement_text"]:
old_obj_props[k] = getattr(bpy.types.Object, k)[1].get('default', None)
# Get default values of pov camera props.
@ -189,7 +193,7 @@ def update2_0_0_9():
class RenderCopySettings(bpy.types.Operator):
"""Update old POV properties to new ones"""
"""Update old POV properties to new ones."""
bl_idname = "scene.pov_update_properties"
bl_label = "PovRay render: Update to script v0.0.9"
@ -253,19 +257,13 @@ def register():
# Not a real pov option, just to know if we should write
Scene.pov_radio_enable = BoolProperty(
name="Enable Radiosity",
description="Enable POV-Rays radiosity calculation",
default=False,
name="Enable Radiosity", description="Enable POV-Rays radiosity calculation", default=False
)
Scene.pov_radio_display_advanced = BoolProperty(
name="Advanced Options",
description="Show advanced options",
default=False,
name="Advanced Options", description="Show advanced options", default=False
)
Scene.pov_media_enable = BoolProperty(
name="Enable Media",
description="Enable POV-Rays atmospheric media",
default=False,
name="Enable Media", description="Enable POV-Rays atmospheric media", default=False
)
Scene.pov_media_samples = IntProperty(
name="Samples",
@ -288,9 +286,7 @@ def register():
)
Scene.pov_baking_enable = BoolProperty(
name="Enable Baking",
description="Enable POV-Rays texture baking",
default=False,
name="Enable Baking", description="Enable POV-Rays texture baking", default=False
)
Scene.pov_indentation_character = EnumProperty(
name="Indentation",
@ -311,9 +307,7 @@ def register():
)
Scene.pov_comments_enable = BoolProperty(
name="Enable Comments",
description="Add comments to pov file",
default=True,
name="Enable Comments", description="Add comments to pov file", default=True
)
# Real pov options
@ -339,11 +333,7 @@ def register():
)
Scene.pov_antialias_depth = IntProperty(
name="Antialias Depth",
description="Depth of pixel for sampling",
min=1,
max=9,
default=3,
name="Antialias Depth", description="Depth of pixel for sampling", min=1, max=9, default=3
)
Scene.pov_antialias_threshold = FloatProperty(
@ -504,9 +494,7 @@ def register():
# max_sample - not available yet
Scene.pov_radio_media = BoolProperty(
name="Media",
description="Radiosity estimation can be affected by media",
default=False,
name="Media", description="Radiosity estimation can be affected by media", default=False
)
Scene.pov_radio_minimum_reuse = FloatProperty(
@ -529,9 +517,7 @@ def register():
)
Scene.pov_radio_normal = BoolProperty(
name="Normals",
description="Radiosity estimation can be affected by normals",
default=False,
name="Normals", description="Radiosity estimation can be affected by normals", default=False
)
Scene.pov_radio_recursion_limit = IntProperty(
@ -638,9 +624,7 @@ def register():
)
Mat.pov_fake_caustics = BoolProperty(
name="Fake Caustics",
description="use only (Fast) fake refractive caustics",
default=True,
name="Fake Caustics", description="use only (Fast) fake refractive caustics", default=True
)
Mat.pov_fake_caustics_power = FloatProperty(
@ -654,9 +638,7 @@ def register():
)
Mat.pov_photons_refraction = BoolProperty(
name="Refractive Photon Caustics",
description="more physically correct",
default=False,
name="Refractive Photon Caustics", description="more physically correct", default=False
)
Mat.pov_photons_dispersion = FloatProperty(
@ -752,9 +734,7 @@ def register():
# DOF Toggle
Cam.pov_dof_enable = BoolProperty(
name="Depth Of Field",
description="Enable POV-Ray Depth Of Field ",
default=True,
name="Depth Of Field", description="Enable POV-Ray Depth Of Field ", default=True
)
# Aperture (Intensity of the Blur)
@ -816,12 +796,12 @@ def unregister():
Obj = bpy.types.Object
Cam = bpy.types.Camera
Text = bpy.types.Text
del Scene.pov_tempfiles_enable # CR
del Scene.pov_scene_name # CR
del Scene.pov_deletefiles_enable # CR
del Scene.pov_scene_path # CR
del Scene.pov_renderimage_path # CR
del Scene.pov_list_lf_enable # CR
del Scene.pov_tempfiles_enable
del Scene.pov_scene_name
del Scene.pov_deletefiles_enable
del Scene.pov_scene_path
del Scene.pov_renderimage_path
del Scene.pov_list_lf_enable
del Scene.pov_radio_enable
del Scene.pov_radio_display_advanced
del Scene.pov_radio_adc_bailout
@ -836,56 +816,56 @@ def unregister():
del Scene.pov_radio_nearest_count
del Scene.pov_radio_normal
del Scene.pov_radio_recursion_limit
del Scene.pov_radio_pretrace_start # MR
del Scene.pov_radio_pretrace_end # MR
del Scene.pov_media_enable # MR
del Scene.pov_media_samples # MR
del Scene.pov_media_color # MR
del Scene.pov_baking_enable # MR
del Scene.pov_max_trace_level # MR
del Scene.pov_photon_spacing # MR
del Scene.pov_photon_max_trace_level # MR
del Scene.pov_photon_adc_bailout # MR
del Scene.pov_photon_gather_min # MR
del Scene.pov_photon_gather_max # MR
del Scene.pov_antialias_enable # CR
del Scene.pov_antialias_method # CR
del Scene.pov_antialias_depth # CR
del Scene.pov_antialias_threshold # CR
del Scene.pov_antialias_gamma # CR
del Scene.pov_jitter_enable # CR
del Scene.pov_jitter_amount # CR
del Scene.pov_command_line_switches # CR
del Scene.pov_indentation_character # CR
del Scene.pov_indentation_spaces # CR
del Scene.pov_comments_enable # CR
del Mat.pov_irid_enable # MR
del Mat.pov_mirror_use_IOR # MR
del Mat.pov_mirror_metallic # MR
del Mat.pov_conserve_energy # MR
del Mat.pov_irid_amount # MR
del Mat.pov_irid_thickness # MR
del Mat.pov_irid_turbulence # MR
del Mat.pov_interior_fade_color # MR
del Mat.pov_caustics_enable # MR
del Mat.pov_fake_caustics # MR
del Mat.pov_fake_caustics_power # MR
del Mat.pov_photons_refraction # MR
del Mat.pov_photons_dispersion # MR
del Mat.pov_photons_reflection # MR
del Mat.pov_refraction_type # MR
del Mat.pov_replacement_text # MR
del Tex.pov_tex_gamma_enable # MR
del Tex.pov_tex_gamma_value # MR
del Tex.pov_replacement_text # MR
del Obj.pov_importance_value # MR
del Obj.pov_collect_photons # MR
del Obj.pov_replacement_text # MR
del Cam.pov_dof_enable # MR
del Cam.pov_dof_aperture # MR
del Cam.pov_dof_samples_min # MR
del Cam.pov_dof_samples_max # MR
del Cam.pov_dof_variance # MR
del Cam.pov_dof_confidence # MR
del Cam.pov_replacement_text # MR
del Text.pov_custom_code # MR
del Scene.pov_radio_pretrace_start
del Scene.pov_radio_pretrace_end
del Scene.pov_media_enable
del Scene.pov_media_samples
del Scene.pov_media_color
del Scene.pov_baking_enable
del Scene.pov_max_trace_level
del Scene.pov_photon_spacing
del Scene.pov_photon_max_trace_level
del Scene.pov_photon_adc_bailout
del Scene.pov_photon_gather_min
del Scene.pov_photon_gather_max
del Scene.pov_antialias_enable
del Scene.pov_antialias_method
del Scene.pov_antialias_depth
del Scene.pov_antialias_threshold
del Scene.pov_antialias_gamma
del Scene.pov_jitter_enable
del Scene.pov_jitter_amount
del Scene.pov_command_line_switches
del Scene.pov_indentation_character
del Scene.pov_indentation_spaces
del Scene.pov_comments_enable
del Mat.pov_irid_enable
del Mat.pov_mirror_use_IOR
del Mat.pov_mirror_metallic
del Mat.pov_conserve_energy
del Mat.pov_irid_amount
del Mat.pov_irid_thickness
del Mat.pov_irid_turbulence
del Mat.pov_interior_fade_color
del Mat.pov_caustics_enable
del Mat.pov_fake_caustics
del Mat.pov_fake_caustics_power
del Mat.pov_photons_refraction
del Mat.pov_photons_dispersion
del Mat.pov_photons_reflection
del Mat.pov_refraction_type
del Mat.pov_replacement_text
del Tex.pov_tex_gamma_enable
del Tex.pov_tex_gamma_value
del Tex.pov_replacement_text
del Obj.pov_importance_value
del Obj.pov_collect_photons
del Obj.pov_replacement_text
del Cam.pov_dof_enable
del Cam.pov_dof_aperture
del Cam.pov_dof_samples_min
del Cam.pov_dof_samples_max
del Cam.pov_dof_variance
del Cam.pov_dof_confidence
del Cam.pov_replacement_text
del Text.pov_custom_code