PDA

View Full Version : Materials: Let's kick it up a notch!


CyberSlag5k
03-14-2005, 03:06 PM
Thanks to Bobo, my script properly exports basic mesh geometry: a vertex list, an indexed face list, and vertex normals. However I'm only assuming that last part is correct, as I have no way to test the normals. Why? Because I have no materials. OpenGL uses colors to fake a materials list, which is really just 100% ambient based on whatever color parameters you pass it.

So, the next thing to do is get some materials going on this baby. I know how to grab the material IDs for the faces (material properites can't go by vertex, right? I hope not as that seriously complicates the way I'm going to be rendering these objects), which is handy. But I can't seem to find out how to grab the different materials themselves. At least not in the SDK. Can someone recommend a resource to learn how to do this? Or perhaps just throw out a function and a return type ;)

Also, in crawling around the maxscript reference document, I stumbled across this:


<Editable_Poly>.separateByMaterial Boolean default: false -- boolean; Separate_by_Material_ID

Get/Set the Separate By Materials checkbox in the Subdivision Surface rollout.



Does that do what I think it does?

For those of you familiar with OpenGL, I plan on using vertex arrays. What this means is I will be making a single call to OpenGL that will tell it to draw the entire mesh. That complicates things for any object that will have more than one material (as most do). So the current plan is to grab all of the faces with their respective material IDs, then sort them when they reach my program (I could sort them using maxscript but it'll be much easier in my native C++). This allows me to set the first material property and draw all of the vertices with that ID, set the second, draw the next set, etc.

However! If this separateByMaterials thing (what exactly is it? A member function of editablemesh?) can just allow me to sort my mesh snapshot by material ID as I grab it...well that's just awsome.

So does it work like that? Can I do something like this:

tmesh = snapshotAsMesh.separateByMaterial myObj

Please say yes :)

CyberSlag5k
03-15-2005, 12:53 AM
Ok so between searching the SDK, checking google, and this post and another on gamedev.net, I've had no response. Granted it's only a been a day but I'd have thought I'd turn up something. Can this just not be done for whatever reason? Is there really no way to export groups of material properties using maxscript? There has to be. I mean if you can export the material IDs, you'd have to be able to export the materials...

Anybody? Please O:-/

Bobo
03-15-2005, 01:07 AM
Nope, it does not do what you think. As the description says, it controls the state of the Separate By Material checkbox in the Subdivision rollout (I could not have been clearer ;))

When using MeshSmooth (SubDivision Surfaces), you have the option to limit the SDS algorithm either by material IDs, Smoothing Groups or both. This is what the property controls, it has nothing to do with collecting faces, it simply exposes a UI checkbox option to MAXScript.

You WILL have to do the sorting yourself, but that shouldn't be too difficult.

CyberSlag5k
03-15-2005, 01:14 AM
Cool, thanks Bobo. It's a trivial amount of work to sort the faces, but I'll probly just do it in C++ as it'll be quicker for me to program and I can use a complicated but quick sorting routine.

I'm sorry I sort of emphasized that portion of the post when really the thing I wanted to know was, how do I grab the material properties of the mesh? I can grab the face IDs but that does me little good without the materials themselves. I don't see that anywhere in the SDK nor can I find it at scriptspot. And hell, while we're at it what about textures as well?

I'm sorry to ask questions like these in a forum. I know it's poor ettequate to just say "how can I do this" but honestly I can't find this information anywhere out there. Luckily there's an animation exporter on scriptspot, or I'd be asking about that one too (and most likely still will have a question or two).

Thanks!

CyberSlag5k
03-15-2005, 02:54 AM
Haha, I think I found it. Man, I must have done a dozen searches in the SDK, and none of them contained just the word "materials". I was doing "get materials" "materials snapshop" "materials export", etc. Oh well.

Anyway is this getMeditMaterial function what I'm looking for? Will the values I can grab from it just be from the generic scene? So I can do something like this:

mat_1 = getMeditMaterial[1]

Which will give me the first group in the form of...actually I don't see anything here on the data type. The SDK has this StandardMaterial : Material heading. Is one a derived class of the other? And is either the return type of getMeditMaterial? If so, I see (at least one of them) has all sorts of the wonderful components I've been searching for: ambient, diffuse, specular, and much much more!

