Advertisement
Vearie

bz2xsi.py - BZ2 XSI Parser

Feb 6th, 2022
1,071
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 32.34 KB | None | 0 0
  1. """This module provides BZ2 XSI utilities, including a parser and writer for XSI files."""
  2. VERSION = 1.1
  3.  
  4. # No print calls will be made by the module if this is False
  5. ALLOW_PRINT = True
  6.  
  7. DEFAULT_DIFFUSE = (0.7, 0.7, 0.7, 1.0)
  8. DEFAULT_SPECULAR = (0.35, 0.35, 0.35)
  9. DEFAULT_EMISSIVE = (0.0, 0.0, 0.0)
  10. DEFAULT_AMBIENT = (0.5, 0.5, 0.5)
  11. DEFAULT_HARDNESS = 200.0
  12. DEFAULT_SHADING_TYPE = 2
  13. DEFAULT_TEXTURE = None
  14.  
  15. DEFAULT_XSI_NAME = "<XSI ROOT>"
  16.  
  17. RENAME_DUPLICATE_NAMED_FRAMES = True
  18. DUPLICATE_FRAME_NOEXCEPT = False
  19.  
  20. class DuplicateFrame(Exception): pass
  21.  
  22. # XSI & Frame inherit from this internal class
  23. class _FrameContainer:
  24.     def __init__(self):
  25.         self.xsi = self
  26.         self.frames = []
  27.    
  28.     def add_frame(self, name):
  29.         if name in self.xsi.frame_table and not DUPLICATE_FRAME_NOEXCEPT:
  30.             raise DuplicateFrame("Duplicate Frame %r" % name)
  31.        
  32.         frame = Frame(name)
  33.         frame.parent = self if not self is self.xsi else None # XSI container itself is not a parent
  34.         frame.xsi = self.xsi
  35.        
  36.         self.xsi.frame_table[name] = frame
  37.         self.frames.append(frame)
  38.        
  39.         return frame
  40.    
  41.     def get_all_frames(self):
  42.         frames = []
  43.         for frame in self.frames:
  44.             frames += [frame] + frame.get_all_frames()
  45.        
  46.         return frames
  47.    
  48.     def find_frame(self, name):
  49.         for frame in self.get_all_frames():
  50.             if frame.name == name:
  51.                 return frame
  52.    
  53.     def get_animated_frames(self):
  54.         for frame in self.get_all_frames():
  55.             if frame.animation_keys:
  56.                 yield frame
  57.    
  58.     def get_skinned_frames(self):
  59.         for frame in self.get_all_frames():
  60.             if frame.envelopes:
  61.                 yield frame
  62.    
  63.     def get_bone_frames(self):
  64.         for frame in self.get_all_frames():
  65.             if frame.is_bone:
  66.                 yield frame
  67.    
  68.     def get_all_meshes(self):
  69.         for frame in self.get_all_frames():
  70.             if frame.mesh:
  71.                 yield frame.mesh
  72.    
  73.     def get_envelope_count(self):
  74.         """Returns total amount of envelopes in each frame."""
  75.         return sum((len(f.envelopes) for f in self.get_skinned_frames()))
  76.  
  77. class XSI(_FrameContainer):
  78.     def __init__(self, filepath=None):
  79.         self.frame_table = {}
  80.         self.lights = []
  81.         self.cameras = []
  82.         self.frames = []
  83.         self.xsi = self
  84.        
  85.         self.name = filepath if filepath else DEFAULT_XSI_NAME
  86.        
  87.         if filepath:
  88.             self.read(filepath)
  89.    
  90.     def read(self, filepath, re_skip=None):
  91.         with open(filepath, "r") as f:
  92.             self.name = filepath
  93.             Reader(f, bz2xsi_xsi=self, re_skip=re_skip, log_name=self.name)
  94.    
  95.     def write(self, filepath):
  96.         with open(filepath, "w") as f:
  97.             Writer(self, f)
  98.    
  99.     def is_skinned(self):
  100.         return len(list(self.get_skinned_frames())) >= 1
  101.    
  102.     def is_animated(self):
  103.         return len(list(self.get_animated_frames())) >= 1
  104.    
  105.     # String representation will result in XML output
  106.     def __str__(self):
  107.         return "%s<XSI>%s%s</XSI>" % (
  108.             "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n",
  109.             "".join(map(str, self.lights)),
  110.             "".join(map(str, self.frames)),
  111.         )
  112.  
  113. class PointLight:
  114.     def __init__(self, name, rgb=None, location_xyz=None):
  115.         self.name = name
  116.         self.rgb = rgb if rgb else (1.0, 1.0, 1.0)
  117.        
  118.         if not location_xyz:
  119.             location_xyz = (0.0, 0.0, 0.0)
  120.        
  121.         self.transform = Matrix(posit=(*location_xyz, 1.0))
  122.    
  123.     def __str__(self):
  124.         return "<PointLight>%s (%f, %f, %f)</PointLight>" % (self.name, *self.rgb)
  125.  
  126. class Camera:
  127.     def __init__(self, name, location_xyz=None, look_at_xyz=None, roll=0.0, near_plane=0.001, far_plane=1000.0):
  128.         self.name = name
  129.         self.roll = roll
  130.         self.near_plane = near_plane
  131.         self.far_plane = far_plane
  132.        
  133.         if not location_xyz:
  134.             location_xyz = (0.0, 0.0, 0.0)
  135.        
  136.         if not look_at_xyz:
  137.             look_at_xyz = (0.0, 0.0, 0.0)
  138.        
  139.         self.transform = Matrix(posit=(*location_xyz, 1.0))
  140.         self.target = Matrix(posit=(*look_at_xyz, 1.0))
  141.    
  142.     def __str__(self):
  143.         return "<Camera>%s</Camera>" % self.name
  144.  
  145. class Frame(_FrameContainer):
  146.     def __init__(self, name):
  147.         self.name = name
  148.         self.is_bone = False
  149.        
  150.         self.transform = None
  151.         self.pose = None
  152.         self.mesh = None
  153.        
  154.         self.parent = None
  155.         self.frames = []
  156.         self.animation_keys = []
  157.        
  158.         # About envelopes:
  159.         # Frames (meshes) which are NOT bones contain envelopes.
  160.         # Frames which ARE bones do NOT contain envelopes, but are referenced BY envelopes.
  161.         self.envelopes = []
  162.    
  163.     def __str__(self):
  164.         return "<Frame>%s%s%s%s%s%s%s</Frame>" % (
  165.             self.name,
  166.             str(self.transform),
  167.             str(self.pose),
  168.             str(self.mesh),
  169.             "".join(map(str, self.frames)),
  170.             "".join(map(str, self.animation_keys)),
  171.             "".join(map(str, self.envelopes)),
  172.         )
  173.    
  174.     def get_chained_name(self, delimiter=" -> "):
  175.         frm, chain = self, []
  176.        
  177.         while frm:
  178.             chain += [frm.name]
  179.             frm = frm.parent
  180.        
  181.         return delimiter.join(reversed(chain))
  182.    
  183.     def add_animationkey(self, *args):
  184.         self.animation_keys.append(AnimationKey(*args))
  185.         return self.animation_keys[-1]
  186.    
  187.     def add_envelope(self, *args):
  188.         self.envelopes.append(Envelope(*args))
  189.         return self.envelopes[-1]
  190.  
  191. class Matrix:
  192.     def __init__(self, right=None, up=None, front=None, posit=None):
  193.         self.right = right if right else (1.0, 0.0, 0.0, 0.0)
  194.         self.up    = up    if up    else (0.0, 1.0, 0.0, 0.0)
  195.         self.front = front if front else (0.0, 0.0, 1.0, 0.0)
  196.         self.posit = posit if posit else (0.0, 0.0, 0.0, 1.0)
  197.    
  198.     def __str__(self):
  199.         return "<Matrix>(x=%f y=%f z=%f)</Matrix>" % tuple(self.posit[0:3])
  200.    
  201.     def to_list(self):
  202.         return [list(self.right), list(self.up), list(self.front), list(self.posit)]
  203.  
  204. class Mesh:
  205.     def __init__(self, name=None):
  206.         self.name=name
  207.        
  208.         self.vertices = []
  209.         self.faces = []
  210.        
  211.         self.normal_vertices = []
  212.         self.normal_faces = []
  213.        
  214.         self.uv_vertices = []
  215.         self.uv_faces = []
  216.        
  217.         self.face_materials = []
  218.        
  219.         self.vertex_colors = []
  220.         self.vertex_color_faces = []
  221.    
  222.     def __str__(self):
  223.         def XML(name, vertices, faces):
  224.             if vertices or faces:
  225.                 return "<%s>%d Vertices %d Faces</%s>" % (
  226.                     name,
  227.                     len(vertices),
  228.                     len(faces),
  229.                     name
  230.                 )
  231.             else:
  232.                 return ""
  233.        
  234.         indices, materials = self.get_material_indices()
  235.        
  236.         return "<Mesh>%d Vertices %d Faces%s%s%s%s</Mesh>" % (
  237.             len(self.vertices),
  238.             len(self.faces),
  239.             "".join(map(str, materials)),
  240.             XML("Normals", self.normal_vertices, self.normal_faces),
  241.             XML("UV-Map", self.uv_vertices, self.uv_faces),
  242.             XML("Vertex-Colors", self.vertex_colors, self.vertex_color_faces)
  243.         )
  244.    
  245.     def get_material_indices(self):
  246.         materials = []
  247.         indices = []
  248.         for material in self.face_materials:
  249.             if not material in materials:
  250.                 materials += [material]
  251.            
  252.             indices += [materials.index(material)]
  253.        
  254.         return indices, materials
  255.  
  256. class Material:
  257.     def __init__(self,
  258.                 diffuse=None, hardness=DEFAULT_HARDNESS, specular=None,
  259.                 ambient=None, emissive=None, shading_type=DEFAULT_SHADING_TYPE,
  260.                 texture=None
  261.             ):
  262.         self.diffuse  = diffuse  if diffuse  else list(DEFAULT_DIFFUSE)
  263.         self.specular = specular if specular else list(DEFAULT_SPECULAR)
  264.         self.emissive = emissive if emissive else list(DEFAULT_EMISSIVE)
  265.         self.ambient  = ambient  if ambient  else list(DEFAULT_AMBIENT)
  266.        
  267.         self.hardness = hardness
  268.         self.shading_type = shading_type
  269.         self.texture = texture
  270.        
  271.         if len(self.diffuse) == 3:
  272.             self.diffuse += (1.0,) # Append alpha channel
  273.        
  274.         elif len(self.diffuse) != 4:
  275.             raise TypeError("Material Diffuse color must be RGB or RGBA.")
  276.        
  277.         if len(self.specular) != 3:
  278.             raise TypeError("Material Specular color must be RGB.")
  279.        
  280.         if len(self.emissive) != 3:
  281.             raise TypeError("Material Emissive color must be RGB.")
  282.        
  283.         if len(self.ambient) != 3:
  284.             raise TypeError("Material Ambient color must be RGB.")
  285.    
  286.     def __str__(self):
  287.         return "<Material>%r (%f, %f, %f, %f)</Material>" % (str(self.texture), *self.diffuse)
  288.    
  289.     def __eq__(self, other):
  290.         return (
  291.             self.texture == other.texture
  292.             and self.diffuse      == other.diffuse
  293.             and self.hardness     == other.hardness
  294.             and self.specular     == other.specular
  295.             and self.ambient      == other.ambient
  296.             and self.emissive     == other.emissive
  297.             and self.shading_type == other.shading_type
  298.         )
  299.    
  300.     def __nq__(self, other):
  301.         return not self.__eq__(other)
  302.  
  303. class AnimationKey:
  304.     TYPE_SIZE = (
  305.         4, # 0: WXYZ Quaternion Rotation
  306.         3, # 1: XYZ Scale
  307.         3, # 2: XYZ Translate
  308.         3  # 3: XYZ Euler Rotation
  309.     )
  310.    
  311.     def __str__(self):
  312.         return "<AnimationKey>%d:%d Keys</AnimationKey>" % (self.key_type, len(self.keys))
  313.    
  314.     def __init__(self, key_type):
  315.         if not key_type in range(4):
  316.             raise ValueError("Invalid Animation Key Type %d" % key_type)
  317.        
  318.         self.key_type = key_type
  319.         self.keys = []
  320.         self.vector_size = __class__.TYPE_SIZE[self.key_type]
  321.    
  322.     def add_key(self, keyframe, vector):
  323.         if len(vector) != self.vector_size:
  324.             raise ValueError("Incorrect Vector Size")
  325.        
  326.         self.keys.append((keyframe, vector))
  327.        
  328.         return self.keys[-1]
  329.  
  330. class Envelope:
  331.     def __init__(self, bone, vertices=None):
  332.         self.bone = bone # bone is a Frame object which is the bone this envelope refers to.
  333.         self.vertices = vertices if vertices else []
  334.    
  335.     def __str__(self):
  336.         return "<Envelope>Bone %s</Envelope>" % self.bone.name
  337.    
  338.     def add_weight(self, vertex_index, weight_value):
  339.         # (weight_value) is what percent the vertex at index (vertex_index) is influenced by (self.bone)
  340.         self.vertices.append((vertex_index, weight_value))
  341.  
  342. class XSIParseError(Exception): pass
  343.  
  344. class Reader:
  345.     # Prevent number strings like "0.0" from raising exceptions in int()
  346.     def int_float(value):
  347.         return int(float(value))
  348.    
  349.     BLOCK_EOF = None
  350.     BLOCK_END = False
  351.     MAX_WORD = 128
  352.     UNNAMED_FRAME_NAME = "unnamed"
  353.     NON_CHARACTERS = "\r"
  354.    
  355.     # These regular expressions allow for looser syntax matching of different XSI template names.
  356.     import re
  357.     RE_HEADER             = re.compile(r"(?i)^\s*xsi\s*0101txt\s*0032\s*$")
  358.     RE_FRAME              = re.compile(r"(?i)(?:SI_)?Frame")
  359.     RE_TRANSFORM_MATRIX   = re.compile(r"(?i)(?:SI_)?(?:Frame)?(?:Transform)?Matrix")
  360.     RE_POSE_MATRIX        = re.compile(r"(?i)(?:SI_)?(?:Frame)?(?:Base)(?:Pose)?Matrix")
  361.     RE_MESH               = re.compile(r"(?i)(?:SI_)?Mesh")
  362.     RE_MESH_MATERIALLIST  = re.compile(r"(?i)(?:SI_)?(?:Mesh)?MaterialList")
  363.     RE_MESH_MATERIAL      = re.compile(r"(?i)(?:SI_)?(?:Mesh)?Material")
  364.     RE_MESH_TEXTURE       = re.compile(r"(?i)(?:SI_)?(?:Texture|TextureFilename)(?:2D)?")
  365.     RE_MESH_NORMALS       = re.compile(r"(?i)(?:SI_)?(?:Mesh)?Normals")
  366.     RE_MESH_VERTEX_COLORS = re.compile(r"(?i)(?:SI_)?(?:Mesh)?VertexColors")
  367.     RE_MESH_UVMAP         = re.compile(r"(?i)(?:SI_)?(?:Mesh)?TextureCoords")
  368.     RE_ANIMATION_SET      = re.compile(r"(?i)(?:SI_)?AnimationSet")
  369.     RE_ANIMATION          = re.compile(r"(?i)(?:SI_)?Animation")
  370.     RE_ANIMATION_KEY      = re.compile(r"(?i)(?:SI_)?AnimationKey")
  371.     RE_ENVELOPE_LIST      = re.compile(r"(?i)(?:SI_)?EnvelopeList")
  372.     RE_ENVELOPE           = re.compile(r"(?i)(?:SI_)?Envelope")
  373.     RE_LIGHT              = re.compile(r"(?i)(?:SI_)?Light")
  374.     RE_CAMERA             = re.compile(r"(?i)(?:SI_)?Camera")
  375.  
  376.     # Silently skip any blocks that match:
  377.     RE_JUNK = re.compile(r"(?i)(?:SI_)?(?:Fog|Ambience|Angle|Coord.+?|AnimationParam.+?)")
  378.     DEFAULT_RE_SKIP = set((RE_JUNK,))
  379.    
  380.     def __init__(self, f, bz2xsi_xsi=None, re_skip=None, log_name="XSI"):
  381.         self.f = f
  382.         self.xsi = bz2xsi_xsi if bz2xsi_xsi else XSI()
  383.         self.re_skip = Reader.DEFAULT_RE_SKIP.copy() if re_skip is None else re_skip
  384.        
  385.         self.log_name = log_name # Name used in Reader.pos
  386.         self.line = 1
  387.         self.col = 1
  388.        
  389.         self.read()
  390.    
  391.     def pos(self, info=""):
  392.         return "%s:%d:%d:%s" % (self.log_name, self.line, self.col, info)
  393.    
  394.     # Cleans prefix information out of data block names
  395.     def clean(self, name):
  396.         if name[0] == "{" and name[-1] == "}":
  397.             name = name[1:-1]
  398.        
  399.         if name[0:4].casefold() == "frm-":
  400.             return name[4::]
  401.        
  402.         elif name[0:5].casefold() == "anim-":
  403.             return name[5::]
  404.        
  405.         if not name:
  406.             name = Reader.UNNAMED_FRAME_NAME
  407.        
  408.         return name
  409.    
  410.     def parse_block_headers(self):
  411.         while True:
  412.             word = ""
  413.             name, parameters = None, []
  414.            
  415.             for i in range(Reader.MAX_WORD):
  416.                 c = self.f.read(1)
  417.                
  418.                 # No more data in file
  419.                 if not c:
  420.                     # yield Reader.BLOCK_EOF, None
  421.                     return
  422.                
  423.                 # Update line/col for debugging and error reporting
  424.                 if c in Reader.NON_CHARACTERS:
  425.                     continue
  426.                 elif c == "\n":
  427.                     self.line += 1
  428.                     self.col = 1
  429.                 else:
  430.                     self.col += 1
  431.                
  432.                 # Create word if delimiter is found
  433.                 if c in " \t\n{}":
  434.                     if word:
  435.                         if name == None:
  436.                             name = word
  437.                         else:
  438.                             parameters.append(word)
  439.                        
  440.                         word = ""
  441.                 else:
  442.                     # Treat ';' & ',' as delimiters until first character is found
  443.                     if word or not c in ",;":
  444.                         word += c
  445.                
  446.                 if c == "{":
  447.                     yield name, parameters
  448.                     break
  449.                
  450.                 elif c == "}":
  451.                     # yield Reader.BLOCK_END, None
  452.                     return
  453.             else:
  454.                 raise XSIParseError(self.pos("Single Data Word Exceeded %d" % Reader.MAX_WORD))
  455.    
  456.     def parse_word(self):
  457.         word = ""
  458.         in_quote = False
  459.        
  460.         for i in range(Reader.MAX_WORD):
  461.             c = self.f.read(1)
  462.            
  463.             # No more data in file
  464.             if not c:
  465.                 return Reader.BLOCK_EOF
  466.            
  467.             # Being in a quote always takes presedence
  468.             if in_quote:
  469.                 if c == "\n":
  470.                     continue
  471.                     # raise XSIParseError(self.pos("Unterminated String"))
  472.                
  473.                 if not c in Reader.NON_CHARACTERS:
  474.                     self.col += 1
  475.                    
  476.                     if c == in_quote:
  477.                         return word
  478.                    
  479.                     word += c
  480.                    
  481.                     continue
  482.            
  483.             # Update line/col for debugging and error reporting
  484.             if c in Reader.NON_CHARACTERS:
  485.                 continue
  486.             elif c == "\n":
  487.                 self.line += 1
  488.                 self.col = 1
  489.             else:
  490.                 self.col += 1
  491.            
  492.             # Check for string quote start
  493.             if c in "'\"":
  494.                 in_quote = c
  495.                
  496.                 continue
  497.            
  498.             # Finalize word if it's been delimited
  499.             if c in " \t\n,;":
  500.                 if word:
  501.                     return word
  502.                
  503.                 continue
  504.            
  505.             word += c
  506.         else:
  507.             raise XSIParseError(self.pos("Single Data Word Exceeded %d" % Reader.MAX_WORD))
  508.    
  509.     def parse_type(self, data_type):
  510.             word = self.parse_word()
  511.            
  512.             if word == None:
  513.                 raise XSIParseError(self.pos("Unexpected EOF"))
  514.            
  515.             try:
  516.                 return data_type(word)
  517.            
  518.             except ValueError:
  519.                 raise XSIParseError(self.pos("Expected %s, got %r" % (data_type.__name__, word)))
  520.    
  521.     def parse_types(self, *read_as_data_type):
  522.         segments = []
  523.         for data_type in read_as_data_type:
  524.             segments.append(self.parse_type(data_type))
  525.        
  526.         return segments
  527.    
  528.     def parse_3d_data(self, vector=(float, float, float), faces_are_indexed=True):
  529.         vertices = []
  530.         for index in range(self.parse_type(Reader.int_float)):
  531.             vertices.append(self.parse_types(*vector))
  532.        
  533.         faces = []
  534.         if faces_are_indexed:
  535.             for face_index in range(self.parse_type(Reader.int_float)):
  536.                 index, count = self.parse_types(int, int)
  537.                 vector = (int,) * count
  538.                 faces.append(self.parse_types(*vector))
  539.         else:
  540.             for face_index in range(self.parse_type(Reader.int_float)):
  541.                 count = self.parse_type(int)
  542.                 vector = (int,) * count
  543.                 faces.append(self.parse_types(*vector))
  544.        
  545.         return vertices, faces
  546.    
  547.     def skip_block(self):
  548.         depth = 1 # Only call skip_block if parser already parsed past initial '{' of block being skipped!
  549.        
  550.         while depth:
  551.             word = self.parse_word()
  552.            
  553.             if word == None:
  554.                 return Reader.BLOCK_EOF # raise XSIParseError(self.pos("Unexpected EOF"))
  555.            
  556.             if word == "{":
  557.                 depth += 1
  558.             elif word == "}":
  559.                 depth -= 1
  560.        
  561.         return Reader.BLOCK_END
  562.    
  563.     def read(self):
  564.         header = " ".join(self.parse_types(str, str, str))
  565.        
  566.         if not Reader.RE_HEADER.match(header):
  567.             raise XSIParseError(self.pos("Invalid XSI Header %r" % header))
  568.        
  569.         for block_type, parameters in self.parse_block_headers():
  570.             if any(r.match(block_type) for r in self.re_skip):
  571.                 self.skip_block()
  572.            
  573.             elif Reader.RE_LIGHT.match(block_type):
  574.                 self.read_light(self.xsi, parameters)
  575.            
  576.             elif Reader.RE_CAMERA.match(block_type):
  577.                 self.read_camera(self.xsi, parameters)
  578.            
  579.             elif Reader.RE_FRAME.match(block_type):
  580.                 self.read_frame(self.xsi, parameters)
  581.            
  582.             elif Reader.RE_ANIMATION_SET.match(block_type):
  583.                 self.read_animation_set()
  584.            
  585.             elif Reader.RE_ENVELOPE_LIST.match(block_type):
  586.                 self.read_envelope_list()
  587.            
  588.             else:
  589.                 if ALLOW_PRINT:
  590.                     print(self.pos("Unknown Block %r In XSI" % block_type))
  591.                
  592.                 self.skip_block()
  593.    
  594.     def read_light(self, parent_container, parameters):
  595.         name = self.clean(parameters[0]) if parameters else Reader.UNNAMED_FRAME_NAME
  596.        
  597.         # 0 = point
  598.         # 1 = directional
  599.         # 2 = spot
  600.         # 3 = Softimage infinite light
  601.         light_type = self.parse_type(Reader.int_float)
  602.        
  603.         if light_type == 0:
  604.             parent_container.lights.append(
  605.                 PointLight(
  606.                     name=name,
  607.                     rgb=self.parse_types(float, float, float),
  608.                     location_xyz=self.parse_types(float, float, float)
  609.                 )
  610.             )
  611.        
  612.         self.skip_block()
  613.    
  614.     def read_camera(self, parent_container, parameters):
  615.         name = self.clean(parameters[0]) if parameters else Reader.UNNAMED_FRAME_NAME
  616.        
  617.         parent_container.cameras.append(
  618.             Camera(
  619.                 name=name,
  620.                 location_xyz=self.parse_types(float, float, float),
  621.                 look_at_xyz=self.parse_types(float, float, float),
  622.                 roll=self.parse_type(float),
  623.                 near_plane=self.parse_type(float),
  624.                 far_plane=self.parse_type(float)
  625.             )
  626.         )
  627.        
  628.         self.skip_block()
  629.    
  630.     def read_frame(self, parent_frame, parameters):
  631.         name = self.clean(parameters[0]) if parameters else Reader.UNNAMED_FRAME_NAME
  632.        
  633.         if RENAME_DUPLICATE_NAMED_FRAMES:
  634.             for index in range(9999):
  635.                 if not name in self.xsi.frame_table:
  636.                     break
  637.                
  638.                 if ALLOW_PRINT:
  639.                     print(self.pos("Duplicate Frame %r Renamed" % name))
  640.                 name += "_"
  641.             else:
  642.                 raise XSIParseError("Failed To Generate Unique Frame Name")
  643.        
  644.         frame = parent_frame.add_frame(name)
  645.        
  646.         for block_type, parameters in self.parse_block_headers():
  647.             if any(r.match(block_type) for r in self.re_skip):
  648.                 self.skip_block()
  649.            
  650.             elif Reader.RE_TRANSFORM_MATRIX.match(block_type):
  651.                 frame.transform = self.read_matrix()
  652.        
  653.             elif Reader.RE_POSE_MATRIX.match(block_type):
  654.                 frame.pose = self.read_matrix()
  655.            
  656.             elif Reader.RE_MESH.match(block_type):
  657.                 frame.mesh = self.read_mesh()
  658.            
  659.             elif Reader.RE_FRAME.match(block_type):
  660.                 self.read_frame(frame, parameters)
  661.            
  662.             # Sometimes XSI files might have unclosed braces for frames.
  663.             # In this scenario the animation set or envelope list may appear as a child of the last frame.
  664.             # Because these are non-hierarchical blocks we can just read them as if they were parsed globally
  665.             # outside of frames at root level.
  666.             elif Reader.RE_ANIMATION_SET.match(block_type):
  667.                 self.read_animation_set()
  668.            
  669.             elif Reader.RE_ENVELOPE_LIST.match(block_type):
  670.                 self.read_envelope_list()
  671.            
  672.             else:
  673.                 if ALLOW_PRINT:
  674.                     print(self.pos("Unknown Block %r In Frame %r" % (block_type, frame.name)))
  675.                
  676.                 self.skip_block()
  677.        
  678.         return frame
  679.    
  680.     def read_matrix(self):
  681.         matrix = Matrix(
  682.             self.parse_types(float, float, float, float),
  683.             self.parse_types(float, float, float, float),
  684.             self.parse_types(float, float, float, float),
  685.             self.parse_types(float, float, float, float)
  686.         )
  687.        
  688.         self.skip_block()
  689.         return matrix
  690.    
  691.     def read_mesh(self):
  692.         mesh = Mesh()
  693.        
  694.         mesh.vertices, mesh.faces = self.parse_3d_data((float, float, float), False)
  695.        
  696.         for block_type, parameters in self.parse_block_headers():
  697.             if any(r.match(block_type) for r in self.re_skip):
  698.                 self.skip_block()
  699.            
  700.             elif Reader.RE_MESH_MATERIALLIST.match(block_type):
  701.                 self.read_material_list(mesh)
  702.            
  703.             elif Reader.RE_MESH_NORMALS.match(block_type):
  704.                 mesh.normal_vertices, mesh.normal_faces = self.parse_3d_data((float, float, float), True)
  705.                 self.skip_block()
  706.            
  707.             elif Reader.RE_MESH_UVMAP.match(block_type):
  708.                 mesh.uv_vertices, mesh.uv_faces = self.parse_3d_data((float, float), True)
  709.                 self.skip_block()
  710.            
  711.             elif Reader.RE_MESH_VERTEX_COLORS.match(block_type):
  712.                 mesh.vertex_colors, mesh.vertex_color_faces = self.parse_3d_data((float, float, float, float), True)
  713.                 self.skip_block()
  714.            
  715.             else:
  716.                 print(self.pos("Unknown Block %r In Mesh" % block_type))
  717.                 self.skip_block()
  718.        
  719.         return mesh
  720.    
  721.     def read_material_list(self, mesh):
  722.         material_count = self.parse_type(Reader.int_float)
  723.         material_face_count = self.parse_type(Reader.int_float)
  724.         material_face_indices = self.parse_types(*(int,) * material_face_count)
  725.         materials = []
  726.        
  727.         for block_type, parameters in self.parse_block_headers():
  728.             if any(r.match(block_type) for r in self.re_skip):
  729.                 self.skip_block()
  730.            
  731.             elif Reader.RE_MESH_MATERIAL.match(block_type):
  732.                 materials.append(self.read_material())
  733.            
  734.             else:
  735.                 if ALLOW_PRINT:
  736.                     print(self.pos("Unknown Block %r In Mesh Material List" % block_type))
  737.                 self.skip_block()
  738.        
  739.         # Make sure material count matches the one specified in the header.
  740.         while len(materials) < material_count:
  741.             if ALLOW_PRINT:
  742.                 print(self.pos("Missing Material %d in Mesh Material List" % len(materials)))
  743.             materials.append(Material()) # Assign default material
  744.        
  745.         # Assign data to Mesh object
  746.         for index in material_face_indices:
  747.             # Note: material face index being > material list size would result in IndexError being raised
  748.             mesh.face_materials.append(materials[index])
  749.    
  750.     def read_material(self):
  751.         material = Material(
  752.             diffuse      = self.parse_types(float, float, float, float),
  753.             hardness     = self.parse_type(float),
  754.             specular     = self.parse_types(float, float, float),
  755.             emissive     = self.parse_types(float, float, float),
  756.             shading_type = self.parse_type(Reader.int_float),
  757.             ambient      = self.parse_types(float, float, float)
  758.         )
  759.        
  760.         for block_type, parameters in self.parse_block_headers():
  761.             if any(r.match(block_type) for r in self.re_skip):
  762.                 self.skip_block()
  763.            
  764.             elif Reader.RE_MESH_TEXTURE.match(block_type):
  765.                 material.texture = self.parse_type(str)
  766.                 self.skip_block() # BZ2 only uses texture file name
  767.            
  768.             else:
  769.                 if ALLOW_PRINT:
  770.                     print("Unknown Block In Material %r" % block_type)
  771.                 self.skip_block()
  772.        
  773.         return material
  774.    
  775.     def read_animation_set(self):
  776.         for block_type, parameters in self.parse_block_headers():
  777.             if any(r.match(block_type) for r in self.re_skip):
  778.                 self.skip_block()
  779.            
  780.             elif Reader.RE_ANIMATION.match(block_type):
  781.                 self.read_animation(parameters)
  782.            
  783.             else:
  784.                 if ALLOW_PRINT:
  785.                     print(self.pos("Unknown Block %r In Animation Set" % block_type))
  786.                 self.skip_block()
  787.    
  788.     def read_animation(self, parameters):
  789.         name = self.clean(parameters[0]) if parameters else Reader.UNNAMED_FRAME_NAME
  790.         frame_name = self.clean(self.parse_type(str))
  791.        
  792.         if not frame_name in self.xsi.frame_table:
  793.             if ALLOW_PRINT:
  794.                 print(self.pos("Invalid Frame %r Referenced By Animation %r" % (frame_name, name)))
  795.             self.skip_block()
  796.         else:
  797.             frame = self.xsi.frame_table[frame_name]
  798.            
  799.             for block_type, parameters in self.parse_block_headers():
  800.                 if any(r.match(block_type) for r in self.re_skip):
  801.                     self.skip_block()
  802.                
  803.                 elif Reader.RE_ANIMATION_KEY.match(block_type):
  804.                     self.read_animation_key(frame)
  805.                
  806.                 else:
  807.                     if ALLOW_PRINT:
  808.                         print(self.pos("Unknown Block %r In Animation %r" % (block_type, name)))
  809.                     self.skip_block()
  810.    
  811.     def read_animation_key(self, frame):
  812.         try:
  813.             key = AnimationKey(self.parse_type(Reader.int_float))
  814.             key_count = self.parse_type(Reader.int_float)
  815.            
  816.             for animation_index in range(key_count):
  817.                 keyframe = self.parse_type(Reader.int_float)
  818.                 vector = (float,) * self.parse_type(int)
  819.                 key.add_key(keyframe, self.parse_types(*vector))
  820.            
  821.             frame.animation_keys.append(key)
  822.         except ValueError as msg:
  823.             if ALLOW_PRINT:
  824.                 print(self.pos(msg))
  825.        
  826.         self.skip_block()
  827.    
  828.     def read_envelope_list(self):
  829.         envelope_count = self.parse_type(Reader.int_float)
  830.        
  831.         for block_type, parameters in self.parse_block_headers():
  832.             if any(r.match(block_type) for r in self.re_skip):
  833.                 self.skip_block()
  834.            
  835.             elif Reader.RE_ENVELOPE.match(block_type):
  836.                 self.read_envelope()
  837.                 envelope_count -= 1
  838.            
  839.             else:
  840.                 if ALLOW_PRINT:
  841.                     print(self.pos("Unknown Block %r In Envelope List" % block_type))
  842.                 self.skip_block()
  843.        
  844.         if envelope_count != 0:
  845.             if ALLOW_PRINT:
  846.                 print("Envelope Count Mismatch In Envelope List")
  847.        
  848.         self.skip_block()
  849.    
  850.     def read_envelope(self):
  851.         frame_name = self.clean(self.parse_type(str))
  852.         bone_name  = self.clean(self.parse_type(str))
  853.         error_format = None
  854.        
  855.         if not frame_name in self.xsi.frame_table:
  856.             error_format = ("Frame", frame_name, "Bone", bone_name)
  857.             if ALLOW_PRINT:
  858.                 print(self.pos("Invalid %s %r Used By Envelope For %s %r" % error_format))
  859.        
  860.         if not bone_name in self.xsi.frame_table:
  861.             error_format = ("Bone", bone_name, "Frame", frame_name)
  862.             if ALLOW_PRINT:
  863.                 print(self.pos("Invalid %s %r Used By Envelope For %s %r" % error_format))
  864.        
  865.         if error_format:
  866.             self.skip_block()
  867.             return
  868.        
  869.         frame = self.xsi.frame_table[frame_name]
  870.         bone = self.xsi.frame_table[bone_name]
  871.         bone.is_bone = True
  872.         weight_count = self.parse_type(Reader.int_float)
  873.         envelope = frame.add_envelope(bone)
  874.        
  875.         for weight_index in range(weight_count):
  876.             envelope.add_weight(*self.parse_types(int, float))
  877.        
  878.         self.skip_block()
  879.  
  880. class Writer:
  881.     def __init__(self, bz2xsi_xsi, f):
  882.         self.xsi = bz2xsi_xsi
  883.         self.file = f
  884.        
  885.         if f:
  886.             self.write_xsi()
  887.    
  888.     def get_safe_name(self, name, sub="_"):
  889.         ENABLE_NAME_WARNING = False
  890.        
  891.         if not name:
  892.             name = "unnamed"
  893.             if ENABLE_NAME_WARNING:
  894.                 print("XSI WRITER WARNING: Object with no name renamed to %r." % name)
  895.        
  896.         allowed = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890_-"
  897.         new_name = "".join((c if c in allowed else sub) for c in name)
  898.        
  899.         if ENABLE_NAME_WARNING and new_name != name:
  900.             print("XSI WRITER WARNING: Object %r renamed to %r." % (new_name, name))
  901.        
  902.         return new_name
  903.    
  904.     def write(self, t=0, data=""):
  905.         self.file.write("\t" * t + data + "\n")
  906.    
  907.     def write_vector_list(self, t, format_string, vectors):
  908.         self.write(t, "%d;" % len(vectors))
  909.         if not vectors: return
  910.        
  911.         for vector in vectors[0:-1]:
  912.             self.write(t, format_string % tuple(vector) + ",")
  913.         else:
  914.             self.write(t, format_string % tuple(vectors[-1],) + ";")
  915.    
  916.     def write_face_list(self, t, faces, indexed=True):
  917.         def make_face(face):
  918.             return "%d;" % len(face) + ",".join(str(i) for i in face) + ";"
  919.        
  920.         self.write(t, "%d;" % len(faces))
  921.         if not faces: return
  922.        
  923.         if not indexed:
  924.             for face in faces[0:-1]:
  925.                 self.write(t, make_face(face) + ",")
  926.             self.write(t, make_face(faces[-1]) + ";")
  927.         else:
  928.             for index, face in enumerate(faces[0:-1]):
  929.                 self.write(t, "%d;" % index + make_face(face) + ",")
  930.             self.write(t, "%d;" % (len(faces)-1) + make_face(faces[-1]) + ";")
  931.    
  932.     def write_face_vertices(self, t, format_string, faces, vertices):
  933.         self.write(t, "%d;" % len(vertices))
  934.         if not vertices: return
  935.        
  936.         for face in faces:
  937.             for index in face[0:-1]:
  938.                 self.write(t, format_string % tuple(vertices[index]) + ",")
  939.             else:
  940.                 self.write(t, format_string % tuple(vertices[face[-1]]) + ";")
  941.    
  942.     def write_animationkeys(self, t, keys):
  943.         self.write(t, "%d;" % len(keys))
  944.         if not keys: return
  945.        
  946.         vector_size = len(keys[0][1])
  947.         format_string = "%d;%d;" + ";".join(["%f"] * vector_size) + ";;%s"
  948.        
  949.         for keyframe, vector in keys[0:-1]:
  950.             self.write(t, format_string % (keyframe, vector_size, *vector, ","))
  951.         self.write(t, format_string % (keys[-1][0], vector_size, *keys[-1][1], ";"))
  952.    
  953.     def write_xsi(self):
  954.         self.write(0, "xsi 0101txt 0032\n")
  955.        
  956.         self.write(0, "SI_CoordinateSystem coord {")
  957.         self.write(1, "1;")
  958.         self.write(1, "0;")
  959.         self.write(1, "1;")
  960.         self.write(1, "0;")
  961.         self.write(1, "2;")
  962.         self.write(1, "5;")
  963.         self.write(0, "}")
  964.        
  965.         for root_frame in self.xsi.frames:
  966.             self.write()
  967.             self.write_frame(0, root_frame)
  968.        
  969.         animated_frames = tuple(self.xsi.get_animated_frames())
  970.         if animated_frames:
  971.             self.write(0, "\nAnimationSet {")
  972.            
  973.             for frame in animated_frames:
  974.                 self.write_animation(1, frame)
  975.            
  976.             self.write(0, "}")
  977.        
  978.         skinned_frames = tuple(self.xsi.get_skinned_frames())
  979.         if skinned_frames:
  980.             self.write(0, "\nSI_EnvelopeList {")
  981.             self.write(1, "%d;" % self.xsi.get_envelope_count())
  982.            
  983.             for frame in skinned_frames:
  984.                 for envelope in frame.envelopes:
  985.                     self.write_envelope(1, frame, envelope)
  986.            
  987.             self.write(0, "}")
  988.    
  989.     def write_frame(self, t, frame):
  990.         self.write(t, "Frame frm-%s {" % self.get_safe_name(frame.name))
  991.        
  992.         if frame.transform:
  993.             self.write_matrix(t + 1, frame.transform, "FrameTransformMatrix")
  994.        
  995.         if frame.pose:
  996.             self.write_matrix(t + 1, frame.pose, "SI_FrameBasePoseMatrix")
  997.        
  998.         if frame.mesh:
  999.             self.write_mesh(t + 1, frame.mesh, frame.mesh.name if frame.mesh.name else frame.name)
  1000.        
  1001.         for sub_frame in frame.frames:
  1002.             self.write_frame(t + 1, sub_frame)
  1003.        
  1004.         self.write(t, "}")
  1005.    
  1006.     def write_matrix(self, t, matrix, block_name):
  1007.         self.write(t, block_name + " {")
  1008.         self.write(t + 1, "%f,%f,%f,%f,"  % tuple(matrix.right))
  1009.         self.write(t + 1, "%f,%f,%f,%f,"  % tuple(matrix.up))
  1010.         self.write(t + 1, "%f,%f,%f,%f,"  % tuple(matrix.front))
  1011.         self.write(t + 1, "%f,%f,%f,%f;;" % tuple(matrix.posit))
  1012.         self.write(t, "}")
  1013.    
  1014.     def write_mesh(self, t, mesh, name):
  1015.         self.write(t, "Mesh %s {" % self.get_safe_name(name))
  1016.        
  1017.         if mesh.vertices:
  1018.             self.write_vector_list(t + 1, "%f;%f;%f;", mesh.vertices)
  1019.            
  1020.             if mesh.faces:
  1021.                 self.write_face_list(t + 1, mesh.faces, indexed = False)
  1022.            
  1023.             if mesh.face_materials and mesh.faces:
  1024.                 face_material_indices, materials = mesh.get_material_indices()
  1025.                
  1026.                 self.write(t + 1, "MeshMaterialList {")
  1027.                 self.write(t + 2, "%d;" % len(materials))
  1028.                 self.write(t + 2, "%d;" % len(face_material_indices))
  1029.                 for index in face_material_indices[0:-1]:
  1030.                     self.write(t + 2, "%d," % index)
  1031.                 else:
  1032.                     self.write(t + 2, "%d;" % face_material_indices[-1])
  1033.                
  1034.                 for material in materials:
  1035.                     self.write_material(t + 2, material)
  1036.                
  1037.                 self.write(t + 1, "}")
  1038.            
  1039.             if mesh.normal_vertices:
  1040.                 self.write(t + 1, "SI_MeshNormals {")
  1041.                 self.write_vector_list(t + 2, "%f;%f;%f;", mesh.normal_vertices)
  1042.                
  1043.                 if mesh.normal_faces:
  1044.                     self.write_face_list(t + 2, mesh.normal_faces, indexed=True)
  1045.                
  1046.                 self.write(t + 1, "}")
  1047.            
  1048.             if mesh.uv_vertices:
  1049.                 self.write(t + 1, "SI_MeshTextureCoords {")
  1050.                 self.write_vector_list(t + 2, "%f;%f;", mesh.uv_vertices)
  1051.                
  1052.                 if mesh.uv_faces:
  1053.                     self.write_face_list(t + 2, mesh.uv_faces, indexed=True)
  1054.                
  1055.                 self.write(t + 1, "}")
  1056.            
  1057.             if mesh.vertex_colors and mesh.vertex_color_faces:
  1058.                 self.write(t + 1, "SI_MeshVertexColors {")
  1059.                 self.write_face_vertices(
  1060.                     t + 2,
  1061.                     "%f;%f;%f;%f;",
  1062.                     mesh.vertex_color_faces,
  1063.                     mesh.vertex_colors
  1064.                 )
  1065.                 self.write_face_list(t + 2, mesh.vertex_color_faces, indexed=True)
  1066.                 self.write(t + 1, "}")
  1067.        
  1068.         self.write(t, "}")
  1069.    
  1070.     def write_material(self, t, material):
  1071.         self.write(t, "SI_Material {")
  1072.         self.write(t + 1, "%f;%f;%f;%f;;" % tuple(material.diffuse))
  1073.         self.write(t + 1, "%f;" % material.hardness)
  1074.         self.write(t + 1, "%f;%f;%f;;" % tuple(material.specular))
  1075.         self.write(t + 1, "%f;%f;%f;;" % tuple(material.emissive))
  1076.         self.write(t + 1, "%d;" % material.shading_type)
  1077.         self.write(t + 1, "%f;%f;%f;;" % tuple(material.ambient))
  1078.        
  1079.         if material.texture:
  1080.             self.write(t + 1, "SI_Texture2D {")
  1081.             self.write(t + 2, "\"%s\";" % material.texture)
  1082.             self.write(t + 1, "}")
  1083.        
  1084.         self.write(t, "}")
  1085.    
  1086.     def write_animation(self, t, frame):
  1087.         self.write(t, "Animation anim-%s {" % self.get_safe_name(frame.name))
  1088.         self.write(t + 1, "{frm-%s}" % self.get_safe_name(frame.name))
  1089.        
  1090.         for anim_key in frame.animation_keys:
  1091.             self.write(t + 1, "SI_AnimationKey {")
  1092.             self.write(t + 2, "%d;" % anim_key.key_type)
  1093.             self.write_animationkeys(t + 2, anim_key.keys)
  1094.             self.write(t + 1, "}")
  1095.        
  1096.         self.write(t, "}")
  1097.    
  1098.     def write_envelope(self, t, frame, envelope):
  1099.         self.write(t, "SI_Envelope {")
  1100.         self.write(t + 1, "\"frm-%s\";" % self.get_safe_name(frame.name))
  1101.         self.write(t + 1, "\"frm-%s\";" % self.get_safe_name(envelope.bone.name))
  1102.         self.write_vector_list(t + 1, "%d;%f;", envelope.vertices)
  1103.         self.write(t, "}")
  1104.  
  1105. def read(filepath, regex_skip_types=None):
  1106.     with open(filepath, "r") as f:
  1107.         if regex_skip_types == None:
  1108.             reader = Reader(f, bz2xsi_xsi=None, log_name=filepath) # Use defaults
  1109.         else:
  1110.             reader = Reader(f, bz2xsi_xsi=None, log_name=filepath, re_skip=regex_skip_types)
  1111.        
  1112.         return reader.xsi
  1113.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement