Halloween Costume ideas 2015

PYTHON / Additional Examples and Usages of XML

In previous post, I talked a lot about XML and probably some of you got confused on how we can really use this XML writing and reading knowledge to do export import of data.

This is fine because with the XML Read and Write, you really have a lot of concepts to digest:
  • Using Element Tree XML built in module
  • Concept of XML: tag, element
  • Using File function of Python to write out to file
  • Using and constructing data that is like Dictionary: keys, items, values
  • Data type conversions
  • Understanding of Path Location of XML file in your Operating Systems
This XML concept is also still fairly new to me and I only understand the basic to write out the XML style format and read it back in.

Take it easy and slowly with this XML. See the pattern.

However as we know, even the basic knowledge can be really useful. I will further expand the idea from previous post. Feel free to ask me questions if you don't have clue what I am talking about.

DIY Write & Read of XML

For this, I will start by copying and pasting the template construct from previous post on XML and modify it slightly each time for each example below. It would be better to understand the xml.etree module, but we could also simply use the construct as long you know what it does in the background.

We will definitely use some simple BPY to collect information and data we needed to export about the 3D scenes. Our focus is on how the data can flow out of Blender and bring it in back into Blender or to other 3D packages.

Remember that Python scripting, like any other programming language, is something you learn and master by actually practicing it and making mistake and fixing it.

If you understand my style of blog writing, I actually improvise while writing. Often I do not actually plan the topic. This blog is more like Journal. I explore certain feature, I am testing and documenting each steps and then I will write it down. I guess you could do the same thing.

Hopefully with this examples, you can start to get curious and try your own XML export import.

It will get easier every time you try it yourself. My examples are there to assist you. Try doing the same thing without looking at my examples and you will start to understand.

Keep digging the Python language and Python in Blender, it will be worth it.

EXAMPLE 1: XML Transform Information (Position, Rotation, Scale) of objects

The idea is to get the Name, Translations, Rotations and Scales data of every selected objects and writing it out as XML. Below is such example of code. It can be optimized, but below should work and the code is pretty easy to read.

Maybe I have bunch of Boxes in random Position, Rotation, Scale in Blender that I want to export out as XML data.



WRITE OUT:


'''
Taken from: Blender Sushi Blog
Code by: Jimmy Gunawan
Last update: 20130420
'''





import bpy
from xml.etree import cElementTree as ElementTree

# PRETTY PRINT
# function to pretty print the XML code
def prettyPrint(element, level=0):
'''
Printing in elementTree requires a little massaging
Function taken from elementTree site:
http://effbot.org/zone/element-lib.htm#prettyprint

'''
indent = '\n' + level * ' '
if len(element):
if not element.text or not element.text.strip():
element.text = indent + ' '

if not element.tail or not element.tail.strip():
element.tail = indent

for element in element:
prettyPrint(element, level + 1)

if not element.tail or not element.tail.strip():
element.tail = indent

else:
if level and (not element.tail or not element.tail.strip()):
element.tail = indent

return element

### Collecting some information data about the scene ###

# In this example we collect the selected object Transforms
# aka Position (Translation/Location), Rotation, and Scale XYZ
# and writing out the data as XML format

# Create ROOT node
rootNode = ElementTree.Element('world')

# Get Selected Objects
selected_objects = bpy.context.selected_objects

for object in selected_objects:
myLocs = object.location
myRots = object.rotation_euler
myScales = object.scale

# Create node = CHILD element
elementNode = ElementTree.Element('{object}'.format(object=object.name))

# Parent CHILD node to ROOT
rootNode.append(elementNode)

# Adding attribute translations
elementNode.attrib['TX'] = str(myLocs[0])
elementNode.attrib['TY'] = str(myLocs[1])
elementNode.attrib['TZ'] = str(myLocs[2])

# Adding attribute rotations
elementNode.attrib['RX'] = str(myRots[0])
elementNode.attrib['RY'] = str(myRots[1])
elementNode.attrib['RZ'] = str(myRots[2])

# Adding attribute scales
elementNode.attrib['SX'] = str(myScales[0])
elementNode.attrib['SY'] = str(myScales[1])
elementNode.attrib['SZ'] = str(myScales[2])

# Adding text
elementNode.text = '{object}'.format(object=object.name)


### WRITING OUT IN A CLEAN WAY IS A BIT COMPLEX... WE USE THE PRETTY PRINT

# Pretty print the rootNode
prettyPrint(element=rootNode)

# We need to convert to STRING before we can see the whole thing
xmlText = ElementTree.tostring(rootNode)

# FINAL RESULT, we need to append the HEADER
print ( '<?xml version="1.0" ?>\n{xmlText}'.format(xmlText=xmlText) )

### NOW WE LIKE TO WRITE OUT THE XML TO FILE
outputPath = r'C:\pythonInBlender\transOut.xml'

fileObject = open(outputPath, 'w')
fileObject.write('<?xml version="1.0" ?>\n' + xmlText.decode('utf8'))
fileObject.close()