So, I guess to sum things up, my general questions are:

1. Will getMeditMaterial provide access to the material groups that my faces have ID indexes to?
2. What is the return type of that function (getMeditMaterial)?

Thanks as always, guys!

ofer_z
03-15-2005, 03:10 AM
So, I guess to sum things up, my general questions are:

1. Will getMeditMaterial provide access to the material groups that my faces have ID indexes to?
2. What is the return type of that function (getMeditMaterial)?

Thanks as always, guys!


not exactly. getMeditMaterial returns the material stored in a material editor slot, however, this material doesn't have to be assigned to any object.
what you're looking for (assuming your object is stored in the variable obj) is:
obj.material

this will return the object's material (the class of which may change depending on the type of material assigned to the object) if your object has muldiple material ID's and each one has a different material, the returned material will be multimaterial. a multimaterial has a property materialList which contains all the sub-materials of this material. the faces' material ID's are the index numbers of the materials in that array, so if you have a face with a mat. ID set to 3 for e.g. and you want to get it's material you can do this:
obj.material.materialList[3]


here are some functions that will help you query the types and properties of object:
show obj -- shows the properties of obj (obj can be any maxscript object)
classOf obj --shows the class of obj


hOpe this helps,
o

Bobo
03-15-2005, 03:34 AM
...the faces' material ID's are the index numbers of the materials in that array, so if you have a face with a mat. ID set to 3 for e.g. and you want to get it's material you can do this:
obj.material.materialList[3]



Actually, this changed around Max 4 I think.
The order of the materials on the list does not correspond to the MatIDs anymore.
There is a separate array of indices called .materialIDListthat provides the real correspondence.

Basically, if the class of the material is NOT multiMaterial, then the material is used for all faces, so no need to use reordering.

If the class of the material IS multiMaterial, the sub-materials of the material (which can also be taken by index between 1 and .numsubs, like theObject.material[3] would return the 3rd sub-material of the multi/sub material) have to be collected and used to draw faces with corresponding Material IDs.
For each of these sub-materials, let's say there are 10 of them, look at the corresponding element of the materialIDList.
Normally, the values stored there would be the same as their indices, like 1,2,3,4,5,6,7,8,9,10, but the user could enter ANY valid MatID there.
Also, if there is a face with Material ID that does not have a corresponding sub-material, a modulo operation is performed, for example, if there is a face with MatID 11 but only 10 sub-materials are defined, it will use the 1st sub-material. (because mod 11 10 gives a reminder of 1.0)

CyberSlag5k
03-15-2005, 04:18 AM
Thank you for your responses, guys. I'm a little (ok alot) confused, though.

I'm not really familar with the concept of submaterials or multimaterials. Us OpenGL simpletons only know about one type of materials, and they only hve ambient, diffuse, and specular properties (although I'm excited to see what all I can do with all of these components in the material type).

So how should I be accessing these types? Just obj.material[i]? Or will I have to work around these sub/multi things?

Bobo
03-15-2005, 04:47 AM
Thank you for your responses, guys. I'm a little (ok alot) confused, though.

I'm not really familar with the concept of submaterials or multimaterials. Us OpenGL simpletons only know about one type of materials, and they only hve ambient, diffuse, and specular properties (although I'm excited to see what all I can do with all of these components in the material type).

So how should I be accessing these types? Just obj.material[i]? Or will I have to work around these sub/multi things?

Trying to program in MAX without knowing how MAX works is like trying to repair an UFO in Area 51 ;)

Here is the deal: Because 3ds max is object oriented, and a scene node can have only one Material object assigned to its .Material property, there is obviously no direct way to assign materials to faces. To solve this, 3ds max 1.0 introduced a special Material class called the Multi/Sub Material which is a top-level holder of up to 1000 (in Max 1.0 even 65535) sub-materials, each one corresponding to a face Material ID. This way, it is possible to assign a "fork" material to the .material property of the node, and there is a sub-material on each spike of the fork corresponding to the face Mat.ID.

