#4 Fix for traversal of multi-material hierarchy

open
nobody
None
5
2008-10-20
2008-10-20
hoey
No

I've found a problem in version 3.05b (it may exist in previous versions, though I haven't checked) where the mapping of material instances to materials is incorrect. I believe this problem will occur in many cases where multi-materials are in use.

There is an open bug (https://sourceforge.net/tracker/index.php?func=detail&aid=1965418&group_id=136478&atid=735983) that mentions this exact problem. I believe the fix suggested there is incomplete, however, since the problem is not limited to MaterialExporter::GetSubMaterialById. It also extends to the way material IDs are mapped to materials in GeometryExporter::FlattenMaterials.

Based on a great deal of experimentation with 3ds Max, here is what I believe to be the case with the way Max maps multi-materials to material indices that it uses on individual mesh faces:

Using the material of a face, max starts with the material of the mesh. If the material is not a multi-material, max uses that material. If the material is a multi-material, max uses the face material index mod the number of sub-materials of that material to obtain a sub-material. If that sub-material is not a multi-material, max uses that sub-material. If the sub-material is a multi-material, max uses the face material index mod the number of sub-materials of that sub-material to find the sub-material at that level as well. Max will continue in this way until it finds a material that is not a multi-material.

This algorithm would appear to make some sub-materials inaccessible. For instance, if you have a top level material that is a multi-material and you add a standard material in the first slot, a multi-material in the second slot and a standard material in the third slot, then you create 3 standard materials as sub-materials of the multi-material in the second slot, there should be no way to access the 1st or 3rd sub-material of the multi-material in the second slot. This is true, and in fact unless you drag and drop one of those inaccessible materials, there's no way to assign a material ID to a face on a mesh that uses either of those two materials. It turns out that as soon as you drag and drop an otherwise inaccessible sub-material to a selected face in max (as long as there are still other materials assigned to other faces) max will fabricate a new top level material that contains all of the same materials and sub-materials as the original but with the inaccessible material appended to the top level multi-material.

As an example, create a material as described above, assigning unique ambient colors to each standard material. Create a sphere and add a editable mesh. Select a face of the mesh. For whatever reason, max defaults the material ID of all the faces of a sphere to '2'. If you assign the top level material to the sphere, the whole sphere will be drawn with the second sub-material of the second sub-material of the top level material. If you assign the material ID '1' or '3' to a face, max will draw that face with the first or third sub-material of the top level material. If you assign the material ID '4' to a face, max will draw that face with the first sub-material of the top level material. If you select another face and drag the first sub-material of the second sub-material of the top level material, max will draw that face with the material that was dropped on it and assign it material ID '5'. It will also draw the face with material ID '4' with a default gray material. This is because max is no longer using the top level material that was originally assigned to the sphere. It has created a new top level material with a default standard material with index '4' and a copy at index '5' of the first sub-material of the second sub-material of the original top level material. You can verify this by selecting a new material in the material editor and using the 'Pick Material from Object' tool.

The existing algorithm in GeometryExporter::FlattenMaterials fails to pass the sub-material index of the top level material when flattening child multi-materials. I believe this results in the first sub-material of multi-materials that are children of a multi-material. This is only correct when recursing into a multi-material that happens to be the first child of a top level multi-material.

The situation gets more complicated by the fact that the 3ds Max UI allows you to assign material indices to sub-materials. The existing algorithm in MaterialExporter::GetSubMaterialById works fine as long as the sub-materials have not been re-ordered using the UI.

It turns out that the Mtl::GetSubMtl method returns the materials in the order that they are assigned unique IDs in the UI, not in the order that they are displayed in the UI. The technique used in the existing implementation of MaterialExporter::GetSubMaterialById might be useful for recreating the list of materials as displayed in the UI, but it's not useful for traversing a tree of multi-materials.

I've created a simple example box.max file that demonstrates at least one aspect of the problem. In the example, there are two boxes. Both boxes have material ID '2' assigned to the faces of the top and material ID '1' assigned to the rest of the faces. The first box (called 'good_box') is assigned a material with two standard sub-materials. The first sub-material is assigned ID '1' and is a standard material with ambient color yellow. The second sub-material is assigned ID '2' and is a standard material with ambient color green. The second box (called 'bad_box') is assigned a different material, also with two sub-materials. The first sub-material is assigned ID '2' and is a standard material with ambient color red. The second sub-material is assigned ID '1' and is a standard material with ambient color blue. If this max file is exported with version 3.05b, the instance_material references of the visual_scene nodes for the good_box have a symbol that matches the target. However, the instance_material references for the bad_box are backwards. The instance_material with symbol="blue_mtl" has target="#red_mtl" and the instance_material with symbol="red_mtl" has target="#blue_mtl".

The attached zip file contains the example box.max file, a fix for both problems I've described above in multimaterial.patch, the erroneous output of ColladaMax exporter version 3.05b in prefixbox.dae, and the output of the the ColladaMax exporter that includes the fix in fixedbox.dae.

I've only tested these changes with 3ds Max 2008 and I've not tested it with external reference materials.

Discussion

  • hoey
    hoey
    2008-10-20

    Example of problem and patch containing fix

     
    Attachments