MAX SDK之对象处理(三)

来源:互联网 发布:淘宝店铺怎么导入模板 编辑:程序博客网 时间:2024/05/23 19:30

三、对象处理

      MAX SDK中的对象非常多,包括结点、网格、位图、材质、灯光、纹理、修改器等等。这里为了简单起见,并且作为适用于入门的材料,我们只介绍其中几个非常重要并且常见的对象。其他对象可以在MAX SDK中查阅。

      在介绍这些对象之前,我们简单了解一下对象创建的过程。

3.1 对象创建过程

      下面讨论开发者如何在插件中控制对象创建的过程。

     开发者可以有集中方法在场景中创建对象,一些方法是通过命令模式或鼠标过程来提供一个自定义的创建过程;另一些方法是使用默认的对象创建管理。无论是哪种方法,开发者都需要创建这些插件的DLL形式并且让3ds max在启动的时候装载它们。插件可以访问这些标准3dsmax基本体的参数块,也可以在创建的对象上附加修改器。

     多数情况下由系统控制对象的创建过程,例如创建一个过程球对象,开发者仅仅需要提供一个用户/鼠标的交互手段,这可以使用BaseObject::GetCreateMouseCallBack()方法来实现。这个方法返回一个回调对象,该回调对象控制用户在创建过程中的输入,系统负责在用户使用鼠标交互时调用该回调方法。它同样可以处理插件类的创建,场景结点的创建,并且设置结点的对象引用使之指向插件对象,因此,系统控制大部分工作。

     在某些情况下,开发者需要在更底层的级别上控制对象的创建过程,以便完成一些特殊的功能。插件类表述符的两个方法BeginCreate()和EndCreate()允许插件控制这一进程。当系统要创建一个插件对象时,BeginCreate()方法被调用,如果插件不需要自定义的创建过程这个方法返回0,然后系统调用默认的创建过程。这个方法的默认返回值就是返回0,也就是说如果不覆盖该方法,系统将控制创建过程。如果开发需要自定义创建,则应该覆盖该方法,并且该方法应该返回一个非0值以表示不想要调用默认的创建过程。

     目标摄像机插件是一个很好的例子,它必须自己控制创建过程,这是因为它需要在场景中创建两个结点:摄像机结点和目标结点。它还需要使用注视控制器来关联摄像机对象和目标对象,以使得摄像机能够始终“注视”目标对象。系统重置的创建过程是使用GetCreateMouseCallBack()来实现的,不能完成插件的这种需求。因此目标摄像机插件实现了ClassDesc::BeginCreate()方法和ClassDesc::EndCreate()方法来满足这一需求。

      注意GetCreateMouseCallback()是纯虚函数,因此从BaseObject派生的插件并使用ClassDesc::BeginCreate()必须为该方法提供一个空的过程体,即:

      另外一个控制自己创建过程的插件是环数组系统插件,它也重载了BeginCreate()方法和EndCreate()方法。

      最后要注意的是,插件开发者可以通过调用Interface::StopCreating()方法来强迫系统调用ClassDesc::EndCreate(),这将终止创建过程。