print('XML file has been written in here: {outputPath}'.format(outputPath=outputPath))









THE XML:



READ IN:


'''
Taken from: Blender Sushi Blog
Code by: Jimmy Gunawan
Last update: 20130420
'''





import bpy

from xml.etree import cElementTree as ElementTree

xmlPath = 'C:/pythonInBlender/transOut.xml'

xmlRoot = ElementTree.parse(xmlPath).getroot()


def applyTransform():

for element in xmlRoot:

# Get the NAME of objects
objectName = element.tag
print(objectName)

# Tell Blender to get the current object data by its name
object = bpy.data.objects['{objectName}'.format(objectName=objectName)]
print(object)

# Get all element keys
myKeys = element.keys()
# print(myKeys)

# Get Position, Rotation and Scale data
posX, posY, posZ = float(element.get('TX')), float(element.get('TY')), float(element.get('TZ'))
rotX, rotY, rotZ = float(element.get('RX')), float(element.get('RY')), float(element.get('RZ'))
scaleX, scaleY, scaleZ = float(element.get('SX')), float(element.get('SY')), float(element.get('SZ'))

# Apply Position, Rotation and Scale data to corresponding object by its name
object.location = posX, posY, posZ
object.rotation_euler = rotX, rotY, rotZ
object.scale = scaleX, scaleY, scaleZ

applyTransform()









The code above is very specific about Object Name and that is just one way we can do this. If for the same objects we reset their position, we can then run the script and magically put them back into positions specified from XML.

The above example is not particularly amazing example because we can achieve the same thing using DAE Collada Export Import, for example. Although with DAE, we usually have limited options and flexibility.

We could bring the XML data to other 3D package and actually do ANYTHING with it. Keep in mind that the World XYZ Orientation might be different in other package.

This kind of code is probably good for saving out "Presets"or maybe "Character Poses". I have not looked at it deeply. It has potential for Assembly script. Although Blender File System and Referencing are actually already pretty cool and we can probably do the script based on that system. But if we want to bring it outside and inside other package, then XML seems to be nice.

From the above basic example, we can take the concept to place some Monkeys in the 3D scene, if we can think of every Cubes as Bounding Box:


READ IN, BUT CAST AS MONKEYS:


'''
Taken from: Blender Sushi Blog
Code by: Jimmy Gunawan
Last update: 20130420
'''





import bpy

from xml.etree import cElementTree as ElementTree

xmlPath = 'C:/pythonInBlender/transOut.xml'

xmlRoot = ElementTree.parse(xmlPath).getroot()


def createMonkeyFromData():

for element in xmlRoot:

objectName = element.tag

if 'TX' in element.keys():

# Get Position Data
posX, posY, posZ = float(element.get('TX')), float(element.get('TY')), float(element.get('TZ'))

# Create Suzanne based at position XYZ
bpy.ops.mesh.primitive_monkey_add( location=(posX, posY, posZ) )

monkey = bpy.context.selected_objects[0]
print (monkey)

# Get Rotation and Scale data
rotX, rotY, rotZ = float(element.get('RX')), float(element.get('RY')), float(element.get('RZ'))
scaleX, scaleY, scaleZ = float(element.get('SX')), float(element.get('SY')), float(element.get('SZ'))

# Rotate and Scale monkey accordingly from data
monkey.rotation_euler = rotX, rotY, rotZ
monkey.scale = scaleX, scaleY, scaleZ


createMonkeyFromData()










NOT EXACT DIMENSION SIZE
We apply the Transform data from the Cubes into the Monkeys, although is not really the accurate Bounding Box or Dimension data. Because some parts of the monkey is poking out.

If you are questioning about it.We could export out the Dimension data as XML from the first place and apply it to the monkeys to get more accurate result.

Example on how to get dimension data of object to pass on:
bpy.data.objects[{object_name}].dimensions
bpy.data.objects['Cube'].dimensions

EXAMPLE 2: Reading In Custom Attribute Data

Let say we have XML data with arbitrary Custom Attribute and values. We did not create the date from Blender. Someone else did it. Maybe we grab the data from the Internet.


The XML data can be anything:
1. Yahoo! Weather XML data
  • City Location
  • Temperature High
  • Temperature Low
  • Today's Date
2. Facebook XML data
  • Person's Name
  • Person's Photo
  • Person's Age
  • Person's Likes
3. Google Map data
  • Latitude
  • Longitude
  • Name of State
  • etc
What we can do is simply READING those data and then visualizing it in Blender. 

A simple example, XML like below:

<world>

<Object1 COLOR="red" SIZE="5.0"/>

<Object2 COLOR="green" SIZE="2.0"/>

<Object3 COLOR="blue" SIZE="0.5"/>
</world>

OR MORE PROPERLY:

