7*}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~YEiEhj>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',