Advertisement
Guest User

Blender Xaml Exporter

a guest
Feb 18th, 2018
429
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 36.47 KB | None | 0 0
  1. # Microsoft Permissive License (Ms-PL)
  2. #
  3. # This license governs use of the accompanying software. If you use the
  4. # software, you accept this license. If you do not accept the license, do not
  5. # use the software.
  6. #
  7. # 1. Definitions
  8. #
  9. # The terms "reproduce," "reproduction," "derivative works," and "distribution"
  10. # have the same meaning here as under U.S. copyright law.
  11. #
  12. # A "contribution" is the original software, or any additions or changes to the
  13. # software.
  14. #
  15. # A "contributor" is any person that distributes its contribution under this
  16. # license.
  17. #
  18. # "Licensed patents" are a contributor's patent claims that read directly on
  19. # its contribution.
  20. #
  21. # 2. Grant of Rights
  22. #
  23. # (A) Copyright Grant- Subject to the terms of this license, including the
  24. # license conditions and limitations in section 3, each contributor grants you
  25. # a non-exclusive, worldwide, royalty-free copyright license to reproduce its
  26. # contribution, prepare derivative works of its contribution, and distribute
  27. # its contribution or any derivative works that you create.
  28. #
  29. # (B) Patent Grant- Subject to the terms of this license, including the license
  30. # conditions and limitations in section 3, each contributor grants you a non-
  31. # exclusive, worldwide, royalty-free license under its licensed patents to make,
  32. # have made, use, sell, offer for sale, import, and/or otherwise dispose of its
  33. # contribution in the software or derivative works of the contribution in the
  34. # software.
  35. #
  36. # 3. Conditions and Limitations
  37. #
  38. # (A) No Trademark License- This license does not grant you rights to use any
  39. # contributors' name, logo, or trademarks.
  40. #
  41. # (B) If you bring a patent claim against any contributor over patents that you
  42. # claim are infringed by the software, your patent license from such contributor
  43. # to the software ends automatically.
  44. #
  45. # (C) If you distribute any portion of the software, you must retain all
  46. # copyright, patent, trademark, and attribution notices that are present in the
  47. # software.
  48. #
  49. # (D) If you distribute any portion of the software in source code form, you may
  50. # do so only under this license by including a complete copy of this license
  51. # with your distribution. If you distribute any portion of the software in
  52. # compiled or object code form, you may only do so under a license that complies
  53. # with this license.
  54. #
  55. # (E) The software is licensed "as-is." You bear the risk of using it. The
  56. # contributors give no express warranties, guarantees or conditions. You may
  57. # have additional consumer rights under your local laws which this license
  58. # cannot change. To the extent permitted under your local laws, the contributors
  59. # exclude the implied warranties of merchantability, fitness for a particular
  60. # purpose and non-infringement.
  61. #
  62. # Co-Author support and thanks too:
  63. #
  64. #  Robert Ludewig
  65. #  Leslie Godwin
  66. #  Tiaan Geldenhuys
  67. #
  68.  
  69. bl_info = {
  70.     "name": "XAML format (.xaml)",
  71.     "author": "Daniel Lehenbauer and Robert Hogue",
  72.     "version": (0, 0, 48),
  73.     "blender": (2, 71, 0),
  74.     "location": "File > Export > XAML",
  75.     "description": "Export mesh to XAML",
  76.     "warning": "",
  77.     "wiki_url": "https://archive.codeplex.com/?p=xamlexporter",
  78.     "category": "Import-Export"}
  79.  
  80. from io import StringIO
  81. from math import *
  82.  
  83. import bpy
  84.  
  85. #import Blender
  86. #from Blender import Scene
  87. #from Blender import Texture
  88. #from Blender import Mathutils
  89. #from Blender import Lamp
  90. #import BPyMesh
  91. #import BPyObject
  92.  
  93. # Ignore rotations which are less than 1/2 a degree
  94. _rotationLimit = 0.5
  95.  
  96. #
  97. #  Script Configuration
  98. #
  99.  
  100. #def getConfig():
  101. #    return Blender.Registry.GetKey("Xaml_Export", True)
  102.  
  103. #def getBatchExportFilename():
  104. #    config = getConfig()
  105. #    if config:
  106. #        return config["Batch_Export"]
  107. #    return;
  108.  
  109. #
  110. #  Debug
  111. #
  112.  
  113. TraceLevels = {"None": 0, "Error": 1, "Warning": 2, "Trace": 3, "Verbose": 4}
  114. DEBUG=TraceLevels["Warning"]
  115. DEBUG_INDENT=0
  116.  
  117. def debugPrint(level, message):
  118.     global DEBUG, DEBUG_INDENT
  119.     if level <= DEBUG:
  120.         for i in range(DEBUG_INDENT):
  121.             print("   "),
  122.         print(message)
  123.  
  124. def indent():
  125.     global DEBUG_INDENT
  126.     DEBUG_INDENT += 1
  127.    
  128. def unindent():
  129.     global DEBUG_INDENT
  130.     DEBUG_INDENT -= 1
  131.  
  132. def verbose(message):
  133.     debugPrint(TraceLevels["Trace"], message)
  134.  
  135. def trace(message):
  136.     debugPrint(TraceLevels["Trace"], message)
  137.  
  138. def traceMatrix(matrix):
  139.     indent()
  140.     for i in xrange(4):
  141.         str = ""
  142.         for j in xrange(4):
  143.             str += "%f" % matrix[i][j]
  144.             if j < 3: str += ","
  145.         trace(str)
  146.     unindent()
  147.  
  148. def warn(message):
  149.     debugPrint(TraceLevels["Warning"], message)
  150.  
  151. def error(message):
  152.     debugPrint(TraceLevels["Error"], message)
  153.        
  154. #
  155. #  Simple XamlWriter
  156. #
  157. class XamlWriter :
  158.     def __init__(self):
  159.         self.indentLevel = 0
  160.         self.tagStack = []
  161.         self.singleLineElement = True
  162.         self.outfile = StringIO()
  163.    
  164.     def writeIndentation(self):
  165.         for i in range(self.indentLevel):
  166.              self.outfile.write("    ")
  167.    
  168.     def beginElement(self, name):
  169.         if len(self.tagStack) > 0 and self.singleLineElement:
  170.             self.outfile.write(">\n")
  171.         self.writeIndentation()
  172.         self.singleLineElement = True
  173.         self.indentLevel += 1
  174.         self.outfile.write("<" + name)
  175.         self.tagStack.append(name)
  176.  
  177.     def endElement(self):
  178.         name = self.tagStack.pop()
  179.         self.indentLevel -= 1
  180.         if self.singleLineElement:
  181.             self.outfile.write("/>\n")
  182.         else:
  183.             self.writeIndentation()
  184.             self.outfile.write("</" + name + ">\n")
  185.         self.singleLineElement = False
  186.        
  187.     def writeAttribute(self, name, value):
  188.         self.outfile.write(" " + name + '="' + value + '"')
  189.  
  190.     def writeFragment(self, fragment):
  191.         if fragment == "":
  192.             return
  193.            
  194.         if len(self.tagStack) > 0 and self.singleLineElement:
  195.             self.outfile.write(">\n")
  196.             self.singleLineElement = False
  197.  
  198.         lines = fragment.split("\n")
  199.         for line in lines:
  200.             self.writeIndentation()
  201.             self.outfile.write(line)
  202.             self.outfile.write("\n")
  203.  
  204.     def toString(self):
  205.         return self.outfile.getvalue()
  206.  
  207.     def writeString(self, str):
  208.         self.outfile.write(str)
  209.         return
  210.  
  211. #
  212. #  Math Utility
  213. #
  214.  
  215. def degToRad(angle):
  216.     return angle/180*pi
  217.  
  218. def radToDeg(angle):
  219.     return angle*180/pi
  220.  
  221. # Mathutils.Euler seems to expect euler angles in degrees where
  222. # Object.rot is euler angles in radians.
  223. def rotToEuler(rot):
  224.     return Mathutils.Euler(
  225.         radToDeg(rot[0]),
  226.         radToDeg(rot[1]),
  227.         radToDeg(rot[2]))
  228.  
  229. # Blender objects seem to have a rest orientation facing 0,-1,0.
  230. def rotToDirection(rot):
  231.     euler = rotToEuler(rot)
  232.     matrix = euler.toMatrix()
  233.     restDirection = Mathutils.Vector(0,-1,0)
  234.     return tuple(restDirection * matrix)
  235.    
  236. def rotToQuaternion(rot):
  237.     return rot.toQuat()    
  238.     #return rotToEuler(rot).toQuat()
  239.  
  240. #
  241. #  General Utility
  242. #
  243.  
  244. _usedNames = {}
  245.  
  246. def incrementNameUsage(name):
  247.     global _usedNames
  248.  
  249.     n = 0
  250.     if name in _usedNames:
  251.         n = _usedNames[name]
  252.     n = n + 1
  253.     _usedNames[name] = n
  254.  
  255.     return n
  256.  
  257. def createSafeName(name):
  258.     result = ""
  259.  
  260.     # CLS identifiers may not begin with a digit
  261.     if name[0].isdigit():
  262.         result = "_"
  263.  
  264.     # CLS identifiers may contain [_a-zA-Z0-9]
  265.     for i in xrange(len(name)):
  266.         if name[i].isalnum():
  267.             result = result + name[i]
  268.         else:
  269.             result = result + "_"
  270.  
  271.     n = incrementNameUsage(result)
  272.     if n > 1:
  273.         result = result + "_%d" % n
  274.  
  275.     return result
  276.  
  277. def rgbToHexString(rgb):
  278.     return "#%.2x%.2x%.2x" % (rgb[0]*255, rgb[1]*255, rgb[2]*255)
  279.  
  280. def writeXYZ(writer, propertyName, value):
  281.     dimensions = ["X", "Y", "Z"]
  282.     for i in xrange(3):
  283.         writer.writeAttribute(propertyName + dimensions[i], value[i])
  284.  
  285. def listToString(format, list):
  286.     str = ""
  287.     spc = ""
  288.     tempLength = 0
  289.     tempstr = ""
  290.    
  291.     for i in xrange(len(list)):
  292.         tempstr = spc + format % list[i]
  293.         tempLength += len(tempstr)
  294.         str += tempstr
  295.         spc = " "
  296.  
  297.         if tempLength > 4096 :
  298.             str += "\n"
  299.             tempLength = 0;
  300.            
  301.     return str
  302.  
  303. def compactFloat(number):
  304.     str = "%.6f" % number
  305.  
  306.     if len(str) == 0 : return str
  307.  
  308.     backStr = str[-5:]
  309.     frontStr = str[:-5]
  310.    
  311.     str = frontStr + backStr.rstrip("0")
  312.  
  313.     return str
  314.  
  315. # Runs the contained tuple values through compactFloat() and breaks lines at ~4k chars.
  316. def listToStringCompact(list):
  317.     str = ""
  318.     spc = ""
  319.     tempLength = 0    
  320.  
  321.     for i in xrange(len(list)):
  322.         tuple = list[i]
  323.  
  324.         if tempLength > 4096 :
  325.             str += "\n"
  326.             tempLength = 0;
  327.    
  328.         for j in xrange(len(tuple)) :
  329.             strCompact = compactFloat(tuple[j])
  330.             tempLength += len(strCompact)
  331.             str += spc + compactFloat(tuple[j])
  332.             spc = " "
  333.  
  334.     return str
  335.  
  336. def matrixToString(matrix):
  337.     str = ""
  338.    
  339.     for i in xrange(4):
  340.         for j in xrange(4):
  341.             str += "%f" % matrix[i][j]
  342.             if j < 3: str += ","
  343.         if i < 3: str += " "
  344.  
  345.     return str
  346.  
  347. def quaternionToString(quat):
  348.     # Blender appears to store quaternions as WXYZ
  349.     return "%f,%f,%f,%f" % (quat[1], quat[2], quat[3], quat[0])
  350.  
  351. #
  352. #  Xaml Exporter
  353. #
  354.  
  355. class XamlExporter :
  356.     def __init__(self):
  357.         self.writer = XamlWriter()
  358.  
  359.     #
  360.     #  Transforms
  361.     #
  362.  
  363.     def writeTranslateTransform(self, writer, loc):
  364.         trace("loc: %f,%f,%f" % tuple(loc))
  365.         if loc[0] == 0 and loc[1] == 0 and loc[2] == 0:
  366.             return False
  367.            
  368.         writer.beginElement("TranslateTransform3D")
  369.         writeXYZ(writer, "Offset", loc)
  370.         writer.endElement()
  371.         return True
  372.  
  373.     def writeScaleTransform(self, writer, size):
  374.         trace("size: %f,%f,%f" % tuple(size))
  375.         if size[0] == 1 and size[1] == 1 and size[2] == 1:
  376.             return False
  377.            
  378.         writer.beginElement("ScaleTransform3D")
  379.         writeXYZ(writer, "Scale", size)
  380.         writer.endElement()
  381.         return True
  382.  
  383.     def writeQuaternionRotation(self, writer, quaternion):
  384.         # Blender appears to store quaternions as WXYZ
  385.         if quaternion[0] > _quaternionWLimit:
  386.             writer.beginElement("AxisAngleRotation3D")
  387.             writer.endElement()
  388.             return False
  389.            
  390.         writer.beginElement("AxisAngleRotation3D")
  391.  
  392.         # Blender appears to store Quaternions in WXYZ order
  393.         len = sqrt(1 - quaternion[0]*quaternion[0])
  394.         x = quaternion[1]/len
  395.         y = quaternion[2]/len
  396.         z = quaternion[3]/len
  397.  
  398.         angle = radToDeg(2 * acos(quaternion[0]))
  399.  
  400.         writer.writeAttribute("Axis", "%f,%f,%f" % (x,y,z))
  401.         writer.writeAttribute("Angle", angle)
  402.        
  403.         writer.endElement()
  404.         return True
  405.        
  406.     def writeQuaternionRotateTransform(self, writer, quaternion):
  407.         rotationWriter = XamlWriter()
  408.  
  409.         if not self.writeQuaternionRotation(rotationWriter, quaternion):
  410.             return False
  411.  
  412.         writer.beginElement("RotateTransform3D")
  413.         writer.beginElement("RotateTransform3D.Rotation")
  414.         writer.writeFragment(rotationWriter.toString())
  415.         writer.endElement()
  416.         writer.endElement()
  417.         return True
  418.  
  419.     def writeRotateTransform(self, writer, rot):
  420.         trace("rot: %f,%f,%f" % tuple(rot))
  421.  
  422.         return self.writeQuaternionRotateTransform(writer, rotToQuaternion(rot))
  423.        
  424.     def writeTransformGroup(self, writer, transformWriter, numTransforms):
  425.         assert(numTransforms >= 0)
  426.         assert(numTransforms > 0 or len(transformWriter.toString()) == 0)
  427.        
  428.         if numTransforms == 0:
  429.             return False
  430.  
  431.         if numTransforms > 1:    
  432.             writer.beginElement("Transform3DGroup")
  433.         writer.writeFragment(transformWriter.toString())
  434.         if numTransforms > 1:    
  435.             writer.endElement()
  436.  
  437.         return True
  438.  
  439.     def writeTransforms(self, writer, obj):
  440.         numTransforms = 0
  441.    
  442.         transformWriter = XamlWriter()
  443.  
  444.         if self.writeRotateTransform(transformWriter, obj.matrixLocal.rotationPart().toEuler()):
  445.             numTransforms += 1
  446.         if self.writeScaleTransform(transformWriter, obj.matrixLocal.scalePart()):
  447.             numTransforms += 1
  448.         if self.writeTranslateTransform(transformWriter, obj.matrixLocal.translationPart()):
  449.             numTransforms += 1
  450.  
  451.         return self.writeTransformGroup(writer, transformWriter, numTransforms)
  452.  
  453.     #
  454.     #  Materials
  455.     #
  456.    
  457.     def writeSolidColorBrush(self, writer, rgbCol):
  458.         trace("rgbCol: %s" % rgbToHexString(rgbCol))
  459.    
  460.         writer.beginElement("SolidColorBrush")
  461.         writer.writeAttribute("Color", rgbToHexString(rgbCol))
  462.         writer.endElement()                        
  463.  
  464.     def writeImageBrush(self, writer, texture):
  465.         assert texture.getType() == "Image"
  466.  
  467.         textureName = texture.getName()
  468.         image = texture.image
  469.         trace("image: %s" % image.getName())
  470.         trace("filename: %s" % image.filename)
  471.        
  472.         writer.beginElement("ImageBrush")
  473.         writer.writeAttribute("x:Name", createSafeName("TE_" + textureName))
  474.         writer.writeAttribute("ImageSource", image.filename)
  475.  
  476.         # By default WPF brush space is relative to the bounds of the
  477.         # texture coordinates.  Blender texture coordinates are absolute
  478.         # in the [0,0] - [1,1] range.
  479.         writer.writeAttribute("ViewportUnits", "Absolute")
  480.        
  481.         # The default WPF brush space has (0,0) in the upper left corner.
  482.         # Blender's UV space has (0,0) in the bottom left corner.
  483.         writer.writeAttribute("Transform", "1,0,0,-1,0,1")
  484.  
  485.         # Blender handles texture coordinates outside of the [0,0] - [1,1]
  486.         # range by tiling.  We need to enable this behavior in WPF.
  487.         writer.writeAttribute("TileMode", "Tile")
  488.        
  489.         writer.endElement()
  490.  
  491.     def writeMaterial(self, writer, material):
  492.    
  493.         if material == None:
  494.            return False
  495.        
  496.         materialName = material.getName()
  497.         trace("Material ('%s'):" % materialName)
  498.         indent()
  499.  
  500.         writer.beginElement("DiffuseMaterial")
  501.         writer.writeAttribute("x:Name", createSafeName("MA_" + materialName))
  502.         writer.beginElement("DiffuseMaterial.Brush")
  503.  
  504.         foundTexture = False
  505.  
  506.         for mTex in material.getTextures():
  507.             if mTex != None:
  508.                 texture = mTex.tex
  509.                 trace("Texture ('%s'):" % texture.getName())
  510.                 indent()
  511.    
  512.                 textureType = texture.getType()
  513.  
  514.                 if textureType == "Image":
  515.                     self.writeImageBrush(writer, texture)
  516.                     foundTexture = True
  517.                 elif textureType != "None":
  518.                     warn("Skipping unsupported Texture type: '%s'" % textureType)
  519.  
  520.                 unindent()
  521.  
  522.         if foundTexture == False:
  523.             trace("Texture not found for Material '%s', falling back to solid color." % materialName)
  524.             self.writeSolidColorBrush(writer, material.rgbCol)
  525.  
  526.         writer.endElement()
  527.         writer.endElement()
  528.  
  529.         unindent()
  530.    
  531.         return True
  532.  
  533.     def writeDefaultMaterial(self, writer):
  534.         writer.beginElement("DiffuseMaterial")
  535.         writer.beginElement("DiffuseMaterial.Brush")
  536.         self.writeSolidColorBrush(writer, [0.8, 0.8, 0.8])
  537.         writer.endElement()
  538.         writer.endElement()
  539.  
  540.     def writeMaterials(self, writer, mesh):
  541.         numMaterials = 0
  542.         materialWriter = XamlWriter()
  543.  
  544.         for material in mesh.materials:
  545.             if self.writeMaterial(materialWriter, material):
  546.                 numMaterials += 1
  547.  
  548.         if numMaterials == 0:
  549.             warn("No material found for mesh '%s'. Using default material." % mesh.name)
  550.             self.writeDefaultMaterial(writer)
  551.             return True
  552.  
  553.         if numMaterials > 1:
  554.             writer.beginElement("MaterialGroup")
  555.         writer.writeFragment(materialWriter.toString())
  556.         if numMaterials > 1:
  557.             writer.endElement()
  558.         return True
  559.  
  560.     #
  561.     #  Mesh
  562.     #
  563.        
  564.     def writeMesh(self, meshObj):
  565.         objectName = meshObj.getName()
  566.         trace("Object ('%s'):" % objectName)
  567.         indent()
  568.  
  569.         scn = Scene.GetCurrent()
  570.         mesh = BPyMesh.getMeshFromObject(meshObj, None, True, False, scn)
  571.                    
  572.         if not mesh:
  573.             return
  574.            
  575.         meshName = mesh.name
  576.         trace("Mesh ('%s'):" % meshName)
  577.         indent()
  578.  
  579.         positions = []
  580.         normals = []
  581.         textureCoordinates = []
  582.         triangleIndices = []
  583.  
  584.         # WPF does not currently support indexed normals or texture coordinates.
  585.         # To work around this we split the vertices for each face.  In order
  586.         # to correlate the original indices with the WPF indices we maintain a
  587.         # dictionary which maps each Blender index to a list of WPF indices.
  588.         indexMap = {}
  589.  
  590.         # check for particles - if there are no faces but verts are found, the verts are single points. TODO: create a mesh out of the vert points which include UV mapping. Each point could be a plane, billboarding would need to happen on the wpf code side.
  591.         if len(mesh.faces) == 0:
  592.             if len(mesh.verts) > 0:
  593.                 warn("Particles Not currently supported.")
  594.                 return
  595.  
  596.         for face in mesh.faces:
  597.             numVerts = len(face.v)
  598.  
  599.             # We expect mesh faces to be triangles or quads
  600.             assert 3 <= numVerts and numVerts <= 4
  601.  
  602.             newIndices = []
  603.             uvIndex = 0;
  604.            
  605.             for vertex in face.v:
  606.                 index = vertex.index
  607.  
  608.                 position = mesh.verts[index].co
  609.  
  610.                 if face.smooth:
  611.                     # If face is smooth shaded use vertex normal
  612.                     normal = mesh.verts[index].no
  613.                 else:
  614.                     # If face is solid (flat) shaded use face normal
  615.                     normal = face.no
  616.  
  617.                 if mesh.faceUV:
  618.                     textureCoordinate = face.uv[uvIndex]
  619.                     uvIndex += 1
  620.                
  621.                 if not indexMap.has_key(index):
  622.                     indexMap[index] = []
  623.                 else:
  624.                 # If this index is already mapped scan our entries to see if
  625.                     # there is already a position/normal/textureCoordinate set
  626.                     # which matches.
  627.                     for i in indexMap[index]:
  628.                         if positions[i] == position and normals[i] == normal and textureCoordinates[i] == textureCoordinate:
  629.                             newIndices.append(index)
  630.                             continue
  631.                
  632.                 # We do not already have a matching position/normal/textureCoordinate
  633.                 # for this index so we create one and add it to our indexMap.
  634.                 indexMap[index].append(len(positions))
  635.                 newIndices.append(len(positions))
  636.                 positions.append(tuple(position))
  637.                 normals.append(tuple(normal))
  638.    
  639.                 if mesh.faceUV:
  640.                     textureCoordinates.append(tuple(textureCoordinate))
  641.  
  642.             assert len(newIndices) == numVerts
  643.    
  644.             triangleIndices.extend([newIndices[0], newIndices[1], newIndices[2]])
  645.        
  646.             if numVerts == 4:
  647.                 triangleIndices.extend([newIndices[0], newIndices[2], newIndices[3]])
  648.    
  649.         assert len(positions) == len(normals)
  650.         assert len(textureCoordinates) == len(positions) or len(textureCoordinates) == 0
  651.    
  652.         self.writer.beginElement("GeometryModel3D")
  653.         self.writer.writeAttribute("x:Name", createSafeName("OB_" + objectName))
  654.  
  655.         materialWriter = XamlWriter()
  656.         backmaterialWriter = XamlWriter()
  657.         if self.writeMaterials(materialWriter, mesh):
  658.             self.writer.beginElement("GeometryModel3D.Material")
  659.             self.writer.writeFragment(materialWriter.toString())
  660.             self.writer.endElement()
  661.  
  662.             isDoubleSided = mesh.mode & Blender.NMesh.Modes.TWOSIDED
  663.             print(isDoubleSided)
  664.  
  665.             if isDoubleSided:
  666.                 if self.writeMaterials(backmaterialWriter, mesh):
  667.                     self.writer.beginElement("GeometryModel3D.BackMaterial")
  668.                     self.writer.writeFragment(backmaterialWriter.toString())
  669.                     self.writer.endElement()
  670.  
  671.         self.writer.beginElement("GeometryModel3D.Geometry")
  672.         self.writer.beginElement("MeshGeometry3D")
  673.         self.writer.writeAttribute("x:Name", createSafeName("ME_" + meshName))
  674.  
  675.         self.writer.writeString("\n")
  676.         self.writer.writeAttribute("Positions", listToStringCompact(positions))
  677.         self.writer.writeString("\n")
  678.         self.writer.writeAttribute("Normals", listToStringCompact(normals))
  679.         self.writer.writeString("\n")
  680.         self.writer.writeAttribute("TextureCoordinates", listToStringCompact(textureCoordinates))
  681.         self.writer.writeString("\n")
  682.         self.writer.writeAttribute("TriangleIndices", listToString("%d", triangleIndices))
  683.         self.writer.writeString("\n")
  684.        
  685.         self.writer.endElement()
  686.         self.writer.endElement()
  687.         self.writer.endElement()
  688.  
  689.         unindent()    
  690.         unindent()    
  691.  
  692.     def writeLightCommon(self, lampObj):
  693.         lamp = lampObj.getData()
  694.    
  695.         #BUGBUG: lamp.getName() disappeared in Blender 2.45.
  696.         #self.writer.writeAttribute("x:Name", createSafeName("LA_" + lamp.getName()))
  697.  
  698.         self.writer.writeAttribute("Color", rgbToHexString(lamp.col))
  699.  
  700.     def writeDirectionalLight(self, lampObj):
  701.         trace("type: Sun (Directional)")
  702.         self.writer.beginElement("DirectionalLight")
  703.         self.writeLightCommon(lampObj)
  704.         self.writer.writeAttribute("Direction", "%f,%f,%f" % rotToDirection(lampObj.rot))
  705.         self.writer.endElement()
  706.    
  707.     def writePointLightCommon(self, lampObj):
  708.         loc = lampObj.loc
  709.         lamp = lampObj.getData()
  710.  
  711.         self.writer.writeAttribute("Position", "%f,%f,%f" % lampObj.loc)
  712.  
  713.         mode = lamp.getMode()
  714.         energy = lamp.getEnergy()
  715.         dist = lamp.getDist()
  716.  
  717.         if mode & Lamp.Modes.Sphere:
  718.             warn("The Sphere feature on lamps is approximated during export.");
  719.             self.writer.writeAttribute("Range", dist)
  720.            
  721.         cAttn = 1/energy;
  722.         lAttn = 1/(energy * dist);
  723.         qAttn = 0;
  724.  
  725.         if mode & Lamp.Modes.Quad:
  726.             warn("The Quad feature on lamps is approximated during export.");
  727.             qAttn = lAttn * lamp.getQuad1();
  728.             lAttn = lAttn * lamp.getQuad2();
  729.    
  730.         if cAttn != 1: self.writer.writeAttribute("ConstantAttenuation", cAttn)
  731.         if lAttn != 0: self.writer.writeAttribute("LinearAttenuation", lAttn)
  732.         if qAttn != 0: self.writer.writeAttribute("QuadraticAttenuation", qAttn)
  733.        
  734.     def writePointLight(self, lampObj):
  735.         trace("type: Lamp (Point)")
  736.         self.writer.beginElement("PointLight")
  737.         self.writeLightCommon(lampObj)
  738.         self.writePointLightCommon(lampObj)
  739.         self.writer.endElement()
  740.    
  741.     def writeSpotLight(self, lampObj):
  742.         trace("type: Spot")
  743.         lamp = lampObj.getData()
  744.    
  745.         self.writer.beginElement("SpotLight")
  746.         self.writeLightCommon(lampObj)
  747.         self.writer.writeAttribute("Direction", "%f,%f,%f" % rotToDirection(lampObj.rot))
  748.         self.writePointLightCommon(lampObj)
  749.         self.writer.writeAttribute("OuterConeAngle", "%f" % lamp.getSpotSize())
  750.         if lamp.getSpotBlend() > 0:
  751.             innerAngle = lamp.getSpotSize() * (1 - lamp.getSpotBlend())
  752.             assert innerAngle < lamp.getSpotSize()
  753.  
  754.             self.writer.writeAttribute("InnerConeAngle", "%f" % innerAngle)
  755.         self.writer.endElement()
  756.    
  757.     def writeCameras(self, objects):
  758.         i = 0;
  759.  
  760.         for ob in objects:
  761.    
  762.             childType = ob.getType()
  763.             if childType == "Camera":
  764.                 i += 1
  765.                 self.writeCamera(ob)
  766.  
  767.         if i == 0:
  768.             self.writeCamera(None)
  769.             print("default camera")
  770.          
  771.     #end def writeCameras
  772.    
  773.     def writeCamera(self, cameraObj):
  774.    
  775.         camera = None
  776.         if cameraObj != None:
  777.             camera = cameraObj.getData()
  778.        
  779.         cameraTypeName = "PerspectiveCamera"
  780.         if camera != None and camera.type == "ortho":
  781.             cameraTypeName = "OrthographicCamera"
  782.        
  783.         #compute all the variables to set the camera
  784.         yfov = 60
  785.         clipStart = 0.10
  786.         clipEnd = 100.0
  787.         if camera != None:    
  788.             matrix = cameraObj.getMatrix('worldspace')    # matrix with euler and translation information
  789.             rotMat = matrix.rotationPart()
  790.             euler = rotMat.toEuler()
  791.             rotQuat = euler.toQuat()
  792.             loc = matrix[3][0], matrix[3][1], matrix[3][2]
  793.             yfov = 2 * ( atan( 16.0 / camera.lens ) ) * ( 180.0 / 3.1415926 )
  794.             clipEnd = camera.getClipEnd()
  795.             clipStart = camera.getClipStart()
  796.        
  797.         # write attributes        
  798.         self.writer.beginElement(cameraTypeName)
  799.         self.writer.writeAttribute("FarPlaneDistance", "%f" % clipEnd)
  800.         self.writer.writeAttribute("NearPlaneDistance", "%f" % clipStart)
  801.         self.writer.writeAttribute("UpDirection", "%s" % "0,1,0")
  802.         self.writer.writeAttribute("LookDirection", "%s" % "0,0,-1")
  803.         self.writer.writeAttribute("Position", "%s" % "0,0,0")
  804.  
  805.         if cameraTypeName == "OrthographicCamera":
  806.             self.writer.writeAttribute("Width", "%f" % camera.scale)
  807.         else:
  808.             self.writer.writeAttribute("FieldOfView", "%f" % yfov)
  809.            
  810.         # write Transforms
  811.         if camera != None:
  812.             tranformName = cameraTypeName + ".Transform"
  813.             self.writer.beginElement(tranformName)
  814.             self.writer.beginElement("Transform3DGroup")
  815.             self.writeQuaternionRotateTransform(self.writer, rotQuat)  
  816.             self.writeTranslateTransform(self.writer, loc)
  817.             self.writer.endElement()    #</Transform3DGroup >
  818.             self.writer.endElement()    #</cameratype.Transform >
  819.  
  820.         self.writer.endElement()    #</cameratype >
  821.        
  822.     #end def WriteCamera       
  823.        
  824.     def writeLamp(self, lampObj):
  825.         lamp = lampObj.getData()
  826.        
  827.         #BUGBUG: lamp.getName() disappeared in Blender 2.45.
  828.         #lampName = lamp.getName()
  829.         lampName = "?"
  830.        
  831.         trace("Lamp ('%s'):" % lampName)
  832.         indent()
  833.  
  834.         lampType = lamp.getType()
  835.         if lampType == Lamp.Types.Sun:
  836.             self.writeDirectionalLight(lampObj)
  837.         elif lampType == Lamp.Types.Lamp:
  838.             self.writePointLight(lampObj)
  839.         elif lampType == Lamp.Types.Spot:
  840.             self.writeSpotLight(lampObj)
  841.         else:
  842.             unsupportedLamps = { 3 : "Hemi", 4 : "Area", 5 : "Photon" }
  843.             if unsupportedLamps.has_key(lampType):
  844.                 warn("Skipping unsupported Lamp type '%s'" % unsupportedLamps[lampType])
  845.             else:
  846.                 warn("Skipping unknown Lamp type '%u'" % lampType)
  847.  
  848.         trace("loc: %f,%f,%f" % (lampObj.loc))
  849.        
  850.         #BUGBUG: In Blender 2.45 I began having to coerce rotation values to a float tuple.
  851.         #trace("rot: %f,%f,%f" % (lampObj.rot))
  852.         trace("rot: %f,%f,%f" % (lampObj.rot[0], lampObj.rot[1], lampObj.rot[2]))
  853.  
  854.         trace("bias: %f" % lamp.getBias())
  855.         trace("clipEnd: %f" % lamp.getClipEnd())
  856.         trace("clipStart: %f" % lamp.getClipStart())
  857.         trace("col: %s" % rgbToHexString(lamp.getCol()))
  858.         trace("dist: %f" % lamp.getDist())
  859.         trace("energy: %f" % lamp.getEnergy())
  860.         trace("haloInt: %f" % lamp.getHaloInt())
  861.         trace("haloStep: %i" % lamp.getHaloStep())
  862.         trace("mode: %i" % lamp.getMode())
  863.         trace("quad1: %f" % lamp.getQuad1())
  864.         trace("quad2: %f" % lamp.getQuad2())
  865.         trace("samples: %i" % lamp.getSamples())
  866.         trace("softness: %f" % lamp.getSoftness())
  867.         trace("spotBlend: %f" % lamp.getSpotBlend())
  868.         trace("spotSize: %f" % lamp.getSpotSize())
  869.  
  870.         unindent()
  871.  
  872.     def writeBeginModel3DGroup(self, object):
  873.        
  874.         self.writer.beginElement("Model3DGroup")
  875.         self.writer.writeAttribute("x:Name", createSafeName("MG_" + object.name))
  876.    
  877.         transformWriter = XamlWriter()
  878.         if self.writeTransforms(transformWriter, object):
  879.             self.writer.beginElement("Model3DGroup.Transform")
  880.             self.writer.writeFragment(transformWriter.toString())
  881.             self.writer.endElement()
  882.    
  883.     def writeEndModel3DGroup(self):
  884.         self.writer.endElement() # end Model3DGroup
  885.  
  886.     def GetIndentString (self, level):
  887.         str = ""
  888.         for i in range(level):
  889.             str = str + "   "
  890.         return str
  891.  
  892.     def FindNameIndex (self, objects, object):
  893.         i = 0
  894.        
  895.         for ob in objects:
  896.             if ob.name == object.name
  897.                 return i
  898.             i+=1
  899.      
  900.         return -1
  901.        
  902.        
  903.     #
  904.     # Write out mesh or lamp object based on results of getDerievedObjects
  905.     #
  906.     def writeDerivedObject (self, object):
  907.         for ob, ob_mat in BPyObject.getDerivedObjects(object):
  908.                
  909.             childType = ob.getType()
  910.                    
  911.             if childType == "Mesh":
  912.                 self.writeMesh(ob)
  913.             elif childType == "Lamp":
  914.                 self.writeLamp(ob)
  915.         #end for
  916.  
  917.     # end def writeDerivedObject
  918.  
  919.     #    
  920.     # recusively write out the objects based on parent relationships  
  921.     #    
  922.     def writeChildrenObjects(self, objects, sorted_objects,root):
  923.    
  924.         for ob in objects:
  925.    
  926.             parent = ob.parent
  927.             if parent == None:
  928.                 continue
  929.    
  930.             if parent.name == root.name:
  931.                 findIndex = self.FindNameIndex(sorted_objects, ob)
  932.  
  933.                 if findIndex != -1:
  934.                     continue
  935.    
  936.                 self.writeBeginModel3DGroup(ob)
  937.    
  938.                 if ob.sel == 1:
  939.                     self.writeDerivedObject (ob)
  940.                 sorted_objects.append(ob)
  941.                 self.writeChildrenObjects (objects, sorted_objects, ob)
  942.  
  943.                 self.writeEndModel3DGroup()
  944.    
  945.    
  946.     #def end  writeChildrenObjects
  947.  
  948.     #    
  949.     # setup the lists for a recursive call to write out children
  950.     #
  951.     def writeBlenderObjects(self, objects):
  952.         rootNodes = []
  953.         sorted_objects = []
  954.        
  955.         # add the root nodes, and ignore the camera
  956.         for ob in objects:
  957.             childType = ob.getType()
  958.  
  959.             if childType == "Camera":
  960.                 continue       
  961.             if ob.parent == None:
  962.                 if ob.sel == 1:
  963.                     rootNodes.append(ob)
  964.  
  965.         # write out the tree based on he root nodes
  966.         for ob in rootNodes:
  967.             findIndex = self.FindNameIndex(sorted_objects,ob)
  968.             if findIndex > 0:
  969.                 continue
  970.  
  971.             self.writeBeginModel3DGroup(ob)
  972.    
  973.             self.writeDerivedObject(ob)
  974.             sorted_objects.append(ob)
  975.             self.writeChildrenObjects (objects, sorted_objects, ob)
  976.  
  977.             self.writeEndModel3DGroup()
  978.  
  979.  
  980.     #def end  writeBlenderObjects
  981.  
  982.     def writeScene(self, sceneObj, fileout):
  983.         self.writer.beginElement("Viewport3D")
  984.         self.writer.writeAttribute("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
  985.         self.writer.writeAttribute("xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml")
  986.        
  987.         orig_scene = sceneObj
  988.         orig_scene.makeCurrent() # If already current, this is not slow.
  989.         context = orig_scene.getRenderingContext()
  990.         orig_frame = Blender.Get('curframe')
  991.        
  992.         # Export an animation?
  993.         # if EXPORT_ANIMATION:
  994.         #   scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame.
  995.         # else:
  996.         scene_frames = [orig_frame] # Dont export an animation.
  997.            
  998.         # Loop through all frames in the scene and export.
  999.         for frame in scene_frames:
  1000.             #if EXPORT_ANIMATION: # Add frame to the filename.
  1001.             #   context_name[2] = '_%.6d' % frame
  1002.            
  1003.             Blender.Set('curframe', frame)
  1004.             scn = Scene.GetCurrent()
  1005.            
  1006.             all_objects = scn.objects
  1007.            
  1008.             # write out any cameras
  1009.             self.writer.beginElement("Viewport3D.Camera")
  1010.             self.writeCameras(all_objects)
  1011.             self.writer.endElement()
  1012.            
  1013.             # write out ModelVisual3D to hold all the objects
  1014.             self.writer.beginElement("ModelVisual3D")
  1015.             self.writer.beginElement("ModelVisual3D.Content")
  1016.             self.writer.beginElement("Model3DGroup")
  1017.  
  1018.             # write out all the blender objects
  1019.             all_objects = scn.objects
  1020.             self.writeBlenderObjects(all_objects)
  1021.  
  1022.             #test code for quick debugging
  1023.             #print("------")
  1024.             #for ob_main in all_objects:
  1025.             #    for ob, ob_mat in BPyObject.getDerivedObjects(ob_main):
  1026.             #      
  1027.             #        childType = ob.getType()
  1028.             #        
  1029.             #        if childType == "Mesh":
  1030.             #            print(ob.name)
  1031.             #            print(ob.matrixLocal)
  1032.             #            print(ob.matrixLocal.translationPart())
  1033.             #            print(ob.matrixLocal.scalePart())
  1034.             #            print(ob.matrixLocal.rotationPart().toEuler())
  1035.             #            print(ob.rot)
  1036.             #       elif childType == "Lamp":
  1037.             #            self.writeLamp(ob)
  1038.             #       elif childType == "Camera":
  1039.             #           self.writeCamera(ob)
  1040.             #       else:
  1041.             #            warn("Skipping unsupported object type in scene: '" + ob.getType() + "'")
  1042.             #            
  1043.         # end loop through all frames
  1044.            
  1045.         self.writer.endElement() # </Model3DGroup>
  1046.         self.writer.endElement() # </ModelVisual3D.Content>
  1047.         self.writer.endElement() # </ModelVisual3D>
  1048.         self.writer.endElement() # </Viewport3D>
  1049.        
  1050.         fileout.write(self.writer.toString())
  1051.  
  1052.    
  1053.  
  1054. #
  1055. #  Global
  1056. #
  1057.  
  1058. class ExportXaml():
  1059.     """ Guess these are properties"""
  1060.     bl_idname = "export.xaml"
  1061.     bl_label = "Export to XAML"
  1062.     filename_ext = ".xaml"
  1063.  
  1064.     _quaternionWLimit = cos(degToRad(_rotationLimit)/2)
  1065.  
  1066.     def export(filename):
  1067.         trace("Exporting to file: '" + filename)
  1068.  
  1069.         verbose("Configuration:")
  1070.         indent()
  1071.         verbose("_rotationLimit='%f'" % _rotationLimit)
  1072.         verbose("_quaternionWLimit='%f'" % _quaternionWLimit)
  1073.         unindent()
  1074.    
  1075.         exporter = XamlExporter()
  1076.         fileout = open(filename, 'w')
  1077.         try:
  1078.             exporter.writeScene(Scene.GetCurrent(), fileout)
  1079.         finally:
  1080.             fileout.close()
  1081.    
  1082.     def onFileSelected(filename):
  1083.         if filename.find('.xaml', -5) <= 0:
  1084.             filename += '.xaml'
  1085.  
  1086. #        if Blender.sys.exists(filename):
  1087. #            if Blender.Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0") != 1:
  1088. #                return
  1089.  
  1090.         export(filename)
  1091.  
  1092.     @classmethod
  1093.     def poll(cls, context):
  1094.         active = context.active_object
  1095.         selected = context.selected_objects
  1096.         camera = context.scene.camera
  1097.         ok = selected or camera
  1098.         return ok
  1099.  
  1100.     def execute(self, context):
  1101.         filepath = self.filepath
  1102.         filepath = bpy.path.ensure_ext(filepath, self.filename_ext)
  1103.         export(self.filepath)
  1104.  
  1105. ### REGISTER ###
  1106.  
  1107. def menu_func(self, context):
  1108.     self.layout.operator(ExportXaml.bl_idname, text="XAML (.xaml)")
  1109.  
  1110. def register():
  1111.     bpy.utils.register_module(__name__)
  1112.     bpy.types.INFO_MT_file_export.append(menu_func)
  1113.  
  1114. def unregister():
  1115.     bpy.utils.unregister_module(__name__)
  1116.     bpy.types.INFO_MT_file_export.remove(menu_func)
  1117.  
  1118. if __name__ == "__main__":
  1119.     register()
  1120.  
  1121. trace("io_export_xaml.py Finished.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement