Difference between revisions of "HowTo:Use POV-Ray with Blender/Developers page"
m |
m |
||
Line 120: | Line 120: | ||
− | + | ==Separate Python (*.py) files== | |
− | The script was later split into a [https://docs.python.org/3/tutorial/modules.html#packages package] (several files into a folder) using as few files as possible to keep the flow of data easier to understand. There were 3 main files: __init__.py | + | The script was later split into a [https://docs.python.org/3/tutorial/modules.html#packages package] (several files into a folder) using as few files as possible to keep the flow of data easier to understand. There were initially 3 main files: |
+ | *__init__.py | ||
+ | *render.py | ||
+ | *ui.py | ||
However, some of them reaching over 10 000 lines for such an add-on was still too much. | However, some of them reaching over 10 000 lines for such an add-on was still too much. | ||
− | So it is currently tolerated that the biggest file | + | So it is currently tolerated that the biggest file stays below 7000 lines. Most probably the files could lose some weight, by removing deprecated 32 bits support and modularizing some of the code into a few more reusable functions. Here are the current files composing the add-on and their respective use: |
====__init__.py==== | ====__init__.py==== | ||
− | + | Initialize properties | |
− | ( | + | (Note the double underscores around the file name. this makes it the first file launched) |
defining the 'main' unit for a package; it also causes Python to treat the specific directory as a package. It is the unit that will be used when you call import cherryPy (and cherryPy is a directory). This is briefly explained in the Modules tutorial. | defining the 'main' unit for a package; it also causes Python to treat the specific directory as a package. It is the unit that will be used when you call import cherryPy (and cherryPy is a directory). This is briefly explained in the Modules tutorial. | ||
====ui.py==== | ====ui.py==== | ||
− | + | Provide property buttons for the user to set up the variables | |
====render.py==== | ====render.py==== | ||
− | + | Translate geometry and UI properties (Blender and POV native) to the POV file | |
====primitives.py==== | ====primitives.py==== | ||
− | + | Display some POV native primitives in 3D view for input and output | |
====shading.py==== | ====shading.py==== | ||
− | + | Translate shading properties to declared textures at the top of a pov file | |
====nodes.py==== | ====nodes.py==== | ||
− | + | Translate node trees to the pov file | |
====df3.py==== | ====df3.py==== | ||
− | + | Render smoke to *.df3 files | |
====update_files.py==== | ====update_files.py==== | ||
− | + | Update new variables to values from older API. This file needs an update | |
Along these essential files also coexist a few additional libraries to help make | Along these essential files also coexist a few additional libraries to help make | ||
− | Blender stand up to other | + | Blender stand up to other Persistence of Vision compatible frontends such as ''povwin'' or ''QTPOV'' |
− | ==== | + | ==Presets== |
− | + | =====Material (sss)===== | |
− | + | apple.py | |
− | + | chicken.py | |
− | + | cream.py | |
− | + | Ketchup.py | |
− | + | marble.py | |
− | + | potato.py | |
− | + | skim_milk.py | |
− | + | skin1.py | |
− | + | skin2.py | |
− | + | whole_milk.py | |
− | + | =====Radiosity===== | |
− | + | 01_Debug.py | |
− | + | 02_Fast.py | |
− | + | 03_Normal.py | |
− | + | 04_Two_Bounces.py | |
− | + | 05_Final.py | |
− | + | 06_Outdoor_Low_Quality.py | |
− | + | 07_Outdoor_High_Quality.py | |
− | + | 08_Outdoor(Sun)Light.py | |
− | + | 09_Indoor_Low_Quality.py | |
− | + | 10_Indoor_High_Quality.py | |
− | + | =====World===== | |
− | + | 01_Clear_Blue_Sky.py | |
− | + | 02_Partly_Hazy_Sky.py | |
− | + | 03_Overcast_Sky.py | |
− | + | 04_Cartoony_Sky.py | |
− | + | 05_Under_Water.py | |
− | + | =====Light===== | |
− | + | 01_(4800K)_Direct_Sun.py | |
− | + | 02_(5400K)_High_Noon_Sun.py | |
− | + | 03_(6000K)_Daylight_Window.py | |
− | + | 04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py | |
− | + | 05_(4000K)_100W_Metal_Halide.py | |
− | + | 06_(3200K)_100W_Quartz_Halogen.py | |
− | + | 07_(2850K)_100w_Tungsten.py | |
− | + | 08_(2600K)_40w_Tungsten.py | |
− | + | 09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py | |
− | + | 10_(4300K)_40W_Vintage_Fluorescent_T12.py | |
− | + | 11_(5000K)_18W_Standard_Fluorescent_T8 | |
− | + | 12_(4200K)_18W_Cool_White_Fluorescent_T8.py | |
+ | 13_(3000K)_18W_Warm_Fluorescent_T8.py | ||
+ | 14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py | ||
+ | 15_(3200K)_40W_Induction_Fluorescent.py | ||
+ | 16_(2100K)_150W_High_Pressure_Sodium.py | ||
+ | 17_(1700K)_135W_Low_Pressure_Sodium.py | ||
+ | 18_(6800K)_175W_Mercury_Vapor.py | ||
+ | 19_(5200K)_700W_Carbon_Arc.py | ||
+ | 20_(6500K)_15W_LED_Spot.py | ||
+ | 21_(2700K)_7W_OLED_Panel.py | ||
+ | 22_(30000K)_40W_Black_Light_Fluorescent.py | ||
+ | 23_(30000K)_40W_Black_Light_Bulb.py | ||
+ | 24_(1850K)_Candle.py | ||
+ | ==templates== | ||
+ | abyss.pov | ||
+ | biscuit.pov | ||
+ | bsp_Tango.pov | ||
+ | chess2.pov | ||
+ | cornell.pov | ||
+ | diffract.pov | ||
+ | diffuse_back.pov | ||
+ | float5 | ||
+ | gamma_showcase.pov | ||
+ | grenadine.pov | ||
+ | isocacti.pov | ||
+ | mediasky.pov | ||
+ | patio-radio.pov | ||
+ | subsurface.pov | ||
+ | wallstucco.pov |
Revision as of 09:33, 19 July 2020
The Blender to POV-Ray add-on and vice versa (aka "POV Converter") started out as a single Python file. To be compliant with blender add-on packaging system this add-on had to:
- provide some general information about the add-on: its name and version,
- define some code to perform actions, mostly through 'operators' or 'functions'
- and make sure these are registered so that they can be used.
bpy
Blender has an embedded Python interpreter which is loaded when Blender is started and stays active while Blender is running. This interpreter runs scripts to draw the user interface and is used for some of Blender’s internal tools as well. Blender’s embedded interpreter provides a typical Python environment, so code from tutorials on how to write Python scripts can also be run with Blender’s interpreter. Blender provides its Python modules, such as bpy and mathutils, to the embedded interpreter so they can be imported into a script and give access to Blender’s data, classes, and functions. Scripts that deal with Blender data will need to import the modules to work. So of all modules, if no other, bpy is generally always imported in blender scripts. (except in interactive console were this import is automatically done for convenience)
bl_info
bl_info = {
"name": "Persistence of Vision",
"author": "Campbell Barton, "
"Maurice Raybaud, "
"Leonid Desyatkov, "
"Bastien Montagne, "
"Constantin Rahn, "
"Silvio Falcinelli",
"version": (0, 1, 1),
"blender": (2, 81, 0),
"location": "Render Properties > Render Engine > Persistence of Vision",
"description": "Persistence of Vision integration for blender",
"doc_url": "{BLENDER_MANUAL_URL}/addons/render/povray.html",
"category": "Render",
"warning": "Under active development, seeking co-maintainer(s)",
}
The general information about the add-on is defined in a dictionary with the name
bl_info
Each key of this dictionary provides Blender with specific information about the add-on although not all are equally important. Most of the information is used in the user preferences dialog and helps the user to find and select an add-on.
name
Kind of a code name for the addon, by which it is referenced in the Blender source... (UI name can show something else)
author
If you contribute you will be credited here !
version
The version of the add-on. Any numbering scheme is valid, as long as it is a tuple of three integers. Preferably only move the last position but we might choose for a more structured scheme later on.
blender
The minimal Blender version needed by this add-on. Again a tuple of three integers. Even if you expect things to work with older and nnewer versions it might be a good idea to list the earliest version that you actually tested your add-on with! Developing with something stable enough is important, and then test with latest version just before commit but don't generally update compatible version number then, only when something breaks at that stage.
category
The category in the user preferences our add-on is grouped under. It operates POV compatible engines which are renderers so it made sense to add it to the Render category historically. Unfortunately it doesnt reflect the multipurpose nature it ended up growing (such as shipped in importer, text editor syntax highlighting etc...) If several categories are possible maybe we should add them.
location
Where to find the add-on once it is enabled. This might a reference to a specific panel or inout case, a description of its location in a menu.
description
A concise description of what the add-on does.
warning
If this is not an empty string, the add-on will show up with a warning sign in the user preferences. You might use this to mark an add-on as experimental for example.
doc_url
the public on-line documentation, it is a derivative of the work happening here. It will be a click-able item in the user preferences.
tracker_url
The POV add-on being a bundled part of Blender it does have its own bug tracker entry associated with it and this key will provide a pointer to it.
Comments
Single line comments begin with a hash (#) character immediately followed by one blank space and the comment starting with a capital but not ending with a dot. They can be added after a short enough line of code (leave one blank space before the #).
For instance, at the beginning of the file, the comment
# <pep8 compliant>
means that one of the text formatting / coding style guideline to use is PEP8 so, for instance, you should take care to make short enough lines throughout all the script's files of less than 79 characters and less than 72 for docstrings (see below) or comments.
While multi-line comments can begin with triple quotes. It is the convention that three double quotes are used for docstrings, a specific kind of comment not doing anything in the program but still appearing when console is used to get help about a function.
"""This is an explanation of general relevance"""
It is good practice to put a doc string at the start of every class to describe it and also at the start of every file to explain its relative use.
Operators
Most add-ons define new operators, they are classes that implement specific functionality. The actual definition of the operator takes the form a class that is derived from bpy.types.Operator
import bpy
class MyTool(bpy.types.Operator):
"""Do things to an object"""
bl_idname = "object.do_things_to_object"
bl_label = "Do things to an object"
bl_options = {'REGISTER', 'UNDO'}
The docstring at the start of the class definition will be used as a tooltip anywhere this operator will be available, for example in a menu, while the bl_label defines the actual label used in the menu entry itself. Here we kept both the same. Operators will be part of Blender's data, and operators are stored in the module bpy.ops. This bl_idname will make sure this operator's entry will be called bpy.ops.object.move_object. Operators are normally registered in order to make them usable and that is indeed the default of bl_options. However, if we also want theadd-on to show up in the history so it can be undone or repeated, we should add UNDO to the set of flags that is assigned to bl_options, as is done here.
Functions
Functions are defined with the def keyword, followed by the name of the function and a pair of parenthesis. When the function is referenced to move it around and pass it along between files, it no longer needs the parenthesis, but pretty much every other time, they do follow function's name and in between them, some attributes can eventually be specified.
The execute() function
An operator class can have any number of member functions but to be useful it normally overrides the execute() function:
def execute(self, context):
context.active_object.rotation.z += 33
return {'FINISHED'}
The execute() function is passed a reference to a context object. This context object contains among other things an active_object attribute which points to, Blenders active object. Each object in Blender has a rotation attribute which is a vector with x, y and z components. Changing the rotation of an object is as simple as changing one of these components, which is exactly what we do in line 2.
The execute() function signals successful completion by returning a set of flags, in this case a set consisting solely of a string FINISHED.
Defining an operator is not in itself enough to make this operator usable. In order for the user to find and use an operator, for example by pressing SPACE in the 3D view window and typing the label of the operator, we must register the operator. Adding a registered operator to a menu requires a separate action.
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_object.remove(menu_func)
def menu_func(self, context):
self.layout.operator(MyTool.bl_idname, icon='MESH_CUBE')
When we check the Enable an add-on check-box in the user preferences, Blender will look for a register() function and execute it. Likewise, when disabling an add-on the unregister() function is called. Here we use this to both register our operator with Blender and to insert a menu entry that refers to our operator. The bpy.utils.register_module() function will register any class in a module that has REGISTER defined in its bl_options set. In order to create a menu entry we have to do two things:
- Create a function that will produce a menu entry
- And append this function to a suitable menu.
Now almost everything in Blender is available as a Python object and menus are no exception. We want to add our entry to the Object menu in the 3D view so we call
bpy.types.VIEW3D_MT_object.append()
and pass it a reference to the function we define a little below. How do we know how this menu object is called? If you have checked File⇒ User preferences ⇒ Interface ⇒ Python Tooltips the name of the menu will be shown in a tooltip when you hover over a menu.From the image above we can see that we can use bpy.types.VIEW3D_MT_object.append() to add something to the Object menu because VIEW3D_MT_object is shown in the balloon text. Note that the menu_func() function does not implement an action itself but will, when called, append a user interface element to the object that is passed to it in the self parameter. This user interface element in turn will interact with the user. Here we will simply add an operator entry (that is, an item that will execute our operator when clicked). The self argument that is passed to menu_func() refers to the menu. This menu has a layout attribute with an operator() function to which we pass the name of our operator. This will ensure that every time a user hovers over the Object menu, our operator will be shown in the list of options. The name of our new MyTool operator can be found in its bl_idname attribute so that is why we pass MyTool.bl_idname.
The name of the entry and its tooltip is determined by looking at the bl_label and docstring defined in our MyTool class and the icon used in the menu is determined by passing an optional icon parameter to the operator() function. This may sound overly complicated but it makes it possible for example to show different things than just click-able entries in a menu for example to group several operators in a box.
Separate Python (*.py) files
The script was later split into a package (several files into a folder) using as few files as possible to keep the flow of data easier to understand. There were initially 3 main files:
- __init__.py
- render.py
- ui.py
However, some of them reaching over 10 000 lines for such an add-on was still too much.
So it is currently tolerated that the biggest file stays below 7000 lines. Most probably the files could lose some weight, by removing deprecated 32 bits support and modularizing some of the code into a few more reusable functions. Here are the current files composing the add-on and their respective use:
__init__.py
Initialize properties (Note the double underscores around the file name. this makes it the first file launched) defining the 'main' unit for a package; it also causes Python to treat the specific directory as a package. It is the unit that will be used when you call import cherryPy (and cherryPy is a directory). This is briefly explained in the Modules tutorial.
ui.py
Provide property buttons for the user to set up the variables
render.py
Translate geometry and UI properties (Blender and POV native) to the POV file
primitives.py
Display some POV native primitives in 3D view for input and output
shading.py
Translate shading properties to declared textures at the top of a pov file
nodes.py
Translate node trees to the pov file
df3.py
Render smoke to *.df3 files
update_files.py
Update new variables to values from older API. This file needs an update
Along these essential files also coexist a few additional libraries to help make Blender stand up to other Persistence of Vision compatible frontends such as povwin or QTPOV
Presets
Material (sss)
apple.py chicken.py cream.py Ketchup.py marble.py potato.py skim_milk.py skin1.py skin2.py whole_milk.py
Radiosity
01_Debug.py 02_Fast.py 03_Normal.py 04_Two_Bounces.py 05_Final.py 06_Outdoor_Low_Quality.py 07_Outdoor_High_Quality.py 08_Outdoor(Sun)Light.py 09_Indoor_Low_Quality.py 10_Indoor_High_Quality.py
World
01_Clear_Blue_Sky.py 02_Partly_Hazy_Sky.py 03_Overcast_Sky.py 04_Cartoony_Sky.py 05_Under_Water.py
Light
01_(4800K)_Direct_Sun.py 02_(5400K)_High_Noon_Sun.py 03_(6000K)_Daylight_Window.py 04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py 05_(4000K)_100W_Metal_Halide.py 06_(3200K)_100W_Quartz_Halogen.py 07_(2850K)_100w_Tungsten.py 08_(2600K)_40w_Tungsten.py 09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py 10_(4300K)_40W_Vintage_Fluorescent_T12.py 11_(5000K)_18W_Standard_Fluorescent_T8 12_(4200K)_18W_Cool_White_Fluorescent_T8.py 13_(3000K)_18W_Warm_Fluorescent_T8.py 14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py 15_(3200K)_40W_Induction_Fluorescent.py 16_(2100K)_150W_High_Pressure_Sodium.py 17_(1700K)_135W_Low_Pressure_Sodium.py 18_(6800K)_175W_Mercury_Vapor.py 19_(5200K)_700W_Carbon_Arc.py 20_(6500K)_15W_LED_Spot.py 21_(2700K)_7W_OLED_Panel.py 22_(30000K)_40W_Black_Light_Fluorescent.py 23_(30000K)_40W_Black_Light_Bulb.py 24_(1850K)_Candle.py
templates
abyss.pov biscuit.pov bsp_Tango.pov chess2.pov cornell.pov diffract.pov diffuse_back.pov float5 gamma_showcase.pov grenadine.pov isocacti.pov mediasky.pov patio-radio.pov subsurface.pov wallstucco.pov