3.2 结点对象(Node)

      这一部分主要介绍结点是意义、结点在MAX中的作用、和结点相关的类,以及结点的引用结构。

      • 结点的概念

      在3ds max中,结点与场景中的对象存在着一一对应的关系,每一个过程对象,包括灯光、摄像机、辅助对象等等,出现在视口中的每个对象都关联一个结点。结点存储着它关联的对象在场景中的相关属性信息,这些属性包括空间变换控制器、用于渲染的材质、可见性控制器、隐藏/非隐藏状态、冻结/解冻状态、线宽显示颜色以及其他属性。熟悉MAX脚本的朋友肯定对脚本中的结点类非常了解,它是INode的简单版本。

      • 结点的方法

      MAX SDK中的结点类是INode,它提供了一系列的方法比如评估该结点的几何管线、获取或设置结点的名称,处理结点的父子层次关系、设置结点的可视性属性、以及提供访问控制器的方法等。

      结点存储了两个变换矩阵,一个是结点的变换矩阵,另一个是结点关联对象相对于结点的偏移矩阵。另外,结点类是不能被实例化的,要取得结点的实例,必须实例化结点所指向的对象,所以,当实例化结点所指向的对象后,结点就相当于被实例化了。

      • 结点、对象以及派生对象的创建方法

      开发者可能想自己创建标准3ds max中的基本体,比如导入导出插件想创建标准的3ds max球体,椎体等等。通过max sdk的API可以创建场景中的对象和结点对象。一些附加的插件可以创建添加了修改器的对象即派生对象。下面将讨论如何使用这些创建方法。

      标准的3ds max基本体,或者在3ds max注册过的过程对象,可以根据它们的类描述符(ClassID)和超类描述符(SuperClassID)来创建,如:

      

      上面的API创建一个注册过的对象实例,它调用对象类描述符中的Create()方法,该方法返回实例的指针。注意,这个方法是Interface类中的方法,但是同样有一个全局的API,可以在任何时间使用。关于MAX中注册过的对象标识符(ClassID)和其超类标识符(SuperClassID)可以从SDK文档中获取。

      在创建这些对象时,需要输入这个对象所需要的参数,而参数块的指针可以通过以下这个baseObject类的方法得到:

      如果需要,可以得到其他对象的参数块,下面的方法得到一个对象的参数块指针:

      如果一个插件需要使得它的参数块可用,就必须用#define为其参数索引定义一些宏。这些宏并不是直接由参数块使用,而是转换使得插件可以识别各个参数。这样的话,以后的版本中如果参数发生了变化,通过这些宏仍然可以重新得到映射关系。参数块的标识符可以在MAX SDK中获取。

      创建场景结点需要与其关联的场景对象指针,然后调用下面的方法:

      这个方法为给定的对象创建一个新的场景结点,INode的方法允许结点命名,并且关联一个空间变化控制器对象。

      开发者同样可以创建一个派生对象并且为该对象增加修改器。IDerivedObject类的方法可以实现:

      另外一种创建对象的方法是使用NonMouseCreate(),这里不在赘述,有兴趣的话参见SDK。

      • 结点自定义属性和数据块

      在很多时候,开发者需要将一些特定的数据悬挂在结点下,在SDK中,有两种方法可以完成这一目的。一种是自定义属性,另外一种是数据块。首先我们介绍以下数据块:

      (1) 数据块(AppData)

      数据块是一种程序特定的数据,可以依附于场景中从Anaimatable类继承的对象上,通过这种接口,任何3ds Max对象都可以存在自定义数据。这些数据块可以存储在.max文件中并通过它们所依附的物体来访问。

      创建数据块的方法是Animatable类中的方法,它通过三个标识符在存取,前两个是数据所有者的超类标识符和类标识符,后一个是子索引。(因为同一个所有者可能有多个数据块)下面的几个方法介绍了数据块的使用过程:

     

      注意,虽然允许在场景对象和根结点下悬挂数据块,但这些数据块不会被保存到.max文件中,因为场景对象和根结点对象并没有真正保存。

      (2) 自定义属性(User Properties)

      另外一种可以悬挂在结点下面的数据类型是自定义属性,这种数据可以是ASCII字符串、整型、浮点型或者是布尔型。它们通过关键字字符串(Key String)来存取,关键字字符串是任意长度的字符串,唯一的限制是不能使用空格和"="号。

      自定义属性的使用方法如下所示

     

      (3) 保存类数据(Saving Class Data)

      ClassDesc的三个方法用来存储与某个类相关的数据,它能够被存储在3ds max中,如果需要存储这一类型数据,必须实现NeedsToSave()并返回TRUE,并实现Save()和Load()方法以完成保存和载入。

     

      注意保存和载入的数据在打开新文件或者重置文件时,不会被清空。

      • 结点组

      MAX用户可以将多个结点联合成组,成组后允许用户一次性选择组内的所有结点而不需要逐个选取,组还可以进行嵌套,即结点组可以与其他结点或组再组成结点组,从而成为新的几点组中的一个元素。INode类中的IsGroupMember()可以判断该结点是否属于某一个组,其返回TRUE表示结点属于某一个组否则返回FALSE。

      组是这样被创建的:首先创建一个虚拟物体,然后将所有的组元素关联到该虚拟物体上,这个虚拟物体结点称之为组的首结点。INode的IsGroupHead()方法判断一个虚拟对象的结点是否是组的首结点。

      如果已知一个结点指针,而又想知道它所在组的头结点指针,可以调用GetParentNode()。有时候需要将组打开(注意:不是解组)以便编辑其中的某单个结点,IsOpenGroupMember()方法返回TRUE的话说明该结点属于一个被打开的组中,否则返回FALSE。IsOpenGroupHead()方法可以判断该头结点是否被打开。

      • 结点引用

      一个结点维持6个引用,以便结点在引用发生变化时得到通知,这六个引用分别是:

      (1) 引用0: 空间变换控制器(The Transform Controller),存储结点的变换矩阵;

      (2) 引用1: 对象引用(The Object Reference),每个结点都维持一个指向对象的指针;

      (3) 引用2: 反向动力学节点(The Pin Node for IK),该引用指向IK的节点指针,默认初始化为NULL;

      (4) 引用3: 材质引用(The Materical Reference),每个结点都关联一个材质指针,可以通过INode::GetMtl()和INode::SetMtl()存取;

      (5) 引用4: 可见性控制器(The Visiblity Controller),一旦指定,它控制结点的可见性,默认初始化为NULL,可以在轨迹视图中编辑设置;

      (6) 引用5: 图像模糊控制器(The Image Blur Controller),默认初始化为NULL。

      • 结点层次关系

      结点在场景中是以父/子这种层次关系组织的,开发者可以通过INode类来访问层次上的各个结点。每个结点都会以子结点的身份关联到另一个结点上,一个结点可以有很多子结点,但只能有一个父结点。3ds max通过子结点数组访问各个子结点。

      当一个结点被关联时发送REFMSG_NODE_LINK消息,当解除关联也发送该消息。开发者可以通过以下方法来控制这种层次关系:INode::AttachChild(),Detach(),GetParentNode(),可以用GetParentNode()来判断该结点是否已经被关联到另一结点上,如果该方法返回的是根结点说明还没有被关联到其他结点(根结点由IsRootNode()判断)。

      结点类层次关系的主要方法有:

      注意循环链接的问题:如果一个结点的空间变换控制器依赖于它的子结点或者其它子孙结点,那么这种循环是非法的;也有特殊的情况,如果一个结点的对象引用依赖于它的子孙结点则不认为是一种循环,而是合法的。

      • 结点捕捉

      对象捕捉是一种插件类型,它允许对象提供一些点以便3ds max的捕捉系统可以捕捉对象。比如一个模型可以提供捕捉顶点、断点或中心点的能力;一个过程球体的开发者希望提供能够捕捉球体中心和象限点的能力;通过从Osnap类继承并实现它的方法,开发者可以很容易地提供这些功能。

      3ds max存在一个单独的对象叫作捕捉管理器(Snap Manager),在MAX启动时它装载所有的捕捉插件(后缀名为DLS),捕捉插件都是从Osnap类派生的,该类有各种各样的方法以使得系统可以访问到将有捕捉的对象信息。当在3ds max点击时,捕捉管理器就会调用每一个插件来捕捉一个特定的结点。Osnap有一个方法可以告诉捕捉管理器只捕捉某一类型的对象(通过超类标识符和类标识符确定)。比如NUBRS捕捉插件只捕捉NUBRS类型的对象;切线点和垂线点仅仅捕捉样条曲线对象。如果一个对象捕捉插件想要捕捉某种对象类型,它们可以注册一系列的点用以捕捉,比如球体可以注册它的球心和象限点。

      系统允许开发者实现一些特定的捕捉行为,比如一个门存在一种特殊的捕捉方式来捕捉它的铰链点。

      主要的类包括:Osnap, IOsnapManager, OsnapHit, OsnapMarker等。

原创粉丝点击