[Closed] Materials to XML
Here’s a bit of code I’ve been working on to save materials to XML format.
Since I’ve started working on MatManager I’ve been feeling lees and lees impressed with the .mat format, and since I’d like to be able to save my own data along with the materials I figured why not create my own format.
Normally MAX allows you to save materials to a .mat file, however this format is not backwards compatible, thus making it impossible to (for example) to open a file saved in MAX 2009 in MAX 9. This script makes it possible to circumvent that “limitation”…
Since this has been common complaint amongst MAX users I figured someone might benefit from the code. I’ll add an importer a maybe a GUI once I have some time.
/*********************
MatManager XML Exporter
**********************/
--By Marco Brunetta
--UNSUPPORTED: Curve Control // Color Gradient // SubMaterials
--ADD: Multi/Sub Material support & Blend material support
--ADD: keep undefinedClass from saving data
--ADD: check if the propertyElement has subProperties, if it does do not assign an inner text (should be able to remove a couple fo the classes when writing the XML using this)
(
dotNet.loadAssembly "system.xml" --Loading of XML assembly
local theMat = (getMeditMaterial 1) --The Material to save
local theSavePath = ("D:\\TEMP\\"+theMat.name+".mmmat") --The path where the XML will be saved, chenge ".mmm" to ".txt" if you want to check the contents
STRUCT propertyElement --Object used for gathering material info
(
name,
type,
value,
subProperties
)
fn collectProperties theObject = --Function that gathers the material properties (techinically it could be used for any object I guess)
(
local collectedProperties =#()
local thePropNames = (getPropNames theObject)
FOR propName in thePropNames DO
(
local propertyValue = getProperty theObject propName
local thePropElement = propertyElement name:propName type:(classOf propertyValue) value:propertyValue subProperties:#()
IF (TRY (getPropNames thePropElement.value).count CATCH(0)) > 0 DO
(
thePropElement.subProperties = collectProperties thePropElement.value
)
IF thePropElement.type == ArrayParameter DO
(
local oldProperties = thePropElement.value
thePropElement.value = #()
FOR oldProp in oldProperties DO
(
local arrayPropelement = propertyElement name:"arrayItem" type:(classOf oldProp) value:oldProp subProperties:#()
IF (TRY (getPropNames oldProp).count CATCH(0)) > 0 DO
(
arrayPropelement.subProperties = collectProperties arrayPropelement.value
)
append thePropElement.value arrayPropelement
)
)
append collectedProperties thePropElement
)
collectedProperties
)
local theProperties = collectProperties theMat
local xmlDoc = dotNetObject "system.xml.xmlDocument"
local theRoot = xmlDoc.createElement "MatManagerMaterial"
theRoot.setAttribute "type" ((classOf theMat) as string)
xmlDoc.appendChild theRoot
fn createPropertyNode propElement = --functions that writes the properties into XML format
(
local XMLNode = xmlDoc.createElement (propElement.name as string)
XMLNode.setAttribute "type" (propElement.type as string)
CASE propElement.type OF
(
default:
(
IF classOf propElement.type == textureMap THEN --saving of textureMaps
(
local theNameNode = xmlDoc.createElement "name"
theNameNode.innerText = propElement.value.name
theNameNode.setAttribute "value" "String"
XMLNode.appendChild theNameNode
FOR subProp in propElement.subProperties DO
(
local newSubProp = createPropertyNode subProp
XMLNode.appendChild newSubProp
)
)
ELSE --saving of normal stuff (blooean, integer, float, etc)
(
XMLNode.innerText = (propElement.value as string)
FOR subProp in propElement.subProperties DO
(
local newSubProp = createPropertyNode subProp
XMLNode.appendChild newSubProp
)
)
)
StandardUVGen: --saving of 2d coordenates
(
FOR subProp in propElement.subProperties DO
(
local newSubProp = createPropertyNode subProp
XMLNode.appendChild newSubProp
)
)
StandardXYZGen: --saving of 3d coordenates
(
FOR subProp in propElement.subProperties DO
(
local newSubProp = createPropertyNode subProp
XMLNode.appendChild newSubProp
)
)
StandardTextureOutput: --saving of texture output
(
FOR subProp in propElement.subProperties DO
(
local newSubProp = createPropertyNode subProp
XMLNode.appendChild newSubProp
)
)
Point2: --saving of point2D
(
local xProp = xmlDoc.createElement "X"
xProp.innerText = (propElement.value[1]) as string
XMLNode.appendChild xProp
local yProp = xmlDoc.createElement "Y"
yProp.innerText = (propElement.value[2]) as string
XMLNode.appendChild yProp
)
Point3: --saving of point3d
(
local xProp = xmlDoc.createElement "X"
xProp.innerText = (propElement.value[1]) as string
XMLNode.appendChild xProp
local yProp = xmlDoc.createElement "Y"
yProp.innerText = (propElement.value[2]) as string
XMLNode.appendChild yProp
local zProp = xmlDoc.createElement "Z"
zProp.innerText = (propElement.value[3]) as string
XMLNode.appendChild zProp
)
Matrix3: --saving of matrix3
(
local matrixA = xmlDoc.createElement "A"
local aXProp = xmlDoc.createElement "X"
aXProp.innerText = (propElement.value[1][1]) as string
matrixA.appendChild aXProp
local aYProp = xmlDoc.createElement "Y"
aYProp.innerText = (propElement.value[1][2]) as string
matrixA.appendChild aYProp
local aZProp = xmlDoc.createElement "Z"
aZProp.innerText = (propElement.value[1][3]) as string
matrixA.appendChild aZProp
XMLNode.appendChild matrixA
local matrixB = xmlDoc.createElement "B"
local bXProp = xmlDoc.createElement "X"
bXProp.innerText = (propElement.value[2][1]) as string
matrixB.appendChild bXProp
local bYProp = xmlDoc.createElement "Y"
bYProp.innerText = (propElement.value[2][2]) as string
matrixB.appendChild bYProp
local bZProp = xmlDoc.createElement "Z"
bZProp.innerText = (propElement.value[2][3]) as string
matrixB.appendChild bZProp
XMLNode.appendChild matrixB
local matrixC = xmlDoc.createElement "C"
local cXProp = xmlDoc.createElement "X"
cXProp.innerText = (propElement.value[3][1]) as string
matrixC.appendChild cXProp
local cYProp = xmlDoc.createElement "Y"
cYProp.innerText = (propElement.value[3][2]) as string
matrixC.appendChild cYProp
local cZProp = xmlDoc.createElement "Z"
cZProp.innerText = (propElement.value[3][3]) as string
matrixC.appendChild cZProp
XMLNode.appendChild matrixC
local matrixD = xmlDoc.createElement "D"
local dXProp = xmlDoc.createElement "X"
dXProp.innerText = (propElement.value[4][1]) as string
matrixD.appendChild dXProp
local dYProp = xmlDoc.createElement "Y"
dYProp.innerText = (propElement.value[4][2]) as string
matrixD.appendChild dYProp
local dZProp = xmlDoc.createElement "Z"
dZProp.innerText = (propElement.value[4][3]) as string
matrixD.appendChild dZProp
XMLNode.appendChild matrixD
)
Color: --saving of colors
(
local newRProp = xmlDoc.createElement "Red"
newRProp.innerText = propElement.value.r as string
XMLNode.appendChild newRProp
local newGProp = xmlDoc.createElement "Green"
newGProp.innerText = propElement.value.g as string
XMLNode.appendChild newGProp
local newBProp = xmlDoc.createElement "Blue"
newBProp.innerText = propElement.value.b as string
XMLNode.appendChild newBProp
local newAProp = xmlDoc.createElement "Alpha"
newAProp.innerText = propElement.value.a as string
XMLNode.appendChild newAProp
)
ArrayParameter: --saving of Arrays
(
FOR arrayProp in propElement.value DO
(
local newArrayProp = createPropertyNode arrayProp
XMLNode.appendChild newArrayProp
)
)
)
return XMLNode
)
local theNameNode = xmlDoc.createElement "name" --create name node
theNameNode.innerText = theMat.name
theNameNode.setAttribute "value" "String"
theRoot.appendChild theNameNode
FOR propElement in theProperties DO --save the material properties
(
local newNode = createPropertyNode propElement
theRoot.appendChild (xmlDoc.importNode newNode true)
)
xmlDoc.save theSavePath --save the XML file
)
While not everything is 100% supported, the script should be able to save any material type with any number of maps. Support for submaterials has not been added yet.
Opinions welcomed.
seems like a more code-agnostic form of bobo’s file format, in a way, at least as to its purpose.
we coded something similar to BFF for materials and unfortunately one of the major hurdles in this is that simply not everything -can- be accessed (read and/or write) from maxscript. it’s still great for all the basic materials, but add in something common as a gradient ramp, or a mix map with output curve tweaking, and there goes the connection
Code-agnostic? What delightfully convoluted assessment of my mad coding skillz :p. Yhea I know, some parts are bit too “hardcodedlish” and some are just darn fugly. It was just a day’s work mate cut me some slack.
Anyways, yes I noticed the limitations…however being able to share 80% of the material with older versions of max is still better than no sharing at all.
Hey Marco, have you checked this out? http://forums.cgsociety.org/showthread.php?f=98&t=618821&highlight=material+xml
Maybe it can help you out and save you some time coding.
Cheers and good work.
I actually had a quick look after I posted this… might have been a good idea to do it before.
I don’t normally use Mental Ray materials though, so it’s better for me to have something more “generic”.