So you have to enumerate these sub-materials (which are typically of Standard Material class and have a .diffuse, .ambient and so on properties) and figure out which one corresponds to which Face Material ID. You can throw away the top-level Multi/Sub material after that, as it is only a holder for the bunch of sub-materials.

I would suggest spending a couple of weeks reading through the MAX Reference (not the SDK, just the user manuals) to get an idea how thing work on the surface before you try to mess with them under the hood. ;)

CyberSlag5k
03-15-2005, 09:29 PM
Trying to program in MAX without knowing how MAX works is like trying to repair an UFO in Area 51 ;)


So I've learned...


Here is the deal: Because 3ds max is object oriented, and a scene node can have only one Material object assigned to its .Material property, there is obviously no direct way to assign materials to faces. To solve this, 3ds max 1.0 introduced a special Material class called the Multi/Sub Material which is a top-level holder of up to 1000 (in Max 1.0 even 65535) sub-materials, each one corresponding to a face Material ID. This way, it is possible to assign a "fork" material to the .material property of the node, and there is a sub-material on each spike of the fork corresponding to the face Mat.ID.

So you have to enumerate these sub-materials (which are typically of Standard Material class and have a .diffuse, .ambient and so on properties) and figure out which one corresponds to which Face Material ID. You can throw away the top-level Multi/Sub material after that, as it is only a holder for the bunch of sub-materials.


Ah, I think I see. So which function do I call to pull the submaterials out? And do these directly correspond to the material IDs? So is it something like this:

for m = 1 to obj.numsubs do
(
sub = obj.materialIDList m
amb = sub.ambient
WriteFloat outFile amb.x
WriteFloat outFile amb.y
WriteFloat outFile amb.z
)

I would suggest spending a couple of weeks reading through the MAX Reference (not the SDK, just the user manuals) to get an idea how thing work on the surface before you try to mess with them under the hood. ;)

Yes, I agree that would be wise. Weeks, though, I'm afraid I cannot afford. I had hoped to have my modeller drawing with materials applied by tomorrow (Wednesday) and animations by next week. Perhaps there's a section with a crash course on the specifics of max's object oriented structure? Maybe something like the MFC hierarchy? That would be awsome.

I would normally spend more time researching something like this, however this specific project (or rather half of it as the other half is the exported file's application in my main program) is really my only interest in maxscript. Once I have my exporter written, I doubt I'll return to maxscript any time soon. Perhaps one day if I pursue CG as a hobby, but this project is really only a way to get the data I need in a format that's sensible (ASE files get huge with keyframe animation).

Thanks for your response, Bobo :)

Bobo
03-15-2005, 11:40 PM
Ok, since you are NOT writing a universal translator of materials from Max to anything that is going into to the market tomorrow, let's simplify things :)
As I said, it is POSSIBLE that the order of the materials might not match the IDs, but in 99% of the cases they are orderly numbered as 1,2,3,4,5,6,7,8,9,10...

So you can go the way you suggested, something like (from top of my head, not tested!):

...

fn exportTheMaterial theMat outFile =
(
amb = theMat.ambient --grab the ambient
WriteFloat outFile amb.r --note it is RGB, not XYZ, as it is a color value, not Point3
WriteFloat outFile amb.g --also note RBG are 0-255 integers, not 0.0 -1.0 floats, you might
WriteFloat outFile amb.b --want to divide by 255.0
dif = theMat.diffuse / 255.0 --like this
WriteFloat outFile dif.r
WriteFloat outFile dif.g
WriteFloat outFile dif.b
)

--assuming you have opened the outFile already, you can do for your object's materials:

theMat = obj.material
if classof theMat == MultiMaterial then --if multi-sub, then export its children:
(
WriteShort outFile theMat.numsubs --output the number of materials to follow...
for m = 1 to theMat.numsubs do --loop through the submaterials,
exportTheMaterial theMat[m] outFile --export the sub-material
)
else --if not multi-sub material, export just the top-level material:
(
WriteShort outFile 1
exportTheMaterial theMat outFile
)
...

There are some other material classes that are compounds and do not have diffuse and ambiern on top level, but you are surely not going to use then, just Standard and Multi/Sub ...

