def _start_saving(self, mdl0): AutoFix.info('Exporting {} to {}...'.format( os.path.basename(self.brres.name), self.mdl_file)) self.start = time.time() self.image_library = set() if mdl0 is None: mdl0 = self.mdl0 if mdl0 is None: self.mdl0 = mdl0 = self.brres.models[0] else: self.mdl0 = mdl0 if type(mdl0) == str: self.mdl0 = mdl0 = self.brres.get_model(mdl0) if mdl0 is None: raise RuntimeError('No mdl0 file found to export!') self.polygons = [ x for x in mdl0.objects if self._should_include_geometry(x) ] mats = [] for x in self.polygons: if x.material not in mats: mats.append(x.material) self.materials = mats self.cwd = os.getcwd() work_dir, name = os.path.split(self.mdl_file) if work_dir: os.chdir(work_dir) base_name, ext = os.path.splitext(name) self.image_dir = base_name + '_maps' self.json_file = base_name + '.json' self.influences = mdl0.get_influences() self.bones = {} self.tex0_map = {} return base_name, mdl0
def unpack(self, pt, binfile): start = binfile.start() l = binfile.readLen() binfile.advance(4) binfile.store() binfile.advance(4) pt.index, pt.comp_count, pt.format, pt.divisor, pt.stride, pt.count = binfile.read( '3I2BH', 16) if not self.is_valid_comp_count(pt.comp_count): AutoFix.error( '{} has invalid component count {}, using default {}'.format( pt.name, pt.comp_count, pt.default_comp_count)) pt.comp_count = pt.default_comp_count if not is_valid_format(pt.format): # determine the format using the file length binfile.recall(pop=False) old_format = pt.format bytes_remaining = start + l - binfile.offset width = bytes_remaining // (pt.count * pt.comp_count) if width >= 4: pt.format = point.FMT_FLOAT elif width >= 2: # assumes unsigned pt.format = point.FMT_INT16 else: pt.format = point.FMT_INT8 AutoFix.error('{} has invalid format {}, using format {}'.format( pt.name, old_format, point.FMT_STR[pt.format]))
def rename(self): node = self.get_indexed_item(self.clicked_index) if self.level == 0: self.handler.save_as_dialog() elif self.level == 1: brres = node.parent old_name = node.name text, ok = QInputDialog.getText(self, 'Rename Node', 'Rename to:', text=old_name) if ok and text != old_name: if brres.get_model(text): AutoFix.error( 'Model with name {} already exists!'.format(text)) return node.rename(text) self.handler.on_rename_update(node, old_name) elif self.level == 2: mdl0 = node.parent text, ok = QInputDialog.getText(self, 'Rename Node', 'Rename to:', text=node.name) if ok: if text in [x.name for x in mdl0.objects]: AutoFix.error( 'Polygon with name {} already exists!'.format(text)) return node.rename(text)
def pack(self, fog, binfile): AutoFix.warn('packing scn0 fog is not supported.') pack_header(binfile, fog.name, fog.node_id, fog.real_id) binfile.write('4BI2f', fog.flags, 0, 0, 0, fog.type, fog.start, fog.end) binfile.write('4B', fog.color) binfile.end()
def info(self, key=None, indentation_level=0): trace = '>' + ' ' * indentation_level if indentation_level else '>' + str(self.parent.getMaterialName()) if key: AutoFix.get().info('{}->Stage:{}\t{}:{}'.format(trace, self.name, key, self[key]), 1) else: AutoFix.get().info('{}Stage:{}\tMapId:{} ColorScale:{}'.format( trace, self.name, self['mapid'], self['colorscale']), 1)
def set_lightset_str(self, str): val = int(str) if val > 0: AutoFix.warn("Unusual lightset " + str + ", expected -1") if self.lightset != val: self.lightset = val self.mark_modified()
def decode(self, tex0, dest_file, overwrite=None, num_mips=0): if overwrite is None: overwrite = self.OVERWRITE_IMAGES if not dest_file: dest_file = tex0.name + '.png' elif os.path.isdir(dest_file): dest_file = os.path.join(dest_file, tex0.name + '.png') elif os.path.splitext( os.path.basename(dest_file))[1].lower() != '.png': dest_file += '.png' if not overwrite and os.path.exists(dest_file): AutoFix.warn('File {} already exists!'.format(dest_file)) return None tmp = self.get_temp_dest() f = BinFile(tmp, 'w') tex0.pack(f) f.commitWrite() if num_mips == 0: mips = '--no-mipmaps' elif num_mips == -1: mips = '--n-mm=auto' else: mips = '--n-mm=' + str(num_mips) result = subprocess.call( [self.converter, 'decode', tmp, '-d', dest_file, mips, '-qo'], startupinfo=self.si) if tmp != dest_file: os.remove(tmp) if result: raise DecodeError('Failed to decode {}'.format(tex0.name)) return dest_file
def __init__(self, filename, read_file=True): self.geometries = [] self.vertices = [] self.normals = [] self.texcoords = [] self.materials = {} self.images = set() self.filename = filename if read_file: self.mtllib = None self.parse_file(filename) to_remove = [] for geo in self.geometries: try: geo.normalize(self.vertices, self.normals, self.texcoords) except ValueError: to_remove.append(geo) AutoFix.warn('No geometry found for {}'.format(geo.name)) if to_remove: self.geometries = [ x for x in self.geometries if x not in to_remove ] else: dir, name = os.path.split(filename) base_name = os.path.splitext(name)[0] self.mtllib = base_name + '.mtl'
def add_texture_link(self, name): if name != 'Null' and not self.parent.get_texture(name): tex = fuzzy_match(name, self.parent.textures) notify = 'Adding reference to unknown texture "{}"'.format(name) if tex: notify += ', did you mean ' + tex.name + '?' AutoFix.info(notify, 4)
def post_unpacking(self, brres): for x in brres.textures: brres.texture_map[x.name] = x brres.unused_pat0 = self.post_unpack_anim(self.pat0, 'materials', 'pat0') brres.unused_srt0 = self.post_unpack_anim(self.srt0, 'materials', 'srt0') if brres.unused_pat0 or brres.unused_srt0: AutoFix.warn('Unused animations detected')
def run(self, prefix, cmd): line = prefix + ' ' + cmd self.cmd_queue.append(line) try: Command.run_commands([Command(line)]) except (ParsingException, NoSuchFile) as e: AutoFix.get().exception(e)
def __encode_vertices(self, polygon, vertices, mdl0): vert = Vertex(self.name, mdl0) mdl0.add_to_group(mdl0.vertices, vert) linked_bone = self.linked_bone points = vertices.points if polygon.has_weighted_matrix(): AutoFix.get().warn( f'Polygon weighting is experimental, {polygon.name} will likely be incorrect.' ) for i in range(len(vertices)): influence = self.influences[i] points[i] = influence.apply_to(points[i], decode=False) vertex_format, vertex_divisor, remapper = vertices.encode_data( vert, True) if remapper is not None: new_inf_map = {} old_inf_map = self.influences.influences for i in old_inf_map: new_inf_map[remapper[i]] = old_inf_map[i] self.influences.influences = new_inf_map else: rotation_matrix = get_rotation_matrix( np.array(linked_bone.get_transform_matrix(), dtype=float)) for i in range(len(points)): points[i] = np.dot(rotation_matrix, points[i]) inv_matrix = np.array(linked_bone.get_inv_transform_matrix(), dtype=float) vertices.points = apply_matrix(inv_matrix, vertices.points) vertices.encode_data(vert, False) polygon.vertices = vert self.fmt_str += get_index_format(vert) return True
def remove_tex0(self, name): try: tex = self.texture_map.pop(name) self.textures.remove(tex) self.mark_modified() except KeyError: AutoFix.get().warn('No texture {} in {}'.format(name, self.name))
def set_pat0(self, anim): if self.pat0: AutoFix.error( 'Multiple Pat0 for {} in {}!'.format(self.name, self.getBrres().name), 1) return False self.pat0 = anim return True
def info(self, key=None, indentation_level=0): trace = ' ' * indentation_level + '>' + self.name if indentation_level else self.parent.name + "->" + self.name AutoFix.info("{}:\t{} material(s)".format(trace, len(self.materials)), 1) indentation_level += 1 # pass it along for x in self.materials: x.info(key, indentation_level)
def unpack_unknown(self): if self.uk_offsets: AutoFix.warn('Unknown files {}, may be loosely supported'.format([x[0] for x in self.uk_offsets])) uk = [] for name, offset in self.uk_offsets: self.binfile.offset = offset uk.append(UnknownUnpacker(self.binfile, self.section_offsets, name).node) return uk
def hook_textures(self, textures): m = len(textures) for x in self.node.frames: if x.tex >= m: x.tex = textures[0] AutoFix.get().warn('Unpacked Pat0 {} tex_id out of range'.format(self.node.name), 1) else: x.tex = textures[x.tex]
def set_srt0(self, anim): """This is called by model to set up the srt0 reference""" if self.srt0: AutoFix.error('Multiple Srt0 for {}'.format(self.name), 1) return False self.srt0 = anim anim.set_material(self) return True
def set_light_ref_str(self, value): i = int(value) if i > 0: AutoFix.warn('{} set unusual light ref {}, expected -1'.format( self.name, value)) if self.scn0_light_ref != i: self.scn0_light_ref = i self.mark_modified()
def try_parse(self, text): try: self.cmds_run.append(text) if len(self.cmds_run) > 10: self.cmds_run.pop(0) return Command.run_commands([Command(text)]) except ParsingException as e: AutoFix.get().error(str(e)) return False
def info(self, key=None, indentation=0): prefix = '>' + ' ' * indentation + self.name if indentation else '>(PAT0)' + self.name if key: AutoFix.get().info('{}: {}'.format(prefix, self.get_str(key)), 1) else: val = prefix + ': ' for x in self.SETTINGS: val += ' ' + x + ':' + str(self.get_str(x)) AutoFix.get().info(val, 1)
def run_commands(commandlist): try: for cmd in commandlist: cmd.run_cmd() except (ValueError, SaveError, PasteError, MaxFileLimit, NoSuchFile, FileNotFoundError, ParsingException, OSError, UnpackingError, PackingError, NotImplementedError, NoImgConverterError, RuntimeError) as e: AutoFix.get().exception(e) return False return True
def _create_image_library(self, tex0s): if not tex0s: return True converter = ImgConverter() if not converter: AutoFix.get().error('No image converter found!') return False converter.batch_decode(tex0s, self.image_dir) return True
def on_submit(self): path = self.path_edit.text() dir = os.path.dirname(path) if not os.path.exists(dir): AutoFix.get().error('Path {} does not exist!'.format(path)) else: ImgConverter().decode(self.tex0, path, overwrite=True, num_mips=self.mipmap_count.value()) AutoFix.get().info('Exported {} to {}'.format(self.tex0.name, path)) self.close()
def rename(self): if self.material is not None: current_name = self.material.name text, ok = QInputDialog.getText(self, 'Rename Node', 'Rename to:', text=current_name) if ok and text != current_name: if self.material.parent.get_material_by_name(text) is not None: AutoFix.get().error('Material with name {} already exists!'.format(text)) return self.material.rename(text)
def encode(self, mdl, visible_bone=None, encoder=None, use_default_colors_if_none_found=True, priority=None, has_uv_mtx=None): if priority is not None: self.priority = priority if has_uv_mtx is not None: self.has_uv_mtx = has_uv_mtx if not visible_bone: if not mdl.bones: mdl.add_bone(mdl.name) visible_bone = mdl.bones[0] # if not linked_bone: # self.linked_bone = visible_bone visible_bone.has_geometry = True self.encoded = p = Polygon(self.name, mdl) if encoder is not None: self.encoder = encoder encoder.before_encode(self) self.fmt_str = '>' if self.__encode_influences(p, self.influences, mdl, visible_bone): p.weight_index = self.ipp() self.__encode_tex_matrices(p, self.has_uv_mtx) if self.__encode_vertices(p, self.vertices, mdl): p.vertex_index = self.ipp() if self.__encode_normals(p, self.normals, mdl): p.normal_index = self.ipp() if self.__encode_colors(p, self.colors, mdl, use_default_colors_if_none_found): p.color0_index = self.ipp() self.__encode_texcoords(p, self.texcoords, mdl) tris = self.__construct_tris(p, p.has_weights()) data, p.face_count, p.facepoint_count = self.__encode_tris( tris, p.has_weights()) past_align = len(data) % 0x20 if past_align: data.extend(b'\0' * (0x20 - past_align)) if p.face_count <= 0: # todo, cleanup? return None p.data = data p.encode_str = self.fmt_str mdl.objects.append(p) material = mdl.get_material_by_name(self.material_name) mdl.add_definition(material, p, visible_bone, self.priority) if self.colors and self.ENABLE_VERTEX_COLORS: AutoFix.info( '{} has colors, enabled vertex color in light channel.'.format( self.name)) material.enable_vertex_color() if encoder: encoder.after_encode(p) return p
def save_as_dialog(self): if self.brres: fname, filter = QFileDialog.getSaveFileName( self, 'Save as', self.cwd, 'Brres files (*.brres)') if fname: self.cwd = os.path.dirname(fname) self.brres.save(fname, overwrite=True) self.update_status('Wrote file {}'.format(fname)) else: AutoFix.get().error('No Brres file selected.')
def _end_loading(self): mdl0 = self.mdl0 mdl0.rebuild_header() self.brres.add_mdl0(mdl0) if self.is_map: mdl0.add_map_bones() os.chdir(self.cwd) AutoFix.get().info('\t... finished in {} secs'.format( round(time.time() - self.start, 2))) return mdl0
def _try_import_textures(brres, image_paths): if len(image_paths): try: converter = ImgConverter() converter.batch_encode(image_paths.values(), brres, overwrite=converter.OVERWRITE_IMAGES) except EncodeError: AutoFix.get().warn('Failed to encode images') return image_paths
def enqueue(self, converter): if converter == self.item: AutoFix.get().error('Conversion already in progress!') return False for x in self.queue: if x == converter: AutoFix.get().error('Conversion already in progress!') return False self.queue.append(converter) return True