From a498525984552c0de5dd9a58deaa1ccff9c364c4 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: Sun, 11 Oct 2020 11:22:53 +0400 Subject: [PATCH] 0.7.0 Small code refactoring --- .idea/vcs.xml | 1 - 3d_converter.egg-info/PKG-INFO | 2 +- 3d_converter.egg-info/SOURCES.txt | 6 +- README.md | 2 +- models_converter/formats/__init__.py | 10 +- .../__pycache__/dae_write.cpython-39.pyc | Bin 9191 -> 0 bytes .../formats/{dae_write.py => dae.py} | 376 +++++++++++++++++ models_converter/formats/dae_read.py | 386 ------------------ .../formats/{obj_read.py => obj.py} | 61 ++- models_converter/formats/obj_write.py | 62 --- models_converter/formats/scw.py | 4 +- models_converter/utils/reader.py | 46 +-- models_converter/utils/reader.pyi | 56 +++ models_converter/utils/writer.py | 38 +- models_converter/utils/writer.pyi | 53 +++ setup.py | 2 +- 16 files changed, 598 insertions(+), 507 deletions(-) delete mode 100644 models_converter/formats/__pycache__/dae_write.cpython-39.pyc rename models_converter/formats/{dae_write.py => dae.py} (51%) delete mode 100644 models_converter/formats/dae_read.py rename models_converter/formats/{obj_read.py => obj.py} (71%) delete mode 100644 models_converter/formats/obj_write.py create mode 100644 models_converter/utils/reader.pyi create mode 100644 models_converter/utils/writer.pyi diff --git a/.idea/vcs.xml b/.idea/vcs.xml index e5d2772..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/3d_converter.egg-info/PKG-INFO b/3d_converter.egg-info/PKG-INFO index ba24113..3f68798 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.6.9 +Version: 0.7.0 Summary: Python 3D Models Converter Home-page: https://github.com/vorono4ka/3d-converter Author: Vorono4ka diff --git a/3d_converter.egg-info/SOURCES.txt b/3d_converter.egg-info/SOURCES.txt index b0fb7c5..952246a 100644 --- a/3d_converter.egg-info/SOURCES.txt +++ b/3d_converter.egg-info/SOURCES.txt @@ -8,11 +8,9 @@ models_converter/__init__.py models_converter/chunks/__init__.py models_converter/chunks/chunk.py models_converter/formats/__init__.py -models_converter/formats/dae_read.py -models_converter/formats/dae_write.py +models_converter/formats/dae.py models_converter/formats/gltf.py -models_converter/formats/obj_read.py -models_converter/formats/obj_write.py +models_converter/formats/obj.py models_converter/formats/scw.py models_converter/utils/__init__.py models_converter/utils/reader.py diff --git a/README.md b/README.md index fcb08ac..163830a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## `Python 3D Models Converter` -**Version**: 0.6.8 +**Version**: 0.7.0 ### Thanks a lot for motivating [AMIRMISTIK]! diff --git a/models_converter/formats/__init__.py b/models_converter/formats/__init__.py index 3381a39..3e518f9 100644 --- a/models_converter/formats/__init__.py +++ b/models_converter/formats/__init__.py @@ -1,8 +1,6 @@ __all__ = [ - 'dae_write', - 'dae_read', - 'obj_write', - 'obj_read', - 'gltf', - 'scw' + 'scw', + 'dae', + 'obj', + 'gltf' ] diff --git a/models_converter/formats/__pycache__/dae_write.cpython-39.pyc b/models_converter/formats/__pycache__/dae_write.cpython-39.pyc deleted file mode 100644 index c3b6355f3317390d9dfff203db616b3729e30603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9191 zcmcIqNq8GacJ2+01_&;qKvL9VQ;*(@dGQFF>6$DH!{FvpzxlrQ@=uhh|LMqF#_J9uVG2|8%8bG^rsvgyW^1a# z3>NuJvGr>TGg1lWh4oZ*et0$gn0Tdh0cs#d$X8lZ- z%4$Cd^&*Q5DHhA+Clj`WGbNdeD39=>>Y)^)ghcZd~o#k-Rm*`*9GO!CsQ zll9!Yr&({dR9tho=kU9Q5_9rydKa-!;)M*TSjI`;=Q+>mTiNi{v`HPXkSJLP=vlk*Xe4ER(^>*N>YAO%SS z>iJeWoh#>uoGG+Ttr`{TR|?Y~Yo03fO;s34 z1$pCGFJSbo9Ja^v#S1rOo@saw%8z4bNHk zdJHB%#F%KgtPA$by zyopbs;^Tn)NUpe2hA-k>gzrYuWAZl$mV{D&L}QyHR*9f15rO3c?Lk=+Nn>Wc5^H=jU~(fy2AxuVPhA=siXb6)Vu|gQjZZ#Id7T|aYOVB{ntq z_C^UeNw^sX-qNY8Dg1BQ9?uAJthrO!R67(&*GhE2@9o4A{7{|yy4oljS+Y~9Hlf^L zZK8>_KhUv6H^Gf{Jl3ks_4sNFaxH8h>lDp$8to@q;3DjRXr`LgJ;?202hq+}K&|W$ zdyTyg{9ed8(5bL4$V!IrVZd!6+zq%rgpUC3K<&NkDERF2A|kn|dS)3d=6?hHKGp?% zr%19lA_{mOJA{E5RM?yB_-Bepj-bEQB7*}uc5J>;YD5y__;d=7~Am-w8NeA*?ScFCt*@;R`>rz7CQzm{B% zfXhMA@e-F_$)!Vb>5yDHB$tB$m({N^o_%sW^|@!&L*kGe&)2{)%KF7?7{>uPJQ|zV zfqR{uf|M>$y-juIo`0?^D%Hb3RoH20y&GS$h9mf%!S|?ViKFZ+Sy1&2(ap}?(;n(v zW9O^glJf;|1a_cTkD=_HH3enY#o?_OyU5;UgO9Xr6_!N#_g>0h+RYbFpziy!?wX{% zyjv#oLv?v=D=xakF?QuA%7V&t^TCMvea(eeGgzi(}~j$m5{@ zZwhUzLA=S%i(}ic+lSifub~Gy7v}z<+5*cSMW2u3TbmJG>?$;Hd=uLO%#3RfjA{=` zKaiUHqr*eX?CW zqF3~Zx3&$m%vdGg(!)N)SoLFk2E=iU)ye89aSGqA>f7RNxwSYd2DW0D=|?wJ(lzRR zhF9SAq@@gCi_xFfAL^@rkDh+ii9Yi$!S6KQGmv1h>#}v@p@z-^cb26vzXQ9`*#zw7 z9Get}FhUW2kbNwz@erFN9l$EiOF8GIMV@0*(!U&H(^5khs~5zXt(dq#+M)Kb8Cm-R zn+gvN`t2D`kW13QLFm_#OBUywn$P$^^C+aDpz*v1yFJdvbi87%63vM9C)-Cma5E)#Edmc~BJXT%`0DK9nj zzSQq!wjljHdA3U-W{EJcSJt2Q8t1tkrI?4cSYLuD0q)B9^|AaxeC~YHAVTq0`kE5 z8op(GKM>d08tgmDe~emJ*?rj32jD;cSZ7!*s$;;8v2|$mLqH$04L~2EZiU(k3%@R| zvmb!ZIKIR$1*(sj5aZyJl50-&0nL!;>W$(uWc@B=S)5kZtL%rNtbzY`S$|hdhFXIx zjMdfUMs|9THk4Rz+3%s96PQtxct1v8K1F^?Ovx2?pgJw4*^hvk@tW8s`|N>QofVUE z*3O8D1F$>kC8yc%gVT>Y6;KoOlO5=%oyvkp9z7w;Lc!e3!rW9)$#l~#8bb0aS9ic#3pAHri@5tEp+e^ftb>nI*dijTk5 z2LwER4vO3Cj~CTIW>NvSU)1{&NWaUzl=6NVddN9BKj+w=)LOCQ5$4#RN*v_Y;eH`; zkVH7i@6TYR)Pr9>*5&ul!TB$wO`EIKU)n`}g<0_l;7`~i_82qzFR9$?kY#_xwjO9( z`?hg}O?S!z%67_+WLzA7{5|>HJkRirG(s5;&t>wi-&{jE3kwtm@tbOhTp_cF14(Nw zzvz^3F5o%f_SSHSk9ZtG0-XGJ2{T1h^$-cDze) z+vx` zQ@|xxTughVQa*@Fm`hHEIlR(1J$$q4Mi9EV*kxOEaK`c0wPU+VGU$xtTQvml#6G_g z?G03BH|L5tHWd+m2oS+>5Fx2D&--Rb)U?7&3pt#-h9}0yCsKZF=cw+R!CAdWwRIN( zjA*7XpOX<1mRndTyAbRYaxR5daI9T%vgLe+Bha-}DlR^y6UWovAj#Ipfaq*ON8M?> zE*;SqVGVPMAf&db{#;u%rh}8u6O|93{E6mkeFMIM(^TQ56OkVuq!S?8xN>O~r2mFj zsdRTibXGsWOiQS=S=tVb3^6B)F1^Bt zf-jO01&Ac0WMG{*S>Vi;1dG5*DW>bfxEwiN=)oi^*p^$KPm{4eQSF96A3QGHWba6lv#7q^SL4;Z;TkZAK&$+umH*+GI}ppM0;dNt)Y(i6@~tG(_Jd@ zNuWeOvX;r0oo}04|5($Ty+_J)moh6(t(hi813mZu(gMD;qtzEx`!6-Yz8NS5UN#U{ zX$yf~YEiE&#hj>Ez4x8m;u8FIJVhZcX}3@1+=zTQF_xNnN>Ls=HZwJpn!bt&VO!F` z=|+O6YQO)fTl%gZM8H&%18 z!g&!k<9m_Tyd%wAlObr>Dw({muK9`jCEjj1&4z2YE7P>-S6Z=1@1VLQ4ikpL+0skd zks|J3+#q0j2G~lxufPAqoy$4)UUx<7p7RYNu;WV^7twyci2Y4N@J$DPGRWA*@I-3H z-@CI&@6Iy6u{NXRW=ICB?Fiuzhjlj!^QFAKXQy6m^+zM`QzJbjxCR@Y7_TF$xo|(e zfNKUP&9Tw)O$occ72tBLM|(>1mHz+<9Gv0iDvk{g;^t`Ei-Yk$x$X#Q2?Y14o)!RX3&ykl60hNbI8AC{Bupl+lg%NYI*yYHk_UxD8V%|kW z&6FIagtjemCF7?Ek{Q?_u#|n!ok;Z zo5;^o_5vmEP;!xycPXL!SpFU*6p-fcQ*s%J-B>e4x#rbnX`5;{1UpU{@#nPk@DZvM z4L6{ClyZr1lNoSGggecUUv0Y?W@~#+ndKi5$7_^)K*<;-A0n|EYqlR&+sRU^_zcm_ zQbKn`JBv2W)SZkZyhSv0nPfMFmwFO?gD{P?MK)*(BhnL8kdUUI4sLnuc#xCY2w{>F z+*oCaz9YEK^&FOdfhDvQEaP@$g;qJcIaqyy@x^*$Cqe`!H{kva zr;5NV=oTt0l-lJ~!Ekq@pVGR$?*hNR?-fs_kqXZv_ZEc z(&h0AaiPh9*%o@a^!i4{PV7u4%Ef}YL?D?s@se8<*VcLIzKwHIzFa8sPl?%&DA}Y$ z?jYs~qO*Y?kw+48Chk63)Ny5;&=a_wuX~I*<+Ox)&}hOnViWGqn$#qH)h1x&qY*By zw%|^`6;!W6i4d1GiAPe6LyDo{;)|{;aUW-BbamN+r&Ut6XhwJ))XVeV%?$=t-b_2YVYCC9J)mF3@Ed(V=Bl?;Yx@?O>%6H|H zVL57|ZaerTskK_G+HRWE!sjMw2hvCmNwu_A$e`B7)pn8vO$BYId&mSXXuk!E_DapD zhAuB{(ZW`(Tg5{?qnwmrN%`$^6q;lopbz<=4;J;|+wuwEsgKF9*VZ?N8T9u>$b=;8 zc^tY9duCP=^(TT9Zf5CPmG@H8M+sd7;;Ju~_4v;yFU@Q>AzS08k&uISX<30Lbcq2o zyA6w2AEG6W(-&T;&j7aD%k-C(zQDM{4|bw*S(5)836?tI`)vY!ivl6Iy}hK8REwl) K3Ehe|4g4o-A1s&v diff --git a/models_converter/formats/dae_write.py b/models_converter/formats/dae.py similarity index 51% rename from models_converter/formats/dae_write.py rename to models_converter/formats/dae.py index d01857b..77a48d7 100644 --- a/models_converter/formats/dae_write.py +++ b/models_converter/formats/dae.py @@ -417,3 +417,379 @@ def write(self, data: dict): # self.writen = tostring(dae.collada, xml_declaration=True).decode() + + +class Parser: + def node(self, nodes): + nodes_list = [] + for node in nodes: + instance_geometry = node.findall('collada:instance_geometry', self.namespaces) + instance_controller = node.findall('collada:instance_controller', self.namespaces) + + children = self.node(node.findall('collada:node', self.namespaces)) + node_data = { + 'name': node.attrib['name'], + 'has_target': True if instance_geometry or instance_controller else False + } + + if node_data['has_target']: + instance = None + binds = [] + + if instance_geometry: + instance_geometry = instance_geometry[0] + instance = instance_geometry + elif instance_controller: + instance_controller = instance_controller[0] + instance = instance_controller + + bind_material = instance.find('collada:bind_material', self.namespaces) + technique_common = bind_material[0] + + for instance_material in technique_common: + binds.append({ + 'symbol': instance_material.attrib['symbol'], + 'target': instance_material.attrib['target'][1:] + }) + + if instance_geometry: + node_data['target_type'] = 'GEOM' + + geometry_url = instance_geometry.attrib['url'] + node_data['target'] = geometry_url[1:] + elif instance_controller: + node_data['target_type'] = 'CONT' + + controller_url = instance_controller.attrib['url'] + node_data['target'] = controller_url[1:] + + node_data['binds'] = binds + + matrix = node.findall('collada:matrix', self.namespaces) + if matrix: + matrix_data = matrix[0].text.split() + matrix_data = [[float(value) for value in matrix_data[x:x + 4]] for x in range(0, len(matrix_data), 4)] + + node_data['matrix'] = matrix_data + + node_data['children'] = children + + nodes_list.append(node_data) + + return nodes_list + + def fix_nodes_list(self, nodes, parent: str = ''): + for node in nodes: + node_data = { + 'name': node['name'], + 'parent': parent, + 'has_target': node['has_target'] + } + + if node_data['has_target']: + node_data['target_type'] = node['target_type'] + node_data['target'] = node['target'] + node_data['binds'] = node['binds'] + + if not node_data['has_target']: + node_data['frames_settings'] = [0, 0, 0, 0, 0, 0, 0, 0] + node_data['frames'] = [] + + if 'matrix' in node: + matrix = Matrix4x4(matrix=node['matrix']) + + # scale = matrix.get_scale() + position = matrix.get_position() + + frame_data = { + 'frame_id': 0, + 'rotation': {'x': 0, 'y': 0, 'z': 0, 'w': 0}, + 'position': position, + 'scale': {'x': 1, 'y': 1, 'z': 1} + } + + node_data['frames'].append(frame_data) + else: + node_data['frames'] = [] + + # node_data['frames'] = node['frames'] + self.parsed['nodes'].append(node_data) + self.fix_nodes_list(node['children'], node['name']) + + def __init__(self, file_data): + self.parsed = {'header': {'version': 2, + 'frame_rate': 30, + 'materials_file': 'sc3d/character_materials.scw'}, + 'materials': [], + 'geometries': [], + 'cameras': [], + 'nodes': []} + + self.geometry_info = {} + + root = fromstring(file_data) + + self.namespaces = { + 'collada': 'http://www.collada.org/2005/11/COLLADASchema' + } + + # + self.library_materials = root.find('./collada:library_materials', self.namespaces) + self.library_effects = root.find('./collada:library_effects', self.namespaces) + + self.library_geometries = root.find('./collada:library_geometries', self.namespaces) + self.library_controllers = root.find('./collada:library_controllers', self.namespaces) + + self.instance_scene = root.find('./collada:scene', self.namespaces).find('collada:instance_visual_scene', + self.namespaces) + self.library_scenes = root.find('./collada:library_visual_scenes', self.namespaces) + # + + if self.library_materials is None: + self.library_materials = [] + + def parse(self): + for material in self.library_materials: + material_name = material.attrib['name'] + + instance_effect = material.find('collada:instance_effect', self.namespaces) + if instance_effect is not None: + effect_url = instance_effect.attrib['url'][1:] + effect = self.library_effects.find(f'collada:effect[@id="{effect_url}"]', self.namespaces) + + if effect is not None: + profile = None + for item in effect: + if 'profile' in item.tag: + profile = item + technique = profile.find('collada:technique', self.namespaces) + + emission_data = None + ambient_data = None + diffuse_data = None + + emission = technique[0].find('collada:emission', self.namespaces) + ambient = technique[0].find('collada:ambient', self.namespaces) + diffuse = technique[0].find('collada:diffuse', self.namespaces) + + if 'color' in emission[0].tag: + emission_data = [float(item) for item in emission[0].text.split()] + emission_data[3] *= 255 + elif 'texture' in emission[0].tag: + # emission_data = emission[0].attrib['texture'] + emission_data = '.' + + if 'color' in ambient[0].tag: + ambient_data = [float(item) for item in ambient[0].text.split()] + ambient_data[3] *= 255 + elif 'texture' in ambient[0].tag: + # ambient_data = ambient[0].attrib['texture'] + ambient_data = '.' + + if 'color' in diffuse[0].tag: + diffuse_data = [float(item) for item in diffuse[0].text.split()] + diffuse_data[3] *= 255 + elif 'texture' in diffuse[0].tag: + # diffuse_data = diffuse[0].attrib['texture'] + diffuse_data = '.' + + material_data = { + 'name': material_name, + 'effect': { + 'ambient': ambient_data, + 'diffuse': diffuse_data, + 'specular': '.', + 'colorize': [255, 255, 255, 255], + 'emission': emission_data, + 'lightmaps': { + 'diffuse': 'sc3d/diffuse_lightmap.png', + 'specular': 'sc3d/specular_lightmap.png' + }, + 'tint': [0, 0, 0, 0] + } + } + + self.parsed['materials'].append(material_data) + + scene_url = self.instance_scene.attrib['url'][1:] + scene = self.library_scenes.find(f'collada:visual_scene[@id="{scene_url}"]', self.namespaces) + + nodes = self.node(scene.findall('collada:node', self.namespaces)) + self.fix_nodes_list(nodes) + self.parse_nodes() + + def parse_nodes(self): + nodes = self.parsed['nodes'] + for node_index in range(len(nodes)): + node = nodes[node_index] + if node['has_target']: + controller = None + geometry = None + + if node['target_type'] == 'CONT': + controller = self.library_controllers \ + .find(f'collada:controller[@id="{node["target"]}"]', self.namespaces) + + geometry_url = controller[0].attrib['source'][1:] + geometry = self.library_geometries \ + .find(f'collada:geometry[@id="{geometry_url}"]', self.namespaces) + elif node['target_type'] == 'GEOM': + geometry = self.library_geometries \ + .find(f'collada:geometry[@id="{node["target"]}"]', self.namespaces) + + node['target'] = geometry.attrib['name'] + + if node['target'][-5:] in ['-skin', '-cont']: + node['target'] = node['target'][:-5] + if node['target'][-5:] in ['-mesh', '-geom']: + node['target'] = node['target'][:-5] + + self.parsed['nodes'][node_index] = node + + if geometry is not None: + self.geometry_info = {'name': '', + 'group': node['parent'], + 'vertices': [], + 'have_bind_matrix': False, + 'materials': []} + if controller is not None: + self.parse_controller(controller) + + self.parse_geometry(geometry) + + def parse_controller(self, controller): + self.geometry_info['have_bind_matrix'] = True + + skin = controller[0] + + bind_shape_matrix = skin.find('collada:bind_shape_matrix', self.namespaces).text + bind_shape_matrix = [float(x) for x in bind_shape_matrix.split()] + + self.geometry_info['bind_matrix'] = bind_shape_matrix + + self.geometry_info['joints'] = [] + joints = skin.find('collada:joints', self.namespaces) + joint_inputs = joints.findall('collada:input', self.namespaces) + for _input in joint_inputs: + # semantic = _input.attrib['semantic'] + source_url = _input.attrib['source'] + source = skin.find(f'collada:source[@id="{source_url[1:]}"]', self.namespaces) + + accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) + accessor_stride = int(accessor.attrib['stride']) + accessor_source_url = accessor.attrib['source'] + accessor_source = source.find(f'collada:*[@id="{accessor_source_url[1:]}"]', self.namespaces) + params = accessor.findall('collada:param', self.namespaces) + + for param in params: + param_name = param.attrib['name'] + # param_type = param.attrib['type'] + + if param_name == 'JOINT': + for name in accessor_source.text.split(): + self.geometry_info['joints'].append({ + 'name': name + }) + + if param_name == 'TRANSFORM': + for x in range(int(accessor_source.attrib['count']) // int(accessor_stride)): + matrix = [] + for y in accessor_source.text.split()[x * accessor_stride:(x + 1) * accessor_stride]: + matrix.append(float(y)) + self.geometry_info['joints'][x]['matrix'] = matrix + + self.geometry_info['weights'] = {} + vertex_weights = skin.find('collada:vertex_weights', self.namespaces) + vertex_weights_inputs = vertex_weights.findall('collada:input', self.namespaces) + for _input in vertex_weights_inputs: + semantic = _input.attrib['semantic'] + source_url = _input.attrib['source'] + source = skin.find(f'collada:source[@id="{source_url[1:]}"]', self.namespaces) + + if semantic == 'WEIGHT': + accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) + accessor_source_url = accessor.attrib['source'] + accessor_source = source.find(f'collada:*[@id="{accessor_source_url[1:]}"]', self.namespaces) + + params = accessor.findall('collada:param', self.namespaces) + for param in params: + param_name = param.attrib['name'] + # param_type = param.attrib['type'] + + if param_name == 'WEIGHT': + weights = [float(x) for x in accessor_source.text.split()] + self.geometry_info['weights']['weights'] = weights + + vcount = vertex_weights.find('collada:vcount', self.namespaces).text + vcount = [int(x) for x in vcount.split()] + self.geometry_info['weights']['vcount'] = vcount + + v = vertex_weights.find('collada:v', self.namespaces).text + v = [int(x) for x in v.split()] + self.geometry_info['weights']['vertex_weights'] = v + + def parse_geometry(self, geometry): + name = geometry.attrib['name'] + + if name[-5:] in ['-mesh', '-geom']: + name = name[:-5] + + self.geometry_info['name'] = name + + mesh = geometry[0] + + triangles = mesh.findall('collada:triangles', self.namespaces) + if triangles: + pass + else: + triangles = mesh.findall('collada:polylist', self.namespaces) + inputs = triangles[0].findall('collada:input', self.namespaces) + for _input in inputs: + semantic = _input.attrib['semantic'] + source_link = _input.attrib['source'][1:] + source = mesh.find(f'*[@id="{source_link}"]') + + if semantic == 'VERTEX': + vertices_input = source[0] + semantic = vertices_input.attrib['semantic'] + source_link = vertices_input.attrib['source'][1:] + source = mesh.find(f'*[@id="{source_link}"]') + + float_array = source.find('collada:float_array', self.namespaces) + accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) + + vertex_temp = [float(floating) for floating in float_array.text.split()] + + scale = max(max(vertex_temp), abs(min(vertex_temp))) + if scale < 1: + scale = 1 + if semantic == 'TEXCOORD': + vertex_temp[1::2] = [1 - x for x in vertex_temp[1::2]] + vertex_temp = [value / scale for value in vertex_temp] + + vertex = [] + for x in range(0, len(vertex_temp), len(accessor)): + vertex.append(vertex_temp[x: x + len(accessor)]) + + self.geometry_info['vertices'].append({'type': semantic, + 'index': 0, + 'scale': scale, + 'vertex': vertex}) + for triangle in triangles: + triangles_material = triangle.attrib['material'] + + p = triangle.find('collada:p', self.namespaces) + polygons_temp = [int(integer) for integer in p.text.split()] + + polygons = [] + for x in range(0, len(polygons_temp), len(inputs) * 3): + temp_list = [] + for x1 in range(len(inputs)): + second_temp_list = [] + for x2 in range(3): + second_temp_list.append(polygons_temp[x + x1 + x2]) + temp_list.append(second_temp_list) + polygons.append(temp_list) + self.geometry_info['materials'].append({'name': triangles_material, + 'polygons': polygons}) + self.parsed['geometries'].append(self.geometry_info) diff --git a/models_converter/formats/dae_read.py b/models_converter/formats/dae_read.py deleted file mode 100644 index b41c25c..0000000 --- a/models_converter/formats/dae_read.py +++ /dev/null @@ -1,386 +0,0 @@ -from xml.etree.ElementTree import * - -from ..utils.matrix.matrix4x4 import Matrix4x4 - - -def _(*args): - print('[ScwUtils]', end=' ') - for arg in args: - print(arg, end=' ') - print() - - -class Parser: - def node(self, nodes): - nodes_list = [] - for node in nodes: - instance_geometry = node.findall('collada:instance_geometry', self.namespaces) - instance_controller = node.findall('collada:instance_controller', self.namespaces) - - children = self.node(node.findall('collada:node', self.namespaces)) - node_data = { - 'name': node.attrib['name'], - 'has_target': True if instance_geometry or instance_controller else False - } - - if node_data['has_target']: - instance = None - binds = [] - - if instance_geometry: - instance_geometry = instance_geometry[0] - instance = instance_geometry - elif instance_controller: - instance_controller = instance_controller[0] - instance = instance_controller - - bind_material = instance.find('collada:bind_material', self.namespaces) - technique_common = bind_material[0] - - for instance_material in technique_common: - binds.append({ - 'symbol': instance_material.attrib['symbol'], - 'target': instance_material.attrib['target'][1:] - }) - - if instance_geometry: - node_data['target_type'] = 'GEOM' - - geometry_url = instance_geometry.attrib['url'] - node_data['target'] = geometry_url[1:] - elif instance_controller: - node_data['target_type'] = 'CONT' - - controller_url = instance_controller.attrib['url'] - node_data['target'] = controller_url[1:] - - node_data['binds'] = binds - - matrix = node.findall('collada:matrix', self.namespaces) - if matrix: - matrix_data = matrix[0].text.split() - matrix_data = [[float(value) for value in matrix_data[x:x + 4]] for x in range(0, len(matrix_data), 4)] - - node_data['matrix'] = matrix_data - - node_data['children'] = children - - nodes_list.append(node_data) - - return nodes_list - - def fix_nodes_list(self, nodes, parent: str = ''): - for node in nodes: - node_data = { - 'name': node['name'], - 'parent': parent, - 'has_target': node['has_target'] - } - - if node_data['has_target']: - node_data['target_type'] = node['target_type'] - node_data['target'] = node['target'] - node_data['binds'] = node['binds'] - - if not node_data['has_target']: - node_data['frames_settings'] = [0, 0, 0, 0, 0, 0, 0, 0] - node_data['frames'] = [] - - if 'matrix' in node: - matrix = Matrix4x4(matrix=node['matrix']) - - # scale = matrix.get_scale() - position = matrix.get_position() - - frame_data = { - 'frame_id': 0, - 'rotation': {'x': 0, 'y': 0, 'z': 0, 'w': 0}, - 'position': position, - 'scale': {'x': 1, 'y': 1, 'z': 1} - } - - node_data['frames'].append(frame_data) - else: - node_data['frames'] = [] - - # node_data['frames'] = node['frames'] - self.parsed['nodes'].append(node_data) - self.fix_nodes_list(node['children'], node['name']) - - def __init__(self, file_data): - self.parsed = {'header': {'version': 2, - 'frame_rate': 30, - 'materials_file': 'sc3d/character_materials.scw'}, - 'materials': [], - 'geometries': [], - 'cameras': [], - 'nodes': []} - - self.geometry_info = {} - - root = fromstring(file_data) - - self.namespaces = { - 'collada': 'http://www.collada.org/2005/11/COLLADASchema' - } - - # - self.library_materials = root.find('./collada:library_materials', self.namespaces) - self.library_effects = root.find('./collada:library_effects', self.namespaces) - - self.library_geometries = root.find('./collada:library_geometries', self.namespaces) - self.library_controllers = root.find('./collada:library_controllers', self.namespaces) - - self.instance_scene = root.find('./collada:scene', self.namespaces).find('collada:instance_visual_scene', - self.namespaces) - self.library_scenes = root.find('./collada:library_visual_scenes', self.namespaces) - # - - if self.library_materials is None: - self.library_materials = [] - - def parse(self): - for material in self.library_materials: - material_name = material.attrib['name'] - - instance_effect = material.find('collada:instance_effect', self.namespaces) - if instance_effect is not None: - effect_url = instance_effect.attrib['url'][1:] - effect = self.library_effects.find(f'collada:effect[@id="{effect_url}"]', self.namespaces) - - if effect is not None: - profile = None - for item in effect: - if 'profile' in item.tag: - profile = item - technique = profile.find('collada:technique', self.namespaces) - - emission_data = None - ambient_data = None - diffuse_data = None - - emission = technique[0].find('collada:emission', self.namespaces) - ambient = technique[0].find('collada:ambient', self.namespaces) - diffuse = technique[0].find('collada:diffuse', self.namespaces) - - if 'color' in emission[0].tag: - emission_data = [float(item) for item in emission[0].text.split()] - emission_data[3] *= 255 - elif 'texture' in emission[0].tag: - # emission_data = emission[0].attrib['texture'] - emission_data = '.' - - if 'color' in ambient[0].tag: - ambient_data = [float(item) for item in ambient[0].text.split()] - ambient_data[3] *= 255 - elif 'texture' in ambient[0].tag: - # ambient_data = ambient[0].attrib['texture'] - ambient_data = '.' - - if 'color' in diffuse[0].tag: - diffuse_data = [float(item) for item in diffuse[0].text.split()] - diffuse_data[3] *= 255 - elif 'texture' in diffuse[0].tag: - # diffuse_data = diffuse[0].attrib['texture'] - diffuse_data = '.' - - material_data = { - 'name': material_name, - 'effect': { - 'ambient': ambient_data, - 'diffuse': diffuse_data, - 'specular': '.', - 'colorize': [255, 255, 255, 255], - 'emission': emission_data, - 'lightmaps': { - 'diffuse': 'sc3d/diffuse_lightmap.png', - 'specular': 'sc3d/specular_lightmap.png' - }, - 'tint': [0, 0, 0, 0] - } - } - - self.parsed['materials'].append(material_data) - - scene_url = self.instance_scene.attrib['url'][1:] - scene = self.library_scenes.find(f'collada:visual_scene[@id="{scene_url}"]', self.namespaces) - - nodes = self.node(scene.findall('collada:node', self.namespaces)) - self.fix_nodes_list(nodes) - self.parse_nodes() - - def parse_nodes(self): - nodes = self.parsed['nodes'] - for node_index in range(len(nodes)): - node = nodes[node_index] - if node['has_target']: - controller = None - geometry = None - - if node['target_type'] == 'CONT': - controller = self.library_controllers \ - .find(f'collada:controller[@id="{node["target"]}"]', self.namespaces) - - geometry_url = controller[0].attrib['source'][1:] - geometry = self.library_geometries \ - .find(f'collada:geometry[@id="{geometry_url}"]', self.namespaces) - elif node['target_type'] == 'GEOM': - geometry = self.library_geometries \ - .find(f'collada:geometry[@id="{node["target"]}"]', self.namespaces) - - node['target'] = geometry.attrib['name'] - - if node['target'][-5:] in ['-skin', '-cont']: - node['target'] = node['target'][:-5] - if node['target'][-5:] in ['-mesh', '-geom']: - node['target'] = node['target'][:-5] - - self.parsed['nodes'][node_index] = node - - if geometry is not None: - self.geometry_info = {'name': '', - 'group': node['parent'], - 'vertices': [], - 'have_bind_matrix': False, - 'materials': []} - if controller is not None: - self.parse_controller(controller) - - self.parse_geometry(geometry) - - def parse_controller(self, controller): - self.geometry_info['have_bind_matrix'] = True - - skin = controller[0] - - bind_shape_matrix = skin.find('collada:bind_shape_matrix', self.namespaces).text - bind_shape_matrix = [float(x) for x in bind_shape_matrix.split()] - - self.geometry_info['bind_matrix'] = bind_shape_matrix - - self.geometry_info['joints'] = [] - joints = skin.find('collada:joints', self.namespaces) - joint_inputs = joints.findall('collada:input', self.namespaces) - for _input in joint_inputs: - # semantic = _input.attrib['semantic'] - source_url = _input.attrib['source'] - source = skin.find(f'collada:source[@id="{source_url[1:]}"]', self.namespaces) - - accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) - accessor_stride = int(accessor.attrib['stride']) - accessor_source_url = accessor.attrib['source'] - accessor_source = source.find(f'collada:*[@id="{accessor_source_url[1:]}"]', self.namespaces) - params = accessor.findall('collada:param', self.namespaces) - - for param in params: - param_name = param.attrib['name'] - # param_type = param.attrib['type'] - - if param_name == 'JOINT': - for name in accessor_source.text.split(): - self.geometry_info['joints'].append({ - 'name': name - }) - - if param_name == 'TRANSFORM': - for x in range(int(accessor_source.attrib['count']) // int(accessor_stride)): - matrix = [] - for y in accessor_source.text.split()[x * accessor_stride:(x + 1) * accessor_stride]: - matrix.append(float(y)) - self.geometry_info['joints'][x]['matrix'] = matrix - - self.geometry_info['weights'] = {} - vertex_weights = skin.find('collada:vertex_weights', self.namespaces) - vertex_weights_inputs = vertex_weights.findall('collada:input', self.namespaces) - for _input in vertex_weights_inputs: - semantic = _input.attrib['semantic'] - source_url = _input.attrib['source'] - source = skin.find(f'collada:source[@id="{source_url[1:]}"]', self.namespaces) - - if semantic == 'WEIGHT': - accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) - accessor_source_url = accessor.attrib['source'] - accessor_source = source.find(f'collada:*[@id="{accessor_source_url[1:]}"]', self.namespaces) - - params = accessor.findall('collada:param', self.namespaces) - for param in params: - param_name = param.attrib['name'] - # param_type = param.attrib['type'] - - if param_name == 'WEIGHT': - weights = [float(x) for x in accessor_source.text.split()] - self.geometry_info['weights']['weights'] = weights - - vcount = vertex_weights.find('collada:vcount', self.namespaces).text - vcount = [int(x) for x in vcount.split()] - self.geometry_info['weights']['vcount'] = vcount - - v = vertex_weights.find('collada:v', self.namespaces).text - v = [int(x) for x in v.split()] - self.geometry_info['weights']['vertex_weights'] = v - - def parse_geometry(self, geometry): - name = geometry.attrib['name'] - - if name[-5:] in ['-mesh', '-geom']: - name = name[:-5] - - self.geometry_info['name'] = name - - mesh = geometry[0] - - triangles = mesh.findall('collada:triangles', self.namespaces) - if triangles: - pass - else: - triangles = mesh.findall('collada:polylist', self.namespaces) - inputs = triangles[0].findall('collada:input', self.namespaces) - for _input in inputs: - semantic = _input.attrib['semantic'] - source_link = _input.attrib['source'][1:] - source = mesh.find(f'*[@id="{source_link}"]') - - if semantic == 'VERTEX': - vertices_input = source[0] - semantic = vertices_input.attrib['semantic'] - source_link = vertices_input.attrib['source'][1:] - source = mesh.find(f'*[@id="{source_link}"]') - - float_array = source.find('collada:float_array', self.namespaces) - accessor = source.find('collada:technique_common/collada:accessor', self.namespaces) - - vertex_temp = [float(floating) for floating in float_array.text.split()] - - scale = max(max(vertex_temp), abs(min(vertex_temp))) - if scale < 1: - scale = 1 - if semantic == 'TEXCOORD': - vertex_temp[1::2] = [1 - x for x in vertex_temp[1::2]] - vertex_temp = [value / scale for value in vertex_temp] - - vertex = [] - for x in range(0, len(vertex_temp), len(accessor)): - vertex.append(vertex_temp[x: x + len(accessor)]) - - self.geometry_info['vertices'].append({'type': semantic, - 'index': 0, - 'scale': scale, - 'vertex': vertex}) - for triangle in triangles: - triangles_material = triangle.attrib['material'] - - p = triangle.find('collada:p', self.namespaces) - polygons_temp = [int(integer) for integer in p.text.split()] - - polygons = [] - for x in range(0, len(polygons_temp), len(inputs) * 3): - temp_list = [] - for x1 in range(len(inputs)): - second_temp_list = [] - for x2 in range(3): - second_temp_list.append(polygons_temp[x + x1 + x2]) - temp_list.append(second_temp_list) - polygons.append(temp_list) - self.geometry_info['materials'].append({'name': triangles_material, - 'polygons': polygons}) - self.parsed['geometries'].append(self.geometry_info) diff --git a/models_converter/formats/obj_read.py b/models_converter/formats/obj.py similarity index 71% rename from models_converter/formats/obj_read.py rename to models_converter/formats/obj.py index b0bb0d2..ecdada4 100644 --- a/models_converter/formats/obj_read.py +++ b/models_converter/formats/obj.py @@ -5,6 +5,63 @@ def _(*args): print() +class Writer: + def __init__(self): + self.writen = '' + + self.temp_vertices_offsets = { + 'POSITION': 0, + 'TEXCOORD': 0, + 'NORMAL': 0 + } + + self.vertices_offsets = { + 'POSITION': 0, + 'TEXCOORD': 0, + 'NORMAL': 0 + } + + def write(self, data: dict): + for geom in data['geometries']: + for key in self.vertices_offsets.keys(): + self.vertices_offsets[key] = self.temp_vertices_offsets[key] + prefix = '' + + name = geom['name'] + vertices = geom['vertices'] + materials = geom['materials'] + for vertex in vertices: + if vertex['type'] == 'POSITION': + prefix = 'v ' + elif vertex['type'] == 'NORMAL': + prefix = 'vn ' + elif vertex['type'] == 'TEXCOORD': + prefix = 'vt ' + + self.temp_vertices_offsets[vertex['type']] += len(vertex['vertex']) + + for item in vertex['vertex']: + temp_string = prefix + for subitem in item: + temp_string += str(subitem * vertex['scale']) + ' ' + self.writen += f'{temp_string}\n' + self.writen += '\n\n' + for material in materials: + self.writen += f'o {name}_{material["name"]}\n\n' + for item in material['polygons']: + temp_string = 'f ' + for subitem in item: + temp_list = [ + str(subitem[0] + self.vertices_offsets['POSITION'] + 1), # POSITION + str(subitem[2] + self.vertices_offsets['TEXCOORD'] + 1), # TEXCOORD + str(subitem[1] + self.vertices_offsets['NORMAL'] + 1) # NORMAL + ] + + temp_string += '/'.join(temp_list) + ' ' + self.writen += f'{temp_string}\n' + self.writen += '\n\n' + + class Parser: def __init__(self, file_data: str): # @@ -32,7 +89,7 @@ def __init__(self, file_data: str): self.parse() def parse(self): - geometry_name = 'This model haven\'t a chunk_name!:( Its VERY SAD!' + geometry_name = 'This model haven \'t a chunk_name!:( Its VERY SAD!' is_first_name = True for line in self.lines: items = line.split()[1:] @@ -122,6 +179,8 @@ def parse(self): 'materials': [{'chunk_name': 'character_mat', 'polygons': self.polygons}] }) + # TODO: nodes + @staticmethod def get_vertex_scale(vertex_data: list): vertex_scale = max(max(vertex_data), abs(min(vertex_data))) diff --git a/models_converter/formats/obj_write.py b/models_converter/formats/obj_write.py deleted file mode 100644 index 473b834..0000000 --- a/models_converter/formats/obj_write.py +++ /dev/null @@ -1,62 +0,0 @@ -def _(*args): - print('[ScwUtils]', end=' ') - for arg in args: - print(arg, end=' ') - print() - - -class Writer: - def __init__(self): - self.writen = '' - - self.temp_vertices_offsets = { - 'POSITION': 0, - 'TEXCOORD': 0, - 'NORMAL': 0 - } - - self.vertices_offsets = { - 'POSITION': 0, - 'TEXCOORD': 0, - 'NORMAL': 0 - } - - def write(self, data: dict): - for geom in data['geometries']: - for key in self.vertices_offsets.keys(): - self.vertices_offsets[key] = self.temp_vertices_offsets[key] - prefix = '' - - name = geom['name'] - vertices = geom['vertices'] - materials = geom['materials'] - for vertex in vertices: - if vertex['type'] == 'POSITION': - prefix = 'v ' - elif vertex['type'] == 'NORMAL': - prefix = 'vn ' - elif vertex['type'] == 'TEXCOORD': - prefix = 'vt ' - - self.temp_vertices_offsets[vertex['type']] += len(vertex['vertex']) - - for item in vertex['vertex']: - temp_string = prefix - for subitem in item: - temp_string += str(subitem * vertex['scale']) + ' ' - self.writen += f'{temp_string}\n' - self.writen += '\n\n' - for material in materials: - self.writen += f'o {name}_{material["name"]}\n\n' - for item in material['polygons']: - temp_string = 'f ' - for subitem in item: - temp_list = [ - str(subitem[0] + self.vertices_offsets['POSITION'] + 1), # POSITION - str(subitem[2] + self.vertices_offsets['TEXCOORD'] + 1), # TEXCOORD - str(subitem[1] + self.vertices_offsets['NORMAL'] + 1) # NORMAL - ] - - temp_string += '/'.join(temp_list) + ' ' - self.writen += f'{temp_string}\n' - self.writen += '\n\n' diff --git a/models_converter/formats/scw.py b/models_converter/formats/scw.py index 6e96328..e3c72ca 100644 --- a/models_converter/formats/scw.py +++ b/models_converter/formats/scw.py @@ -24,7 +24,7 @@ def write(self, data: dict): self.write_chunk(head) - # TODO: make materials + # TODO: materials # for material in data['materials']: # mate = MATE(header) # mate.from_dict(material) @@ -37,7 +37,7 @@ def write(self, data: dict): self.write_chunk(geom) - # TODO: make cameras + # TODO: cameras for camera in data['cameras']: came = CAME(header) came.from_dict(camera) diff --git a/models_converter/utils/reader.py b/models_converter/utils/reader.py index 7f414f8..a131dd5 100644 --- a/models_converter/utils/reader.py +++ b/models_converter/utils/reader.py @@ -1,16 +1,16 @@ class Reader: - def __init__(self, buffer: bytes, endian: str = 'big'): + def __init__(self, buffer, endian='big'): self.buffer = buffer self.endian = endian self.i = 0 - def read(self, length: int = 1): + def read(self, length=1): result = self.buffer[self.i:self.i + length] self.i += length return result - def readUInteger(self, length: int = 1) -> int: + def readUInteger(self, length=1): result = 0 for x in range(length): byte = self.buffer[self.i] @@ -24,22 +24,22 @@ def readUInteger(self, length: int = 1) -> int: return result - def readInteger(self, length: int = 1) -> int: + def readInteger(self, length=1): integer = self.readUInteger(length) result = integer - if integer > 2**(length * 8) / 2: - result -= 2**(length * 8) + if integer > 2 ** (length * 8) / 2: + result -= 2 ** (length * 8) return result - def readUInt64(self) -> int: + def readUInt64(self): return self.readUInteger(8) - def readInt64(self) -> int: + def readInt64(self): return self.readInteger(8) - def readFloat(self) -> float: - asInt = self.readUInt32() - binary = bin(asInt) + def readFloat(self): + as_int = self.readUInt32() + binary = bin(as_int) binary = binary[2:].zfill(32) sign = -1 if binary[0] == '1' else 1 @@ -63,31 +63,31 @@ def readFloat(self) -> float: result = sign * 2 ** exponent * mantissa return result - def readUInt32(self) -> int: + def readUInt32(self): return self.readUInteger(4) - def readInt32(self) -> int: + def readInt32(self): return self.readInteger(4) - def readNUInt16(self) -> float: + def readNUInt16(self): return self.readUInt16() / 65535 - def readUInt16(self) -> int: + def readUInt16(self): return self.readUInteger(2) - def readNInt16(self) -> float: + def readNInt16(self): return self.readInt16() / 32512 - def readInt16(self) -> int: + def readInt16(self): return self.readInteger(2) - def readUInt8(self) -> int: + def readUInt8(self): return self.readUInteger() - def readInt8(self) -> int: + def readInt8(self): return self.readInteger() - def readBool(self) -> bool: + def readBool(self): if self.readUInt8() >= 1: return True else: @@ -108,12 +108,12 @@ def readBool(self) -> bool: readUByte = readUInt8 readByte = readInt8 - def readChar(self, length: int = 1) -> str: + def readChar(self, length=1): return self.read(length).decode('utf-8') - def readString(self) -> str: + def readString(self): length = self.readUShort() return self.readChar(length) - def tell(self) -> int: + def tell(self): return self.i diff --git a/models_converter/utils/reader.pyi b/models_converter/utils/reader.pyi new file mode 100644 index 0000000..044dde3 --- /dev/null +++ b/models_converter/utils/reader.pyi @@ -0,0 +1,56 @@ +class Reader: + def __init__(self, buffer: bytes, endian: str = 'big'): + self.buffer = buffer + self.endian = endian + self.i = 0 + + def read(self, length: int = 1): ... + + def readUInteger(self, length: int = 1) -> int: ... + + def readInteger(self, length: int = 1) -> int: ... + + def readUInt64(self) -> int: ... + + def readInt64(self) -> int: ... + + def readFloat(self) -> float: ... + + def readUInt32(self) -> int: ... + + def readInt32(self) -> int: ... + + def readNUInt16(self) -> float: ... + + def readUInt16(self) -> int: ... + + def readNInt16(self) -> float: ... + + def readInt16(self) -> int: ... + + def readUInt8(self) -> int: ... + + def readInt8(self) -> int: ... + + def readBool(self) -> bool: ... + + readUInt = readUInteger + readInt = readInteger + + readULong = readUInt64 + readLong = readInt64 + + readNUShort = readNUInt16 + readNShort = readNInt16 + + readUShort = readUInt16 + readShort = readInt16 + + readUByte = readUInt8 + readByte = readInt8 + + def readChar(self, length: int = 1) -> str: ... + + def readString(self) -> str: ... + + def tell(self) -> int: ... diff --git a/models_converter/utils/writer.py b/models_converter/utils/writer.py index 2ff136a..11d1b2a 100644 --- a/models_converter/utils/writer.py +++ b/models_converter/utils/writer.py @@ -1,24 +1,24 @@ class Writer: - def __init__(self, endian: str = 'big'): + def __init__(self, endian='big'): self.endian = endian self.buffer = b'' - def write(self, data: bytes): + def write(self, data): self.buffer += data - def writeUInteger(self, integer: int, length: int = 1): + def writeUInteger(self, integer, length=1): self.buffer += integer.to_bytes(length, self.endian, signed=False) - def writeInteger(self, integer: int, length: int = 1): + def writeInteger(self, integer, length=1): self.buffer += integer.to_bytes(length, self.endian, signed=True) - def writeUInt64(self, integer: int): + def writeUInt64(self, integer): self.writeUInteger(integer, 8) - def writeInt64(self, integer: int): + def writeInt64(self, integer): self.writeInteger(integer, 8) - def writeFloat(self, floating: float): + def writeFloat(self, floating): exponent = 0 sign = 1 @@ -52,8 +52,8 @@ def writeFloat(self, floating: float): mantissa_bin = '' for x in range(24): bit = '0' - if mantissa >= 1/2**x: - mantissa -= 1/2**x + if mantissa >= 1 / 2 ** x: + mantissa -= 1 / 2 ** x bit = '1' mantissa_bin += bit @@ -64,28 +64,28 @@ def writeFloat(self, floating: float): self.writeUInt32(as_integer) - def writeUInt32(self, integer: int): + def writeUInt32(self, integer): self.writeUInteger(integer, 4) - def writeInt32(self, integer: int): + def writeInt32(self, integer): self.writeInteger(integer, 4) - def writeNUInt16(self, integer: int): + def writeNUInt16(self, integer): self.writeUInt16(round(integer * 65535)) - def writeUInt16(self, integer: int): + def writeUInt16(self, integer): self.writeUInteger(integer, 2) - def writeNInt16(self, integer: int): + def writeNInt16(self, integer): self.writeInt16(round(integer * 32512)) - def writeInt16(self, integer: int): + def writeInt16(self, integer): self.writeInteger(integer, 2) - def writeUInt8(self, integer: int): + def writeUInt8(self, integer): self.writeUInteger(integer) - def writeInt8(self, integer: int): + def writeInt8(self, integer): self.writeInteger(integer) def writeBool(self, boolean: bool): @@ -109,11 +109,11 @@ def writeBool(self, boolean: bool): writeUByte = writeUInt8 writeByte = writeInt8 - def writeChar(self, string: str): + def writeChar(self, string): for char in list(string): self.buffer += char.encode('utf-8') - def writeString(self, string: str): + def writeString(self, string): encoded = string.encode('utf-8') self.writeUShort(len(encoded)) self.buffer += encoded diff --git a/models_converter/utils/writer.pyi b/models_converter/utils/writer.pyi new file mode 100644 index 0000000..4b0cf3f --- /dev/null +++ b/models_converter/utils/writer.pyi @@ -0,0 +1,53 @@ +class Writer: + def __init__(self, endian: str = 'big'): + self.endian = endian + self.buffer = b'' + + def write(self, data: bytes) -> None: ... + + def writeUInteger(self, integer: int, length: int = 1) -> None: ... + + def writeInteger(self, integer: int, length: int = 1) -> None: ... + + def writeUInt64(self, integer: int) -> None: ... + + def writeInt64(self, integer: int) -> None: ... + + def writeFloat(self, floating: float) -> None: ... + + def writeUInt32(self, integer: int) -> None: ... + + def writeInt32(self, integer: int) -> None: ... + + def writeNUInt16(self, integer: int) -> None: ... + + def writeUInt16(self, integer: int) -> None: ... + + def writeNInt16(self, integer: int) -> None: ... + + def writeInt16(self, integer: int) -> None: ... + + def writeUInt8(self, integer: int) -> None: ... + + def writeInt8(self, integer: int) -> None: ... + + def writeBool(self, boolean: bool) -> None: ... + + writeUInt = writeUInteger + writeInt = writeInteger + + writeULong = writeUInt64 + writeLong = writeInt64 + + writeNUShort = writeNUInt16 + writeNShort = writeNInt16 + + writeUShort = writeUInt16 + writeShort = writeInt16 + + writeUByte = writeUInt8 + writeByte = writeInt8 + + def writeChar(self, string: str) -> None: ... + + def writeString(self, string: str) -> None: ... diff --git a/setup.py b/setup.py index 27af549..412a335 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='3d-converter', - version='0.6.9', + version='0.7.0', author='Vorono4ka', author_email='crowo4ka@gmail.com', description='Python 3D Models Converter',