2012-05-31 08:26:19| 分类: RealFlow探索 | 标签: |举报 |字号大中小 订阅
现在你已经学会了RealFlow中Python脚本基础知识。你可以遍历发射器所有粒子,也明白了向量和else-if语句。接下来的这节,是关于获取(Get)和设置(Set)RealFlow属性值的。这是RealFlow拓展的Python核心内容。通过get和set语句,你能直接访问发射器,模型,辅助器等等所有的参数。
各种get和set语句似乎是无穷无尽的。所以要精炼地讲解这些。本节,我们讲解大部分通用的get和set指令。RealFlow也提供了命令用来Get&Set点(vertices),几何面(polygon face),动画帧(animation keys),图片像素(image pixel)等等更多东西。
你已经学习过的一些语句:
getEmitter( )
getPosition( )
getFirstParticle( )
getNextParticle( )
这些指令曾用来获得场景中发射器,访问单独粒子或得到粒子位置数据。用不同的get指令,得到不同的值和结果,例如,Integers,Vectors,Floats 和String.
在线帮助手册,有很多不同的get和set指令。这些指令都写成一种特定的形式,像:
Emitter getEmitter( string )
Object getObject( string )
第一个单词描述了接受对像类型,像emitter,object,camera,mesh等等。第二部分是特殊的get指令,能用于很多参数和RealFlow对像。
下一个单词决定了数据类型,你必须使用这个特定的指令。
数据类型指定了,什么样参数可以使用:
Particle getParticle( int )
这个操作被用来取粒子的值,是一个Integer。添加一个辅助器,string型,如用Gravity01,会导致出错。
有时手册给出更多提示:
Emitter [] getEmitters( )
方括号[ ],意思是数据类型是一个List。你还记得吗?标量只能存储一个值,而List可以携带两个或更多的值。
所有可用的命令只有一种工作方法,像例子中讲的。一条表达式,你能预测对像和数据类型,如果你不确定,就参考一下手册:
? Help > Contents... (opens a new application) > Index
? Help > Contents... > Contents > Scripting Reference
第二条检索方法更有条理,是按对像类型排列。
(上面格式一定要记牢,这是进入帮助学习的钥匙。大部分时候我们写脚本,要不断的参考帮助,但如何看懂,就是这里说的)
get 和set语句原理很简单,但非常好用,因为可以直接联系到RealFlow的用户界面。这两个命令完全共享同样的名称,你可以在节点(Nodes)和节点参数(Node Param)窗口看到。主要区别是get和set命令需要的参数(number)和数据类型参数不同。这命令不仅可用于遍历粒子,它们可以用在任何地方。
在这个例子里,Cone(圆锥体)已经添加到场景。参数列表包含了所有你需要使用的对像参数。最有趣的参数都在Node 标签下。在这你能找到最基本属性,像位移(position),Rotation(旋转)或Scale(缩放).在线帮助文档介绍的相应的get参数如下:
any getParameter( any )
告诉我们getParameter命令可以使用任何对像或任何数据类型。操作不限于参数,发射器,或摄像机。数据类型也可以是Integer,Float,Vector等。
你的数据类型可以使用getParameter命令,从Node标签下读出。当你看到三元值时,一定就是矢量:
Position 0.0 0.0 0.0
Rotation 0.0 0.0 0.0
Scale 1.0 1.0 1.0
Pivot 0.0 0.0 0.0
请注意:单个的矢量值(分量)是Float类型。甚至颜色也是用三个值,红,绿,蓝(RGB).Simulation和Dynamice数据类型是String(字符型).字段可以包含字句或表达式:
Simulation Active
Dynamics No
如果WetDry贴图设置为Yes,你可Integer值,像
@ resolution 256
@filter强度和@ageing值也是Float类型。
如前面所说,你要知道,get命令会输出什么数据类型,因为是setParameter的逆过程。你用确切的数据类型去改变合适的值。让我们看一个例子:
cone = scene.getObject("Cone01")
pos_global = cone.getParameter("Position")
结果是一个矢量,由三个Float组成。当计算完成后,你可以建立一个新的矢量。新的向量必须也要有三个Float(integers也是可以的)。你不能用String去替换。不然会产生语法错误:
pos_new = Vector.new("left", "up", "right")
下一个例子是一个完整的脚本。在RealFlow里准备一下,脚本作用是给圆锥y轴每帧位移增加0.2:
Events Script: ChangeConeYPos.rfs
def onSimulationFrame():
cone = scene.getObject("Cone01")
# 1. Get global position data in Vector format
pos_g = cone.getParameter("Position")
# 2. Get components from the position Vector
pos_x = pos_g.getX()
pos_y = pos_g.getY()
pos_z = pos_g.getZ()
# 3. Add 0.2 to the y component
new_pos_y = pos_y + 0.2
# 4. Build a new Vector
new_pos_g = Vector.new(pos_x, new_pos_y, pos_z)
# 5. Use the new Vector for position change
cone.setParameter("Position", new_pos_g)
这个脚本与第三节的事件脚本非常相似。那个脚本增加了y和z方向 所有粒子位移。这里,我们只是针对单个对像。get语句过程的,分离和组合是和向量一样的。主要不同是最后一个语句:
cone.setParameter("Position", new_pos_g)
对于粒子,是这样:
particle.setPosition(new_pos_g)
注意粒子与别的对像是使用不一样的set和get命令。对emitter或mesh,是可以直接在对应Node参数窗口看到值的。Position或Velocity值不是都显示出来的。他们在内部使用。为了更快的访问速度和更好的区别,粒子属性不使用Parameter关键字。
还有另外一个不同点:getParameter命令只能取得一个参数,名称是“position".setParameter显示了”position"和new_pos_g.
因此用setParameter你必须要告诉RealFlow你想用哪种类型的数据改变(Position,pivot,roughness),然后新的值要用正确的数据类型。
在模拟时,可以在节点参数窗口监视更改的值。只是试一试y分量的position值。
用getParameter和setParameter能获得很多乐趣,因为你能更改,改变,打开关闭几乎所有东西。一些任务可以手动方便的解决,但脚本更精确一点。特别是碰撞,有时不能轻松的得到完全相同的碰撞。在两个模型,或粒子之间。使用Python脚本,解决起来只要短短几行代码。这是初始情况:
我们目的是让物体落下,碰撞到地板。所有物体都处于不同的位置。手动停止他们,可能很难。这个场景仅有三个模型,但假如有 20,30,或60个模型呢。根据你现在为止看到的方法,我们一直调用单个对像,如果有更多的对像时,就变成一个麻烦的事。为了使这个任务轻松,Realflow 有强大的对像选择工具。操作是,我将要用这个命令,名称是:
getSelectedNodes()
使用这个命令,你可以在节点窗口里选择到你想起作用的对像。
RealFlow分辨出你选中的,再把所有对像存储到List对像中。正确的表达式是:
items = scene.getSelectedNodes()
相像一下:选中三个对像,圆锥体(Cone),方盒(Cube),圆柱体(Cylinder),内部元素就会是:
items = [Cone,Cube,Cylinder]
下一步就是搜集所有正在碰撞到地面的对像项目。RealFlow也有能达到这个目的的函数。用这个命令要再创建一个List数据类型,因为有不只一个对像碰撞到地面:
floor = scene.getObject("Floor")
colliding_objects = floor.getCollidingObjects()
现在,我们要通过列表浏览碰撞对像并适当停用掉原始物体
if (colliding_objects != [ ]):
for object in colliding_objects:
object.setParameter("Simulation","Inactive")
用if条件语句检查,List colliding_objects 是否空的。表达式是
if (colliding_objects != [ ]):
意思是:如果 列表 colliding_objects 不等于空的,不执行下下面。空的列表写作[ ].你一种方法是检查列表长度(len)。
如果碰撞物体检测到就把存储到colliding_objects.并把Simulation参数设置为Inactive(不活动)。换句话说:当物体一完全碰撞到地面,就会被设置为不活动,并释放封闭的流体。下一页你将看到这个脚本的列表。
Events Script: InactiveOnCollision.rfs
def onSimulationBegin():
items = scene.getSelectedNodes()
for object in items:
object.setParameter("Simulation", "Active")
object.setParameter("Dynamics", "Rigid body")
def onSimulationStep():
items = scene.getSelectedNodes()
floor = scene.getObject("Floor")
colliding_objects = floor.getCollidingObjects()
if ( colliding_objects != [] ):
for object in colliding_objects:
state = object.getParameter("Simulation")
if (state == "Inactive"):
pass
else:
object.setParameter("Simulation", "Inactive")
object.setParameter("Dynamics", "No")
第一步分是初使化选中的项目并设置为活动。这在初使有多个模拟传递的情况下很有用,因为你不用每次模拟这个场景都要切换到之前值(初使值)。
程序能识别所有与地面进行碰撞的物体,并把它们存储到List变量中:
colliding_objects = floor.getCollidingObjects()
最后的if语句只是做很小的(建设)检查,Simulation状态是否改变了。所以如果Simulation状态设置成Inactive,脚本什么也不做。
图14.碰撞后溶解(散开)
唯一的不足是,物体要与地面进行完全碰撞(后才能散开),之前他们都是失活(inactivate)状态。
第一次接触后就保持活动状态。一个繁琐的方法是利用模型的面或点进行检查。你可以把碰撞的面加入到一个列表。如果列表的长度大于0,物体就变为活动。
然后检查每个步骤或帧的对像状态是否活动,优化脚本。
现在为止,你学习到很多关于物体get和set属性知识。正如我之前提过的,粒子有一个属于自己的类(class),并提供直接访问特殊的值和特征。当你想使用粒子时,你必须要创建一个循环函数并要单独调用它们。为了这一目的,需要区分不同发射器和粒子基本属性。发射器属性是直接显示在结点参数窗口里的:
同时通过遍历粒子,可以访问各种属性。最常用的值可能是这两个命令:
getVelocity( )
getPosition( )
直接执行计算后,通过创建一个新的Vector是可以改变速度(Velocity)和位置(Position)。另一个值对改变参数是不重要的,但可以控制结构。例如Density或Pressure.基于流体的物理参数,你可以创建泡沫。这是非常有用的方法制作真实的水花。这里有几个很重要也经常使用的指令:
getMass( )
getDensity( )
getPressure( )
getNormal( )
getNeighbors( )
这些属性不出现在RealFlow窗口,因为他们仅仅用作内部计算。在这告诉你怎么使用他们:
emitter = scene.getEmitter("emitter_name_here")
particle = emitter.getFirstParticle()
while (particle):
particle.getVelocity()
particle.getPosition()
particle.getMass()
particle.getPressure()
...
particle = particle.getNextParticle()
在很多情况下重要的是获得(合适的值)。是大是小,是Foat还是Integer? 特别是使用if语句,知道(magnitude)大小是很重要的.RealFlow有可以输出任何各类值的函数:
scene.message(str(variable_name))
这条语句直接给出提示信息在RealFlow信息窗口,但在有些情况你可能得到像这样的结果:
RealFlow vector at <0xdee2>
这结果像前面的结果,是显示当你从List,Vector或Dictionary输出的值。只要有一个以上的值存储在变量中,RealFlow就会显示成十六进制的值(hexadecimal).只有这些复合变量的分量才能输出成可读的:
vec_z = vector.getZ()
entry = list[2]
scene.message(str(vec_z))
scene.message(str(entry))
str命令是Python中把值转换为字符串的功能。只有这种数据类型可以显示在信息窗口。当然str也能改变多个变量。只要是用户定义的字符串,就要写在引号里:
vec_z = 3.5
entry = Cube04
scene.message(str("Values: "+vec_z+" / "+entry))
Result: Values: 3.5 / Cube04
有了这个小助手,你可以用来估计值,判断或限制力的值。值得注意的是,场景输出信息时会显著变慢。
一个好的小程序是对发射器的大量基础函数(使用)。
下面的脚本只是读出每个粒子的质量值,并将它们添加进去。如果总重量大于给出的阈值,发射器就停止发射粒子。当然这个脚本也能基于Pressure或Density.
正如你看到的脚本不必很复杂,但是有效。即使最简单的手段只要达到好的结果。用辅助器来做将很难达到,像这。
def onSimulationStep():
total_mass = 0
mass_threshold = 1000
emitter = scene.getEmitter("Circle01")
speed = emitter.getParameter("Speed")
particle = emitter.getFirstParticle()
while (particle):
mass = particle.getMass()
total_mass = total_mass + mass
if (total_mass >= mass_threshold):
emitter.setParameter("Speed", 0.0)
particle = particle.getNextParticle()
这脚本再一次说明了最基本的道理,例如收集场景和粒子数据,或创建循环结构。还用了if条件语句触发粒子事件。如果total_mass变量达到1000(mass_threshold),发射器速度(speed)就设置为0.0. 也就是说:阻止了新粒子的产生。这个脚本的好处是粒子的质量(mass)取决于它的密度(density)。在节点参数窗口设置Density到 100.0 ,大约会产生10倍的超过标准密度1000的粒子:
第一步是创建辅助器脚本(Scripted Daemon):
Edit > Add > Daemons > Scripted
> Scripted
你可以用这个默认的名称Scripted01或者重新设置一个新名称。在这个例子里我决定重命名为Heightdaemon.
def applyForceToEmitter(emitter):
accel = -9.81
factor = 7.0
emitter = scene.getEmitter("Square01")
particle = emitter.getFirstParticle()
while (particle):
pos_g = particle.getPosition()
pos_y = pos_g.getY()
gravity = accel / ((pos_y * factor) + 0.001)
force = Vector.new(0.0, gravity, 0.0)
particle.setExternalForce(force)
particle = particle.getNextParticle()
OK,让我们遍历这个脚本。第一部分是初始代必须的变量,accel是重力加速度(acceleration of gravity),factor是用来防止粒子减慢太快。factor值越小,最终的力就越强。while循环遍历Square01发射器的粒子并永久检查高度。
在循环中,脚本计算高度依赖重力:
gravity = accel / ((pos_y * factor) + 0.001)
在这个公式中有所有初始的变量。如你所看到的,脚本要计算除法,你得注意到不能让被除数为0.可能会使粒子直接离开发射器。避免0做被除数,我简单的加了一个很小的值0.001.由于计算法则,括号不是必须要的,但有时候它能提高可读性。表达式:
((pos_y * factor) + 0.01)
也能写作
(pos_y * factor + 0.001)
因为乘(*)和除(/)有较高的优先级。下一步是创建一个新的矢量表示力,在y轴负方向。用辅助器脚本(Scripted Daemon)特别的指令 setExternalforce(force),新的力就会被添加。
总结
RealFlow的Python脚本的可能性几乎是无止境的。例子,技术,或方法,可以写10本或更多,在这里我只是给脚本做了一个简单入门介绍。以下案例什么都可能涉及到。他们范围是从初级到中级,也会包含到高级脚本。也就是说本节,是最后纯技术的一节,后面都是案例了。所以本节,一定要反复阅读。本人读了不下20次。可能我比较笨。本节还有一个最大知识点就是教你如何用Realflow帮助,这一定要重视,是非常重要的。
评论