diff --git a/README.md b/README.md index 84eafd0..8d906c2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ ## Python 3D Models Converter -**Version**: 0.8.8 +A module, which helps convert different 3d formats -### **THIS IS NOT RELEASE VERSION!** - -### Thanks a lot for motivating [AMIRMISTIK]! - - -[AMIRMISTIK]: https://www.youtube.com/channel/UCksd1LeoySP5St6dKlv6mvQ +**Version**: 0.9.0 diff --git a/models_converter/formats/gltf/__init__.py b/models_converter/formats/gltf/__init__.py index 009586a..9eabcaf 100644 --- a/models_converter/formats/gltf/__init__.py +++ b/models_converter/formats/gltf/__init__.py @@ -32,5 +32,6 @@ 'Scene', 'Skin', 'Texture', - 'Parser' + 'Parser', + 'Writer' ] diff --git a/models_converter/formats/gltf/accessor.py b/models_converter/formats/gltf/accessor.py index ad20dd0..7a64eb6 100644 --- a/models_converter/formats/gltf/accessor.py +++ b/models_converter/formats/gltf/accessor.py @@ -21,8 +21,8 @@ def __init__(self): def __init__(self): super().__init__() self.count = None - self.indices = self.Indices() - self.values = self.Values() + self.indices = self.Indices + self.values = self.Values def __init__(self): super().__init__() @@ -35,5 +35,5 @@ def __init__(self): self.normalized = False self.max = None self.min = None - self.sparse = self.Sparse() + self.sparse = self.Sparse self.name = None diff --git a/models_converter/formats/gltf/animation.py b/models_converter/formats/gltf/animation.py index d9476d0..0b85574 100644 --- a/models_converter/formats/gltf/animation.py +++ b/models_converter/formats/gltf/animation.py @@ -21,12 +21,12 @@ def __init__(self): def __init__(self): super().__init__() self.sampler = None - self.target = self.Target() + self.target = self.Target def __init__(self): super().__init__() - self.channels = self.Channel() - self.samplers = self.AnimationSampler() + self.channels = self.Channel + self.samplers = self.AnimationSampler self.name = None diff --git a/models_converter/formats/gltf/gltf.py b/models_converter/formats/gltf/gltf.py index c5a4b7e..eb1b175 100644 --- a/models_converter/formats/gltf/gltf.py +++ b/models_converter/formats/gltf/gltf.py @@ -5,22 +5,22 @@ class GlTF(GlTFProperty): def __init__(self): super().__init__() - self.asset = Asset() + self.asset = Asset self.extensions_used = None self.extensions_required = None - self.accessors = Accessor() - self.animations = Animation() - self.buffers = Buffer() - self.buffer_views = BufferView() - self.cameras = Camera() - self.images = Image() - self.materials = Material() - self.meshes = Mesh() - self.nodes = Node() - self.samplers = Sampler() + self.accessors = Accessor + self.animations = Animation + self.buffers = Buffer + self.buffer_views = BufferView + self.cameras = Camera + self.images = Image + self.materials = Material + self.meshes = Mesh + self.nodes = Node + self.samplers = Sampler self.scene = None - self.scenes = Scene() - self.skins = Skin() - self.textures = Texture() + self.scenes = Scene + self.skins = Skin + self.textures = Texture diff --git a/models_converter/formats/gltf/gltf_property.py b/models_converter/formats/gltf/gltf_property.py index 7d554c4..2f7b83d 100644 --- a/models_converter/formats/gltf/gltf_property.py +++ b/models_converter/formats/gltf/gltf_property.py @@ -1,3 +1,7 @@ +from models_converter.utilities.math import Vector3, Quaternion +from models_converter.utilities.matrix.matrix4x4 import Matrix4x4 + + def to_camelcase(property_name: str): words = property_name.split('_') for word_index in range(len(words)): @@ -9,19 +13,16 @@ def to_camelcase(property_name: str): return camelcase_name -def to_lowercase(property_name: str): - letters = list(property_name) - - for letter_index in range(len(letters)): - letter = letters[letter_index] +def to_lowercase(property_name: str) -> str: + result = '' - if letter.isupper(): - letter = f'_{letter.lower()}' + for char in property_name: + if char.isupper(): + char = f'_{char.lower()}' - letters[letter_index] = letter + result += char - lowercase_name = ''.join(letters) - return lowercase_name + return result class GlTFProperty: @@ -36,13 +37,14 @@ def from_dict(self, dictionary: dict): value_type = type(value) attribute_value = getattr(self, attribute_name) - attribute_value_type = type(attribute_value) - if attribute_value is None or value_type in [int, str]: + if attribute_value is None or value_type in (int, str, bool): attribute_value = value - elif issubclass(attribute_value_type, GlTFProperty): + elif type(attribute_value) in (Vector3, Quaternion, Matrix4x4) and type(value) is list: + attribute_value = type(attribute_value)(*value) + elif issubclass(attribute_value, GlTFProperty): if value_type is list: - value_type = attribute_value_type + value_type = attribute_value values = [] for item in value: @@ -53,7 +55,7 @@ def from_dict(self, dictionary: dict): attribute_value = values else: - attribute_value = attribute_value_type() + attribute_value = attribute_value() attribute_value.from_dict(value) setattr(self, attribute_name, attribute_value) diff --git a/models_converter/formats/gltf/mesh.py b/models_converter/formats/gltf/mesh.py index f636ae1..91076cf 100644 --- a/models_converter/formats/gltf/mesh.py +++ b/models_converter/formats/gltf/mesh.py @@ -14,7 +14,7 @@ def __init__(self): def __init__(self): super().__init__() - self.primitives = self.Primitive() + self.primitives = self.Primitive self.weights = None self.name = None diff --git a/models_converter/formats/gltf/node.py b/models_converter/formats/gltf/node.py index 920e39d..94a8b10 100644 --- a/models_converter/formats/gltf/node.py +++ b/models_converter/formats/gltf/node.py @@ -1,4 +1,6 @@ from .gltf_property import GlTFProperty +from ...utilities.math import Vector3, Quaternion +from ...utilities.matrix.matrix4x4 import Matrix4x4 class Node(GlTFProperty): @@ -7,10 +9,10 @@ def __init__(self): self.camera = None self.children = None self.skin = None - self.matrix = None # Default: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + self.matrix = Matrix4x4(size=(4, 4)) self.mesh = None - self.rotation = None # Default: [0, 0, 0, 1] - self.scale = None # Default: [1, 1, 1] - self.translation = None # Default: [0, 0, 0] + self.rotation = Quaternion() # Default: [0, 0, 0, 1] + self.scale = Vector3(1, 1, 1) # Default: [1, 1, 1] + self.translation = Vector3() # Default: [0, 0, 0] self.weights = None self.name = None diff --git a/models_converter/formats/gltf/parser.py b/models_converter/formats/gltf/parser.py index 391a984..9c6e75a 100644 --- a/models_converter/formats/gltf/parser.py +++ b/models_converter/formats/gltf/parser.py @@ -5,17 +5,13 @@ from models_converter.formats.gltf.gltf import GlTF from models_converter.formats.gltf.node import Node from models_converter.formats.universal import Scene, Geometry -from models_converter.utilities.math import Vector3, Quaternion +from models_converter.interfaces import ParserInterface from models_converter.utilities.reader import Reader -class Parser(Reader): - def __init__(self, initial_bytes: bytes): - super().__init__(initial_bytes, 'little') - - self.magic = self.read(4) - if self.magic != b'glTF': - raise TypeError('File Magic isn\'t "glTF"') +class Parser(ParserInterface): + def __init__(self, data: bytes): + self.file_data = data self.scene = Scene() @@ -32,37 +28,46 @@ def __init__(self, initial_bytes: bytes): self.gltf = GlTF() def parse_bin(self): - super().__init__(self.bin_chunk.data, 'little') + reader = Reader(self.bin_chunk.data, 'little') for buffer in self.gltf.buffers: - parsed_buffer = self.read(buffer.byte_length) + parsed_buffer = reader.read(buffer.byte_length) self.buffers.append(parsed_buffer) for buffer_view in self.gltf.buffer_views: - super().__init__(self.buffers[buffer_view.buffer], 'little') + reader.__init__(self.buffers[buffer_view.buffer], 'little') - self.read(buffer_view.byte_offset) + reader.read(buffer_view.byte_offset) length = buffer_view.byte_length - data = self.read(length) + data = reader.read(length) self.buffer_views.append(data) for accessor in self.gltf.accessors: - super().__init__(self.buffer_views[accessor.buffer_view], 'little') - temp_accessor = [] + reader.__init__(self.buffer_views[accessor.buffer_view], 'little') - self.read(accessor.byte_offset) + reader.read(accessor.byte_offset) types = { - 5120: (self.readByte, 1), - 5121: (self.readUByte, 1), - 5122: (self.readShort, 2), - 5123: (self.readUShort, 2), - 5125: (self.readUInt32, 4), - 5126: (self.readFloat, 4) + 5120: (reader.readByte, 1), + 5121: (reader.readUByte, 1), + 5122: (reader.readShort, 2), + 5123: (reader.readUShort, 2), + 5125: (reader.readUInt32, 4), + 5126: (reader.readFloat, 4) } + if accessor.normalized: + types = { + 5120: (lambda: max(reader.readByte() / 127, -1.0), 1), + 5121: (lambda: reader.readUByte() / 255, 1), + 5122: (lambda: max(reader.readShort() / 32767, -1.0), 2), + 5123: (lambda: reader.readUShort() / 65535, 2), + 5125: (reader.readUInt32, 4), + 5126: (reader.readFloat, 4) + } + items_count = { 'SCALAR': 1, 'VEC2': 2, @@ -73,69 +78,49 @@ def parse_bin(self): 'MAT4': 16 } - component_nb = items_count[accessor.type] - read_type, bytes_per_elem = types[accessor.component_type] - default_stride = bytes_per_elem * component_nb + components_count = items_count[accessor.type] + read_type, bytes_per_element = types[accessor.component_type] + default_stride = bytes_per_element * components_count stride = self.gltf.buffer_views[accessor.buffer_view].byte_stride or default_stride - if default_stride == stride: - for x in range(accessor.count): - temp_list = [] - for i in range(component_nb): - temp_list.append(read_type()) - temp_accessor.append(temp_list) - else: - elems_per_stride = stride // bytes_per_elem - num_elems = (accessor.count - 1) * elems_per_stride + component_nb - temp_list = [] - for i in range(num_elems): - temp_list.append(read_type()) + elements_per_stride = stride // bytes_per_element + elements_count = accessor.count * elements_per_stride - temp_accessor = [temp_list[x:x + component_nb] for x in range(0, num_elems, elems_per_stride)] + temp_list = [] + for i in range(elements_count): + temp_list.append(read_type()) - if accessor.normalized: - for item_index, data in enumerate(temp_accessor): - new_data = [] - for value in data: - if accessor.component_type == 5120: - value = max(value / 127, -1.0) - elif accessor.component_type == 5121: - value /= 255 - elif accessor.component_type == 5122: - value = max(value / 32767, -1.0) - elif accessor.component_type == 5123: - value /= 65535 - new_data.append(value) - temp_accessor[item_index] = new_data - - self.accessors.append(temp_accessor) + self.accessors.append([ + temp_list[i:i + components_count] + for i in range(0, elements_count, elements_per_stride) + ]) def parse(self): - # + reader = Reader(self.file_data, 'little') + + magic = reader.read(4) + if magic != b'glTF': + raise TypeError('Wrong file magic! "676c5446" expected, but given is ' + magic.hex()) - self.version = self.readUInt32() - self.length = self.readUInt32() + self.version = reader.readUInt32() + self.length = reader.readUInt32() self.json_chunk = GlTFChunk() self.bin_chunk = GlTFChunk() - self.json_chunk.chunk_length = self.readUInt32() - self.json_chunk.chunk_name = self.read(4) - self.json_chunk.data = self.read(self.json_chunk.chunk_length) + self.json_chunk.chunk_length = reader.readUInt32() + self.json_chunk.chunk_name = reader.read(4) + self.json_chunk.data = reader.read(self.json_chunk.chunk_length) - self.bin_chunk.chunk_length = self.readUInt32() - self.bin_chunk.chunk_name = self.read(4) - self.bin_chunk.data = self.read(self.bin_chunk.chunk_length) - - # + self.bin_chunk.chunk_length = reader.readUInt32() + self.bin_chunk.chunk_name = reader.read(4) + self.bin_chunk.data = reader.read(self.bin_chunk.chunk_length) self.gltf.from_dict(json.loads(self.json_chunk.data)) self.parse_bin() - # - scene_id = self.gltf.scene scene = self.gltf.scenes[scene_id] @@ -143,10 +128,14 @@ def parse(self): node = self.gltf.nodes[node_id] self.parse_node(node) - # + # TODO: animations + # for animation in self.gltf.animations: + # for channel in animation.channels: + # sampler: Animation.AnimationSampler = animation.samplers[channel.sampler] + # input_accessor = self.accessors[sampler.input] def parse_node(self, gltf_node: Node, parent: str = None): - node_name = gltf_node.name + node_name = gltf_node.name.split('|')[-1] node = universal.Node( name=node_name, @@ -154,8 +143,8 @@ def parse_node(self, gltf_node: Node, parent: str = None): ) instance = None - if gltf_node.mesh: - mesh = self.gltf.meshes[gltf_node.mesh] # TODO: merge _geo.glb and _.*.glb files to fix TypeError + if gltf_node.mesh is not None and type(self.gltf.meshes) is list: + mesh = self.gltf.meshes[gltf_node.mesh] mesh_name = mesh.name.split('|') group = 'GEO' @@ -166,7 +155,7 @@ def parse_node(self, gltf_node: Node, parent: str = None): geometry = Geometry(name=name, group=group) - if gltf_node.skin: + if gltf_node.skin is not None: instance = universal.Node.Instance(name=geometry.get_name(), instance_type='CONT') geometry.set_controller_bind_matrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) @@ -195,11 +184,9 @@ def parse_node(self, gltf_node: Node, parent: str = None): else: instance = universal.Node.Instance(name=geometry.get_name(), instance_type='GEOM') - offsets = { - 'POSITION': 0, - 'NORMAL': 0, - 'TEXCOORD': 0 - } + position_offset = 0 + normal_offset = 0 + texcoord_offset = 0 for primitive in mesh.primitives: if primitive.to_dict() != {}: @@ -226,10 +213,24 @@ def parse_node(self, gltf_node: Node, parent: str = None): if attribute_id == 'POSITION': position = self.accessors[attribute] - points = position + points = list(map( + lambda point: ( + point[0] * gltf_node.scale.x + gltf_node.translation.x, + point[1] * gltf_node.scale.y + gltf_node.translation.y, + point[2] * gltf_node.scale.z + gltf_node.translation.z + ), + position + )) elif attribute_id == 'NORMAL': normal = self.accessors[attribute] - points = normal + points = list(map( + lambda point: ( + point[0] * gltf_node.scale.x, + point[1] * gltf_node.scale.y, + point[2] * gltf_node.scale.z + ), + normal + )) elif attribute_id.startswith('TEXCOORD'): texcoord = self.accessors[attribute] @@ -259,9 +260,9 @@ def parse_node(self, gltf_node: Node, parent: str = None): triangles = [ [ [ - point[0] + offsets['NORMAL'], - point[0] + offsets['POSITION'], - point[0] + offsets['TEXCOORD'] + point[0] + normal_offset, + point[0] + position_offset, + point[0] + texcoord_offset ] for point in triangles[x:x + 3] ] for x in range(0, len(triangles), 3) ] @@ -270,11 +271,11 @@ def parse_node(self, gltf_node: Node, parent: str = None): for attribute_id in attributes: if attribute_id == 'POSITION': - offsets['POSITION'] += len(position) + position_offset += len(position) elif attribute_id == 'NORMAL': - offsets['NORMAL'] += len(normal) + normal_offset += len(normal) elif attribute_id.startswith('TEXCOORD'): - offsets['TEXCOORD'] += len(texcoord) + texcoord_offset += len(texcoord) self.scene.add_geometry(geometry) @@ -283,28 +284,12 @@ def parse_node(self, gltf_node: Node, parent: str = None): self.scene.add_node(node) - if gltf_node.translation or gltf_node.rotation or gltf_node.scale: - node.add_frame(universal.Node.Frame(0, Vector3(), Vector3(1, 1, 1), Quaternion())) - - if gltf_node.translation: - node.get_frames()[0].set_position(Vector3( - gltf_node.translation[0], - gltf_node.translation[1], - gltf_node.translation[2] - )) - if gltf_node.rotation: - node.get_frames()[0].set_rotation(Quaternion( - gltf_node.rotation[0], - gltf_node.rotation[1], - gltf_node.rotation[2], - gltf_node.rotation[3] - )) - if gltf_node.scale: - node.get_frames()[0].set_scale(Vector3( - gltf_node.scale[0], - gltf_node.scale[1], - gltf_node.scale[2] - )) + node.add_frame(universal.Node.Frame( + 0, + gltf_node.translation, + gltf_node.scale, + gltf_node.rotation + )) if gltf_node.children: for child_id in gltf_node.children: diff --git a/models_converter/formats/scw/parser.py b/models_converter/formats/scw/parser.py index 1998e7e..657d56a 100644 --- a/models_converter/formats/scw/parser.py +++ b/models_converter/formats/scw/parser.py @@ -4,22 +4,21 @@ from .chunks import * -class Parser(ParserInterface, Reader): +class Parser(ParserInterface): def __init__(self, file_data: bytes): - Reader.__init__(self, file_data) - self.file_data = file_data self.scene = Scene() self.chunks = [] self.header = None - file_magic = self.read(4) + def parse(self): + reader = Reader(self.file_data) + file_magic = reader.read(4) if file_magic != b'SC3D': raise TypeError('File Magic isn\'t "SC3D"') - def parse(self): - self._split_chunks() + self._split_chunks(reader) for chunk in self.chunks: chunk_name = chunk['chunk_name'] @@ -52,13 +51,13 @@ def parse(self): else: raise TypeError(f'Unknown chunk: {chunk_name}') - def _split_chunks(self): + def _split_chunks(self, reader: Reader): # len(Chunk Length) + len(Chunk Name) + len(Chunk CRC) - while len(self.file_data[self.tell():]) >= 12: - chunk_length = self.readUInt32() - chunk_name = self.readChars(4) - chunk_data = self.read(chunk_length) - chunk_crc = self.readUInt32() + while reader.tell() <= len(self.file_data) - 12: + chunk_length = reader.readUInt32() + chunk_name = reader.readChars(4) + chunk_data = reader.read(chunk_length) + chunk_crc = reader.readUInt32() self.chunks.append({ 'chunk_name': chunk_name, diff --git a/models_converter/formats/universal/node.py b/models_converter/formats/universal/node.py index c5bfc6a..ec380ef 100644 --- a/models_converter/formats/universal/node.py +++ b/models_converter/formats/universal/node.py @@ -104,3 +104,9 @@ def get_frames(self) -> List[Frame]: def add_frame(self, frame: Frame): self._frames.append(frame) + + def set_frames(self, frames: List[Frame]): + self._frames.clear() + + for frame in frames: + self._frames.append(frame) diff --git a/models_converter/formats/universal/scene.py b/models_converter/formats/universal/scene.py index 2125a30..3acfc80 100644 --- a/models_converter/formats/universal/scene.py +++ b/models_converter/formats/universal/scene.py @@ -38,6 +38,13 @@ def get_nodes(self) -> List[Node]: def add_node(self, node: Node): self._nodes.append(node) + def import_nodes(self, animation_scene): + for node in self._nodes: + for animation_node in animation_scene.get_nodes(): + if node.get_name() == animation_node.get_name(): + node.set_frames(animation_node.get_frames()) + break + def get_frame_rate(self) -> int: return self._frame_rate diff --git a/models_converter/interfaces/parser_interface.py b/models_converter/interfaces/parser_interface.py index f20d998..9cfe292 100644 --- a/models_converter/interfaces/parser_interface.py +++ b/models_converter/interfaces/parser_interface.py @@ -1,11 +1,16 @@ +import abc + from models_converter.formats.universal import Scene class ParserInterface: - def __init__(self): + @abc.abstractmethod + def __init__(self, file_data: bytes or str): self.scene: Scene or None = None - raise NotImplementedError('This is an abstract class') - + @abc.abstractmethod def parse(self): - raise NotImplementedError('This is an abstract class') + """ + + :return: + """ diff --git a/models_converter/interfaces/writer_interface.py b/models_converter/interfaces/writer_interface.py index d48f25c..0221572 100644 --- a/models_converter/interfaces/writer_interface.py +++ b/models_converter/interfaces/writer_interface.py @@ -1,13 +1,19 @@ +import abc + from models_converter.formats.universal import Scene class WriterInterface: MAGIC: bytes + @abc.abstractmethod def __init__(self): self.writen: bytes or str = None - raise NotImplementedError('This is an abstract class') - + @abc.abstractmethod def write(self, scene: Scene): - raise NotImplementedError('This is an abstract class') + """ + + :param scene: + :return: + """ diff --git a/models_converter/utilities/math/quaternion.py b/models_converter/utilities/math/quaternion.py index 1038576..c60810a 100644 --- a/models_converter/utilities/math/quaternion.py +++ b/models_converter/utilities/math/quaternion.py @@ -7,3 +7,6 @@ def __init__(self, x: float = 0, y: float = 0, z: float = 0, w: float = 1): def clone(self): return Quaternion(self.x, self.y, self.z, self.w) + + def __repr__(self): + return f'({self.x:.2f}, {self.y:.2f}, {self.z:.2f}, {self.w:.2f})' diff --git a/models_converter/utilities/math/vector3.py b/models_converter/utilities/math/vector3.py index 335ce98..ba149cf 100644 --- a/models_converter/utilities/math/vector3.py +++ b/models_converter/utilities/math/vector3.py @@ -6,3 +6,6 @@ def __init__(self, x: float = 0, y: float = 0, z: float = 0): def clone(self): return Vector3(self.x, self.y, self.z) + + def __repr__(self): + return f'({self.x:.2f}, {self.y:.2f}, {self.z:.2f})' diff --git a/models_converter/utilities/matrix/__init__.py b/models_converter/utilities/matrix/__init__.py index 4589a76..76da29d 100644 --- a/models_converter/utilities/matrix/__init__.py +++ b/models_converter/utilities/matrix/__init__.py @@ -53,7 +53,7 @@ def __str__(self): return str(self.matrix) @staticmethod - def get_identity_matrix(size: tuple): + def get_identity_matrix(size: tuple[int, int]): matrix = [] for y in range(size[1]): diff --git a/models_converter/utilities/reader.py b/models_converter/utilities/reader.py index 467891f..2450a45 100644 --- a/models_converter/utilities/reader.py +++ b/models_converter/utilities/reader.py @@ -1,5 +1,8 @@ +import typing + + class Reader: - def __init__(self, buffer: bytes, endian: str = 'big'): + def __init__(self, buffer: bytes, endian: typing.Literal['big', 'little'] = 'big'): self.buffer = buffer self.endian = endian self.i = 0 diff --git a/models_converter/utils/__pycache__/__init__.cpython-38.pyc b/models_converter/utils/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 7440424..0000000 Binary files a/models_converter/utils/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/models_converter/utils/__pycache__/__init__.cpython-39.pyc b/models_converter/utils/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 485012b..0000000 Binary files a/models_converter/utils/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/__pycache__/reader.cpython-39.pyc b/models_converter/utils/__pycache__/reader.cpython-39.pyc deleted file mode 100644 index d904bf0..0000000 Binary files a/models_converter/utils/__pycache__/reader.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/__pycache__/writer.cpython-39.pyc b/models_converter/utils/__pycache__/writer.cpython-39.pyc deleted file mode 100644 index 70b909f..0000000 Binary files a/models_converter/utils/__pycache__/writer.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/matrix/__pycache__/__init__.cpython-38.pyc b/models_converter/utils/matrix/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 1edbe5e..0000000 Binary files a/models_converter/utils/matrix/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/models_converter/utils/matrix/__pycache__/__init__.cpython-39.pyc b/models_converter/utils/matrix/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 4941021..0000000 Binary files a/models_converter/utils/matrix/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/matrix/__pycache__/matrix2x2.cpython-39.pyc b/models_converter/utils/matrix/__pycache__/matrix2x2.cpython-39.pyc deleted file mode 100644 index 9ec91e1..0000000 Binary files a/models_converter/utils/matrix/__pycache__/matrix2x2.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/matrix/__pycache__/matrix3x3.cpython-39.pyc b/models_converter/utils/matrix/__pycache__/matrix3x3.cpython-39.pyc deleted file mode 100644 index 09c0e41..0000000 Binary files a/models_converter/utils/matrix/__pycache__/matrix3x3.cpython-39.pyc and /dev/null differ diff --git a/models_converter/utils/matrix/__pycache__/matrix4x4.cpython-39.pyc b/models_converter/utils/matrix/__pycache__/matrix4x4.cpython-39.pyc deleted file mode 100644 index d41f0ae..0000000 Binary files a/models_converter/utils/matrix/__pycache__/matrix4x4.cpython-39.pyc and /dev/null differ diff --git a/setup.py b/setup.py index be7912e..7958b50 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='3d-converter', - version='0.8.8', + version='0.9.0', author='Vorono4ka', author_email='crowo4ka@gmail.com', description='Python 3D Models Converter', @@ -20,5 +20,5 @@ 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Operating System :: OS Independent', ], - python_requires='>=3.7', + python_requires='>=3.9', )