<?xml version="1.0" ?>
<world>
  <Object1 COLOR="red" SIZE="5.0"></Object1>
  <Object2 COLOR="green" SIZE="2.0"></Object2>
  <Object3 COLOR="blue" SIZE="0.5"></Object3>
</world>

(Save the bottom one as customData.xml.)

Ok, let say from those data, I will apply it to the Monkey.001, Monkey.002, and Monkey.003 objects in the 3D scene that I have prepared.

The Suzanne Three Sisters
What I am trying to do is to tell Blender:
Object1 --> Monkey.001 --> apply SIZE, apply MATERIAL with Diffuse Color = COLOR
Object2 --> Monkey.002 --> apply SIZE, apply MATERIAL with Diffuse Color = COLOR
Object3 --> Monkey.003 --> apply SIZE, apply MATERIAL with Diffuse Color = COLOR

The script can be as below:


READ IN CUSTOM XML


'''


Taken from: Blender Sushi Blog


Code by: Jimmy Gunawan


Last update: 20130420


'''





import bpy

from xml.etree import cElementTree as ElementTree

xmlPath = 'C:/pythonInBlender/customData.xml'

xmlRoot = ElementTree.parse(xmlPath).getroot()


def applyColorToMonkey():

for element in xmlRoot:

# Create Materials with Color based on the NAME
colorname = element.get('COLOR')
mat = bpy.data.materials.new(name='{colorname}'.format(colorname=colorname))

# Create our own custom dictionary of color
colorDict = {'red':[1.0,0.0,0.0], 'green':[0.0,1.0,0.0], 'blue':[0.0,0.0,1.0]}

# Assign specified Diffuse Color into Material
myColor = colorDict.get(colorname)
bpy.data.materials[colorname].diffuse_color = myColor

# Get the NAME of objects
objectName = element.tag
objectNumber = int(objectName.strip('Object'))
monkeyName = 'Monkey.{objectNumber:03}'.format(objectNumber=objectNumber)

print (monkeyName)

# Tell Blender to get the monkey data by its name
object = bpy.data.objects['{monkeyName}'.format(monkeyName=monkeyName)]

# Assign our Material to corresponding Monkey
object.data.materials.append(mat)

def applyScaleToMonkey():

for element in xmlRoot:

# Get the NAME of objects
objectName = element.tag
objectNumber = int(objectName.strip('Object'))
monkeyName = 'Monkey.{objectNumber:03}'.format(objectNumber=objectNumber)

print (monkeyName)

# Tell Blender to get the monkey data by its name
object = bpy.data.objects['{monkeyName}'.format(monkeyName=monkeyName)]

print(object)

# Get SIZE data and assign it to Scale Variables
scaleX, scaleY, scaleZ = float(element.get('SIZE')), float(element.get('SIZE')), float(element.get('SIZE'))

# Apple Scale to Monkeys
object.scale = scaleX, scaleY, scaleZ


applyColorToMonkey()
applyScaleToMonkey()







If the above code run as planned, we are getting something like below. This is the power of XML and Python and Python in Blender.

We should be able to:
  • Write XML data (free the data to the ouside)
  • Read XML data (from any source)
  • Process the data
  • Apply the data

The code above can be further cleaned up and probably expanded
  • Error Checking
  • Check if object exist
  • Making sure Blender does not keep generating same Material Name when we run the script multiple times.

FURTHER ON WITH XML

See if you can play with the idea further more:
  • Assigning "specific Texture Name" for each Material to be assigned into "specific" objects
  • Procedurally generate XML data 
  • Can we perhaps use other tool like Processing to generate XML and then bring it into Blender?
  • Turn it into Animation, let say if we have XML data in corresponding to Frame Number.
  • Find some XML data from Internet and bring it into Blender and visualize it.
  • Almost "Real time" XML data with refresh?

OTHER VIDEO TUTORIAL RELATED TO XML

Luiz Kruel @Eat3D

Based from his video tutorial, below is the converted Blender Script that does the XML export using MINIDOM.

from xml.dom.minidom import Document
import bpy

doc = Document()

root_node = doc.createElement("Blender Scene Info")
doc.appendChild(root_node)

# Get selected objects
selection = bpy.context.selected_objects

# Just print out selected objects name
for num, item in enumerate(selection):
    print (num, item.name)

# Add into XML tree:

for object in selection:
    object_node = doc.createElement(str(object.name))
    root_node.appendChild(object_node)

    object_locX = object.location.x
    object_locY = object.location.y
    object_locZ = object.location.z
    object_node.setAttribute("locationX", str(object_locX))
    object_node.setAttribute("locationY", str(object_locY))
    object_node.setAttribute("locationZ", str(object_locZ))
    
print()
print()
print()

xml_file = open("C:/test.xml", "w")
xml_file.write(doc.toprettyxml())
xml_file.close()

print ( doc.toprettyxml() )


Post a Comment

MKRdezign

Contact Form

Name

Email *

Message *

Powered by Blogger.
Javascript DisablePlease Enable Javascript To See All Widget