From 5ef57c6b21b4eade590353c876d9917d9de54705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0=20=D0=A9=D0=B5=D0=BB?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=20=28Danila=20Schelkov=29?= <54549682+Vorono4ka@users.noreply.github.com> Date: Fri, 30 Oct 2020 22:52:56 +0400 Subject: [PATCH] 0.7.6 --- .gitignore | 1 - 3d_converter.egg-info/PKG-INFO | 4 +- 3d_converter.egg-info/SOURCES.txt | 14 +- README.md | 2 +- models_converter/chunks/__init__.py | 594 ------------------ .../__pycache__/__init__.cpython-39.pyc | Bin 14101 -> 0 bytes .../chunks/__pycache__/chunk.cpython-39.pyc | Bin 2062 -> 0 bytes .../formats/__pycache__/scw.cpython-39.pyc | Bin 3104 -> 0 bytes models_converter/formats/dae.py | 8 +- models_converter/formats/scw/__init__.py | 7 + .../formats/scw/chunks/__init__.py | 17 + models_converter/formats/scw/chunks/came.py | 29 + .../{ => formats/scw}/chunks/chunk.py | 13 +- models_converter/formats/scw/chunks/geom.py | 268 ++++++++ models_converter/formats/scw/chunks/head.py | 30 + models_converter/formats/scw/chunks/mate.py | 101 +++ models_converter/formats/scw/chunks/node.py | 145 +++++ models_converter/formats/scw/chunks/wend.py | 15 + .../formats/{scw.py => scw/parser.py} | 62 +- models_converter/formats/scw/writer.py | 51 ++ setup.py | 2 +- 21 files changed, 697 insertions(+), 666 deletions(-) delete mode 100644 models_converter/chunks/__init__.py delete mode 100644 models_converter/chunks/__pycache__/__init__.cpython-39.pyc delete mode 100644 models_converter/chunks/__pycache__/chunk.cpython-39.pyc delete mode 100644 models_converter/formats/__pycache__/scw.cpython-39.pyc create mode 100644 models_converter/formats/scw/__init__.py create mode 100644 models_converter/formats/scw/chunks/__init__.py create mode 100644 models_converter/formats/scw/chunks/came.py rename models_converter/{ => formats/scw}/chunks/chunk.py (89%) create mode 100644 models_converter/formats/scw/chunks/geom.py create mode 100644 models_converter/formats/scw/chunks/head.py create mode 100644 models_converter/formats/scw/chunks/mate.py create mode 100644 models_converter/formats/scw/chunks/node.py create mode 100644 models_converter/formats/scw/chunks/wend.py rename models_converter/formats/{scw.py => scw/parser.py} (61%) create mode 100644 models_converter/formats/scw/writer.py diff --git a/.gitignore b/.gitignore index 9bb15ae..2aa2982 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ /upload.bat /build.bat *.pyc -*.pyc diff --git a/3d_converter.egg-info/PKG-INFO b/3d_converter.egg-info/PKG-INFO index 11b0af8..4eda263 100644 --- a/3d_converter.egg-info/PKG-INFO +++ b/3d_converter.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: 3d-converter -Version: 0.7.5 +Version: 0.7.6 Summary: Python 3D Models Converter Home-page: https://github.com/vorono4ka/3d-converter Author: Vorono4ka @@ -8,7 +8,7 @@ Author-email: crowo4ka@gmail.com License: GPLv3 Description: ## `Python 3D Models Converter` - **Version**: 0.7.4 + **Version**: 0.7.6 ### Thanks a lot for motivating [AMIRMISTIK]! diff --git a/3d_converter.egg-info/SOURCES.txt b/3d_converter.egg-info/SOURCES.txt index 952246a..922e012 100644 --- a/3d_converter.egg-info/SOURCES.txt +++ b/3d_converter.egg-info/SOURCES.txt @@ -5,13 +5,21 @@ setup.py 3d_converter.egg-info/dependency_links.txt 3d_converter.egg-info/top_level.txt models_converter/__init__.py -models_converter/chunks/__init__.py -models_converter/chunks/chunk.py models_converter/formats/__init__.py models_converter/formats/dae.py models_converter/formats/gltf.py models_converter/formats/obj.py -models_converter/formats/scw.py +models_converter/formats/scw/__init__.py +models_converter/formats/scw/parser.py +models_converter/formats/scw/writer.py +models_converter/formats/scw/chunks/__init__.py +models_converter/formats/scw/chunks/came.py +models_converter/formats/scw/chunks/chunk.py +models_converter/formats/scw/chunks/geom.py +models_converter/formats/scw/chunks/head.py +models_converter/formats/scw/chunks/mate.py +models_converter/formats/scw/chunks/node.py +models_converter/formats/scw/chunks/wend.py models_converter/utils/__init__.py models_converter/utils/reader.py models_converter/utils/writer.py diff --git a/README.md b/README.md index e38a291..cea42fc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## `Python 3D Models Converter` -**Version**: 0.7.5 +**Version**: 0.7.6 ### Thanks a lot for motivating [AMIRMISTIK]! diff --git a/models_converter/chunks/__init__.py b/models_converter/chunks/__init__.py deleted file mode 100644 index d5f59a2..0000000 --- a/models_converter/chunks/__init__.py +++ /dev/null @@ -1,594 +0,0 @@ -from ..utils.reader import Reader -from ..utils.writer import Writer -from .chunk import Chunk - - -class HEAD(Chunk, Writer, Reader): - def __init__(self, header=None): - super().__init__(header) - self.chunk_name = 'HEAD' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer) - - setattr(self, 'version', self.readUShort()) - setattr(self, 'frame_rate', self.readUShort()) - setattr(self, 'v1', self.readUShort()) - setattr(self, 'v2', self.readUShort()) - setattr(self, 'materials_file', self.readString()) - if self.get('version') == 2: - setattr(self, 'v3', self.readUByte()) - - def encode(self): - Writer.__init__(self) - - self.writeUShort(2) # getattr(self, 'version') - self.writeUShort(getattr(self, 'frame_rate')) - self.writeUShort(0) # getattr(self, 'v1') - self.writeUShort(249) # getattr(self, 'v2') - self.writeString(getattr(self, 'materials_file')) - self.writeUByte(0) # getattr(self, 'v3') - - self.length = len(self.buffer) - - -class MATE(Chunk, Writer, Reader): - def __init__(self, header: dict): - super().__init__(header) - self.chunk_name = 'MATE' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer) - - setattr(self, 'name', self.readString()) - setattr(self, 'shader', self.readString()) - setattr(self, 'v1', self.readUByte()) - - effect = {} - use_ambient_tex = self.readBool() - if use_ambient_tex: - ambient_tex = self.readString() - effect['ambient'] = ambient_tex - else: - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - ambient_color = (r, g, b, a) - effect['ambient'] = ambient_color - - use_diffuse_tex = self.readBool() - if use_diffuse_tex: - diffuse_tex = self.readString() - effect['diffuse'] = diffuse_tex - else: - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - diffuse_color = (r, g, b, a) - effect['diffuse'] = diffuse_color - - use_specular_tex = self.readBool() - if use_specular_tex: - specular_tex = self.readString() - effect['specular'] = specular_tex - else: - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - specular_color = (r, g, b, a) - effect['specular'] = specular_color - - setattr(self, 'v2', self.readString()) - setattr(self, 'v3', self.readString()) - - use_colorize_tex = self.readBool() - if use_colorize_tex: - colorize_tex = self.readString() - effect['colorize'] = colorize_tex - else: - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - colorize_color = (r, g, b, a) - effect['colorize'] = colorize_color - - use_emission_tex = self.readBool() - if use_emission_tex: - emission_tex = self.readString() - effect['emission'] = emission_tex - else: - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - emission_color = (r, g, b, a) - effect['emission'] = emission_color - - setattr(self, 'v4', self.readString()) - setattr(self, 'v5', self.readFloat()) - setattr(self, 'v6', self.readFloat()) - - effect['lightmaps'] = { - 'diffuse': self.readString(), - 'specular': self.readString() - } - - a = self.readUByte() - r = self.readUByte() - g = self.readUByte() - b = self.readUByte() - effect['tint'] = (r, g, b, a) - - setattr(self, 'effect', effect) - - def encode(self): - Writer.__init__(self) - - self.length = len(self.buffer) - - -class GEOM(Chunk, Writer, Reader): - def __init__(self, header: dict): - super().__init__(header) - self.chunk_name = 'GEOM' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer) - - setattr(self, 'name', self.readString()) - setattr(self, 'group', self.readString()) - if self.header['version'] < 2: - matrix = [] - for x in range(4): - temp_list = [] - for x1 in range(4): - temp_list.append(self.readFloat()) - matrix.append(temp_list) - - self.parse_vertices() - self.parse_skin() - self.parse_materials() - - def parse_vertices(self): - vertices = [] - inputs = [] - - vertex_count = self.readUByte() - for x in range(vertex_count): - vertex = [] - vertex_type = self.readString() - vertex_index = self.readUByte() - self.readUByte() # sub_index - vertex_stride = self.readUByte() - vertex_scale = self.readFloat() - vertex_count = self.readUInt32() - - if vertex_type == 'VERTEX': - vertex_type = 'POSITION' - - for x1 in range(vertex_count): - coordinates_massive = [] - for x2 in range(vertex_stride): - coordinate = self.readShort() - coordinates_massive.append(coordinate / 32512) - if vertex_type == 'TEXCOORD': - coordinates_massive[1::2] = [1 - x for x in coordinates_massive[1::2]] - vertex.append(coordinates_massive) - - inputs.append({ - 'type': vertex_type, - 'offset': x, - 'name': f'{vertex_type.lower()}_0' - }) - - vertices.append({ - 'name': f'{vertex_type.lower()}_0', - 'type': vertex_type, - 'index': vertex_index, - 'scale': vertex_scale, - 'vertex': vertex - }) - setattr(self, 'inputs', inputs) - setattr(self, 'vertices', vertices) - - def parse_skin(self): - bind_matrix = [] - - setattr(self, 'have_bind_matrix', self.readBool()) - if getattr(self, 'have_bind_matrix'): - for x in range(16): - bind_matrix.append(self.readFloat()) - - setattr(self, 'bind_matrix', bind_matrix) - - self.parse_joints() - self.parse_weights() - - def parse_joints(self): - joints = [] - - joint_counts = self.readUByte() - for x in range(joint_counts): - joint_matrix = [] - joint_name = self.readString() - for x1 in range(16): - joint_matrix.append(self.readFloat()) - joints.append({'name': joint_name, 'matrix': joint_matrix}) - - setattr(self, 'joints', joints) - - def parse_weights(self): - vertex_weights = [] - weights = [] - vcount = [] - - vertex_weights_count = self.readUInt32() - for x in range(vertex_weights_count): - vcount.append(0) - joint_a = self.readUByte() - joint_b = self.readUByte() - joint_c = self.readUByte() - joint_d = self.readUByte() - weight_a = self.readUShort() - weight_b = self.readUShort() - weight_c = self.readUShort() - weight_d = self.readUShort() - temp_list = [ - [joint_a, weight_a], - [joint_b, weight_b], - [joint_c, weight_c], - [joint_d, weight_d] - ] - for pair in temp_list: - if pair[1] != 0: - vcount[x] += 1 - vertex_weights.append(pair[0]) - if pair[1] / 65535 not in weights: - weights.append(pair[1] / 65535) - vertex_weights.append(weights.index(pair[1] / 65535)) - - setattr(self, 'weights', - { - 'vertex_weights': vertex_weights, - 'weights': weights, - 'vcount': vcount - }) - - def parse_materials(self): - materials = [] - - materials_count = self.readUByte() - for x in range(materials_count): - polygons = [] - material_name = self.readString() - self.readString() - polygons_count = self.readUShort() - inputs_count = self.readUByte() - vertex_id_length = self.readUByte() - for x1 in range(polygons_count): - temp_list = [] - for x2 in range(3): - second_temp_list = [] - for x3 in range(inputs_count): - second_temp_list.append(self.readUInteger(vertex_id_length)) - temp_list.append(second_temp_list) - polygons.append(temp_list) - materials.append({ - 'name': material_name, - 'inputs': getattr(self, 'inputs'), - 'polygons': polygons - }) - - setattr(self, 'materials', materials) - - def encode(self): - Writer.__init__(self) - - self.writeString(self.get('name')) - self.writeString(self.get('group')) - - self.encode_vertices(self.get('vertices')) - - self.encode_skin() - - self.encode_materials() - - self.length = len(self.buffer) - - def encode_vertices(self, vertices: dict): - self.writeUByte(len(vertices)) - for vertex in vertices: - self.writeString(vertex['type']) - self.writeUByte(vertex['index']) - self.writeUByte(0) # sub_index - self.writeUByte(len(vertex['vertex'][0])) - self.writeFloat(vertex['scale']) - self.writeUInt32(len(vertex['vertex'])) - for coordinates_massive in vertex['vertex']: - if vertex['type'] == 'TEXCOORD': - coordinates_massive[1::2] = [1 - x for x in coordinates_massive[1::2]] - for coordinate in coordinates_massive: - # coordinate /= vertex['scale'] - coordinate *= 32512 - self.writeShort(round(coordinate)) - - def encode_skin(self): - self.writeBool(self.get('have_bind_matrix')) - if self.get('have_bind_matrix'): - for x in self.get('bind_matrix'): - self.writeFloat(x) - - self.encode_joints() - - self.encode_weight() - - def encode_joints(self): - if self.get('have_bind_matrix'): - self.writeUByte(len(self.get('joints'))) - - for joint in self.get('joints'): - self.writeString(joint['name']) - for x in joint['matrix']: - self.writeFloat(x) - else: - self.writeUByte(0) - - def encode_weight(self): - if self.get('have_bind_matrix'): - self.writeUInt32(len(self.get('weights')['vcount'])) - past_index = 0 - for vcount in self.get('weights')['vcount']: - temp_list = [] - for x in range(vcount): - vertex_weights_index = x * 2 + past_index * 2 - joint_id = self.get('weights')['vertex_weights'][vertex_weights_index] - weight_id = self.get('weights')['vertex_weights'][vertex_weights_index + 1] - - weight = self.get('weights')['weights'][weight_id] - - if weight > 1: - weight = 1 - elif weight < 0: - weight = 0 - - weight = int(weight * 65535) - - temp_list.append([joint_id, weight]) - past_index += vcount - while len(temp_list) < 4: - temp_list.append([0, 0]) - for x in temp_list: - self.writeUByte(x[0]) - for x in temp_list: - self.writeUShort(x[1]) - else: - self.writeUInt32(0) - - def encode_materials(self): - self.writeUByte(len(self.get('materials'))) - for material in self.get('materials'): - self.writeString(material['name']) - self.writeString('') - self.writeUShort(len(material['polygons'])) - - # Calculate settings - inputs_count = len(material['polygons'][0][0]) - - maximal_value = 0 - for points in material['polygons']: - for point in points: - for vertex in point: - if vertex > maximal_value: - maximal_value = vertex - - short_length = 1 if maximal_value <= 255 else 2 - - # Write Settings - self.writeUByte(inputs_count) - self.writeUByte(short_length) - - # Write Polygons - for points in material['polygons']: - for point in points: - for vertex in point: - self.writeUInteger(vertex, short_length) - - -class CAME(Chunk, Writer, Reader): - def __init__(self, header=None): - super().__init__(header) - self.chunk_name = 'CAME' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer=buffer) - - setattr(self, 'name', self.readString()) - setattr(self, 'v1', self.readFloat()) - setattr(self, 'xFov', self.readFloat()) - setattr(self, 'aspectRatio', self.readFloat()) - setattr(self, 'zNear', self.readFloat()) - setattr(self, 'zFar', self.readFloat()) - - def encode(self): - Writer.__init__(self) - - self.writeString(getattr(self, 'name')) - self.writeFloat(getattr(self, 'v1')) - self.writeFloat(getattr(self, 'xFov')) - self.writeFloat(getattr(self, 'aspectRatio')) - self.writeFloat(getattr(self, 'zNear')) - self.writeFloat(getattr(self, 'zFar')) - - self.length = len(self.buffer) - - -class NODE(Chunk, Writer, Reader): - def __init__(self, header: dict): - super().__init__(header) - self.chunk_name = 'NODE' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer) - nodes = [] - - nodes_count = self.readUShort() - for node in range(nodes_count): - node_data = { - 'name': self.readString(), - 'parent': self.readString() - } - - instances_count = self.readUShort() - node_data['instances'] = [{}] * instances_count - for x in range(instances_count): - instance_type = self.readChar(4) - instance_name = self.readString() - - node_data['instances'][x] = {} - if instance_type in ['GEOM', 'CONT']: - materials_count = self.readUShort() - binds = [] - for bind in range(materials_count): - binds.append({}) - symbol = self.readString() - target = self.readString() - binds[bind] = {'symbol': symbol, - 'target': target} - node_data['instances'][x]['binds'] = binds - elif instance_type in ['CAME']: - target = self.readString() - node_data['instances'][x]['target'] = target - node_data['instances'][x]['instance_name'] = instance_name - node_data['instances'][x]['instance_type'] = instance_type - - frames_count = self.readUShort() - node_data['frames'] = [] - if frames_count > 0: - rotation = {'x': 0, 'y': 0, 'z': 0, 'w': 0} - scale_x, scale_y, scale_z = 0, 0, 0 - pos_x, pos_y, pos_z = 0, 0, 0 - - settings = list(bin(self.readUByte())[2:].zfill(8)) - settings = [bool(int(value)) for value in settings] - node_data['frames_settings'] = settings - for frame in range(frames_count): - frame_data = { - 'frame_id': self.readUShort() - } - - if settings[7] or frame == 0: # Rotation - rotation = { - 'x': self.readNShort(), - 'y': self.readNShort(), - 'z': self.readNShort(), - 'w': self.readNShort() - } - - if settings[4] or frame == 0: # Position X - pos_x = self.readFloat() - if settings[5] or frame == 0: # Position Y - pos_y = self.readFloat() - if settings[6] or frame == 0: # Position Z - pos_z = self.readFloat() - - if settings[1] or frame == 0: # Scale X - scale_x = self.readFloat() - if settings[2] or frame == 0: # Scale Y - scale_y = self.readFloat() - if settings[3] or frame == 0: # Scale Z - scale_z = self.readFloat() - - frame_data['rotation'] = rotation - frame_data['position'] = { - 'x': pos_x, - 'y': pos_y, - 'z': pos_z - } - frame_data['scale'] = { - 'x': scale_x, - 'y': scale_y, - 'z': scale_z - } - - node_data['frames'].append(frame_data) - nodes.append(node_data) - setattr(self, 'nodes', nodes) - - def encode(self): - Writer.__init__(self) - - self.writeUShort(len(self.get('nodes'))) - for node in self.get('nodes'): - self.writeString(node['name']) - self.writeString(node['parent']) - - self.writeUShort(len(node['instances'])) - for instance in node['instances']: - self.writeChar(instance['instance_type']) - self.writeString(instance['instance_name']) - self.writeUShort(len(instance['binds'])) - for bind in instance['binds']: - self.writeString(bind['symbol']) - self.writeString(bind['target']) - - if 'frames_settings' in node: - frames_settings = node['frames_settings'] - else: - frames_settings = None - self.encode_frames(node['frames'], frames_settings) - - self.length = len(self.buffer) - - def encode_frames(self, frames, frames_settings): - self.writeUShort(len(frames)) - if len(frames) > 0: - self.writeUByte(int(''.join([('1' if item else '0') for item in frames_settings])[::], 2)) - for frame in frames: - self.writeUShort(frame['frame_id']) - if frames_settings[7] or frames.index(frame) == 0: # Rotation - self.writeNShort(frame['rotation']['x']) - self.writeNShort(frame['rotation']['y']) - self.writeNShort(frame['rotation']['z']) - self.writeNShort(frame['rotation']['w']) - - if frames_settings[4] or frames.index(frame) == 0: # Position X - self.writeFloat(frame['position']['x']) - if frames_settings[5] or frames.index(frame) == 0: # Position Y - self.writeFloat(frame['position']['y']) - if frames_settings[6] or frames.index(frame) == 0: # Position Z - self.writeFloat(frame['position']['z']) - - if frames_settings[1] or frames.index(frame) == 0: # Scale X - self.writeFloat(frame['scale']['x']) - if frames_settings[2] or frames.index(frame) == 0: # Scale Y - self.writeFloat(frame['scale']['y']) - if frames_settings[3] or frames.index(frame) == 0: # Scale Z - self.writeFloat(frame['scale']['z']) - - -class WEND(Chunk, Writer, Reader): - def __init__(self, header=None): - super().__init__(header) - self.chunk_name = 'WEND' - - def parse(self, buffer: bytes): - Reader.__init__(self, buffer) - - def encode(self): - Writer.__init__(self) - - self.length = len(self.buffer) - - -__all__ = [ - 'HEAD', - 'MATE', - 'GEOM', - 'CAME', - 'NODE', - 'WEND' -] diff --git a/models_converter/chunks/__pycache__/__init__.cpython-39.pyc b/models_converter/chunks/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 81303cc9e63b5fb20233ea0925e6e1f5fc5e5a22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14101 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 diff --git a/models_converter/chunks/__pycache__/chunk.cpython-39.pyc b/models_converter/chunks/__pycache__/chunk.cpython-39.pyc deleted file mode 100644 index 5cdb899f0f7869faf6b9165306ee6d686f8114e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2062 zcmZuyOK&4Z5bo}I+Hsu623BG(W(7+I#7YPx&?;Ji!c#)gN(&dutJcKbiJi5_PR}^P z*7!nl;KYd=ASK-Lm-@shQA!>n1GE7lh3O(m3f#@n9umH!7$ls10bF>K?R)+EQ;|#j1|lO~nz< zeH-ldCfUoqu2_nS#`Nyu&9@;+L^WZi(PwjB@{&p136d)TiAW(m#ju~hXW744!2ThT z_%`h6RB2=DT{=c)=}_Bne{ygDTR70!VbM#3^>aNqFaefaGD8Tz`hNFG=f_-|yz_+` z_Os5jX}6b};WINj(%mBOe5^j~j;2B zi@8?-n^KhCsmIxIgK)Xzd$?uLFZ3|CQLc-$C`{s;8g?+N5Y~ICr#3iA2NUf8sKg4n zY6oUCloSxDc?iJ}`#PVkRdFx;2s|{pn3$}~Y1Y+4r!0d03yMo4J7VYDh5c(4b}1A; zVWxH5ERoC!H_cMa{emvS=YeALunYv;3`At+y)V4QHQi-j+U zeKC(k9kR*m_`w2gwp!)%D&kw@Bl1$aaO8UjgMxu*Xf9^UQ4XW$tUwk^exIH4pHZ|9 z$=Smndb%~*r^;#d(!7<8T8rzns3cV@@oWSanQe4f5!BYd%as1i-1x>i}vMK(Jy5;`(bTCydEJ4h)S<0U^=n+a diff --git a/models_converter/formats/__pycache__/scw.cpython-39.pyc b/models_converter/formats/__pycache__/scw.cpython-39.pyc deleted file mode 100644 index 1734d508e0deec44865e3a3811b6662fc5287d34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3104 zcmZWr&2QYs6`vu8LoT)Yi1XnnOU>XRZMP_!c0y6ao5sL$17XxR}S`%=?&mAHTP~>FJQ+ z`Tm;+@uL~WzNL@JN8sZ+hW-pBWhw8oh^0>I_W2sUMc)~?k;`fB^~J!8Jj`Y4KV*@- z!O|dYJ!EO?$ccP>h3OQ&rZVryje?ZlW}WsE=w;m}4rqrPei6i5zQ0#&V5m)PWJDVd0*fC zkuSiCn-wWp-BEc_b{r#mYEv5*^f6OYu<+wwmfv5$qcf$~m(oFAtlv6F?)TK-mKttl zNvYQt(@V*)_(!J7Osx-wY1Y^A(}j(p8uY-Wll{v(2ZqN19i0f{Z65F$uBI_Rb4%KE zF|K3iw?Q%%@svdl-4(8NGm*Mq@l>SVLlJqIOeMbjG{Bdiws6zX$j?=d10@{d(t z!DS3hkxzNW;@US(_Xmph?d8Q4^8G$Z7nlj1^*5{|YSIRi3F0^}@-mL+NE4}Kd^B5I zygc3`;j~xw61z`4c*rUR&>rDyOb{wQ&kh|5)1h0r$Gmb*I0f_Asa!k5rROQM3x}d~ zE9ZzGdX)!0amwe>Z1*B`xecc*h2``sADr@()3=;!rDu18DmaE4At&Zp&QG}lnw1hK zYT0wN=-Z}{*ERCK%}Wz*X2U^Nsyx%iPkMt)^|bK{B!KStMtroov=X&8)Nl}|c~Y7w z>(V%(>^9=Y>Zdo2SYEohYDD+b70}ODyDObgJqMqt=ZU-kVgwnBg2vQ=5j2ZrZO#aK z8$mXW*heNm8z|2jC3X4&2y+51dFX^KO1;o&yUiQx=bX@y&Zyl)(F#&w*aM%5A;r_j zurd4slZqd+6BJp+1l!^j-*S#!O4Snwb8*a4#PK}1w!HJ~J!D{vv?I<^9{W!@>irWw zmaRFP!hJ%?8M*iJ&5^fPZd|(B35?8&1UFZdgpurL#b$Zm1m!TkcTgfGp*?wAkc;HK zy^W1bnc!Yt^mLNvk(a1s@k+<39sW90JmWhxkF~29T7obhI2ZZo+@yEvSaxv|)B~A` zV-v=NE6sxSpKX0sU9c)$a9<_)`+ zVC~F9YUt5dK*l(c!Tn$HM{L95;KOz#ELO0{8-w#B1R(N(6ma9Mo&vlf)~$^k2me{k zy3kBMptt@AglZL4Ro4qah?BF;<_E(bR@_ZwLo84|9`b-6Iu$^A+f^&2OvMp@062qj zuiNqJJZ}LcGnM!HTD=Gh>LnuQiM&CCTroQTA&$3jv$vTi3%M?STP|E6xVnI9^Yq?M zrmT+@0j7~Vbg9%xn*`bjg*q~=wS%2(RjHxsxOL!(ISTE)2c-Bhyb#Apzo&H^KSeyN z`8s&@%OHf@(s6k6`(NNoCqzMwW@}fTLf{$nCD`CnHTQ%a(w=DC2S<{_0g?3{T)Rc(1CXq!8(65^9rw8>Hi^h}xajZjvXEk2Bt0VT zqE>1*i8AZ=qmZ0)=i{PWyb=X8Uq*vde;~1UL5%F}>`s3apeoBh`Y`cV#dkMeS6S*i!uk&1}8CN;6xuzr<`bd#BhghWo{W4fy*DowiLUs}Fy;s6Znh zaBVShkz}tE`4bVV-fFf=NuT;7$hbpVz_y8fjkbOT5($eTzozd6BEKO*ZSR>pR7+U< zJBB8W_200d>5OysFImW6`q?|gU$v0GfFXGseX>bVdyP(@eh*FRO(K^-jAwgL4M=~c zRkCTO8)!*Tbz^w3nExKJ*qVI{GZJWk|J+ @@ -424,6 +424,10 @@ def node(self, nodes): instances = instance_geometry.extend(instance_controller) children = self.node(node.findall('collada:node', self.namespaces)) + + if 'name' not in node.attrib: + node.attrib['name'] = node.attrib['id'] + node_data = { 'name': node.attrib['name'], 'instances': [] @@ -453,7 +457,7 @@ def node(self, nodes): controller_url = instance_controller.attrib['url'] node_data['instances'][0]['instance_name'] = controller_url[1:] - node_data['instances'][0]['binds'] = binds + node_data['instances'][len(node_data['instances'])-1]['binds'] = binds matrix = node.findall('collada:matrix', self.namespaces) if matrix: diff --git a/models_converter/formats/scw/__init__.py b/models_converter/formats/scw/__init__.py new file mode 100644 index 0000000..cb85844 --- /dev/null +++ b/models_converter/formats/scw/__init__.py @@ -0,0 +1,7 @@ +from .writer import Writer +from .parser import Parser + +__all__ = [ + 'Writer', + 'Parser' +] diff --git a/models_converter/formats/scw/chunks/__init__.py b/models_converter/formats/scw/chunks/__init__.py new file mode 100644 index 0000000..b8bd048 --- /dev/null +++ b/models_converter/formats/scw/chunks/__init__.py @@ -0,0 +1,17 @@ +from .chunk import Chunk +from .head import HEAD +from .mate import MATE +from .geom import GEOM +from .came import CAME +from .node import NODE +from .wend import WEND + +__all__ = [ + 'Chunk', + 'HEAD', + 'MATE', + 'GEOM', + 'CAME', + 'NODE', + 'WEND' +] diff --git a/models_converter/formats/scw/chunks/came.py b/models_converter/formats/scw/chunks/came.py new file mode 100644 index 0000000..eb76959 --- /dev/null +++ b/models_converter/formats/scw/chunks/came.py @@ -0,0 +1,29 @@ +from . import Chunk + + +class CAME(Chunk): + def __init__(self, header=None): + super().__init__(header) + self.chunk_name = 'CAME' + + def parse(self, buffer: bytes): + super().parse(buffer) + + setattr(self, 'name', self.readString()) + setattr(self, 'v1', self.readFloat()) + setattr(self, 'xFov', self.readFloat()) + setattr(self, 'aspectRatio', self.readFloat()) + setattr(self, 'zNear', self.readFloat()) + setattr(self, 'zFar', self.readFloat()) + + def encode(self): + super().encode() + + self.writeString(getattr(self, 'name')) + self.writeFloat(getattr(self, 'v1')) + self.writeFloat(getattr(self, 'xFov')) + self.writeFloat(getattr(self, 'aspectRatio')) + self.writeFloat(getattr(self, 'zNear')) + self.writeFloat(getattr(self, 'zFar')) + + self.length = len(self.buffer) diff --git a/models_converter/chunks/chunk.py b/models_converter/formats/scw/chunks/chunk.py similarity index 89% rename from models_converter/chunks/chunk.py rename to models_converter/formats/scw/chunks/chunk.py index 9ed27c7..4f28584 100644 --- a/models_converter/chunks/chunk.py +++ b/models_converter/formats/scw/chunks/chunk.py @@ -1,7 +1,14 @@ -class Chunk: +from ....utils.reader import Reader +from ....utils.writer import Writer + + +class Chunk(Writer, Reader): def __init__(self, header=None): + super().__init__() + if header is None: header = {} + self.header = header self.chunk_name = '' @@ -53,9 +60,11 @@ def set(self, key: str, value): setattr(self, key, value) def parse(self, buffer: bytes): - pass + Reader.__init__(self, buffer, 'big') def encode(self): + Writer.__init__(self, 'big') + self.length = len(self.buffer) get = __getitem__ diff --git a/models_converter/formats/scw/chunks/geom.py b/models_converter/formats/scw/chunks/geom.py new file mode 100644 index 0000000..d04b133 --- /dev/null +++ b/models_converter/formats/scw/chunks/geom.py @@ -0,0 +1,268 @@ +from . import Chunk + + +class GEOM(Chunk): + def __init__(self, header: dict): + super().__init__(header) + self.chunk_name = 'GEOM' + + def parse(self, buffer: bytes): + super().parse(buffer) + + setattr(self, 'name', self.readString()) + setattr(self, 'group', self.readString()) + if self.header['version'] < 2: + matrix = [] + for x in range(4): + temp_list = [] + for x1 in range(4): + temp_list.append(self.readFloat()) + matrix.append(temp_list) + + self.parse_vertices() + self.parse_skin() + self.parse_materials() + + def parse_vertices(self): + vertices = [] + inputs = [] + + vertex_count = self.readUByte() + for x in range(vertex_count): + vertex = [] + vertex_type = self.readString() + vertex_index = self.readUByte() + self.readUByte() # sub_index + vertex_stride = self.readUByte() + vertex_scale = self.readFloat() + vertex_count = self.readUInt32() + + if vertex_type == 'VERTEX': + vertex_type = 'POSITION' + + for x1 in range(vertex_count): + coordinates_massive = [] + for x2 in range(vertex_stride): + coordinate = self.readShort() + coordinates_massive.append(coordinate / 32512) + if vertex_type == 'TEXCOORD': + coordinates_massive[1::2] = [1 - x for x in coordinates_massive[1::2]] + vertex.append(coordinates_massive) + + inputs.append({ + 'type': vertex_type, + 'offset': x, + 'name': f'{vertex_type.lower()}_0' + }) + + vertices.append({ + 'name': f'{vertex_type.lower()}_0', + 'type': vertex_type, + 'index': vertex_index, + 'scale': vertex_scale, + 'vertex': vertex + }) + setattr(self, 'inputs', inputs) + setattr(self, 'vertices', vertices) + + def parse_skin(self): + bind_matrix = [] + + setattr(self, 'have_bind_matrix', self.readBool()) + if getattr(self, 'have_bind_matrix'): + for x in range(16): + bind_matrix.append(self.readFloat()) + + setattr(self, 'bind_matrix', bind_matrix) + + self.parse_joints() + self.parse_weights() + + def parse_joints(self): + joints = [] + + joint_counts = self.readUByte() + for x in range(joint_counts): + joint_matrix = [] + joint_name = self.readString() + for x1 in range(16): + joint_matrix.append(self.readFloat()) + joints.append({'name': joint_name, 'matrix': joint_matrix}) + + setattr(self, 'joints', joints) + + def parse_weights(self): + vertex_weights = [] + weights = [] + vcount = [] + + vertex_weights_count = self.readUInt32() + for x in range(vertex_weights_count): + vcount.append(0) + joint_a = self.readUByte() + joint_b = self.readUByte() + joint_c = self.readUByte() + joint_d = self.readUByte() + weight_a = self.readUShort() + weight_b = self.readUShort() + weight_c = self.readUShort() + weight_d = self.readUShort() + temp_list = [ + [joint_a, weight_a], + [joint_b, weight_b], + [joint_c, weight_c], + [joint_d, weight_d] + ] + for pair in temp_list: + if pair[1] != 0: + vcount[x] += 1 + vertex_weights.append(pair[0]) + if pair[1] / 65535 not in weights: + weights.append(pair[1] / 65535) + vertex_weights.append(weights.index(pair[1] / 65535)) + + setattr(self, 'weights', + { + 'vertex_weights': vertex_weights, + 'weights': weights, + 'vcount': vcount + }) + + def parse_materials(self): + materials = [] + + materials_count = self.readUByte() + for x in range(materials_count): + polygons = [] + material_name = self.readString() + self.readString() + polygons_count = self.readUShort() + inputs_count = self.readUByte() + vertex_id_length = self.readUByte() + for x1 in range(polygons_count): + temp_list = [] + for x2 in range(3): + second_temp_list = [] + for x3 in range(inputs_count): + second_temp_list.append(self.readUInteger(vertex_id_length)) + temp_list.append(second_temp_list) + polygons.append(temp_list) + materials.append({ + 'name': material_name, + 'inputs': getattr(self, 'inputs'), + 'polygons': polygons + }) + + setattr(self, 'materials', materials) + + def encode(self): + super().encode() + + self.writeString(self.get('name')) + self.writeString(self.get('group')) + + self.encode_vertices(self.get('vertices')) + + self.encode_skin() + + self.encode_materials() + + self.length = len(self.buffer) + + def encode_vertices(self, vertices: dict): + self.writeUByte(len(vertices)) + for vertex in vertices: + self.writeString(vertex['type']) + self.writeUByte(vertex['index']) + self.writeUByte(0) # sub_index + self.writeUByte(len(vertex['vertex'][0])) + self.writeFloat(vertex['scale']) + self.writeUInt32(len(vertex['vertex'])) + for coordinates_massive in vertex['vertex']: + if vertex['type'] == 'TEXCOORD': + coordinates_massive[1::2] = [1 - x for x in coordinates_massive[1::2]] + for coordinate in coordinates_massive: + # coordinate /= vertex['scale'] + coordinate *= 32512 + self.writeShort(round(coordinate)) + + def encode_skin(self): + self.writeBool(self.get('have_bind_matrix')) + if self.get('have_bind_matrix'): + for x in self.get('bind_matrix'): + self.writeFloat(x) + + self.encode_joints() + + self.encode_weight() + + def encode_joints(self): + if self.get('have_bind_matrix'): + self.writeUByte(len(self.get('joints'))) + + for joint in self.get('joints'): + self.writeString(joint['name']) + for x in joint['matrix']: + self.writeFloat(x) + else: + self.writeUByte(0) + + def encode_weight(self): + if self.get('have_bind_matrix'): + self.writeUInt32(len(self.get('weights')['vcount'])) + past_index = 0 + for vcount in self.get('weights')['vcount']: + temp_list = [] + for x in range(vcount): + vertex_weights_index = x * 2 + past_index * 2 + joint_id = self.get('weights')['vertex_weights'][vertex_weights_index] + weight_id = self.get('weights')['vertex_weights'][vertex_weights_index + 1] + + weight = self.get('weights')['weights'][weight_id] + + if weight > 1: + weight = 1 + elif weight < 0: + weight = 0 + + weight = int(weight * 65535) + + temp_list.append([joint_id, weight]) + past_index += vcount + while len(temp_list) < 4: + temp_list.append([0, 0]) + for x in temp_list: + self.writeUByte(x[0]) + for x in temp_list: + self.writeUShort(x[1]) + else: + self.writeUInt32(0) + + def encode_materials(self): + self.writeUByte(len(self.get('materials'))) + for material in self.get('materials'): + self.writeString(material['name']) + self.writeString('') + self.writeUShort(len(material['polygons'])) + + # Calculate settings + inputs_count = len(material['polygons'][0][0]) + + maximal_value = 0 + for points in material['polygons']: + for point in points: + for vertex in point: + if vertex > maximal_value: + maximal_value = vertex + + short_length = 1 if maximal_value <= 255 else 2 + + # Write Settings + self.writeUByte(inputs_count) + self.writeUByte(short_length) + + # Write Polygons + for points in material['polygons']: + for point in points: + for vertex in point: + self.writeUInteger(vertex, short_length) diff --git a/models_converter/formats/scw/chunks/head.py b/models_converter/formats/scw/chunks/head.py new file mode 100644 index 0000000..2b17bbe --- /dev/null +++ b/models_converter/formats/scw/chunks/head.py @@ -0,0 +1,30 @@ +from . import Chunk + + +class HEAD(Chunk): + def __init__(self, header=None): + super().__init__(header) + self.chunk_name = 'HEAD' + + def parse(self, buffer: bytes): + super().parse(buffer) + + setattr(self, 'version', self.readUShort()) + setattr(self, 'frame_rate', self.readUShort()) + setattr(self, 'v1', self.readUShort()) + setattr(self, 'v2', self.readUShort()) + setattr(self, 'materials_file', self.readString()) + if self.get('version') == 2: + setattr(self, 'v3', self.readUByte()) + + def encode(self): + super().encode() + + self.writeUShort(2) # getattr(self, 'version') + self.writeUShort(getattr(self, 'frame_rate')) + self.writeUShort(0) # getattr(self, 'v1') + self.writeUShort(249) # getattr(self, 'v2') + self.writeString(getattr(self, 'materials_file')) + self.writeUByte(0) # getattr(self, 'v3') + + self.length = len(self.buffer) diff --git a/models_converter/formats/scw/chunks/mate.py b/models_converter/formats/scw/chunks/mate.py new file mode 100644 index 0000000..fd5fa30 --- /dev/null +++ b/models_converter/formats/scw/chunks/mate.py @@ -0,0 +1,101 @@ +from . import Chunk + + +class MATE(Chunk): + def __init__(self, header: dict): + super().__init__(header) + self.chunk_name = 'MATE' + + def parse(self, buffer: bytes): + super().parse(buffer) + + setattr(self, 'name', self.readString()) + setattr(self, 'shader', self.readString()) + setattr(self, 'v1', self.readUByte()) + + effect = {} + use_ambient_tex = self.readBool() + if use_ambient_tex: + ambient_tex = self.readString() + effect['ambient'] = ambient_tex + else: + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + ambient_color = (r, g, b, a) + effect['ambient'] = ambient_color + + use_diffuse_tex = self.readBool() + if use_diffuse_tex: + diffuse_tex = self.readString() + effect['diffuse'] = diffuse_tex + else: + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + diffuse_color = (r, g, b, a) + effect['diffuse'] = diffuse_color + + use_specular_tex = self.readBool() + if use_specular_tex: + specular_tex = self.readString() + effect['specular'] = specular_tex + else: + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + specular_color = (r, g, b, a) + effect['specular'] = specular_color + + setattr(self, 'v2', self.readString()) + setattr(self, 'v3', self.readString()) + + use_colorize_tex = self.readBool() + if use_colorize_tex: + colorize_tex = self.readString() + effect['colorize'] = colorize_tex + else: + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + colorize_color = (r, g, b, a) + effect['colorize'] = colorize_color + + use_emission_tex = self.readBool() + if use_emission_tex: + emission_tex = self.readString() + effect['emission'] = emission_tex + else: + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + emission_color = (r, g, b, a) + effect['emission'] = emission_color + + setattr(self, 'v4', self.readString()) + setattr(self, 'v5', self.readFloat()) + setattr(self, 'v6', self.readFloat()) + + effect['lightmaps'] = { + 'diffuse': self.readString(), + 'specular': self.readString() + } + + a = self.readUByte() + r = self.readUByte() + g = self.readUByte() + b = self.readUByte() + effect['tint'] = (r, g, b, a) + + setattr(self, 'effect', effect) + + def encode(self): + super().encode() + + self.length = len(self.buffer) + diff --git a/models_converter/formats/scw/chunks/node.py b/models_converter/formats/scw/chunks/node.py new file mode 100644 index 0000000..b76ae33 --- /dev/null +++ b/models_converter/formats/scw/chunks/node.py @@ -0,0 +1,145 @@ +from . import Chunk + + +class NODE(Chunk): + def __init__(self, header: dict): + super().__init__(header) + self.chunk_name = 'NODE' + + def parse(self, buffer: bytes): + super().parse(buffer) + nodes = [] + + nodes_count = self.readUShort() + for node in range(nodes_count): + node_data = { + 'name': self.readString(), + 'parent': self.readString() + } + + instances_count = self.readUShort() + node_data['instances'] = [{}] * instances_count + for x in range(instances_count): + instance_type = self.readChar(4) + instance_name = self.readString() + + node_data['instances'][x] = {} + if instance_type in ['GEOM', 'CONT']: + materials_count = self.readUShort() + binds = [] + for bind in range(materials_count): + binds.append({}) + symbol = self.readString() + target = self.readString() + binds[bind] = {'symbol': symbol, + 'target': target} + node_data['instances'][x]['binds'] = binds + elif instance_type in ['CAME']: + target = self.readString() + node_data['instances'][x]['target'] = target + node_data['instances'][x]['instance_name'] = instance_name + node_data['instances'][x]['instance_type'] = instance_type + + frames_count = self.readUShort() + node_data['frames'] = [] + if frames_count > 0: + rotation = {'x': 0, 'y': 0, 'z': 0, 'w': 0} + scale_x, scale_y, scale_z = 0, 0, 0 + pos_x, pos_y, pos_z = 0, 0, 0 + + settings = list(bin(self.readUByte())[2:].zfill(8)) + settings = [bool(int(value)) for value in settings] + node_data['frames_settings'] = settings + for frame in range(frames_count): + frame_data = { + 'frame_id': self.readUShort() + } + + if settings[7] or frame == 0: # Rotation + rotation = { + 'x': self.readNShort(), + 'y': self.readNShort(), + 'z': self.readNShort(), + 'w': self.readNShort() + } + + if settings[4] or frame == 0: # Position X + pos_x = self.readFloat() + if settings[5] or frame == 0: # Position Y + pos_y = self.readFloat() + if settings[6] or frame == 0: # Position Z + pos_z = self.readFloat() + + if settings[1] or frame == 0: # Scale X + scale_x = self.readFloat() + if settings[2] or frame == 0: # Scale Y + scale_y = self.readFloat() + if settings[3] or frame == 0: # Scale Z + scale_z = self.readFloat() + + frame_data['rotation'] = rotation + frame_data['position'] = { + 'x': pos_x, + 'y': pos_y, + 'z': pos_z + } + frame_data['scale'] = { + 'x': scale_x, + 'y': scale_y, + 'z': scale_z + } + + node_data['frames'].append(frame_data) + nodes.append(node_data) + setattr(self, 'nodes', nodes) + + def encode(self): + super().encode() + + self.writeUShort(len(self.get('nodes'))) + for node in self.get('nodes'): + self.writeString(node['name']) + self.writeString(node['parent']) + + self.writeUShort(len(node['instances'])) + for instance in node['instances']: + self.writeChar(instance['instance_type']) + self.writeString(instance['instance_name']) + self.writeUShort(len(instance['binds'])) + for bind in instance['binds']: + self.writeString(bind['symbol']) + self.writeString(bind['target']) + + if 'frames_settings' in node: + frames_settings = node['frames_settings'] + else: + frames_settings = None + self.encode_frames(node['frames'], frames_settings) + + self.length = len(self.buffer) + + def encode_frames(self, frames, frames_settings): + self.writeUShort(len(frames)) + if len(frames) > 0: + self.writeUByte(int(''.join([('1' if item else '0') for item in frames_settings])[::], 2)) + for frame in frames: + self.writeUShort(frame['frame_id']) + if frames_settings[7] or frames.index(frame) == 0: # Rotation + self.writeNShort(frame['rotation']['x']) + self.writeNShort(frame['rotation']['y']) + self.writeNShort(frame['rotation']['z']) + self.writeNShort(frame['rotation']['w']) + + if frames_settings[4] or frames.index(frame) == 0: # Position X + self.writeFloat(frame['position']['x']) + if frames_settings[5] or frames.index(frame) == 0: # Position Y + self.writeFloat(frame['position']['y']) + if frames_settings[6] or frames.index(frame) == 0: # Position Z + self.writeFloat(frame['position']['z']) + + if frames_settings[1] or frames.index(frame) == 0: # Scale X + self.writeFloat(frame['scale']['x']) + if frames_settings[2] or frames.index(frame) == 0: # Scale Y + self.writeFloat(frame['scale']['y']) + if frames_settings[3] or frames.index(frame) == 0: # Scale Z + self.writeFloat(frame['scale']['z']) diff --git a/models_converter/formats/scw/chunks/wend.py b/models_converter/formats/scw/chunks/wend.py new file mode 100644 index 0000000..0252602 --- /dev/null +++ b/models_converter/formats/scw/chunks/wend.py @@ -0,0 +1,15 @@ +from . import Chunk + + +class WEND(Chunk): + def __init__(self, header=None): + super().__init__(header) + self.chunk_name = 'WEND' + + def parse(self, buffer: bytes): + super().parse(buffer) + + def encode(self): + super().encode() + + self.length = len(self.buffer) diff --git a/models_converter/formats/scw.py b/models_converter/formats/scw/parser.py similarity index 61% rename from models_converter/formats/scw.py rename to models_converter/formats/scw/parser.py index e3c72ca..df8649f 100644 --- a/models_converter/formats/scw.py +++ b/models_converter/formats/scw/parser.py @@ -1,63 +1,5 @@ -import binascii - -from ..chunks import * -from ..chunks.chunk import Chunk -from ..utils.reader import Reader - - -def _(*args): - print('[ScwUtils]', end=' ') - for arg in args: - print(arg, end=' ') - print() - - -class Writer: - def __init__(self): - self.writen = b'SC3D' - - def write(self, data: dict): - - header = data['header'] - head = HEAD() - head.from_dict(header) - - self.write_chunk(head) - - # TODO: materials - # for material in data['materials']: - # mate = MATE(header) - # mate.from_dict(material) - # - # self.write_chunk(mate) - - for geometry in data['geometries']: - geom = GEOM(header) - geom.from_dict(geometry) - - self.write_chunk(geom) - - # TODO: cameras - for camera in data['cameras']: - came = CAME(header) - came.from_dict(camera) - - self.write_chunk(came) - - node = NODE(header) - node.from_dict({'nodes': data['nodes']}) - - self.write_chunk(node) - - wend = WEND() - - self.write_chunk(wend) - - def write_chunk(self, chunk: Chunk): - chunk.encode() - - self.writen += chunk.length.to_bytes(4, 'big') + chunk.chunk_name.encode('utf-8') + chunk.buffer - self.writen += binascii.crc32(chunk.chunk_name.encode('utf-8') + chunk.buffer).to_bytes(4, 'big') +from ...utils.reader import Reader +from .chunks import * class Parser(Reader): diff --git a/models_converter/formats/scw/writer.py b/models_converter/formats/scw/writer.py new file mode 100644 index 0000000..fede3f4 --- /dev/null +++ b/models_converter/formats/scw/writer.py @@ -0,0 +1,51 @@ +import binascii + +from .chunks import * + + +class Writer: + def __init__(self): + self.writen = b'SC3D' + + def write(self, data: dict): + + header = data['header'] + head = HEAD() + head.from_dict(header) + + self.write_chunk(head) + + # TODO: materials + # for material in data['materials']: + # mate = MATE(header) + # mate.from_dict(material) + # + # self.write_chunk(mate) + + for geometry in data['geometries']: + geom = GEOM(header) + geom.from_dict(geometry) + + self.write_chunk(geom) + + # TODO: cameras + for camera in data['cameras']: + came = CAME(header) + came.from_dict(camera) + + self.write_chunk(came) + + node = NODE(header) + node.from_dict({'nodes': data['nodes']}) + + self.write_chunk(node) + + wend = WEND() + + self.write_chunk(wend) + + def write_chunk(self, chunk: Chunk): + chunk.encode() + + self.writen += chunk.length.to_bytes(4, 'big') + chunk.chunk_name.encode('utf-8') + chunk.buffer + self.writen += binascii.crc32(chunk.chunk_name.encode('utf-8') + chunk.buffer).to_bytes(4, 'big') diff --git a/setup.py b/setup.py index 50abc9c..729bb4d 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='3d-converter', - version='0.7.5', + version='0.7.6', author='Vorono4ka', author_email='crowo4ka@gmail.com', description='Python 3D Models Converter',