Unigine::Skinner(皮肤处理)
Skinner是一种用于动画的可视化脚本系统。它允许将不同的动画混合在一起创造出具皮肤特征的复杂运动。当然有可能将具有物理特性的布偶囊括到用来播放的动画中(针对整个特性或每个骨状物品)。
Skinner基于Unigine::Schemer。 Skinner的脚本可在data/core/systems/skinner文件夹中找到。
预先定义的Skinnerblocks(块)在默认情况下可用,包含在data/core/systems/skinner/blocks/skinner.blocks文件中。
How To Use Skinner Graph(使用Skinner图的方式)
Skinner基于Unigine::Schemer得以建立。它与Schemer使用相同的实体(参看Schemer的 综述)并共享 blocks(块)。这就意味着,您可以添加任意的Schemer块以供在Skinner图中使用。
- Skinner图的创建方式与创建Schemer的方式相同(参看怎样引导)。
- Skinner控制的skinned网格在脚本中进行说明(参看下方详情)。
- 默认情况下,Skinner图应当以命名为 update的schemer.entry开始(参看 下方详情)。
- 可以为播放单一的动画事件创建进入点。进入点可被用来创建瞬时动作,例如钻孔或者拾捡物体的动作,而不是循环播放动画。(参看 下方详情)。
- 如Schemer的节点一样,所有图形中的Skinner节点通过拖拽路径到input而得到触发。在完成其各自的工作之后,在有下一个节点的情况下,使用一个output(输出)路径来触发下一个节点 。
要开始操作,按照下列步骤来创建一副简单的将动画设置到网格上的图:
- 创建命名为 update的 schemer.entry作为图像的起始点。
- 增加将要加载动画的skinner.animation节点。通过拖拽节点将schemer.entry输出同 skinner.animation的input(输入) 联系起来。这样做,可以表明图像运行之后将执行的节点内容。
- 增加schemer.file节点,此节点指定蒙皮动画文件的文件名。将其与skinner.animation节点的 name锚连接起来。
- 增加播放动画的skinner.set节点。(即将动画设置到网格上)。此节点需要包含骨转换数据的动画缓冲。将skinner.animation缓冲连接到skinner.set 缓冲。 将skinner.animation输出连接到skinner.set输入,以此指示下一个将被执行的节点。
- Skinner图创建或更改之后, 点击 Run按钮。图像外会生成遵从运行的脚本。
- 为了使脚本得以加载,在稍后得到编辑并在运行时得到执行,您可以通过Save(保存)键将创建的脚本存为*.script文件。
下方为此简单的Skinner图的模样。节点将按规定的顺序执行。
Available Skinner Blocks(可用Skinner块)
下列是从data/core/systems/skinner/blocks/skinner.blocks文件中加载的预定义Skinner块。
skinner.add | 将两部动画进行求和,例如将两部摆臂动画结合在一起。当一些骨头同时在两部动画中使用时,就需要使用此功能。倘若是单独的动画,使用skinner.combine。此节点会与两部动画的缓冲相乘。
|
---|---|
skinner.animation | 加载一个动画文件并将动画数据设置到缓冲中。在此之后,可在网格上对其进行设置或者作为针对combining(结合)动画作为输入缓冲器使用等。
|
skinner.animation2 | 加载两个动画文件将动画进行混合。最终的插值动画数据会在输出动画缓冲中得到设置。在此之后,此数据可设置到网格中或者作为用于combining(结合)动画的输入缓冲器使用等 。
|
skinner.animation3 | 加载三个动画文件将动画进行混合。最终的插值动画数据会在输出动画缓冲中得到设置。在此之后,此数据可设置到网格中或者作为用于combining(结合)动画的输入缓冲器使用等 。
|
skinner.blend | 将两帧动画混合在一起,每一帧动画的比例有各自的要求。最终的混合动画数据被设为输出动画缓冲(例如,在此之后 设置到网格上)。 混合物应当与两个skinner节点相连,这些节点将数据设置为混合物的两个动画缓冲(例如,skinner.animation节点)。第一个循环动画存在于out 0,进入另一个skinner节点并从此节点在in 0上返回。来自Skinner节点的缓冲进入混合物的buf 0 。第二个循环动画除使用了out 1, in 1 和 buf 1 外,其它都相同。在使用动画数据获得两个缓冲后,合成器会将两个缓冲合并成一个,最终以缓冲的形式输出。 用于每个动画混合的插值权重被加在一起。
|
skinner.blend3 | 将三帧动画混合在一起,每一帧动画的比例有各自的要求。最终的混合动画数据被设为输出动画缓冲(例如,在此之后设置到网格上)。 混合物应当与三个skinner节点相连,这些节点将数据设置为混合物的三个动画缓冲(例如,skinner.animation 节点)。第一个循环动画存在于out 0,进入另一个skinner节点并从此节点在in 0上返回。来自Skinner节点的缓冲进入混合物的buf 0 。其它两个循环动画除使用了out 1 和out 2 (和其它锚)外,其它都相同。 在使用动画数据获得三个缓冲后,合成器会将三个缓冲合并成一个,最终以 缓冲的形式输出。 用于每个动画混合的插值权重被加在一起。
|
skinner.blend4 | 将四帧动画混合在一起,每一帧动画的比例有各自的要求。最终的混合动画数据被设为输出动画缓冲(例如,在此之后 设置 到网格上)。 混合物应当与四个skinner节点相连,这些节点将数据设置为混合物的四个动画缓冲(例如,skinner.animation节点)。 第一个循环动画存在于out 0,进入另一个skinner节点并从此节点在in 0上返回。来自Skinner节点的缓冲进入混合物的 buf 0 。其它三个循环动画除使用了 out 1, out 2 和 out 3 (和其它锚)外,其它都相同。 在使用动画数据获得四个缓冲后,合成器会将四个缓冲合并成一个,最终以缓冲的形式输出。 用于每个动画混合的插值权重被加在一起。
|
skinner.blend5 | 将五帧动画混合在一起,每一帧动画的比例有各自的要求。最终的混合动画数据被设为输出动画缓冲(例如,在此之后 设置 到网格上)。 混合物应当与五个skinner节点相连,这些节点将数据设置为混合物的五个动画缓冲(例如,skinner.animation 节点)。 第一个循环动画存在于out 0,进入另一个skinner节点并从此节点在in 0上返回。来自Skinner节点的缓冲进入混合物的 buf 0 。其它四个循环动画除使用了 out 1,out 2 和 out 3 (和其它锚)外,其它都相同。 在使用动画数据获得五个缓冲后,合成器会将五个缓冲合并成一个,最终以缓冲的形式输出。 用于每个动画混合的插值权重被加在一起。
|
skinner.bone | 为骨设置一个名称。在设置一个骨之后,其可用于 ragdoll(布偶猫)动画或者用于定制转换。
|
skinner.combine | 将两帧单独的动画结合在一起,例如将arms动画同legs动画结合在一起。当两部动画中无同时使用的骨时,使用此功能。否则使用skinner.add。 此合成器应与两个skinner节点相连接,这两个节点将数据设置为合成器的两个动画缓冲。(例如, skinner.animation节点)。第一个循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到 in 0上。来自此节点的缓冲进入合成器的buf 0。第二个循环动画除使用out 1, in 1 和buf 1之外,其它部分与第一个循环动画相同。在使用动画数据得到两个缓冲之后,融合器会将此两个缓冲融合成一个,并将其作为最终缓冲 输出,此缓冲可被设置到网格上。
|
skinner.combine3 | 将三帧单独的动画结合在一起,例如将arms动画,legs动画和torso动画结合在一起。当三部动画中无同时使用的骨时,使用此功能。否则使用 skinner.add。 此合成器应与三个skinner节点相连接,这三个节点将数据设置为合成器的两个动画缓冲。(例如, skinner.animation节点)。第一个循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0上。 来自此节点的缓冲进入合成器的buf 0。第二个与第三个循环动画除使用out 1 和 out 2 (以及其它的锚)之外,其它的都相同。在使用动画数据得到三个缓冲之后,融合器会将此三个缓冲融合成一个,并将其作为最终 缓冲 输出,此缓冲可被 设置 到网格上。
|
skinner.combine4 | 将四帧单独的动画结合在一起,例如将arms动画,legs动画,torso动画和head动画结合在一起。当四部动画中无同时使用的骨时,使用此功能。否则使用 skinner.add。 此合成器应与四个skinner节点相连接,这四个节点将数据设置为合成器的两个动画缓冲。(例如, skinner.animation 节点)。 第一个循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0上。来自此节点的缓冲进入合成器的buf 0 。其它的动画除使用out 1,out 2 和 out 3 (以及其它的锚)之外,都相同。在使用动画数据得到四个缓冲之后,融合器会将此四个缓冲融合成一个,并将其作为最终缓冲 输出,此缓冲可被 设置到网格上。
|
skinner.combine5 | 将五帧单独的动画结合在一起。当动画中无同时使用的骨时,使用此功能。否则使用 skinner.add。 此合成器应与五个skinner节点相连接,这五个节点将数据设置为合成器的两个动画缓冲。(例如, skinner.animation 节点)。 第一个循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0上。来自此节点的缓冲进入合成器的buf 0 。其它的动画除使用 out 1, out 2,out 3 和out 4 (以及其它的锚)之外,都相同。 在使用动画数据得到五个缓冲之后,融合器会将此五个缓冲融合成一个,并将其作为最终 缓冲输出,此缓冲可被设置到网格上。
|
skinner.difference | 计算加载动画参考系与此动画的其它框架之间的差别。它计算用于相对播放动画的每个框架在骨转换时的差别,例如绑定姿势(0框架)。在此之后,此数据会被输出到缓冲并被负载到另一帧动画上,例如,将breathing动画(通过差异性进行计算)与其它含有torso运动的动画结合起来。
|
skinner.get | 在当前播放的skinned动画外创建缓冲。它被用来在skinned动画与ragdoll动画之间进行混合。此节点应当与将动画设置在网格上的节点或者基于框架的节点或者ragdoll节点进行连接。通常,其通过大量的节点得到连接,这些节点在分枝图的末端将动画缓冲设置成skinner.set。
|
skinner.inverse | 使动画发生反转。此节点应与另一个skinner节点相连接,此skinner节点将数据设置为动画缓冲(例如, skinner.animation 节点)。循环动画存在于右方的output上,进入另一个skinner节点并从此节点返回到右方的input。来自某个skinner节点的缓冲进入到右方的缓冲。 此版块根据下列公式进行工作:
|
skinner.lerp | 在两部动画间按照所需比例进行线性插值。 此节点应当与两个skinner节点相连接,这两个节点将数据设置为动画缓冲(例如,skinner.animation节点)。第一部循环动画存在于out 0上,进入另一个skinner节点并从此节点返回到in 0上。来自Skinner节点的缓冲进入插值器的buf 0。第二部循环动画除使用out 1, in 1 和 buf 1外,其它都相同。在使用动画数据获得两个缓冲后,插值器会将两个缓冲篡改成一个,作为最终缓冲输出。
|
skinner.lerp3 | 在三部动画间按照所需比例进行线性插值。 此节点应当与三个skinner节点相连接,这三个节点将数据设置为动画缓冲(例如,skinner.animation节点)。第一部循环动画存在于out 0上,进入另一个skinner节点并从此节点返回到in 0上。来自Skinner节点的缓冲进入插值器的buf 0。其它循环动画除使用out 1和 out 2 (和其它锚) 外,其它都相同。 在使用动画数据获得三个缓冲后,插值器会将三个缓冲篡改成一个,作为最终缓冲输出。
|
skinner.lookat | 考虑到头顶方向的控制。例如,其可被用来控制3D空间内字符在什么情况下显示。 此节点应当和一个skinner节点相连接,此skinner节点将数据设置为动画缓冲(例如,skinner.animation 节点)。循环动画存在于out 0上,进入另一个skinner节点并从此节点返回到in 0上。来自Skinner节点的缓冲进入Lookat节点的buf 0 。
|
skinner.ragdoll | 启用用于skinned网格的物理性驱动ragdoll动画("death")。可针对整个网格(如果无具体指定的骨)或者针对一个单独的骨对其进行设置。
|
skinner.rotation | 沿着指定轴通过所给的角度对骨进行旋转。 此节点应与一个skinner节点相连接,此skinner节点会将数据设置成动画缓冲(例如, skinner.animation 节点)。 循环动画存在于右方的output之上,进入另一个skinner节点,并从此节点返回到右方的input。 来自Skinner节点的缓冲进入Lookat节点右方的缓冲。
|
skinner.sequence | 加载动画,并将动画设置为动画缓冲。当节点通过play(播放) 输入被触发时,此动画仅能播放一次。当动画回放快结束时,定序器会触发下一个通过stop输出而连接的节点。在此之后,动画会一直播放直至结束。为保证定序器进行合适的工作,stop 输出应当时刻返回到右边的输入(如有必要,可在传递给其它节点之后进行)。
|
skinner.set | 将动画设置到网格上进行播放。此节点可与另一个从文件中加载动画且将数据设置为动画缓冲的节点相连接(例如,skinner.animation 节点)。 循环动画存在于右方的 output之上,进入另一个skinner节点,并从此节点返回到右方的input。来自Skinner节点的动画数据进入到缓冲中。
|
skinner.smooth | 允许根据当前帧速率作出的值的平顺改变。在计算动画参数时,使用此块来避免不必要的尖峰电压。使用其计算参数值(三角点)方面的变化并通过 "每秒values(数值)"对其进行限制。
|
skinner.sub | 减去来自第一个skinner的第二幅动画。 此减法单元应当与两个skinner节点相连接,这两个节点将数据设置为两个动画缓冲(例如,skinner.animation 节点)。 第一个循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自Skinner节点的缓冲进入混合器的buf 0。第二帧循环动画除使用out 1,in 1和buf 1 外其它都一样。 在使用动画数据得到两个缓冲之后,减法单元会减去来自第二个缓冲的第一个缓冲并将最终结果输出为缓冲。
|
skinner.switch | 允许依靠触发值在两部不同的动画间进行转换。如果触发值等于0,将播放第一帧动画。如果触发值等于1,会选择第二帧动画。当触发器表明应当完成动画状态间的转换时,对动画的平滑插值会进行。使用所给速度可融合成新的动画状态。可使用转换器,例如在基于文件的动画和物理驱动的ragdoll之间进行转换。 转换器应该与两个skinner节点相连接,这两个节点将数据设置为两部动画缓冲(例如,skinner.animation节点)。第一部循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自Skinner节点的缓冲进入转换器的buf 0。第二部循环动画除使用out 1,in 1和buf 1 外其它都一样。在使用动画数据获得两个缓冲后,转换器会选择将被输出的动画或者以必要的比例将动画混合在一起将结果输出到缓冲中。
|
skinner.switch3 | 允许依靠触发值在三部不同的动画间进行转换。如果触发值等于0,将播放第一帧动画。如果触发值等于1,会选择第二帧动画。如果触发值等于2,将播放第三帧动画。当触发器表明应当完成动画状态间的转换时,对动画的平滑插值会进行。使用所给速度可融合成新的动画状态。 转换器应该与三个skinner节点相连接,这三个节点将数据设置为三部动画缓冲(例如,skinner.animation 节点)。第一部循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自Skinner节点的缓冲进入转换器的buf 0。其它循环动画除使用out 1 和 out 2 (和其它锚)外其它都一样。在使用动画数据获得三个缓冲后,转换器会选择将被输出的动画或者以必要的比例将动画混合在一起将结果输出到缓冲中。
|
skinner.switch4 | 允许依靠触发值在四部不同的动画间进行转换。如果触发值等于0,将播放第一帧动画。如果触发值等于1,会选择第二帧动画等。当触发器表明应当完成动画状态间的转换时,对动画的平滑插值会进行。使用所给速度可融合成新的动画状态。 转换器应该与四个skinner节点相连接,这四个节点将数据设置为四部动画缓冲(例如, skinner.animation节点)。第一部循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自Skinner节点的缓冲进入转换器的buf 0。其它循环动画除使用out 1, out 2和out 3(和其它锚)外其它都一样。在使用动画数据获得四个缓冲后,转换器会选择将被输出的动画或者以必要的比例将动画混合在一起将结果输出到缓冲中。
|
skinner.switch5 | 允许依靠触发值在五部不同的动画间进行转换。如果触发值等于0,将播放第一帧动画。如果触发值等于1,会选择第二帧动画等。当触发器表明应当完成动画状态间的转换时,对动画的平滑插值会进行。使用所给速度可融合成新的动画状态。 转换器应该与五个skinner节点相连接,这五个节点将数据设置为五部动画缓冲(例如,skinner.animation 节点)。第一部循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自Skinner节点的缓冲进入转换器的buf 0。其它循环动画除使用out 1, out 2, out 3 和 out 4(和其它锚)外其它都一样。在使用动画数据获得五个缓冲后,转换器会选择将被输出的动画或者以必要的比例将动画混合在一起将结果输出到缓冲中。
|
skinner.transform | 允许将一个定制的转变设置为骨,例如,对其进行旋转,使用转换的节点对整个动画进行输出。作为输入值,此节点将接收到一个矩阵。 转换器应该与一个skinner节点相连,此节点将数据设置为转换器的一个动画缓冲。(例如,skinner.animation节点)。循环动画存在于out 0之上,进入另一个skinner节点,并从此节点返回到in 0。来自动画的缓冲进入转换器的buf 0。在此之后,其可对具体的骨进行转换,将剩下的动画数据作为改进后的缓冲对结果进行输出。
|
How to Run Skinner Script(怎样运行Skinner脚本)
要初始化Skinner(此处无视觉编辑器),您需要添加如下面所举示例中的代码。首先,指定一个由Skinned控制的网格。之后,同running a Schemer(运行Schmer)相类似,您需要调用update()的功能。然而,其必须从来自世界坐标脚本内的update()才能得到调用并且此功能需要得到用来播放动画的相反FPS的通过。更新方法将一个一个地触发Skinner图中所有节点的执行操作,从名为update的schemer.entry处以指定的顺序开始。
#include <core/scripts/utils.h>
#include <core/systems/skinner/skinner.h>
#include <samples/common/common.h>
Unigine::Skinner::Skinner skinner;
Unigine::Skinner::SkinnerMesh skinner_mesh;
/*
*/
int init() {
// Use Skinner namespace.
using Unigine::Skinner;
// Load the skinned mesh and add it into the editor.
ObjectMeshSkinned mesh = add_editor(node_load("samples/skinner/meshes/agent.node"));
// Create a Skinner.
skinner = new Skinner();
// Create SkinnerMesh that will manage the executable script compiled from a graph.
skinner_mesh = new SkinnerMesh(skinner,mesh);
// Load a graph that scripts character behavior.
skinner_mesh.loadScript("samples/skinner/scripts/agent_00.script");
return 1;
}
int update() {
float ifps = engine.game.getIFps();
// Update Skinner mesh.
skinner_mesh.update(ifps);
return 1;
}
倘若要使用ragdolls,世界脚本中的render()而不是update()就可被用来作为图像的起始点。其允许获取当前帧中的动画转换信息并使用此数据来模仿物理驱动的ragdoll(参看render()的详细信息)。这样,schemer.entry被命名为 render。
int render() {
float ifps = engine.game.getIFps();
// Run the Skinner graph script from "render" entry.
skinner_mesh.render(ifps);
}
return 1;
}
How to Run Event(怎样运行事件)
要创建一个一次性动作,您可以调用Skinner中event()的功能(可在data/core/systems/skinner/skinner_mesh.h中找到)。它将简单地调用用于Sinner脚本的更新功能,此脚本来自使用通过名称的图得以创建。
int id = skinner_mesh.getEventID(entry_name);
skinner_mesh.event(id);