CyberSlag5k
03-16-2005, 04:58 PM
Bobo, you are truly the man. Thank you.

CyberSlag5k
03-16-2005, 05:53 PM
Hmm...do I need to specify the type of theMat in the function I declare? I get an error at that top line:

amb = theMat.ambient

It says ambient is an unknown property. I have the function declared above the main portion of my exporter. Should it go below and will I then need to prototype it?

Also, do I need to specify if I want to do floating precision division when I'm dividing the color components by 255? Is that why you've sort of casted that 255 as a float: 255.0. Is that what is preventing integer division from taking place?

Thanks!

Bobo
03-16-2005, 06:53 PM
Hmm...do I need to specify the type of theMat in the function I declare? I get an error at that top line:

amb = theMat.ambient

It says ambient is an unknown property. I have the function declared above the main portion of my exporter. Should it go below and will I then need to prototype it?

Also, do I need to specify if I want to do floating precision division when I'm dividing the color components by 255? Is that why you've sort of casted that 255 as a float: 255.0. Is that what is preventing integer division from taking place?

Thanks!

Post the complete error message.
It should have said "Unknown property .ambient in ..." so we can see what exactly was passed as theMat. If an object does not have a material, it would say "Unknown property .ambient in undefined", if for some reason the material was a class that did not have .ambient, you could catch that by first asking whether the material has a property called ambient or use an error trap.

Either

if hasProperty theMat "ambient" then amb = theMat.ambient else amb = [0,0,0]

or

try(amb = theMat.ambient)catch(amb = [0,0,0])

Yes, the reason for casting the divisor as float was to avoid integer divisions. You should be safe doing color divisions by 255.0, the results should be floating point color channels between 0.0 and 1.0.

Cheers,
Bobo

CyberSlag5k
03-16-2005, 09:08 PM
The error reads:


--Unknown propery: "ambient" in test: Ink__n_paint


I guess the interpreter cannot infer that theMat is supposed to be of type material based on its parameter list. It certainly has nothing else go on. Here's my code:


fn exportMaterial tempMat file =
(
amb = tempMat.ambient
dif = tempMat.diffuse
spe = tempMat.specular

WriteFloat file (amb.r / 255.0)
WriteFloat file (amb.g / 255.0)
WriteFloat file (amb.g / 255.0)

WriteFloat file (dif.r / 255.0)
WriteFloat file (dif.g / 255.0)
WriteFloat file (dif.g / 255.0)

WriteFloat file (spe.r / 255.0)
WriteFloat file (spe.g / 255.0)
WriteFloat file (spe.g / 255.0)

WriteFloat file tempMat.glossiness
)

theObj = pickObject()

if superclassof theObj == GeometryClass do
(
tmesh = snapshotasmesh theObj
invTran = inverse theObj.transform
file = fopen "C:\\stuff2.dat""wb"

num_verts = tmesh.numverts
num_faces = tmesh.numfaces

WriteLong file num_verts
WriteLong file num_faces

for v = 1 to num_verts do
(
tempVert = (getVert tmesh v) * invTran
WriteFloat file tempVert.x
WriteFloat file tempVert.y
WriteFloat file tempVert.z

tempNorm = (getNormal tmesh v) * invTran
WriteFloat file tempNorm.x
WriteFloat file tempNorm.y
WriteFloat file tempNorm.z
)

for f = 1 to num_faces do
(
tempFace = getFace tmesh f
matID = getFaceMatId tmesh f

WriteLong file (tempFace.x - 1) #unsigned
WriteLong file (tempFace.y - 1) #unsigned
WriteLong file (tempFace.z - 1) #unsigned

WriteLong file matID
)

tempMat = theObj.material
if classof tempMat == MultiMaterial then
(
WriteShort file tempMat.numsubs
for m = 1 to tempMat.numsubs do
exportMaterial tempMat[m] file
)

else
(
WriteShort file 1
exportMaterial tempMat file
)
fclose file
)


Thanks for your help!

EDIT: A quick check to the classof tempMat resolved the problem quite nicely. Now to adapt the model loading code and see if that actually worked :)

CGTalk Moderation
03-16-2005, 09:08 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.