Monthly Archives: April 2011

Hierarchical Data with Sqlite

This source is my practice about the idea of storing hierarchical data. It is implemented in the Python using qslite.

My data is:

1 animal 28
-2 carnivore 15
–3 lion 4
–5 crocodile 8
—6 alligator 7
–9 tiger 10
–11 cheetah 12
–13 wolf 14
-16 herbivore 23
–17 horse 18
–19 cow 20
–21 zebra 22
-24 omnivore 27
–25 human 26

Leave a comment

Filed under Python

Mental Ray Shader Writing Startup

OK, I am still quite a beginner in this area. This note is about how to compile, load, and use the “Hello World” Mental Shader. The public source is quite a good start point and giving a lot of hints, but I want to make it as simple as possible.

Like RenderMan, MR shaders are written in C. But MR has no dedicated shader compiler, Shader to PRMan, to compile source to machine code. Means even writing the simplest MR shader requires some skill about  C/C++ compiler. On Windows, it is MSVC; On Unix, it is GCC. Here I only cover the Windows side.

In the shader source,  illumconstant.cpp, first include the MR headers:

#include "shader.h"
 #include "mi_shader_if.h"

Typically they are located in directory MAYA/devkit/mentalray/include/, so make sure it is in the header search path. For some unknown reason, shader_sds34.h is missing in the dev package, download it here.

Now define the parameters of the shader, as a struct:

