【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 3378|回复: 2

From 3D Studio MAX to Direct 3D: Introduction to Plugin Development

[复制链接]
发表于 2006-10-16 10:55:47 | 显示全部楼层 |阅读模式

If a programmer can translate an artist's work into a real-time 3D environment, the game will be that much better. For many programmers, 3D Studio MAX is the tool of choice for pre-calculating and real-time 3d scenes. And, in the opinion of this author, Microsoft's DirectX API is the most efficient API for games on Windows platforms

This article explains how to create a 3DS MAX plugin to export the artist's work into your real-time 3D engine. My instructions will take you through both the MAX and Direct3D APIs. While no experience is necessary, you should be familiar with Visual C++, Windows, Direct3D (using the Immediate Mode's Draw Primitives) as well as computer graphics basics.

I'll start with the MAX's API and cover how to use it to create a new plugin. With this knowledge, we can implement the the plugin's project using Visual C++. For real concrete stuff, for instance mesh and material exportation, you'll have to wait for the next articles.

Introduction to MAX plugin Development

3DS MAX primarily deals with meshes concerning the realization of 3d objects. Patches and NURBS are also managed, but because the powerful geometry pipeline makes it possible to convert them into meshes, it shouldn't be a problem. The exportation of NURBS-based objects would have made things very difficult and inappropriate for real-time 3D. This flexibility is the reason why 3DS MAX is becoming the standard as far as game development is concerned. If you already own the second version, you'll enjoy its performance compared to the first version. Many changes were made in this second version, and as a result, there is an incompatibility of plugins between the two versions. Getting around this incompatibility requires only a recompilation and maybe a few changes. So, with that in mind, techniques explained in this article will work, regardless of the MAX SDK version you have.

What do I need?

On the software side, you'll need Microsoft Visual C++. While the 4.0 version is good enough to create plugins for the first version of 3DS MAX, you'll imperatively need the 5.0 version if you use the MAX2 SDK. As for APIs, you'll of course need a version of MAX's SDK and DirectX 5.

While CPU Speed is not extremely important on the hardware side as it doesn't have any incidence on the compilation/testing time, I strongly recommend you at least 64 Mb RAM. If you have less, MAX, Visual C++ and your project won't be able to stay in memory. When this happens, compilation time will be multiplied by 5, and MAX will take at least 20 seconds to load. This, needless to say, can be maddening.

128 Mb RAM is sufficient, if your plugins project is inside a workspace which already contains projects such as your 3d game engine.

The MAX SDK gives developers the possibility to create many kinds of plugins, from Procedural Objects to Video Post Processing. A plugin's type called "File Export" is of particular interest. However it's not the type I would recommend. Export plugins are made to export a whole MAX scene, without any interactions with the user, which is, in our present case, not what we want. The utility plugin is more flexible just in case you want to select scene components to export, and how you want to export them.

MAX Philosophy

As a MAX plug-ins developer you'll have to understand MAX's internal structure. The rest of this article will introduce you to fundamental concepts such as the geometry pipeline, the manipulation of the scene's nodes, calculus implementation and the plugins' interface. If you like Oriented Object Programming, you'll love MAX's API: everything has been made according to the OOP philosophy, you'll find classes for almost everything you can think about (Mesh, Face, Point, Matrix…).

Nodes Manipulation

What's a node? In MAX, a node is a visual representation of an object (here, the term 'object' means 3D object, light, camera, shape, etc…). Nodes are linked to each other to make a hierarchy. The root node of this hierarchy is the MAX scene. To realize a MAX exporter plugin, it's important to be familiarized with the manipulation of nodes, as they're the starting point of your conversion.

What can we do with a node? A node is created and controlled from the MAX's class INode. I'm not going to detail this class, the MAX's help file will do it better than me, but I'll just comment on a few things that will be useful for our exporter.

The main function of a node is to reference the object it is associated with. This object's state can be evaluated in the world (its representation in the viewport) at a given time. See the geometry pipeline, for more information. Once this object is evaluated, you can retrieve all of its specific data, we'll see later how to do that.

