摘要
作者:mightyang
由于特殊原因,需要整一下 clarisse,所以需要整理下它的 python api,方便写脚本。按照惯例,先提供下官方文档的基本介绍内容(翻译)。
官方文档
Clarisse Object 模型的介绍
这个章节介绍 Clarisse Object 模型,它是学习 Clarrise API 的关键内容。
Clarisse 项目工程里存放的什么东西
在 Clarisse 里,一个项目工程是一些不同类型的 OfObject 或者 OfClass 实例(instance) 的集合,存放在一种以层级关系作为结构的 OfContext 中。一个 context 类似于文件系统中的文件夹,它也可以控制可见性。想要了解更多有关 context 的内容,请参考用户手册。
整个项目的入口是通过 OfObjectFactory 这个对象工厂进行访问的。在 Python 里,你可以通过调用 ix.application.get_factory()来获取对象工厂,而 C++ 里则通过 AppObject::get_factory 来实现。(从这里可以猜测,想要查看 ix.application 有哪些属性和方法供 Python 调用,直接查看文档里 AppObject 即可,帮助文档没有直接提供 Python 的内容)
什么是类(classes)?
某一项(item)的类型,是通过该项的 OfClass 进行定义的。它们也可以决定 Clarisse 对它们进行处理的时候进行哪些操作。类与它们的属性、可调用的属性,可以被继承。例如,在 Clarisse 里,OfClass|ProjectItem 是非常重要的类。当一个类的实例直接或者间接的从 OfClass|ProjectItem 类继承,它就会被自动的保存在项目工程文件里。
注意
为了避免在 C++ 的类与 OfClass 之间产生混淆,我们已经将文档中所有有关 OfClass 的内容都加上了 OfClass 的前缀。比如 OfClass GeometryPolyMesh 将会显示为 OfClass|GeometryPolymesh。
例如,OfClass|GeometryPolyfile 类定义了一个多边形面,被保存在一个继承自 OfClass|ProjectItem 的外部文件中。这句话解释了为什么 OfClass|GeometryPolyfile 的项被保存在项目工程文件里。如果你想要在 Clarisse 里看到所有类型的层级关系图,你可以使用 Class Explorer (类浏览器) 部件(窗口)进行查看。只需要点击主窗口菜单 Window > Class Explorer... 。
注意
Class Explorer 并不会显示 OfClass 的真实名称。而是根据它们的真实名称,以一种更为人类所能理解的方式进行显示。如果想要显示这些项真实的类名称,只需要启用浏览器的 类型(Type)列即可。想要启用或者禁用这个列,在浏览器的右边,右击列表头的空白区域勾选即可。
如果想要查看 Polyfile(OfClas|GeometryPolyfile)的类型层级图,你可以看到如下内容:
Polyfile (OfClass|GeometryPolyfile) < Polymesh (OfClass|GeometryPolymesh) < OfClass|Geometry < OfClass|SceneObject < Scene Item ( OfClass|SceneItem) < Project Item (OfClass|ProjectItem)
OfClass|GeometryPolyfile 是通过一个外部文件定义的专门用于定义多边形面的类型。OfClass|GeometryPolymesh 定义了一种特殊的集合体类型,用于定义一些常规的几何体面。OfClass|Geometry 定义了几何体的概念。OfClass|SceneObject 定义了提供给 Clarisse 渲染器的所有可渲染物体。OfClass|SceneItem 定义了一个三维场景里的某个元素。它将所有的定义都关联到它的 kinematics。OfClass|ProjectItem 定义了一个抽象类,可以定义一个实例,用于保存在 Clarisse 工程里。
什么是属性?
属性 OfAttr 是一个参数,用户一般是通过修改这些参数来修改上面提到的实例。一个实列的属性可以在属性编辑器里查看。属性可以为以下这些类型,例如:
- 颜色(明度,明度+a,rgb,rgba...)
- 一个或者多个数值
- 布尔值,在属性编辑器里显示为复选框
- 某个实例的一个参考,或者一些参考。
注意
如果属性是类属性或者变量,由于它们可以直接被脚本语言访问,所以它们的真实名称需要符合常规的变量/成员名称的规范。就像 OfClass,属性的真实名称并不会直接显示,Clarisse 的界面会以更为人类所能理解的方式显示。例如属性 enable_subdivision_suface,在属性编辑器里显示为 Enable Subdivision Surface。
对象也可以通过自定义属性来扩展。即使这些自定义属性对于 Clarisse 来说没用任何作用,比如,你可以添加一些元/自定义数据,用于自定义脚本的读取。
属性一般是作为用户修改对象的接口。事实上,每一次属性的修改,都会通知给对象的实例。例如,用户修改了 OfClass|SceneItem 的 Translate 属性,OfClass|SceneItem 类会被告知这个物体的实例的 Translate 属性被修改了。然后 OfClass|SceneItem 定义的模块就会执行刚刚生成的变换矩阵。
例如: 如果你想要修改某个类属性的默认值,可以添加一个启动脚本:
# getting the base polyfile class
polymesh_class = ix.application.get_factory().get_classes().get("GeometryPolyfile")
# setting the base attribute smooting angle of the polyfile class to 0.0
polymesh_class.get_attribute("smoothing_angle").set_double(0.0)
# now each time a polyfile is created its default smoothing angle value will be set to 0.0 instead of 89.0
如果你希望在每次创建某个类的实例时,自动添加一个自定义属性,则如下:
# getting the polyfile class
polymesh_class = ix.application.get_factory().get_classes().get("GeometryPolyfile")
# adding a custom attribute to the polymesh class
polymesh_class.add_attribute("asset_id", ix.api.OfAttr.TYPE_STRING, ix.api.OfAttr.CONTAINER_SINGLE, ix.api.OfAttr.VISUAL_HINT_DEFAULT, "Custom Attributes")
# now each time a polyfile is created it will have an extra attribute asset_id that will be saved in the project.
有一种属性非常重要,即:类型参考属性。类型参考的属性被用于将物体连接在一起。一般情况下,每次某个属性参考另外一个物体时,会创建一条带有从属关系的链路。例如,当你将 OfClass|SceneItem 的 Parent 属性设置为另外一个物体,这条链路会被创建,且两个物体会被连接到一起。然后每次父级物体被修改时,子集也会被告知。这些连接的类型最终创建了 Clarisse 的从属图表。很多 2D 和 3D 软件都禁止通过 imposing directed acyclic graphs(DAG,有向无环图)来创建循环从属关系,你将会在后面看到,Clarisse 在某些状况下允许循环从属关系的存在(译:应该可以做无限分型用,记得 Katana 的文档里也介绍过这个)。
什么是模块?
模块是类实现的接口,大部分情况下,直接在模块里声明。一个类的实现,定义了一个类在属性发生变化时,应该做什么。例如,OfClass|SceneItem 类的实现,可以知道在它的 Translate 属性被修改后,它需要重新构建变换矩阵。
内置模块的内置接口可以在模块库里找到。按照惯例,模块的接口是以 Module 作为前缀。例如 OfClass|SceneItem 的接口叫做 ModuleSceneItem 。这种命名规则适用于 Clarisse 里所有的模块,不过也不是强制的。
模块一般会在动态库里定义,同时包含了类的定义和实现。Clarisse 在启动的时候回去搜索可用的模块。当 Clarisse 启动时,它会扫描特定的模块路径(通过 -module_path 命令行),然后注册所有可用的模块。
注意
多个模块可能会在同一个动态库里声明。模块必须在 C++ 里实现。
最终模块定义了它们的方法,用于访问对象的内部数据。例如, ModuleSceneItem::get_global_matrix_at 用于在某个特定时间接收对象的全局变换矩阵。为了访问某个对象的模块,使用 OfObject:get_module。例如,在 Python 里:
# Returns the name of the ModuleObject class of the first item in the selection
if (ix.selection.get_count() > 0): print ix.selection[0].get_module().get_class_info_name()
有关 Clarisse 软件的基本介绍完毕,大致可以弄清楚 Clarisse 整体架构。接下来是我自己的记录
说明
这个文章主要是为了解决手头项目记录的,所以暂时不会介绍所有。
Python API 简介
Clarisse 里针对 Python 的接口应该是通过 swig 生成的,有关 swig的内容大伙儿自己百度。接口主要为 ix 模块。
ix 有两个子模块:
- api
- cmds
一个子类:
- ApplicationSelection
两个成员:
- application (gui.ClarisseApp)
- selection (ix.ApplicationSelection 实例)
以及一系列的成员函数:
add_attribute(obj, attr_name, attr_type, category='General')
begin_command_batch(batch_name)
check_need_save()
create_context(path)
create_generic_object(object_name)
create_object(item_name, class_name, destination_context=None)
delete_item(item)
disable_command_history()
disable_echo_command()
enable_command_history()
enable_echo_command()
end_command_batch()
export_context_as_project(context, filename)
export_context_as_project_with_dependencies(context, filename)
export_geometries(filename, geometry_objects)
export_geometry(filename, geometry_object)
export_render_archive(filename)
get_current_context()
get_current_frame()
get_item(item_name)
import_geometries(filenames)
import_geometry(filename)
import_image(filename)
import_images(filenames)
import_map_file(filename, class_name, suffix)
import_map_files(filenames, class_name, suffix)
import_project(filename)
import_scene(filename)
import_volume(filename)
import_volumes(filenames)
inspect(item)
is_context_exists(context_name)
is_gui_application()
is_interactive_application()
is_process_application()
item_exists(item_name)
load_project(filename)
log_error(message)
log_info(message)
log_warning(message)
make_absolute_of_path(path)
reference_export_context(context, filename)
reference_file(working_context, filename)
reference_files(working_context, filenames)
reference_make_local(context)
render_image(image)
save_bmp(image, filename, lut_name='')
save_exr16(image, filename, lut_name='', compression=1)
save_exr32(image, filename, lut_name='', compression=1)
save_image(image, filename, format, lut_name='', compression=1)
save_jpg(image, filename, lut_name='')
save_png16(image, filename, lut_name='')
save_png8(image, filename, lut_name='')
save_project(filename)
save_tga(image, filename, lut_name='')
save_tif16(image, filename, lut_name='')
save_tif32(image, filename, lut_name='')
save_tif8(image, filename, lut_name='')
set_current_context(context_path)
set_current_frame(frame_number)
application 成员
application 是 gui.ClarisseApp 的实例,不太清楚为什么属于 gui,按照帮助显示,应该是一个综合性的接口。大致介绍下它所继承的父级,方便查找文档:
- ClarisseApp
- GuiApp
- framework.AppObject
- framework.OfApp
- framework.AppBase
- base.EventObject
- base.CoreCustomData
- base.CoreBaseObject
- base.CoreBaseType
- __builtin__.object
所以我们在查找这个接口的成员函数时,可以查看 sdk 中有关 ClarisseApp 类的内容即可。
selection 成员
selection 成员,顾名思义,应该跟选择有关的。
方法有:
__getitem__(self, index)
add(self, item)
deselect_all(self)
get(self, index)
get_contexts(self)
get_count(self)
get_objects(self)
is_empty(self)
select(self, item)
select_all(self)
selection 是针对 Python 写的成员,C++ 还有个 AppSelection 类,也是针对选择的,可以查看文档了解。
实际使用:
代码风格
从上面的成员与成员函数的编写方式可以知道,Clarisse 的编写风格是以下划线作为单词的分割符号,全部小写,可以根据这个来方便记忆。
获取选择的内容
ix.selection
ix 的成员,可以直接下标访问内容,但不支持直接 for in 迭代ix.application.get_selection()
继承自AppObject的成员函数,返回 AppSelection 类
遍历
可能是 swig 自动生成的原因,Clarisse 所有列表形式的数据都无法像 python 里那样用 for in 来迭代。只能通过列表下标的形式进行访问,如遍历选择的内容:
ix.selection 的访问方式:
for i in range(ix.selection.get_count()): print(ix.selection[i].get_full_name())
AppSelection 的访问方式:
ss = ix.applcation.get_selection() items = ss.get_items() # 返回一个可以下标访问的 CoreVector 类 for i in range(ss.get_count()): print(items[i].get_full_name())
OfContext 类型
上面提到过 Clarisse 的整个工程是由 OfContext 与 各种类型(OfClass) 的对象组合起来的,OfContext 在 Clarisse 里表现为层级中的文件夹。所以上面获取选择内容的时候,如果你选择了某个目录,获取的结果就是一个OfContext 类型。
获取 OfContext 下所有的几何体
在 3.6 版本里,几何体的类型有以下几种:
- GeometryAbcMesh
alembic 参考进来后创建了一个 OfContext,里面放的 shape 就是这个类型的 - SceneObjectCombiner
相当于把其他几何体打了个组再参考过来 - GeometryPolyfile
Obj 文件参考进来就是这种类型 - 其他内置类型,这里不介绍了。
注意
5.0 版本的 Clarisse 里变了不少,不过对于写程序来说就是多加集中类型进去。
接下来是从选择的内容里选出我们想要的东西:
获取子对象并存放在到 OfObjectVector 里
objs = ix.api.OfObjectVector() # 创建一个Vector来存放对象,这点跟 Python 有点不一样,需要先创建后存放 for i in range(ix.selection.get_count()): ix.application.get_match_object()
- 对 ObjectVector 进行过滤
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。