struct zhang_illum_constant_t {
 miColor        surfaceColor;

Just one color in this case. Then define the version number:

extern "C" DLLEXPORT int zhang_illum_constant_version(void) {return(1);}

Finally it is the body of the function:

extern "C" DLLEXPORT miBoolean zhang_illum_constant(
 miColor        *result,
 miState        *state,
 struct zhang_illum_constant_t *paras)
 /* check for illegal calls */
 if (state->type == miRAY_SHADOW || state->type == miRAY_DISPLACE ) {
*result    = *mi_eval_color(&paras->surfaceColor);
result->a  = 1;

The shader does nothing but returns the input color, so it is a Constant. Compile it by build.nt-x64.bat:

cl /TP -c /GR /Zp8 /W3 /GS- /GR /GF /Ox /MD /nologo /W3 /Zc:forScope /EHsc /Ob2 -DQMC -DMI_MODULE= -DMI_PRODUCT_RAY -DWIN_NT -DEVIL_ENDIAN -D_WIN32_WINNT=0x0400 -DNV_CG -D_SECURE_SCL=0 -DBIT64 -DHYPERTHREAD -DX86 -I../../include  illumconstant.cpp
 link /delayload:opengl32.dll /nologo /nodefaultlib:LIBC.LIB /MAP:mapfile /OPT:NOREF /INCREMENTAL:NO /LIBPATH:..\..\lib /STACK:0x400000 ws2_32.lib user32.lib mpr.lib /DLL /OUT:constant.dll illumconstant.obj ../../nt-x64/lib/shader.lib
 mt.exe -nologo -manifest constant.dll.manifest -outputresource:constant.dll;2

This must be run inside MSVC 64-bit command line tool. It links shader.lib which is located in directory MAYA/devkit/mentalray/lib/, so make sure it is in the library search path.

Once it is compile, will bring constant.dll. That is the resulting shader file. Still we need constant.mi:

# guard against multiple inclusions
 $ifndef "constant_mi_included"
 set "constant_mi_included" "true"
min version "2.0.20"
 max version "3.6.99"
#-------------------------------------------- Illumination
declare shader
 color "zhang_illum_constant" (
 color        "surfaceColor",  #: default .2, .7, .9, 1.
 #: nodeid 573101
 apply material
 version 1
 end declare
$endif # if not "my_mi_included"

Basically it declares the shader as a material and set the default value to its parameters. Give it a nodeid, so the custom node will be saved into Maya binary file correctly.

Now copy the .mi file to MAYA/mentalray/include/ and .dll file to MAYA/mentalray/lib/, and reload Maya. On start up, Maya will report parsing and loading those file:

A new node will appear in the MR create materials options:

It has one color parameter:

Assign it to some model and render with MR:

Success! Now you have your first and useless MR shader. Grab the source.

Leave a comment

Filed under Mental Ray

Momentum Equation

If you just have the continuity equation, it is still dead water. Momentum term of the Navier-Stokes equations describes how the fluid reacts to force:

It means the change of velocity is the combined result caused by following effects:

Advection – fluid can transport along its own velocity field.

Pressure – force from pressure gradient. For example, wind blows from high pressure area to where pressure is lower.

External Force applied per unit mass. For example, stirring a cup of tea will cause it to move. Gravity will cause ink to sink in water.

Viscous force due to friction. Sticking glue has more resistance to flow than clean water.

Momentum equation can taken as the fluid version of F = ma.

more information

Leave a comment

Filed under Fluid

Continuity Equation

Time to brush up my math! Today is about the Navier-Stokes (N-S) equations, a set of differential equations describing the behavior of moving fluid. The continuity equation is:

ρ ( rho) is density. u, v, w, is velocity along direction x, y, z. For incompressible fluid density is constant (does not change over time or space), so the equation can be written as


means the divergence (∇) of vector field (velocity in this case) U equals zero (a scalar value). In other words: during the infinitesimal time, through the closed surface of the infinitesimal volume around a given point, inward flow equals outward flow. Anything flows in must flow out, nothing is created, so the mass of the system is preserved. This idea is the very backbone of fluid simulation.

Leave a comment

Filed under Fluid

File Dialog

It is quite simple to bring a file dialog to pickup a file outside Max:

filename = getOpenFileName
 if filename != undefined then
 print filename


filename = getSaveFileName

essentially does the same thing but confirm button label is “Save” rather than “Open”.

The file dialog can have more options like:

filename = getSaveFileName \
caption:"Title of the Dialog Window" \
 types:"Data(*.txt)|*.txt|All|*.*|" \
 filename:(sysinfo.currentdir + @"\untitled.m")

Leave a comment

Filed under Max Script

Build Mesh and Convert to MNMesh

If you want to write a plug-in object in Max, probably you are starting with the Widget example offered by Max SDK. It is a class derived from SimpleObject2, so it can only have triangular Mesh. Inside BuildMesh() method, this is howto create a simple mesh with only one quad face:

mesh.verts[0] = Point3(0.0,0.0,0.0);
 mesh.verts[1] = Point3(10.0,0.0,-0.5);
 mesh.verts[2] = Point3(10.0,10.0,0.0);
 mesh.verts[3] = Point3(0.0,10.0,1.5);
mesh.faces[0].v[0] = 0;
 mesh.faces[0].v[1] = 1;
 mesh.faces[0].v[2] = 2;
 mesh.faces[1].v[0] = 0;
 mesh.faces[1].v[1] = 2;
 mesh.faces[1].v[2] = 3;
mesh.setMapSupport(1, TRUE);
MeshMap& map = mesh.Map(1);
 map.setNumFaces(2);[0]=UVVert(0,0,0);[1]=UVVert(1,0,0);[2]=UVVert(1,1,0);[3]=UVVert(0,1,0);[0].setTVerts(0, 1, 2);[1].setTVerts(0, 2, 3);

Actually there are 2 triangles. But as long as there is:


It will appear to be a single quad face. Now turn off the object display of “Edge Only”, the hidden edges will show up as a dotted line, two overlapped dotted lines indeed.

So how to convert Mesh to MNMesh? In fact you don’t have to, Max will do that for you. If you attached an EditPoly modifier to the Mesh object, it will be converted to Poly (MNMesh), unless you bother to override the ConvertToType() method, in which you can explicitly create an MNMesh and send to the pipeline. Don’t think that is necessary if you want the mesh unchanged.

Then how Max converts Mesh to MNMesh? I suggest Max traverses through all those triangles, looking for hidden edges, and remove those hidden edges to merger triangles into polygons. So the mesh we built above will be converted to a single quad face. The catch is Max will merger any triangles happened to be meeting at the same hidden edge, regardless whether those triangles are from the same “face”. That could lead to unexpected conversion. So set hidden edges carefully.

Leave a comment

Filed under Max SDK

Python XML Basic

This note is about how to read and write XML files by Python DOM.

First we need the module:

import xml.dom.minidom as dom

To have a writer:

writer = dom.Document()

An valid XML file should have at least one element, which can be created by:

root = writer.createElement("root")

then add the element to writer:


Now save the file:

file_out = open('./foo.xml', "w")

This produced a file contains:


Before saving the file, we can add more elements within the root node. Simply create a new element and append to root as a child:

branch = writer.createElement('branch')

Better put it away as a function returns the new element:

def create_branch(doc, direction):
 branch = doc.createElement("branch")
 return branch

An element can have an attribute by:

branch.setAttribute(attribName, attribValue)

It can have its text content by:

text = doc.createTextNode(someText)

You see, the text content is actually a child of the element.

In this way, we can add more branches to the root, and add more branches to a branch.

Now to read the XML:

file_in = open('./foo.xml', "r")
 reader = dom.parse(file_in)

The reader is the doc holding the biggest root node. To access all children within a node:

for e in doc.childNodes:
 # do something

Type of the node can be get by:


So <root/>’s localName is ‘root’

Attribute can be retrieved by:


Text content is:


Better use a recursive function to parse all nodes:

def print_a_document(doc):
 for e in doc.childNodes:
 # do something to e

Send the reader to this function and implement the reading. Grab the source!

Leave a comment

Filed under Python, XML