Concerning the hierarchy…from a node, you can get its parent node, the number of its children and a specific child node, using the INode::GetParentNode(), INode::NumberOfChildren and INode::GetChildNode() methods. To scan the whole scene, you just have to get the root node (using a function of the plugins' interface), and then using the functions given below.

Other properties like the node's transformation matrix (a transformation matrix is made of a position, an orientation and a scale factor), the transformation matrix of the object, its reference and its name can be retrieved.

Now you know how a node basically works, let's explain how objects work with the help of the geometry pipeline.

The Geometry Pipeline

This is the most fundamental concept and innovation of MAX. If you have already worked with 3D Studio/DOS, you know how the creation of meshes work. You could start by creating many 2D shapes with the 2D shaper and then create a 3D object based on those shapes using the 3D lofter. To apply modifications (vertices displacement, Boolean operations, mapping, etc…) on it you could use the 3D editor. Each time you made a modification on a 2D shape or 3D object, the previous state was lost as the modification was directly applied on the entities (faces, vertices, 2d spline) that made up the 2D shape or 3D object.

MAX uses another approach to create geometric components called a "procedural approach". Each node contains a node pipeline. At the beginning of this pipeline there is the Base Object. Each time you apply a modification to your node, the Modifier you used is referenced in the node pipeline, with the parameters you set. Then, the geometry pipeline evaluates the node's pipeline to create the World Space State of the node's object. This World Space State is what you can see in the MAX's viewport.

Ok, let's take model what a "top". Don't expect a nice one, I'm just a programmer!

This model can be made in two steps: the creation of a sphere and the application of a Taper on it.

When you create a sphere, a new node is also created and inserted into the MAX scene. The object referenced by the node is the node's Base Object(the sphere). As it is a procedural object, the sphere object is not made of faces and vertices at this stage. This object can be generated into a mesh, depending of the parameters you specified.

Figure 1 below is the geometry pipeline representation of the node at this stage and its World Space State.


In step two, a Taper Modifier was applied on our node, you can see the result in Figure 2 (I've also assigned a texture map). The node now points on what we call the 'Derived Object'. This Derived Object is the result of the node's pipeline interpreted by the geometry pipeline.

Following the Derived Object is the Taper Modifier, and finally, our Base Object: the sphere.


Suppose that you apply a bend to the object. This Modifier will appear in the pipeline right after Derived Object and before the Taper. With the help of MAX's Modifier Stack you can change parameters of any Modifiers you want (even the base object). You can also remove or insert modifiers anywhere you want in the Stack. Anytime the object's World Space State will be recomposed: that's the job of the geometry pipeline.

The World Space State object of a node can be evaluated using the INode::EvalWorldState() method. This method returns a C++ object of the class ObjecState. From this object, you can retrieve the object evaluated by the geometry pipeline. The evaluated object is also a C++ object of a class that is derived from the Object class. MAX implements many classes that are derived from the Object class.

Later in this series, we will see classes such as:

TriObject: defines a mesh object made of triangles
CameraObject: defines either a Free or Target Camera
LightObject: defines either an Omni, Directional, Target Spot or Free Spot Light

 楼主| 发表于 2006-10-16 10:56:08 | 显示全部楼层

A Bit of Calculus

MAX's API offers a set of classes and functions to make the utilization of matrices, points and quaternions easier. Since the manipulation of points is not really a big challenge, I'm not going to overview the Point3 class. Talking about quaternions would go beyond the scope of this article, as they are mostly used when dealing with animation. So only fundamentals of matrices are explained. Here we go.

Transformation Matrices. Transformation matrices are used to switch from one given coordinate system to another; for example, to transfer the coordinates of an object's vertices from local to the world space coordinate system. Transformation matrices are also used to position nodes in the scene. The transformations provided by these matrices are: translation, rotation and scaling. Such matrices can be created and used with the Matrix3 class of MAX.

In MAX, matrices have 4 rows of 3 columns. Usually, 3D matrices are 4x4 instead of 4x3. It's also the case for MAX, but as the last column is always [0 0 0 1] for such matrices, it's implicitly managed by the class and functions dealing with it.

The coordinate system used by max is a right-handed one, with counter-clockwise angles when looking to the positive way of an axis, as shown in Figure 3 below.


Now let's see how the identity, translation, rotation and scaling are encoded. An identity matrix has the property to keep a vector or a matrix unchanged during a vector/matrix or matrix/matrix multiplication. It's encoded as follows:


You can initialize a matrix to the identity by passing 1 to the constructor of Matrix3 class or by calling the method Matrix3::IdentityMatrix().

Translation is stored in the matrix this way:


Translation can be set using Matrix3::SetTrans(), retrieved using Matrix3::GetTrans(), set to null using Matrix3::NoTrans().

The matrices for rotations around the three axes are shown below. The rotation's angle is always given in radians.

X rotation:

Y rotation:

Z rotation:


These matrices can be created using global functions RotateXMatrix(), RotateYMatrix(), RotateZMatrix(). An incremental rotation around one specific axis can be performed using the Matrix3's methods Matrix3::RotateX(), Matrix3::RotateY(), Matrix3::RotateZ().

A method of the Matrix3 class is also provided to create a Yaw/Pitch/Roll angles matrix (using the rule of Euler's angles) : Matrix3::RotateYPRMatrix().

Scale factors are encoded like this:


Scaling factor can be set using Matrix3::SetScale(), or reset to 1 using Matrix3::NoScale().

For the last part of this article we're going to take a brief look at how the plugin can get access to MAX features.

The Plugin's Interface

The Plugin's Interface is a C++ Interface retrieved during the plugin's initialization. The term Interface signifies that a C++ object contains methods only. The Plugin's Interface is used to provide APIs that the MAX offers to the plugin. From the list of APIs it provides, the following pertains to our exporter.

Command Panel - Rollup Page Method

As the plugin's type we will use is a Utility Plugin, we'll have to design a dialog that will appear during the plugin's initialization in the Utility Plugin's Pan. This dialog will appear under the form of a Rollup Page. Methods of the Plugin's Interface will be used to Add and Delete this Rollup Page dialog.

Nodes Related Methods

By using functions of this API, we can access the Root Node of the scene, accessing a Node from its name.

Node picking

Provides functions to call the Interactive Nodes Selection Dialog of MAX. Useful to select Nodes we want to export.

Standard MAX dialog

The Track View Pick Dialog, Material Browse Dialog can be called using this API.

Last Words

Next time, we'll see how to create a Plugin's Project, how to integrate MFC inside the plugin, how to debug it, and check out a simple example of a Utility Plugin.

Loic Baumann is creator and lead programmer at Fatal Design., a small, French-based, game development company. For the last five months, Mr. Baumann has kept himself busy working on a development environment to create real-time 3d applications. Check out his progress at perso.aic.fr:80/lbaumann.
Mr. Baumann can be reached at lbaumann@aic.fr

 楼主| 发表于 2006-10-16 10:57:10 | 显示全部楼层
From 3D Studio Max to Direct3D, Part 2:
How to Create a Plug-In

This time we're going to talk about how to develop a MAX plug-in. Enclosed with this article are two samples of a simple Utility plug-in -- one for the Visual C++ 4.x / Max 1.x, and the other one for Visual C++ 5.x / Max x.x. These samples will give you an overview of an exporter plug-in's skeleton. To save time, you can personalize them for your own Utility plug-ins.

plug-ins Overview

MAX offers many types of plug-ins. In MAX, a plug-in is a DLL which is explicitly loaded during the MAX's startup. Every type of plug-in has a specific file extension (i.e. .DLU for Utility plug-ins, .DLM for Modifier plug-ins, .DLO for procedural Object plug-ins, etc…). This extension is used by MAX to differentiate common DLLs from plug-ins.

Normally, plug-ins written by third party developers are stored in the \plug-ins directory of MAX. To generate a .DLU DLL stored in the \plug-ins directory of MAX, a VC++ DLL project should be used. Each time a plug-in is recompiled, you have to take care that MAX is not running (or the new DLL version won't be generated as the previous is already running). MAX's startup won't last too long if your computer owns enough RAM for everything (VC++, your project and MAX) to stay continuously inside. For people who never used DLLs here's a brief reminder.

Windows DLLs

A Dynamic Linked Library can be seen as an executable (.EXE) which contains functions callable by another executable. Like an executable, it can also contain its own resources (dialog template, string, icons, etc…). Standard DLLs only give you the possibility to export C-like functions. With Visual C++'s MFCs you can create a new kind of DLL called 'MFC Extension' where it's possible to export C++ classes. When a client program uses a DLL's functions, the DLL's header file must be included in the .cpp files that use the functions, and the DLL's library (file extension .lib) must be linked to the project. Only functions that were declared to exportation can be used by a client program. Exportation declarations can be done using .def file (for both regular and extension DLLs) or using Microsoft's specific tokens (for extension DLLs only).

As MAX is a pure Win32 program, DLLs are not supposed to use MFC and must be of the Regular type only. Using the .def file of your plug-in's project, four functions have to be declared in exportation. You'll have to edit the .def to make it looks like :

LIBRARY themaxplug-in
EXPORTS
LibDescription @1
LibNumberClasses @2
LibClassDesc @3
LibVersion @4
SECTIONS
.data READ WRITE

Each exported functions are associated with a number that will be used to make the dynamic link when the client (MAX) will call the DLL's functions. The most important function is the LibClassDesc(). It's used to returned an object that will be used by MAX to create the plug-in.

MAX is not a MFC based program, which leads to some difficulties when you want to use MFC inside your plug-in. As I'm a lazy programmer, I like to use MFC in my programs, so I'm going to describe the steps for the creation of a MFC based plug-in.

Typical steps to create a MFC based utility plug-in

There's two ways to create a MFC based plug-in: the first with static linking of MFC DLLs, and the second with shared MFC DLLs. Both have advantages and disadvantages. For static linking, the MFC functions and resources will be statically linked to your plug-in. This will, however, make your plug-in bigger in size. The advantage is that you can use MFC transparently, like a typical MFC program. For shared MFC DLLs (also called dynamic linking of MFC DLLs), the advantage is you keep a short DLL, but as your plug-in is also a DLL, the utilization of MFC resources needs some extra code, as nested DLLs are not managed transparently concerning resources. I'll explain the first method only, as its implementation is easier, and more understandable. Your plug-in will increase in size by about 100kb more than with the second method.

For the following steps, you can check the samples provided for more details.

1: Create a new project based on a MFC AppWizard DLL, choose static linked of MFC.
2: Edit the generated .def file as described below.
3: Create a Dialog Resource Template for the plug-in's panel, the width must be 104 units.
4: Include the max.h header file in the DLL's main file.
5:

Override (using class wizard) the InitInstance and ExitInstance of the CApp class create by the AppWizard. Add the following code to the InitInstance method:


//Get the DLL's HINSTANCE
HINSTANCE hInstance = AfxGetResourceHandle();

//Init 3DS Max Custom Controls
InitCustomControls(hInstance);

//Init Win32 Controls
InitCommonControls();

return TRUE;

6 Change compilation settings to generate the DLL in the MAX's \plug-ins directory. Don't forget to change the DLL's extension to .DLU.
7: Add the MAX's "core.lib" and "util.lib" library files to your project.
8: Create a plug-in's description class. Look at the PlugDesc class of the sample.
9: Create a plug-in's panel class. Refer to the sample's PlugPanel class.
10: Generate a new Class ID using the Kinetix's "gencid" program and define a constant of it in your plug-in's header file. i.e. : #define TESTPLUG_CLASS_ID Class_ID(0x24f21f8d, 0x59140b2b)
11: For the Panel class and miscellaneous code implementations, refer to the sample's "PlugTest.cpp" and "PlugTest.h" files.

Once you succeed at compiling your plug-in, you have to test it, and if somehow it hangs (it happens, sometimes…) you may want to debug to see what's going on.

Debugging is an inevitable step of programming, and even if your program doesn't hang, it's always interesting to trace a program to see how everything works. Debugging a MAX's plug-in is not as easy as debugging a program. Normally, you choose the "Debug" compilation mode when you're in the debugging phase of your project, but it's not something you can do with MAX's plug-ins. Visual C++ proposes two compilation modes : Debug and Release. Typically, you develop your project using the Debug Mode, because it offers many advantages like source level debugging, special memory management, crash protection/checking, etc… Once you finish your project, you compile it using the Release Mode which optimizes your code and suppresses debug information and debug purpose checking to make your project shorter and faster at execution.

So, where's the problem? Memory heaps are not managed the same way if you compile in Release or Debug mode. And, if a Release compiled project frees a heap allocated with a Debug compiled project, a crash will occur. The version of MAX you're using was compiled using the Release Mode. It's impossible to compile your plug-in using the Debug Mode. The only choice your have is to create a new compilation Mode that manages heaps the same way as the Release Mode, and which generates Debug Information for being debugged. For the sample, I've called this new compilation mode "Special 3DSMax". To view the setting use the "Settings…" command of the VC++'s "Project" menu.

Provided are the steps to create this new compilation mode:

1: Select the "Configurations…" command of the "Build" menu.
2: For your plug-in's project, choose "Add…"
3: Choose a name for your new compilation mode, copy settings from the Release Mode, keep the Win32 platform.
4: Edit the Setting (ALT+F7) of your plug-in. Choose your new compilation mode.
5: In the "C/C++" tab, change the "Debug Info" to "Program Database", set the "Optimizations" to "Disable(Debug)".
6: In the "Link" tab, check the "Generate Debug Info" check box.

As the Debug Compilation Mode can't be used for the plug-in, you can remove it by repeating Step 1, selecting the Debug Mode of your plug-in, and clicking the "Remove" button. Some features that were available in Debug Mode won't be used with this mode:

- Special memory management, memory leak display at debug exit - TRACE macros. - ASSERT, ASSERTVALID macros.

As the setting was taken from the Release Mode, the Preprocessing define "NDEBUG" is declared. You can remove it ("C/C++" tab of the Project setting panel, "Preprocessor definitions") and set a new define (ie "SPECIALMAX" ) The Sample

There's two zip files :
- PlugTest_VC4.zip, for Visual C++ 4.x, with the MAX 1.x SDK. - PlugTest_VC5.zip, for Visual C++ 5.x, with any version of the MAX SDK.

For your projects to be compiled correctly, you'll have to perform some slight changes:

- Change the path for the plug-in output file name, ALT+F7, link tab. - Change the path of the MAX's libraries. It's easiest to remove the already inserted ones, and add the new ones (using Project/Add to Project/Files…) - Set the MAX's SDK include directory, using Tools/Option…, Directories' tab.

The sample demonstrates a little plug-in that provides basic information about the node you selected. I strongly recommend you look at every line, to understand how the MAX's basic functions work.

If you experience some problems or don't understand something, don't forget to search in the MAX's SDK help.

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-2-6 03:58

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表