diff --git a/README.md b/README.md index 12ce87f..1d52af6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Blender-VWI -Плагины для форматов файлов игрового движка Virtual World Inventor. +Плагины для форматов файлов игрового движка Virtual World Inventor. ##### Текущие планы -* Доработка экспорта. -* Переход на Blender 2.9. -* Поддержка импорта и экспорта игровых ресурсов. +* Доработка экспорта b3d + .res. ## Поддерживаемые игры | Название игры | Название игры (международное) | Год выхода | @@ -12,32 +10,42 @@ | Дальнобойщики - 2 | Hard Truck 2 (King of the Road) | 2000 (2003) ## Поддерживаемые форматы файлов -| Расширение | Описание | Импорт | Экспорт | +| Расширение | Описание | Импорт | Экспорт | |-----------|-----------------------|:----------:|:----------:| -| .b3d | Модели, логика, различные объекты | Да | Да | -| .way | Пути транспорта для ИИ | Да | Да | -| .tch/.tech | Параметры транспорта и динамических объектов | | Да | -| .res/.rmp | Архив игровых ресурсов | | в работе | -| .pro | Архив игровых ресурсов | | в работе | +| .b3d | Модели, логика, различные объекты | Да | Да | +| .b3d + .res | Модели, логика, различные объекты + текстуры | Да | Нет | +| .way | Пути транспорта для ИИ | Да | Нет | +| .tch/.tech | Параметры транспорта и динамических объектов | | Да | ## Файлы в проекте -#### Папка **src/2.79** +#### Папка **src/2.79** Плагины для версии 2.79. -#### Папка **scenes** +#### Папка **src/2.80** + +Плагины для версии 2.80+. +#### Папка **scenes** Готовые сцены для экспорта в игру: **ht2-way.blend** - пути транспорта, **ht2-vehicle-export.blend** - транспорт, **ht2-env-module.blend** - карта -## Как установить плагины +#### Папка **utils** + +Прочие полезные скрипты: +b3dsplit - извлечение из b3d-файла прочих моделей в отдельные файлы для импорта. +Пример: python b3dsplit.py ./TRUCKS.b3d ./split.txt + +## Как установить плагины 1. Распаковать архив. -2. Поместить содержимое в папку Blender/2.79/scripts/addons/. +2. Поместить содержимое в папку Blender/2.79/scripts/addons/. 3. Открыть настройки в Blender (нажать LCtrl + Alt + U ), перейти во вкладку Addons. 4. Найти аддоны (b3d) и активировать их (галка на названии). ## Авторы Юрий Гладышенко и Андрей Прожога. +Обновление: LabVaKars + ## Ссылки [Сообщество VK](https://vk.com/rnr_mods) @@ -49,21 +57,19 @@ Hard Truck classic games (VWI engine) import/export plugins for Blender. #### Roadmap -* Blender 2.9 support -* Add support for game resource files (.res/.rmp) +* .b3d + .res export support -## Supported games and formats - -1. Hard Truck: Road to Victory (1998) - -| Расширение | Описание | Import | +## Supported games +| Title | Title (ENG) | Release year | |-----------|-----------------------|:----------:| -| .b3d | Models, game logic, various objects | Yes | +| Дальнобойщики: Путь к победе | Hard Truck: Road to Victory | 1998 | +| Дальнобойщики - 2 | Hard Truck 2 (King of the Road) | 2000 (2003) -2. Hard Truck: King Of The Road (2003) +## Supported formats -| Расширение | Описание | Import | Export | +| Extension | Description | Import | Export | |-----------|-----------------------|:----------:|:----------:| -| .b3d | Models, game logic, various objects | Yes | Yes | -| .way | AI paths | Yes | Yes | -| .tch/.tech | Transport parameters | | Yes | \ No newline at end of file +| .b3d | Models, game logic, various objects | Yes | Yes | +| .b3d + .res | Models, game logic, various objects + textures | Yes | No | +| .way | AI paths | Yes | No | +| .tch/.tech | Transport parameters | | Yes | \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..67314ae --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +/*.py \ No newline at end of file diff --git a/src/2.79/export_tch/__init__.py b/src/2.79/export_tch/__init__.py index 227b02c..b143490 100644 --- a/src/2.79/export_tch/__init__.py +++ b/src/2.79/export_tch/__init__.py @@ -25,186 +25,186 @@ def read_tch(context, filepath, use_some_setting): print("running read_tch...") #file = open(filepath, 'r', encoding='utf-8') #data = f.read() - - + + file = open(filepath, 'r') - + CollisionPlane = [] - + CPlanes_num = 0 - + field_ind = "" field_x = "" field_y = "" field_z = "" field_offset = "" - + for line in file: ColPlane_line = "" if "CollisionPlane" in line: CollisionPlane.append(line) #CPlanes_num += 1 file.close() - + #print(str(CollisionPlane)) ColPlane_vector = [] - - + + for i in range(len(CollisionPlane)):#(CPlanes_num): if i < 10: ColPlane_vector.append((CollisionPlane[i])[16:].split(" ")) else: ColPlane_vector.append((CollisionPlane[i])[17:].split(" ")) #print(str(i)) - + print(str(ColPlane_vector)) # would normally load the data here #print(data) return {'FINISHED'} - + def export_tch(context, filepath): print("exporting tch...") file = open(filepath, 'w') - + global global_matrix global_matrix = axis_conversion(to_forward="-Z", to_up="Y", ).to_4x4() * Matrix.Scale(1, 4) - + global CollisionPlane_num CollisionPlane_num = 0 - + forChild(bpy.data.objects['tch'],True, file, CollisionPlane_num) - + return {'FINISHED'} - + def forChild(object, root, file, CollisionPlane_num): if (not root): if object.name == "Corner_Mark": verticesL = [] uvs = [] faces = [] - + bm = bmesh.new() bm.from_mesh(object.data) bm.verts.ensure_lookup_table() bm.faces.ensure_lookup_table() - + bm.transform(global_matrix * object.matrix_world) - + bm.to_mesh(object.data) - - + + for v in bm.verts: verticesL.append((v.co)) - + meshdata = object.data - + for i, polygon in enumerate(meshdata.polygons): for i1, loopindex in enumerate(polygon.loop_indices): meshloop = meshdata.loops[loopindex] faces.append(meshloop.vertex_index) - + vLen = len(verticesL) - + #for i,vert in enumerate(verticesL): #file.write("CornerMark_" + str(i) + "=" + str(vert[0][0]) + " " + str(-vert[0][2]) + " " + str(vert[0][1])) #file.write(str(object.data.vert[i][0][0])) - - v_num = 0 - + + v_num = 0 + for vert in object.data.vertices: v_num += 1 file.write("Corner_Mark_" + str(v_num) + "=" + str(round(vert.co.x, 6)) + " " + str(round(-vert.co.z, 6)) + " " + str(round(vert.co.y, 6)) + "\n") #file.write(struct.pack(" " + material_textures[k]) - + return material_textures - + def read(file, context, op, filepath, search_tex_names, textures_format, color_format, tex_path): if file.read(3) == b'b3d': print ("correct file"); else: print ("b3d error") - + file.seek(21,1); Imgs = [] math = [] #texr = [] - + if (search_tex_names == True): colors_list = parse_plm(file.name[:-3] + "plm", color_format) material_textures = parse_pro(file.name[:-3] + "pro", colors_list) #print(material_textures) - + for i in range(struct.unpack('23281193): # break - + ex = openclose(file) if ex == 0: #print('-') @@ -386,7 +386,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f #print((objString)) #print('eob') #continue - elif ex == 1: + elif ex == 1: print(str(cnt)) file.close() break @@ -404,7 +404,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f qu = quat(file).v objString.append(os.path.basename(op.properties.filepath)) ff = file.seek(28,1) - + elif (type == 1): cnt+=1 #b3dObj.append( @@ -413,7 +413,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True + #b3dObj.hide = True b3dObj['2 name'] = file.read(32).decode("cp1251").rstrip('\0') b3dObj['3 name'] = file.read(32).decode("cp1251").rstrip('\0') @@ -428,8 +428,8 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True - + #b3dObj.hide = True + qu = quat(file).v struct.unpack("<4fi",file.read(20)) elif (type == 3): # @@ -441,8 +441,8 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True - + #b3dObj.hide = True + qu = quat(file).v struct.unpack("0: pass #printwrite(writefile,tab1+'0d Coords: '+str(coords)) - - elif (type == 14): #sell_ ? - + + elif (type == 14): #sell_ ? + #cnt+=1 #b3dObj = bpy.data.objects.new(objName, None) #b3dObj['block_type'] = 14 @@ -707,19 +707,19 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f #b3dObj.parent = context.scene.objects[objString[-1]] #context.scene.objects.link(b3dObj) #objString.append(context.scene.objects[0].name) - #b3dObj.hide = True + #b3dObj.hide = True - #qu = quat(file).v + #qu = quat(file).v #file.seek(24, 1) #var = struct.unpack(" 15): skip_len = 20 - + elif (11 < len(name) < 15 or len(name) == 15): skip_len = 16 - + elif (7 < len(name) < 11 or len(name) == 11): skip_len = 12 - + elif (3 < len(name) < 7 or len(name) == 7): skip_len = 8 - + elif (len(name) < 3 or len(name) == 3): skip_len = 4 - + file.seek(skip_len, 1) - - + + elif (type == 16): cnt+=1 b3dObj = bpy.data.objects.new(objName, None) @@ -780,7 +780,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True + #b3dObj.hide = True qu = quat(file).v struct.unpack("11f",file.read(44)) @@ -792,14 +792,14 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True + #b3dObj.hide = True qu = quat(file).v struct.unpack("11f",file.read(44)) - + elif (type == 18): #контейнер "применить к" qu = quat(file).v - + cnt+=1 b3dObj = bpy.data.objects.new(objName, None) b3dObj['block_type'] = 18 @@ -809,8 +809,8 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - - + + elif (type == 19): cnt+=1 b3dObj = bpy.data.objects.new(objName, None) @@ -819,13 +819,13 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True - + #b3dObj.hide = True + num = [] num.append(struct.unpack("i",file.read(4))[0]) #printwrite(writefile,tab1+str(num)) - + elif (type == 20): cnt+=1 #b3dObj = bpy.data.objects.new(objName, None) @@ -835,7 +835,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f #objString.append(context.scene.objects[0].name) qu = quat(file).v - + verts_count = struct.unpack("i",file.read(4))[0] struct.unpack("f",file.read(4)) struct.unpack("f",file.read(4)) @@ -851,7 +851,7 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f #for i in range (title[3]): # coords1.append(struct.unpack("f",file.read(4))[0]) curveData = bpy.data.curves.new('curve', type='CURVE') - + curveData.dimensions = '3D' curveData.resolution_u = 2 @@ -870,8 +870,8 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f #scn = bpy.context.scene #scn.objects.link(curveOB) #scn.objects.active = curveOB - - + + b3dObj.location = (0,0,0) b3dObj['block_type'] = 20 b3dObj['pos'] = str(file.tell()) @@ -879,13 +879,13 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - - #bpy.context.scene.objects.link(object) - - elif (type == 21): #testkey??? + #bpy.context.scene.objects.link(object) + + + elif (type == 21): #testkey??? qu = quat(file).v - + cnt+=1 b3dObj = bpy.data.objects.new(objName, None) b3dObj['block_type'] = 21 @@ -893,13 +893,13 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.parent = context.scene.objects[objString[-1]] context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - #b3dObj.hide = True + #b3dObj.hide = True object = type15(file) - + elif (type == 23): #colision mesh - + cnt+=1 b3dMesh = (bpy.data.meshes.new(objName)) b3dObj = bpy.data.objects.new(objName, b3dMesh) @@ -909,8 +909,8 @@ def read(file, context, op, filepath, search_tex_names, textures_format, color_f b3dObj.hide = True context.scene.objects.link(b3dObj) objString.append(context.scene.objects[0].name) - - + + #qu = quat(file).v var1 = struct.unpack(" 15): text_len = 20 - + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): text_len = 16 - + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): text_len = 12 - + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): text_len = 8 - + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): text_len = 4 - + bytes_len = 8 + text_len - + object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'RNOD': bytes_len = 8 object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'NNAM': text_len = 0 - + if (len(str(object['str'])) > 15): text_len = 20 - + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): text_len = 16 - + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): text_len = 12 - + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): text_len = 8 - + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): text_len = 4 - + bytes_len = 8 + text_len object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'GROM': bytes_len = 8 object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'GDAT': bytes_len = 8 object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'MNAM': text_len = 0 - + if (len(str(object['str'])) > 15): text_len = 20 - + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): text_len = 16 - + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): text_len = 12 - + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): text_len = 8 - + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): text_len = 4 - + bytes_len = 8 + text_len object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'WTWR': bytes_len = 8 object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 - + elif object.name[0:4] == 'FLAG': bytes_len = 12 object['bytes_len'] = bytes_len object['c_bytes_len'] = 0 #object['flag'] = 1 - + for child in object.children: SetBytesLength(child,False) - + def SetCBytesLength(object, root): varCB = 0 if (not root): if object.name[0:4] == 'RSEG': bytes_len = 8 - + elif object.name[0:4] == 'RNOD': for x in range (len(object.children)): varCB += object.children[x]['bytes_len'] - + object['c_bytes_len'] = varCB - + for child in object.children: - SetCBytesLength(child,False) + SetCBytesLength(child,False) -def SetRS(object, root): +def SetRS(object, root): varRS = 0 if (not root): if object.name[0:4] == 'RSEG': for r in range (len(object.children)): varRS += object.children[r]['bytes_len'] + object.children[r]['c_bytes_len'] - + object['c_bytes_len'] = varRS - + for child in object.children: - SetRS(child, False) - + SetRS(child, False) + def SetGR(object, root): varGR = 0 if (not root): if object.name[0:4] == 'GROM': for j in range (len(object.children)): varGR += object.children[j]['bytes_len'] + object.children[j]['c_bytes_len'] - + object['c_bytes_len'] = varGR - + for child in object.children: SetGR(child, False) - + def SetGD(object, root): variable = 0 if (not root): if object.name[0:4] == 'GDAT': for n in range (len(object.children)): variable += object.children[n]['bytes_len'] + object.children[n]['c_bytes_len'] - + object['c_bytes_len'] = variable - + for child in object.children: SetGD(child, False) - + def SetMN(object, root): varMN = 0 if (not root): if object.name[0:4] == 'MNAM': for i in range (len(object.children)): varMN = object.children[i]['bytes_len'] + object.children[i]['c_bytes_len'] - + object['c_bytes_len'] = varMN - + for child in object.children: SetMN(child, False) - + def SetWT(object, root): varWT = 0 if (not root): if object.name[0:4] == 'WTWR': for i in range (len(object.children)): varWT = object.children[i]['bytes_len'] + object.children[i]['c_bytes_len'] - + object['c_bytes_len'] = varWT - + for child in object.children: SetWT(child, False) - - + + def GetCBytesLength(object, root): var1 = 0 if (not root): @@ -769,26 +769,26 @@ def GetCBytesLength(object, root): if (object.children): for i in range (len(object.children)): var1 = object.children[i]['bytes_len'] + object['bytes_len'] - #object['c_bytes_len'] = var1 + #object['c_bytes_len'] = var1 object['c_bytes_len'] = var1 + object['c_bytes_len'] print(str(var1)) - + for child in object.children: GetCBytesLength(child,False) - -def SetRSEGBytes(object, root): + +def SetRSEGBytes(object, root): if (not root): if object['type'] == 'rseg': for subcurve in object.data.splines: object['bytes_len'] = 68 + len(subcurve.points) * 24 - + room = object.parent room['c_bytes_len'] += object['bytes_len'] - + for child in object.children: - SetRSEGBytes(child, False) - -def SetNodeBytes(object, root): + SetRSEGBytes(child, False) + +def SetNodeBytes(object, root): var_node = 0 print("SetNodeBytes") try: @@ -797,24 +797,24 @@ def SetNodeBytes(object, root): text_len1 = 0 if (len(str(object_name)) > 15): text_len1 = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): text_len1 = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): text_len1 = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): text_len1 = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): text_len1 = 4 - + var_node = text_len1 + 16 + 32 + 12 if object['type'] == "ortn": var_node += 104 object['bytes_len'] = var_node - + #object.parent['c_bytes_len'] += var_node room = object.parent room['c_bytes_len'] += var_node @@ -823,38 +823,38 @@ def SetNodeBytes(object, root): SetNodeBytes(child, False) except: pass - -def SetRoomBytes(object, root): + +def SetRoomBytes(object, root): var_r = 0 if object['type'] == 'room': object_name = object.name.split(".")[0] text_len1 = 0 if (len(str(object_name)) > 15): text_len1 = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): text_len1 = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): text_len1 = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): text_len1 = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): text_len1 = 4 - + var_r = text_len1 + 16 - + object['bytes_len'] = var_r #+ 8 - + #way = object.parent #way['c_bytes_len'] += object['bytes_len'] for child in object.children: SetRoomBytes(child, False) - -def SetWayBytes(object, root): + +def SetWayBytes(object, root): if object['type'] == 'room': way = object.parent print(object['bytes_len']) @@ -862,37 +862,37 @@ def SetWayBytes(object, root): for child in object.children: SetWayBytes(child, False) - -def SetWayBytes1(object, root): + +def SetWayBytes1(object, root): if object['type'] == 'way': object['bytes_len'] = 20 + object['c_bytes_len'] for child in object.children: SetWayBytes1(child, False) - -def ClearBytes(object, root): + +def ClearBytes(object, root): if object['type'] == 'room': object['bytes_len'] = 0 object['c_bytes_len'] = 0 - + if object['type'] == 'way': object['bytes_len'] = 0 object['c_bytes_len'] = 0 for child in object.children: ClearBytes(child, False) - + def writeWTWR(object, file): file.write(str.encode('WTWR')) file.write(struct.pack(" 15): text_len1 = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): text_len1 = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): text_len1 = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): text_len1 = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): text_len1 = 4 - + object['c_bytes_len'] = text_len1 + 8 + 32 + 12 - + if object['type'] == "ortn": object['c_bytes_len'] += 104 - + file.write(struct.pack(" 15): file.write(str.encode(object_name)+bytearray(b'\x00'*(20-len(str(object_name))))) text_len = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): file.write(str.encode(object_name)+bytearray(b'\x00'*(16-len(str(object_name))))) text_len = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): file.write(str.encode(object_name)+bytearray(b'\x00'*(12-len(str(object_name))))) text_len = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): file.write(str.encode(object_name)+bytearray(b'\x00'*(8-len(str(object_name))))) text_len = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): file.write(str.encode(object_name)+bytearray(b'\x00'*(4-len(str(object_name))))) text_len = 4 - + #bytes_len = 8 + text_len - + file.write(str.encode('POSN')) file.write(struct.pack(" 15): text_len1 = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): text_len1 = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): text_len1 = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): text_len1 = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): text_len1 = 4 - + object['bytes_len'] = 8 + text_len1 + object['c_bytes_len'] object['c_bytes_len'] = object['c_bytes_len'] + 20 - + file.write(struct.pack(" 15): file.write(str.encode(object_name)+bytearray(b'\x00'*(20-len(str(object_name))))) text_len = 20 - + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): file.write(str.encode(object_name)+bytearray(b'\x00'*(16-len(str(object_name))))) text_len = 16 - + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): file.write(str.encode(object_name)+bytearray(b'\x00'*(12-len(str(object_name))))) text_len = 12 - + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): file.write(str.encode(object_name)+bytearray(b'\x00'*(8-len(str(object_name))))) text_len = 8 - + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): file.write(str.encode(object_name)+bytearray(b'\x00'*(4-len(str(object_name))))) text_len = 4 @@ -1125,28 +1125,28 @@ def execute(self, context): # Only needed if you want to add into a dynamic menu def menu_func_export(self, context): self.layout.operator(ExportSomeData.bl_idname, text="Export KOTR *.way") - + def menu_func_import(self, context): self.layout.operator(ImportSomeData.bl_idname, text="Import KOTR *.way") def register(): bpy.utils.register_class(ExportSomeData) - bpy.types.INFO_MT_file_export.append(menu_func_export) - + bpy.types.TOPBAR_MT_file_export.append(menu_func_export) + bpy.utils.register_class(ImportSomeData) - bpy.types.INFO_MT_file_import.append(menu_func_import) - + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.utils.register_module(__name__) bpy.types.Scene.way_tool = PointerProperty(type=PanelSettings1) def unregister(): bpy.utils.unregister_class(ExportSomeData) - bpy.types.INFO_MT_file_export.remove(menu_func_export) - + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) + bpy.utils.unregister_class(ImportSomeData) - bpy.types.INFO_MT_file_import.remove(menu_func_import) - + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + bpy.utils.unregister_module(__name__) del bpy.types.Scene.way_tool diff --git a/src/2.80/addons/b3d_tools/__init__.py b/src/2.80/addons/b3d_tools/__init__.py new file mode 100644 index 0000000..af21e70 --- /dev/null +++ b/src/2.80/addons/b3d_tools/__init__.py @@ -0,0 +1,84 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +if "bpy" in locals(): + print("Reimporting modules!!!") + import importlib + # importlib.reload(object_UIList) + # importlib.reload(material_UIList) + importlib.reload(custom_UIList) + importlib.reload(common) + importlib.reload(consts) + importlib.reload(way) + importlib.reload(tch) + importlib.reload(b3d) +else: + import bpy + from . import ( + # object_UIList, + # material_UIList, + custom_UIList, + common, + consts, + way, + tch, + b3d + ) + + + + +from .tch import tch_unregister, tch_register + + + +bl_info = { + "name": "King of The Road Tools", + "description": "", + "author": "Andrey Prozhoga, LabVaKars", + "version": (2, 3, 1), + "blender": (2, 93, 0), + "location": "3D View > Tools", + "warning": "", + "wiki_url": "", + "tracker_url": "vk.com/rnr_mods", + "category": "Development" +} + + +# ------------------------------------------------------------------------ +# register and unregister +# ------------------------------------------------------------------------ + + +def register(): + custom_UIList.register() + way.register() + b3d.register() + # tch_register() + +def unregister(): + b3d.unregister() + way.unregister() + custom_UIList.unregister() + # tch_unregister() + +if __name__ == "__main__": + register() diff --git a/src/2.80/addons/b3d_tools/b3d/__init__.py b/src/2.80/addons/b3d_tools/b3d/__init__.py new file mode 100644 index 0000000..6048423 --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/__init__.py @@ -0,0 +1,43 @@ + + +# To support reload properly, try to access a package var, if it's there, +# reload everything +if "bpy" in locals(): + print("Reimporting modules!!!") + import importlib + + importlib.reload(common) + importlib.reload(class_descr) + importlib.reload(classes) + importlib.reload(import_b3d) + importlib.reload(export_b3d) + importlib.reload(imghelp) + importlib.reload(panel) + importlib.reload(menus) +else: + import bpy + from . import ( + common, + class_descr, + classes, + import_b3d, + export_b3d, + imghelp, + panel, + menus + ) + +def register(): + print("registering addon") + class_descr.register() + classes.register() + menus.register() + panel.register() + + +def unregister(): + print("unregistering addon") + panel.unregister() + menus.unregister() + classes.unregister() + class_descr.unregister() diff --git a/src/2.80/addons/b3d_tools/b3d/class_descr.py b/src/2.80/addons/b3d_tools/b3d/class_descr.py new file mode 100644 index 0000000..6a1d97f --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/class_descr.py @@ -0,0 +1,2051 @@ +from email.policy import default +import bpy +import enum + +from bpy.props import (StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + FloatVectorProperty, + CollectionProperty + ) + +from bpy.types import (Panel, + Operator, + PropertyGroup, + ) + +from ..common import log + +from ..consts import ( + collisionTypeList, + generatorTypeList, + b24FlagList, + vTypeList, + BLOCK_TYPE, + LEVEL_GROUP +) + +# Dynamic block exmaple +# block_5 = type("block_5", (bpy.types.PropertyGroup,), { +# '__annotations__': { +# 'name': StringProperty( +# name="Block name", +# default="", +# maxlen=30, +# ), +# 'XYZ': FloatVectorProperty( +# name='Block border coord', +# description='', +# default=(0.0, 0.0, 0.0) +# ), +# 'R': FloatProperty( +# name = "Block border rad", +# description = "", + +# ) +# } +# }) + + + +class fieldType(enum.Enum): + IGNORE = 0 + STRING = 1 + COORD = 2 + RAD = 3 + INT = 4 + FLOAT = 5 + ENUM = 6 + LIST = 7 + ENUM_DYN = 8 + + V_FORMAT = 21 + MATERIAL_IND = 22 + SPACE_NAME = 23 + REFERENCEABLE = 24 + ROOM = 25 + RES_MODULE = 26 + + SPHERE_EDIT = 41 + + +class ActiveBlock(bpy.types.PropertyGroup): + name: StringProperty() + state : BoolProperty() + +class FloatBlock(bpy.types.PropertyGroup): + value: FloatProperty() + +class MaskfileBlock(bpy.types.PropertyGroup): + value: StringProperty() + + is_noload: BoolProperty(default=False) + is_someint: BoolProperty(default=False) + someint: IntProperty(default=0) + +class TextureBlock(bpy.types.PropertyGroup): + value: StringProperty() + + is_memfix: BoolProperty(default=False) + is_noload: BoolProperty(default=False) + is_bumpcoord: BoolProperty(default=False) + is_someint: BoolProperty(default=False) + someint: IntProperty() + +class MaterialBlock(bpy.types.PropertyGroup): + value: StringProperty() + + is_reflect: BoolProperty(default=False) + reflect: FloatProperty(default=0.0) + + is_specular: BoolProperty(default=False) + specular: FloatProperty(default=0.0) + + is_transp: BoolProperty(default=False) + transp: FloatProperty(default=0.0) + + is_rot: BoolProperty(default=False) + rot: FloatProperty(default=0.0) + + is_col: BoolProperty(default=False) + col: IntProperty(default=0) + + is_tex: BoolProperty(default=False) + tex: IntProperty(default=0) + + is_ttx: BoolProperty(default=False) + ttx: IntProperty(default=0) + + is_itx: BoolProperty(default=False) + itx: IntProperty(default=0) + + is_att: BoolProperty(default=False) + att: IntProperty(default=0) + + is_msk: BoolProperty(default=False) + msk: IntProperty(default=0) + + is_power: BoolProperty(default=False) + power: IntProperty(default=0) + + is_coord: BoolProperty(default=False) + coord: IntProperty(default=0) + + is_env: BoolProperty(default=False) + envId: IntProperty(default=0) + env: FloatVectorProperty(default=(0.0, 0.0), size=2) + + is_rotPoint: BoolProperty(default=False) + rotPoint: FloatVectorProperty(default=(0.0, 0.0), size=2) + + is_move: BoolProperty(default=False) + move: FloatVectorProperty(default=(0.0, 0.0), size=2) + + is_noz: BoolProperty(default=False) + is_nof: BoolProperty(default=False) + is_notile: BoolProperty(default=False) + is_notileu: BoolProperty(default=False) + is_notilev: BoolProperty(default=False) + is_alphamirr: BoolProperty(default=False) + is_bumpcoord: BoolProperty(default=False) + is_usecol: BoolProperty(default=False) + is_wave: BoolProperty(default=False) + +class ResBlock(bpy.types.PropertyGroup): + value: StringProperty() + textures: CollectionProperty(type=TextureBlock) + materials: CollectionProperty(type=MaterialBlock) + maskfiles: CollectionProperty(type=MaskfileBlock) + +borderSphereGroup = 'border_sphere' + +# class_descr configuration: + +# prop - Required - Key used to save property in Blenders custom properties. +# group - Optional - Used to determine what elements to group together. +# type - Required - Type of the field. +# Type specific configurations + +# fieldType.STRING + # 'name': 'name', + # 'description': '', + # 'default': '' + +# fieldType.COORD + # 'name': 'Name', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + +# fieldType.INT + # 'name': 'Name', + # 'description': '', + # 'default': 0 + +# fieldType.FLOAT + # 'name': 'Name', + # 'description': '', + # 'default': 0.0 + +# fieldType.ENUM - static + # 'subtype': fieldType.INT, + # 'name': 'Name', + # 'description': '' + +# fieldType.LIST + # 'name': 'Name', + # 'description': '' + +# fieldType.ENUM_DYN - dynamic + # 'subtype': fieldType.STRING + # 'callback': fieldType.SPACE_NAME, + # 'name': 'Name', + # 'description': '' + +# fieldType.V_FORMAT + +# Used as subtypes +# fieldType.MATERIAL_IND +# fieldType.SPACE_NAME +# fieldType.REFERENCEABLE +# fieldType.ROOM +# fieldType.RES_MODULE + +# Custom operators +# fieldType.SPHERE_EDIT + + + + +class pvb_8(): + Normal_Switch = { + 'prop': 'normal_switch', + 'type': fieldType.FLOAT, + 'name': 'Normal switcher', + 'description': '', + 'default': 0.0 + } + Custom_Normal = { + 'prop': 'custom_normal', + 'type': fieldType.COORD, + 'name': 'Custom normal', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + + +class pvb_35(): + Normal_Switch = { + 'prop': 'normal_switch', + 'type': fieldType.FLOAT, + 'name': 'Normal switcher', + 'description': '', + 'default': 0.0 + } + Custom_Normal = { + 'prop': 'custom_normal', + 'type': fieldType.COORD, + 'name': 'Custom normal', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + +class pfb_8(): + Format_Flags = { + 'prop': 'format_flags', + 'type': fieldType.V_FORMAT, + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + +class pfb_28(): + Format_Flags = { + 'prop': 'format_flags', + 'type': fieldType.V_FORMAT, + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + + +class pfb_35(): + Format_Flags = { + 'prop': 'format_flags', + 'type': fieldType.V_FORMAT, + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + +class b_1(): + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Unk. name 1', + 'description': '', + 'default': '' + } + Name2 = { + 'prop': 'name2', + 'type': fieldType.STRING, + 'name': 'Unk. name 2', + 'description': '', + 'default': '' + } + +class b_2(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + +# class b_3(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + +class b_4(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.SPACE_NAME, + 'name': 'Place', + 'description': '' + } + Name2 = { + 'prop': 'name2', + 'type': fieldType.STRING, + 'name': 'Name 2', + 'description': '', + 'default': '' + } + +class b_5(): + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Block name', + 'description': '', + 'default': '' + } + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + + +class b_6(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Name 1', + 'description': '', + 'default': '' + } + Name2 = { + 'prop': 'name2', + 'type': fieldType.STRING, + 'name': 'Name 2', + 'description': '', + 'default': '' + } + +class b_7(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Group name', + 'description': '', + 'default': '' + } + +# class b_8(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + +class b_9(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ = { + 'prop': 'b3d_b9_center', + 'group': 'b9_group', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'b3d_b9_rad', + 'group': 'b9_group', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + Set_B9 = { + 'prop': 'b3d_b9', + 'group': 'b9_group', + 'type': fieldType.SPHERE_EDIT + } + +class b_10(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + LOD_XYZ = { + 'prop': 'b3d_LOD_center', + 'group': 'LOD_group', + 'type': fieldType.COORD, + 'name': 'LOD coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + LOD_R = { + 'prop': 'b3d_LOD_rad', + 'group': 'LOD_group', + 'type': fieldType.RAD, + 'name': 'LOD rad', + 'description': '', + 'default': 0.0 + } + Set_LOD = { + 'prop': 'b3d_LOD', + 'group': 'LOD_group', + 'type': fieldType.SPHERE_EDIT + } + +class b_11(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R1 = { + 'prop': 'unk_R1', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + Unk_R2 = { + 'prop': 'unk_R1', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + +class b_12(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_13(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_14(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_15(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_16(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 2', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Float2 = { + 'prop': 'float2', + 'type': fieldType.FLOAT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0.0 + } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 3', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 4', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_17(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 2', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Float2 = { + 'prop': 'float2', + 'type': fieldType.FLOAT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0.0 + } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 3', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 4', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_18(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Space_Name = { + 'prop': 'space_name', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.SPACE_NAME, + 'name': 'Place name (24)', + 'description': '' + } + Add_Name = { + 'prop': 'add_name', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.REFERENCEABLE, + 'name': 'Transfer block name', + 'description': '' + } + +class b_20(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_21(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + GroupCnt = { + 'prop': 'group_cnt', + 'type': fieldType.INT, + 'name': 'Group count', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + +class b_22(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + +class b_23(): + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Surface = { + 'prop': 'CType', + 'type': fieldType.ENUM, + 'subtype': fieldType.INT, + 'name': 'Surface type', + 'description': '', + 'default': 0, + 'items': collisionTypeList + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +class b_24(): + Flag = { + 'prop': 'flag', + 'type': fieldType.ENUM, + 'subtype': fieldType.INT, + 'name': 'Show flag', + 'description': '', + 'default': 0, + 'items': b24FlagList + } + +class b_25(): + XYZ = { + 'prop': 'XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Name = { + 'prop': 'name', + 'type': fieldType.STRING, + 'name': 'Name 1', + 'description': '', + 'default': '' + } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 2', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Float2 = { + 'prop': 'float2', + 'type': fieldType.FLOAT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0.0 + } + Unk_Float3 = { + 'prop': 'float3', + 'type': fieldType.FLOAT, + 'name': 'Unk. 3', + 'description': '', + 'default': 0.0 + } + Unk_Float4 = { + 'prop': 'float4', + 'type': fieldType.FLOAT, + 'name': 'Unk. 4', + 'description': '', + 'default': 0.0 + } + Unk_Float5 = { + 'prop': 'float5', + 'type': fieldType.FLOAT, + 'name': 'Unk. 5', + 'description': '', + 'default': 0.0 + } + +class b_26(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 2', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ3 = { + 'prop': 'unk_XYZ3', + 'type': fieldType.COORD, + 'name': 'Unk. coord 3', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + +class b_27(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Flag = { + 'prop': 'flag', + 'type': fieldType.INT, + 'name': 'Flag', + 'description': '', + 'default': 0 + } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Material = { + 'prop': 'material', + 'type': fieldType.INT, + 'name': 'Material', + 'description': '', + 'default': 0 + } + +class b_28(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Sprite_Center = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Sprite center coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + #todo: check + +class b_29(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_XYZ = { + 'prop': 'unk_XYZ', + 'type': fieldType.COORD, + 'name': 'Unk. coord', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + +class b_30(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + ResModule1 = { + 'prop': '1_roomName_res', + 'group': 'resModule1', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.RES_MODULE, + 'name': '1. module', + 'description': '', + 'default': '' + } + RoomName1 = { + 'prop': '1_roomName', + 'group': 'resModule1', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.ROOM, + 'name': '1. room', + 'description': '', + 'default': '' + } + ResModule2 = { + 'prop': '2_roomName_res', + 'group': 'resModule2', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.RES_MODULE, + 'name': '2. module', + 'description': '', + 'default': '' + } + RoomName2 = { + 'prop': '2_roomName', + 'group': 'resModule2', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.STRING, + 'callback': fieldType.ROOM, + 'name': '2. room', + 'description': '', + 'default': '' + } + +class b_31(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_R = { + 'prop': 'unk_R', + 'type': fieldType.RAD, + 'name': 'Unk. rad', + 'description': '', + 'default': 0.0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + #todo: check + +class b_33(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Use_Lights = { + 'prop': 'useLight', + 'type': fieldType.INT, + 'name': 'Use lights', + 'description': '', + 'default': 0 + } + Light_Type = { + 'prop': 'lType', + 'type': fieldType.INT, + 'name': 'Light type', + 'description': '', + 'default': 0 + } + Flag = { + 'prop': 'flag', + 'type': fieldType.INT, + 'name': 'Flag', + 'description': '', + 'default': 0 + } + Unk_XYZ1 = { + 'prop': 'unk_XYZ1', + 'type': fieldType.COORD, + 'name': 'Unk. coord 1', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_XYZ2 = { + 'prop': 'unk_XYZ2', + 'type': fieldType.COORD, + 'name': 'Unk. coord 2', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Unk_Float2 = { + 'prop': 'float2', + 'type': fieldType.FLOAT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0.0 + } + Light_R = { + 'prop': 'light_radius', + 'type': fieldType.FLOAT, + 'name': 'Light rad', + 'description': '', + 'default': 0.0 + } + Intens = { + 'prop': 'intensity', + 'type': fieldType.FLOAT, + 'name': 'Light intensity', + 'description': '', + 'default': 0.0 + } + Unk_Float3 = { + 'prop': 'float3', + 'type': fieldType.FLOAT, + 'name': 'Unk. 3', + 'description': '', + 'default': 0.0 + } + Unk_Float4 = { + 'prop': 'float4', + 'type': fieldType.FLOAT, + 'name': 'Unk. 4', + 'description': '', + 'default': 0.0 + } + RGB = { + 'prop': 'rgb', + 'type': fieldType.COORD, + 'name': 'RGB', + 'description': '', + 'default': (0.0, 0.0, 0.0) + } + + +class b_34(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + UnkInt = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + +class b_35(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + MType = { + 'prop': 'mType', + 'type': fieldType.INT, + 'name': 'Type', + 'description': '', + 'default': 0 + } + TexNum = { + 'prop': 'texnum', + 'type': fieldType.ENUM_DYN, + 'subtype': fieldType.INT, + 'callback': fieldType.MATERIAL_IND, + 'name': 'Texture', + 'description': '', + } + +class b_36(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Name 1', + 'description': '', + 'default': '' + } + Name2 = { + 'prop': 'name2', + 'type': fieldType.STRING, + 'name': 'Name 2', + 'description': '', + 'default': '' + } + VType = { + 'prop': 'vType', + 'type': fieldType.ENUM, + 'subtype': fieldType.INT, + 'name': 'Vertex type', + 'description': '', + 'default': 2, + 'items': vTypeList + } + +class b_37(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Name 1', + 'description': '', + 'default': '' + } + VType = { + 'prop': 'vType', + 'type': fieldType.ENUM, + 'subtype': fieldType.INT, + 'name': 'Vertex type', + 'description': '', + 'default': 1, + 'items': vTypeList + } + +class b_39(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Color_R = { + 'prop': 'color_r', + 'type': fieldType.INT, + 'name': 'Color rad', + 'description': '', + 'default': 0 + } + Unk_Float1 = { + 'prop': 'float1', + 'type': fieldType.FLOAT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0.0 + } + Fog_Start = { + 'prop': 'fog_start', + 'type': fieldType.FLOAT, + 'name': 'Unk. 3', + 'description': '', + 'default': 0.0 + } + Fog_End = { + 'prop': 'fog_end', + 'type': fieldType.FLOAT, + 'name': 'Unk. 4', + 'description': '', + 'default': 0.0 + } + Color_Id = { + 'prop': 'color_id', + 'type': fieldType.INT, + 'name': 'Color ID', + 'description': '', + 'default': 0 + } + +class b_40(): + # XYZ = { + # 'prop': 'b3d_border_center', + # 'group': borderSphereGroup, + # 'type': fieldType.COORD, + # 'name': 'Block border coord', + # 'description': '', + # 'default': (0.0, 0.0, 0.0) + # } + # R = { + # 'prop': 'b3d_border_rad', + # 'group': borderSphereGroup, + # 'type': fieldType.RAD, + # 'name': 'Block border rad', + # 'description': '', + # 'default': 0.0 + # } + # Set_Bound = { + # 'prop': 'b3d_border', + # 'group': borderSphereGroup, + # 'type': fieldType.SPHERE_EDIT + # } + Name1 = { + 'prop': 'name1', + 'type': fieldType.STRING, + 'name': 'Name 1', + 'description': '', + 'default': '' + } + Name2 = { + 'prop': 'name2', + 'type': fieldType.ENUM, + 'subtype': fieldType.STRING, + 'name': 'Generator type', + 'description': '', + 'default': '$$TreeGenerator', + 'items': generatorTypeList + } + Unk_Int1 = { + 'prop': 'int1', + 'type': fieldType.INT, + 'name': 'Unk. 1', + 'description': '', + 'default': 0 + } + Unk_Int2 = { + 'prop': 'int2', + 'type': fieldType.INT, + 'name': 'Unk. 2', + 'description': '', + 'default': 0 + } + Unk_List = { + 'prop': 'list1', + 'type': fieldType.LIST, + 'name': 'Unk. params', + 'description': '', + } + +def getClassDefByType(blockNum): + zclass = None + if blockNum == 1: + zclass = b_1 + elif blockNum == 2: + zclass = b_2 + # elif blockNum == 3: + # zclass = b_3 + elif blockNum == 4: + zclass = b_4 + elif blockNum == 5: + zclass = b_5 + elif blockNum == 6: + zclass = b_6 + elif blockNum == 7: + zclass = b_7 + # elif blockNum == 8: + # zclass = b_8 + elif blockNum == 9: + zclass = b_9 + elif blockNum == 10: + zclass = b_10 + elif blockNum == 11: + zclass = b_11 + elif blockNum == 12: + zclass = b_12 + elif blockNum == 13: + zclass = b_13 + elif blockNum == 14: + zclass = b_14 + elif blockNum == 15: + zclass = b_15 + elif blockNum == 16: + zclass = b_16 + elif blockNum == 17: + zclass = b_17 + elif blockNum == 18: + zclass = b_18 + elif blockNum == 20: + zclass = b_20 + elif blockNum == 21: + zclass = b_21 + elif blockNum == 22: + zclass = b_22 + elif blockNum == 23: + zclass = b_23 + elif blockNum == 24: + zclass = b_24 + elif blockNum == 25: + zclass = b_25 + elif blockNum == 26: + zclass = b_26 + elif blockNum == 27: + zclass = b_27 + elif blockNum == 28: + zclass = b_28 + elif blockNum == 29: + zclass = b_29 + elif blockNum == 30: + zclass = b_30 + elif blockNum == 31: + zclass = b_31 + elif blockNum == 33: + zclass = b_33 + elif blockNum == 34: + zclass = b_34 + elif blockNum == 35: + zclass = b_35 + elif blockNum == 36: + zclass = b_36 + elif blockNum == 37: + zclass = b_37 + elif blockNum == 39: + zclass = b_39 + elif blockNum == 40: + zclass = b_40 + return zclass + +_classes = [ + ActiveBlock, + FloatBlock, + TextureBlock, + MaskfileBlock, + MaterialBlock, + ResBlock +] + +def register(): + for cls in _classes: + bpy.utils.register_class(cls) + +def unregister(): + for cls in _classes[::-1]: #reversed + bpy.utils.unregister_class(cls) diff --git a/src/2.80/addons/b3d_tools/b3d/classes.py b/src/2.80/addons/b3d_tools/b3d/classes.py new file mode 100644 index 0000000..3a9536f --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/classes.py @@ -0,0 +1,477 @@ + +import bpy + +from bpy.props import (StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + FloatVectorProperty, + CollectionProperty + ) + +from .class_descr import ( + fieldType, + FloatBlock +) + +from .class_descr import ( + b_1, + b_2, + # b_3, + b_4, + b_5, + b_6, + b_7, + # b_8, + b_9, + b_10, + b_11, + b_12, + b_13, + b_14, + b_15, + b_16, + b_17, + b_18, + b_20, + b_21, + b_22, + b_23, + b_24, + b_25, + b_26, + b_27, + b_28, + b_29, + b_30, + b_31, + b_33, + b_34, + b_35, + b_36, + b_37, + b_39, + b_40, + pfb_8, + pfb_28, + pfb_35, + pvb_8, + pvb_35 +) + +from .common import ( + resMaterialsCallback, + spacesCallback, + referenceablesCallback, + roomsCallback, + modulesCallback, + getMytoolBlockNameByClass +) + + +def setCustObjValue(subtype, bname, pname): + def callback_func(self, context): + + mytool = context.scene.my_tool + result = getattr(getattr(mytool, bname), '{}_enum'.format(pname)) + if subtype == fieldType.INT: + result = int(result) + elif subtype == fieldType.FLOAT: + result = float(result) + elif subtype == fieldType.STRING: + result = str(result) + + setattr( + bpy.context.object, + '["{}"]'.format(pname), + result + ) + + return callback_func + +def createTypeClass(zclass, multipleEdit = True): + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass, multipleEdit) + + block_num = zclass.__name__.split('_')[1] + attributes = { + '__annotations__' : { + + } + } + for attr in attrs: + obj = zclass.__dict__[attr] + pname = obj['prop'] + prop = None + + if multipleEdit: # lock switches only for multiple edit + lockProp = BoolProperty( + name = "On./Off.", + description = "Enable/Disable param for editing", + default = True + ) + + if obj['type'] == fieldType.STRING \ + or obj['type'] == fieldType.COORD \ + or obj['type'] == fieldType.RAD \ + or obj['type'] == fieldType.FLOAT \ + or obj['type'] == fieldType.INT \ + or obj['type'] == fieldType.LIST: + + if multipleEdit: # lock switches only for multiple edit + attributes['__annotations__']["show_"+pname] = lockProp + + if obj['type'] == fieldType.STRING and multipleEdit: + prop = StringProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'], + maxlen = 32 + ) + + elif obj['type'] == fieldType.COORD and multipleEdit: + prop = FloatVectorProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'] + ) + + elif (obj['type'] == fieldType.RAD or obj['type'] == fieldType.FLOAT) and multipleEdit: + prop = FloatProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'] + ) + + elif obj['type'] == fieldType.INT and multipleEdit: + prop = IntProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'] + ) + + elif obj['type'] == fieldType.LIST: + prop = CollectionProperty( + name = obj['name'], + description = obj['description'], + type = FloatBlock + ) + + attributes['__annotations__'][pname] = prop + + elif obj['type'] == fieldType.ENUM \ + or obj['type'] == fieldType.ENUM_DYN: + + + if multipleEdit: # lock switches only for multiple edit + attributes['__annotations__']["show_"+pname] = lockProp + + enum_callback = None + subtype = obj.get('subtype') + + if obj.get('callback') == fieldType.SPACE_NAME: + enum_callback = spacesCallback + elif obj.get('callback') == fieldType.REFERENCEABLE: + enum_callback = referenceablesCallback + elif obj.get('callback') == fieldType.MATERIAL_IND: + enum_callback = resMaterialsCallback + elif obj.get('callback') == fieldType.ROOM: + enum_callback = roomsCallback(bname, '{}_res'.format(pname)) + elif obj.get('callback') == fieldType.RES_MODULE: + enum_callback = modulesCallback + + if obj['type'] == fieldType.ENUM: + prop = None + prop_enum = None + prop_switch = None + + prop_switch = BoolProperty( + name = 'Use dropdown', + description = 'Dropdown selection', + default = False + ) + + prop_enum = EnumProperty( + name = obj['name'], + description = obj['description'], + items = obj['items'], + default = obj['default'], + update = setCustObjValue(subtype, bname, pname) + ) + + if multipleEdit: + if subtype == fieldType.STRING: + prop = StringProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'], + maxlen = 32 + ) + elif subtype == fieldType.INT: + prop = IntProperty( + name = obj['name'], + description = obj['description'], + default = obj['default'] + ) + + attributes['__annotations__']['{}_switch'.format(pname)] = prop_switch + attributes['__annotations__']['{}_enum'.format(pname)] = prop_enum + + if multipleEdit: + attributes['__annotations__']['{}'.format(pname)] = prop + + elif obj['type'] == fieldType.ENUM_DYN: + # prop = None + prop_enum = None + prop_switch = None + + prop_switch = BoolProperty( + name = 'Use dropdown', + description = 'Dropdown selection', + default = False + ) + + prop_enum = EnumProperty( + name = obj['name'], + description = obj['description'], + items = enum_callback, + update = setCustObjValue(subtype, bname, pname) + ) + + + if multipleEdit: + if subtype == fieldType.STRING: + prop = StringProperty( + name = obj['name'], + description = obj['description'], + maxlen = 32 + ) + elif subtype == fieldType.INT: + prop = IntProperty( + name = obj['name'], + description = obj['description'] + ) + + attributes['__annotations__']['{}_switch'.format(pname)] = prop_switch + attributes['__annotations__']['{}_enum'.format(pname)] = prop_enum + + if multipleEdit: + attributes['__annotations__']['{}'.format(pname)] = prop + + elif obj['type'] == fieldType.V_FORMAT: # currently only available in vertex edit + + attributes['__annotations__']["show_{}".format(pname)] = lockProp + + prop1 = BoolProperty( + name = 'Triangulation offset', + description = 'Order in which vertexes are read depends on that', + default = True + ) + attributes['__annotations__']['{}_{}'.format(pname, 'triang_offset')] = prop1 + + prop2 = BoolProperty( + name = 'Use UV', + description = 'If active, writes UV during export.', + default = True + ) + attributes['__annotations__']['{}_{}'.format(pname, 'use_uvs')] = prop2 + + prop3 = BoolProperty( + name = 'Use normals', + description = 'If active, writes normal during export.', + default = True + ) + attributes['__annotations__']['{}_{}'.format(pname, 'use_normals')] = prop3 + + prop4 = BoolProperty( + name = 'Normal switch', + description = 'If active, use for en(dis)abling normals. If not active use for common normals. Is ignored if "Use normals" is inactive', + default = True + ) + attributes['__annotations__']['{}_{}'.format(pname, 'normal_flag')] = prop4 + + if multipleEdit: + newclass = type("{}_gen".format(zclass.__name__), (bpy.types.PropertyGroup,), attributes) + else: + newclass = type("s_{}_gen".format(zclass.__name__), (bpy.types.PropertyGroup,), attributes) + return newclass + + +perFaceBlock_8 = createTypeClass(pfb_8) +perFaceBlock_28 = createTypeClass(pfb_28) +perFaceBlock_35 = createTypeClass(pfb_35) + +perVertBlock_8 = createTypeClass(pvb_8) +perVertBlock_35 = createTypeClass(pvb_35) + +s_block_1 = createTypeClass(b_1, False) +s_block_2 = createTypeClass(b_2, False) +# s_block_3 = createTypeClass(b_3, False) +s_block_4 = createTypeClass(b_4, False) +s_block_5 = createTypeClass(b_5, False) +s_block_6 = createTypeClass(b_6, False) +s_block_7 = createTypeClass(b_7, False) +# s_block_8 = createTypeClass(b_8, False) +s_block_9 = createTypeClass(b_9, False) +s_block_10 = createTypeClass(b_10, False) +s_block_11 = createTypeClass(b_11, False) +s_block_12 = createTypeClass(b_12, False) +s_block_13 = createTypeClass(b_13, False) +s_block_14 = createTypeClass(b_14, False) +s_block_15 = createTypeClass(b_15, False) +s_block_16 = createTypeClass(b_16, False) +s_block_17 = createTypeClass(b_17, False) +s_block_18 = createTypeClass(b_18, False) +s_block_20 = createTypeClass(b_20, False) +s_block_21 = createTypeClass(b_21, False) +s_block_22 = createTypeClass(b_22, False) +s_block_23 = createTypeClass(b_23, False) +s_block_24 = createTypeClass(b_24, False) +s_block_25 = createTypeClass(b_25, False) +s_block_26 = createTypeClass(b_26, False) +s_block_27 = createTypeClass(b_27, False) +s_block_28 = createTypeClass(b_28, False) +s_block_29 = createTypeClass(b_29, False) +s_block_30 = createTypeClass(b_30, False) +s_block_31 = createTypeClass(b_31, False) +s_block_33 = createTypeClass(b_33, False) +s_block_34 = createTypeClass(b_34, False) +s_block_35 = createTypeClass(b_35, False) +s_block_36 = createTypeClass(b_36, False) +s_block_37 = createTypeClass(b_37, False) +s_block_39 = createTypeClass(b_39, False) +s_block_40 = createTypeClass(b_40, False) + +block_1 = createTypeClass(b_1) +block_2 = createTypeClass(b_2) +# block_3 = createTypeClass(b_3) +block_4 = createTypeClass(b_4) +block_5 = createTypeClass(b_5) +block_6 = createTypeClass(b_6) +block_7 = createTypeClass(b_7) +# block_8 = createTypeClass(b_8) +block_9 = createTypeClass(b_9) +block_10 = createTypeClass(b_10) +block_11 = createTypeClass(b_11) +block_12 = createTypeClass(b_12) +block_13 = createTypeClass(b_13) +block_14 = createTypeClass(b_14) +block_15 = createTypeClass(b_15) +block_16 = createTypeClass(b_16) +block_17 = createTypeClass(b_17) +block_18 = createTypeClass(b_18) +block_20 = createTypeClass(b_20) +block_21 = createTypeClass(b_21) +block_22 = createTypeClass(b_22) +block_23 = createTypeClass(b_23) +block_24 = createTypeClass(b_24) +block_25 = createTypeClass(b_25) +block_26 = createTypeClass(b_26) +block_27 = createTypeClass(b_27) +block_28 = createTypeClass(b_28) +block_29 = createTypeClass(b_29) +block_30 = createTypeClass(b_30) +block_31 = createTypeClass(b_31) +block_33 = createTypeClass(b_33) +block_34 = createTypeClass(b_34) +block_35 = createTypeClass(b_35) +block_36 = createTypeClass(b_36) +block_37 = createTypeClass(b_37) +block_39 = createTypeClass(b_39) +block_40 = createTypeClass(b_40) + +_classes = [ + s_block_1, + s_block_2, + # s_block_3, + s_block_4, + s_block_5, + s_block_6, + s_block_7, + # s_block_8, + s_block_9, + s_block_10, + s_block_11, + s_block_12, + s_block_13, + s_block_14, + s_block_15, + s_block_16, + s_block_17, + s_block_18, + s_block_20, + s_block_21, + s_block_22, + s_block_23, + s_block_24, + s_block_25, + s_block_26, + s_block_27, + s_block_28, + s_block_29, + s_block_30, + s_block_31, + s_block_33, + s_block_34, + s_block_35, + s_block_36, + s_block_37, + s_block_39, + s_block_40, + + block_1, + block_2, + # block_3, + block_4, + block_5, + block_6, + block_7, + # block_8, + block_9, + block_10, + block_11, + block_12, + block_13, + block_14, + block_15, + block_16, + block_17, + block_18, + block_20, + block_21, + block_22, + block_23, + block_24, + block_25, + block_26, + block_27, + block_28, + block_29, + block_30, + block_31, + block_33, + block_34, + block_35, + block_36, + block_37, + block_39, + block_40, + + perFaceBlock_8, + perFaceBlock_28, + perFaceBlock_35, + + perVertBlock_8, + perVertBlock_35 +] + +def register(): + for cls in _classes: + bpy.utils.register_class(cls) + +def unregister(): + for cls in _classes[::-1]: #reversed + bpy.utils.unregister_class(cls) \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/b3d/common.py b/src/2.80/addons/b3d_tools/b3d/common.py new file mode 100644 index 0000000..01c0638 --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/common.py @@ -0,0 +1,429 @@ +import os +import struct +import bpy +import logging +import sys +import re +import time +from mathutils import Vector + +from ..common import log +from ..consts import ( + BLOCK_TYPE, + EMPTY_NAME +) + +def getNonCopyName(name): + reIsCopy = re.compile(r'\.[0-9]*$') + matchInd = reIsCopy.search(name) + result = name + if matchInd: + result = name[:matchInd.span()[0]] + return result + +def isRootObj(obj): + return obj.parent is None and obj.name[-4:] == '.b3d' + +def getRootObj(obj): + result = obj + while not isRootObj(result): + result = result.parent + return result + +def isEmptyName(name): + reIsEmpty = re.compile(r'.*{}.*'.format(EMPTY_NAME)) + return reIsEmpty.search(name) + +def isMeshBlock(obj): + # 18 - for correct transform apply + return obj.get("block_type") is not None \ + and (obj["block_type"]==8 or obj["block_type"]==35\ + or obj["block_type"]==28 \ + # or obj["block_type"]==18 + ) + +def isRefBlock(obj): + return obj.get("block_type") is not None and obj["block_type"]==18 + +def unmaskShort(num): + bits = [int(digit) for digit in bin(num)[2:]] + lzeros = 0 + rzeros = 0 + ones = 0 + if num == 0: + return [0, 0, 0] + for bit in bits: + if bit: + ones+=1 + else: + rzeros+=1 + lzeros = 16 - len(bits) + return [lzeros, ones, rzeros] + + +def readCString(file): + try: + chrs = [] + i = 0 + chrs.append(file.read(1).decode("utf-8")) + while ord(chrs[i]) != 0: + chrs.append(file.read(1).decode("utf-8")) + i += 1 + return "".join(chrs[:-1]) + except TypeError as e: + log.error("Error in readCString. Nothing to read") + return "" + + +class HTMaterial(): + + def __init__(self): + self.prefix = "" + self.path = "" + self.reflect = None #HT1 float + self.specular = None #HT1 float + self.transp = None # float + self.rot = None # float + self.col = None # int + self.tex = None # int + self.ttx = None # int + self.itx = None # int + self.att = None # int + self.msk = None # int + self.power = None # int + self.coord = None # int + self.envId = None # int + self.env = None #scale x,y 2 floats + self.rotPoint = None # 2 floats + self.move = None # 2 floats + self.noz = False # bool + self.nof = False # bool + self.notile = False # bool + self.notileu = False # bool + self.notilev = False # bool + self.alphamirr = False # bool + self.bumpcoord = False # bool + self.usecol = False # bool + self.wave = False # bool + +def getColPropertyByName(colProperty, value): + result = None + for item in colProperty: + if item.value == value: + result = item + break + return result + +def getColPropertyIndexByName(colProperty, value): + result = -1 + for idx, item in enumerate(colProperty): + if item.value == value: + result = idx + break + return result + +def existsColPropertyByName(colProperty, value): + for item in colProperty: + if item.value == value: + return True + return False + +def getMaterialIndexInRES(matName): + resModules = bpy.context.scene.my_tool.resModules + delimiterInd = matName.find("_") + resModuleName = matName[:delimiterInd] + materialName = matName[delimiterInd+1:] + curModule = getColPropertyByName(resModules, resModuleName) + curMaterialInd = getColPropertyIndexByName(curModule.materials, materialName) + return curMaterialInd + + +def createMaterials(resModule, palette, texturePath, imageFormat): + materialList = resModule.materials + + for material in materialList: + createMaterial(resModule, palette, texturePath, material, imageFormat) + +#https://blender.stackexchange.com/questions/118646/add-a-texture-to-an-object-using-python-and-blender-2-8 +def createMaterial(resModule, palette, texturepath, mat, imageFormat): + + textureList = resModule.textures + + newMat = bpy.data.materials.new(name="{}_{}".format(resModule.value, mat.value)) + newMat.use_nodes = True + bsdf = newMat.node_tree.nodes["Principled BSDF"] + + if mat.is_col and int(mat.col) > 0: + R = palette[mat.col-1][0] + G = palette[mat.col-1][1] + B = palette[mat.col-1][2] + texColor = newMat.node_tree.nodes.new("ShaderNodeRGB") + texColor.outputs[0].default_value = hex_to_rgb(R,G,B) + newMat.node_tree.links.new(bsdf.inputs['Base Color'], texColor.outputs['Color']) + + if (mat.is_tex and int(mat.tex) > 0) \ + or (mat.is_ttx and int(mat.ttx) > 0) \ + or (mat.is_itx and int(mat.itx) > 0): + texidx = mat.tex | mat.ttx | mat.itx + path = textureList[texidx-1].value + texImage = newMat.node_tree.nodes.new("ShaderNodeTexImage") + texImage.image = bpy.data.images.load(os.path.join(texturepath, "{}.{}".format(path, imageFormat))) + newMat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color']) + + +#https://blender.stackexchange.com/questions/158896/how-set-hex-in-rgb-node-python +def srgb_to_linearrgb(c): + if c < 0: return 0 + elif c < 0.04045: return c/12.92 + else: return ((c+0.055)/1.055)**2.4 + +def hex_to_rgb(r,g,b,alpha=1): + return tuple([srgb_to_linearrgb(c/0xff) for c in (r,g,b)] + [alpha]) + + +def getUsedVerticesAndTransform(vertices, faces): + indices = set() + oldNewTransf = {} + newOldTransf = {} + newVertexes = [] + newFaces = [] + for face in faces: + for idx in face: + indices.add(idx) + indices = sorted(indices) + for idx in indices: + oldNewTransf[idx] = len(newVertexes) + newOldTransf[len(newVertexes)] = idx + newVertexes.append(vertices[idx]) + newFaces = getUsedFaces(faces, oldNewTransf) + + return [newVertexes, newFaces, indices, oldNewTransf, newOldTransf] + +def transformVertices(vertices, idxTransf): + newVertices = [] + for idx in vertices: + newVertices.append(idxTransf[idx]) + return newVertices + +def getUsedFaces(faces, idxTransf): + newFaces = [] + for face in faces: + newFace = getUsedFace(face, idxTransf) + newFaces.append(tuple(newFace)) + return newFaces + +def getUserVertices(faces): + indices = set() + for face in faces: + for idx in face: + indices.add(idx) + return list(indices) + + +def getUsedFace(face, idxTransf): + newFace = [] + for idx in face: + newFace.append(idxTransf[idx]) + return newFace + + +def getPolygonsBySelectedVertices(obj): + data = obj.data + # data = bpy.context.object.data + selectedPolygons = [] + for f in data.polygons: + s = True + for v in f.vertices: + if not data.vertices[v].select: + s = False + break + if s: + selectedPolygons.append(f) + return selectedPolygons + +def getSelectedVertices(obj): + data = obj.data + # data = bpy.context.object.data + selectedVertices = [] + for v in data.vertices: + if v.select: + selectedVertices.append(v) + + return selectedVertices + + +def getAllChildren(obj): + allChildren = [] + currentObjs = [obj] + while(1): + nextChildren = [] + if len(currentObjs) > 0: + for obj in currentObjs: + nextChildren.extend(obj.children) + currentObjs = nextChildren + allChildren.extend(currentObjs) + else: + break + return allChildren + +def getMytoolBlockNameByClass(zclass, multipleClass = True): + bname = '' + btype, bnum = zclass.__name__.split('_') + if btype == 'b': + if multipleClass: + bname = 'block{}'.format(bnum) + else: + bname = 'sBlock{}'.format(bnum) + elif btype == 'pfb': + bname = 'perFaceBlock{}'.format(bnum) + elif btype == 'pvb': + bname = 'perVertBlock{}'.format(bnum) + + return [bname, bnum] + + +def getMytoolBlockName(btype, bnum, multipleClass = False): + bname = '' + if btype == 'b': + if multipleClass: + bname = 'block{}'.format(bnum) + else: + bname = 'sBlock{}'.format(bnum) + elif btype == 'pfb': + bname = 'perFaceBlock{}'.format(bnum) + elif btype == 'pvb': + bname = 'perVertBlock{}'.format(bnum) + + return bname + +def getPythagorLength(p1, p2): + return (sum(map(lambda xx,yy : (xx-yy)**2,p1,p2)))**0.5 + +# https://b3d.interplanety.org/en/how-to-calculate-the-bounding-sphere-for-selected-objects/ +def getMultObjBoundingSphere(objnTransfs, mode='BBOX'): + # return the bounding sphere center and radius for objects (in global coordinates) + # if not isinstance(objects, list): + # objects = [objects] + points_co_global = [] + # if mode == 'GEOMETRY': + # # GEOMETRY - by all vertices/points - more precis, more slow + # for obj in objects: + # points_co_global.extend([obj.matrix_world @ vertex.co for vertex in obj.data.vertices]) + if mode == 'BBOX': + # BBOX - by object bounding boxes - less precis, quick + for objnTransf in objnTransfs: + obj = bpy.data.objects[objnTransf['obj']] + transf = bpy.data.objects[objnTransf['transf']] + points_co_global.extend([transf.matrix_world @ Vector(bbox) for bbox in obj.bound_box]) + + def get_center(l): + return (max(l) + min(l)) / 2 if l else 0.0 + + x, y, z = [[point_co[i] for point_co in points_co_global] for i in range(3)] + b_sphere_center = Vector([get_center(axis) for axis in [x, y, z]]) if (x and y and z) else None + b_sphere_radius = max(((point - b_sphere_center) for point in points_co_global)) if b_sphere_center else None + return [b_sphere_center, b_sphere_radius.length] + +# https://blender.stackexchange.com/questions/62040/get-center-of-geometry-of-an-object +def getSingleCoundingSphere(obj, local = False): + center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector()) + p1 = obj.bound_box[0] + if not local: + center = obj.matrix_world @ center + p1 = obj.matrix_world @ Vector(obj.bound_box[0]) + rad = getPythagorLength(center, p1) + return [center, rad] + + +def recalcToLocalCoord(center, vertexes): + newVertexes = [] + for vert in vertexes: + newVert = [0.0,0.0,0.0] + newVert[0] = vert[0] - center[0] + newVert[1] = vert[1] - center[1] + newVert[2] = vert[2] - center[2] + newVertexes.append(newVert) + + return newVertexes + +def getCenterCoord(vertices): + if len(vertices) == 0: + return (0.0, 0.0, 0.0) + x = [p[0] for p in vertices] + y = [p[1] for p in vertices] + z = [p[2] for p in vertices] + centroid = (sum(x) / len(vertices), sum(y) / len(vertices), sum(z) / len(vertices)) + + return centroid + +def getLevelGroup(obj): + parent = obj.parent + # log.debug(parent) + if parent is None: + return 0 + if parent.get(BLOCK_TYPE) == 444: + return int(parent.name[6]) #GROUP_n + return 0 + +def referenceablesCallback(self, context): + + mytool = context.scene.my_tool + rootObj = getRootObj(context.object) + + referenceables = [cn for cn in rootObj.children if cn.get(BLOCK_TYPE) != 24] + + enumProperties = [(cn.name, cn.name, "") for i, cn in enumerate(referenceables)] + + return enumProperties + +def spacesCallback(self, context): + + mytool = context.scene.my_tool + rootObj = getRootObj(context.object) + + spaces = [cn for cn in bpy.data.objects if cn.get(BLOCK_TYPE) == 24 and getRootObj(cn) == rootObj] + + enumProperties = [(cn.name, cn.name, "") for i, cn in enumerate(spaces)] + + return enumProperties + +def resMaterialsCallback(self, context): + + mytool = context.scene.my_tool + rootObj = getRootObj(context.object) + moduleName = rootObj.name[:-4] + + resModules = mytool.resModules + curModule = getColPropertyByName(resModules, moduleName) + + enumProperties = [(str(i), cn.value, "") for i, cn in enumerate(curModule.materials)] + + return enumProperties + +def roomsCallback(bname, pname): + def callback_func(self, context): + + enumProperties = [] + + mytool = context.scene.my_tool + resModule = context.object.path_resolve('["{}"]'.format(pname)) + + rootObj = bpy.data.objects.get('{}.b3d'.format(resModule)) + if rootObj: + rooms = [cn for cn in rootObj.children if cn.get(BLOCK_TYPE) == 19] + + enumProperties = [(cn.name, cn.name, "") for i, cn in enumerate(rooms)] + + return enumProperties + return callback_func + + +def modulesCallback(self, context): + + mytool = context.scene.my_tool + + modules = [cn for cn in bpy.data.objects if isRootObj(cn)] + + enumProperties = [(cn.name, cn.name, "") for i, cn in enumerate(modules)] + + return enumProperties \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/b3d/export_b3d.py b/src/2.80/addons/b3d_tools/b3d/export_b3d.py new file mode 100644 index 0000000..5cff3bf --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/export_b3d.py @@ -0,0 +1,3062 @@ +import struct +import sys +import timeit +import threading +import pdb +import time + +import bmesh +import bpy +import mathutils +from mathutils import Vector +import os.path +from bpy.props import * +from mathutils import Matrix +from math import radians + +from math import cos +from math import sin + +import bpy_extras.mesh_utils + + +from .class_descr import ( + b_1, + b_2, + # b_3, + b_4, + b_5, + b_6, + b_7, + # b_8, + b_9, + b_10, + b_11, + b_12, + b_13, + b_14, + b_15, + b_16, + b_17, + b_18, + b_20, + b_21, + b_22, + b_23, + b_24, + b_25, + b_26, + b_27, + b_28, + b_29, + b_30, + b_31, + b_33, + b_34, + b_35, + b_36, + b_37, + b_39, + b_40, + pfb_8, + pfb_28, + pfb_35, + pvb_8, + pvb_35 +) + +from .scripts import ( + prop +) + +from ..consts import ( + LEVEL_GROUP, + BLOCK_TYPE +) + +from ..common import ( + log +) + +from .common import ( + getColPropertyByName, + getMaterialIndexInRES, + getNonCopyName, + isRootObj, + getRootObj, + isEmptyName, + getAllChildren, + getLevelGroup, + getMultObjBoundingSphere, + getSingleCoundingSphere +) + +from bpy_extras.io_utils import ( + ImportHelper, + ExportHelper, + #orientation_helper_factory, + path_reference_mode, + axis_conversion, + ) + +from bpy.props import (StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + EnumProperty, + PointerProperty, + ) +from bpy.types import (Panel, + Operator, + AddonPreferences, + PropertyGroup, + ) + +from bpy_extras.image_utils import load_image +from mathutils import Vector +from bpy import context + +RoMesh = True + +def openclose(file): + oc = file.read(4) + if (oc == (b'\x4D\x01\x00\x00')): #Begin Chunk + return 2 + elif oc == (b'\x2B\x02\x00\x00'): #End Chunk + # print('}') + return 0 + elif oc == (b'\xbc\x01\x00\x00'): #Group Chunk + #print('BC01') + return 3 + elif oc == (b'\xde\x00\00\00'): #End Chunks + print ('EOF') + return 1 + else: + print(str(file.tell())) + print (str(oc)) + print('brackets error') + sys.exit() + +def uv_from_vert_first(uv_layer, v): + for l in v.link_loops: + uv_data = l[uv_layer] + return uv_data.uv + return None + +def uv_from_vert_average(uv_layer, v): + uv_average = Vector((0.0, 0.0)) + total = 0.0 + for loop in v.link_loops: + uv_average += loop[uv_layer].uv + total += 1.0 + + if total != 0.0: + return uv_average * (1.0 / total) + else: + return None + + +def MsgBox(label = "", title = "Error", icon = 'ERROR'): + + def draw(self, context): + self.layout.label(label) + + bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) + +def export_pro(file, textures_path): + file = open(myFile_pro+'.pro','w') + + #file.write('TEXTUREFILES ' + str(len(bpy.data.materials)) + '\n') + + #imgs = [] + #for img in bpy.data.images: + # imgs.append(img.name) + + #for material in bpy.data.materials: + # file.write('txr\\' + material.active_texture.name + '.txr' + '\n') + + #file.write('\n') + + file.write('MATERIALS ' + str(len(bpy.data.materials)) + '\n') + + materials = [] + num_tex = 0 + + for material in bpy.data.materials: + materials.append(material.name) + + for i in range(len(materials)): + num_tex = i#imgs.index[i] + #print(str(imgs[i])) + file.write(materials[i] + ' tex ' + str(num_tex + 1) + '\n') + + file.write('TEXTUREFILES ' + str(len(bpy.data.materials)) + '\n') + + imgs = [] + for img in bpy.data.images: + imgs.append(img.name) + + for material in bpy.data.materials: + try: + file.write(textures_path + material.texture_slots[0].texture.image.name[:-3] + 'txr' + '\n') + except: + file.write(textures_path + "error" + '.txr' + '\n') + + file.write('\n') + +def writeName(name, file): + objName = '' + if not isEmptyName(name): + objName = name + nameLen = len(objName) + if nameLen <= 32: + file.write(objName.encode("cp1251")) + file.write(bytearray(b'\00'*(32-nameLen))) + return + +def clone_node(): + b3d_node = bpy.data.objects['b3d'] + bpy.context.scene.objects.active = b3d_node + bpy.ops.object.select_grouped(type='CHILDREN_RECURSIVE') + b3d_node.select = True + bpy.ops.object.duplicate(linked=False, mode='TRANSLATION') + bpy.context.scene.objects.active.name = "b3d_temporary" + +def delete_clone(): + b3d_node = bpy.data.objects['b3d_temporary'] + bpy.context.scene.objects.active = b3d_node + bpy.ops.object.select_grouped(type='CHILDREN_RECURSIVE') + b3d_node.select = True + bpy.ops.object.delete() + +def getRoomName(curResModule, roomString): + result = '' + roomSplit = roomString.split(':') + resModule = roomSplit[0] + roomName = roomSplit[1] + + if curResModule == resModule: + result = roomName + else: + result = roomString + + return result + +borders = {} +currentRes = '' +currentRoomName = '' + + +meshesInEmpty = {} +emptyToMeshKeys = {} +uniqueArrays = {} +createdBounders = {} + +def fillBoundingSphereLists(): + + global meshesInEmpty + global emptyToMeshKeys + global uniqueArrays + global createdBounders + + # Getting 'empty' objects + objs = [cn for cn in bpy.data.objects if cn.get(BLOCK_TYPE) in [8, 28, 35]] + + for obj in objs: + curMeshName = obj.name + curObj = obj.parent + while not isRootObj(curObj): + if curObj.get(BLOCK_TYPE) != 444: + if meshesInEmpty.get(curObj.name) is None: + meshesInEmpty[curObj.name] = [] + meshesInEmpty[curObj.name].append({ + "obj": curMeshName, + "transf": curMeshName + }) + else: + meshesInEmpty[curObj.name].append({ + "obj": curMeshName, + "transf": curMeshName + }) + + curObj = curObj.parent + + #extend with 18 blocks + objs = [cn for cn in bpy.data.objects if cn.get(BLOCK_TYPE) == 18] + + for obj in objs: + referenceableName = obj.get(prop(b_18.Add_Name)) + spaceName = obj.get(prop(b_18.Space_Name)) + curMeshList = meshesInEmpty.get(referenceableName) + # log.debug(curMeshList) + if curMeshList is not None: + + # global coords for 18 blocks parents + globalTransfMeshList = [{ + "obj": cn.get("obj"), + "transf": spaceName + # "transf": cn.get("transf") + } for cn in curMeshList] + + curObj = obj.parent + while not isRootObj(curObj): + if curObj.get(BLOCK_TYPE) != 444: + if meshesInEmpty.get(curObj.name) is None: + meshesInEmpty[curObj.name] = [] + meshesInEmpty[curObj.name].extend(globalTransfMeshList) + else: + meshesInEmpty[curObj.name].extend(globalTransfMeshList) + + curObj = curObj.parent + + # local coords for 18 block itself + curObj = obj + localTransfMeshList = [{ + "obj": cn.get("obj"), + # "transf": spaceName + "transf": cn.get("transf") + } for cn in curMeshList] + + if meshesInEmpty.get(curObj.name) is None: + meshesInEmpty[curObj.name] = [] + meshesInEmpty[curObj.name].extend(localTransfMeshList) + else: + meshesInEmpty[curObj.name].extend(localTransfMeshList) + + # meshesInEmptyStr = [str(cn) for cn in meshesInEmpty.values()] + + # for m in meshesInEmptyStr: + # log.debug(m) + + for emptyName in meshesInEmpty.keys(): + meshesInEmpty[emptyName].sort(key= lambda x: "{}{}".format(str(x["obj"]), str(x["transf"]))) + + key = "||".join(["{}{}".format(str(cn["obj"]), str(cn["transf"])) for cn in meshesInEmpty[emptyName]]) + emptyToMeshKeys[emptyName] = key + if not key in uniqueArrays: + uniqueArrays[key] = meshesInEmpty[emptyName] + + for key in uniqueArrays.keys(): + # objArr = [bpy.data.objects[cn] for cn in uniqueArrays[key]] + # createdBounders[key] = getMultObjBoundingSphere(objArr) + createdBounders[key] = getMultObjBoundingSphere(uniqueArrays[key]) + +def createBorderList(): + + global borders + global currentRes + borders = {} + + borderBlocks = [cn for cn in bpy.data.objects if cn.get(BLOCK_TYPE) == 30 \ + and (cn.get(prop(b_30.ResModule1)) == currentRes or cn.get(prop(b_30.ResModule2)) == currentRes)] + + for bb in borderBlocks: + + border1 = '{}:{}'.format(bb[prop(b_30.ResModule1)], bb[prop(b_30.RoomName1)]) + border2 = '{}:{}'.format(bb[prop(b_30.ResModule2)], bb[prop(b_30.RoomName2)]) + + if not border1 in borders: + borders[border1] = [] + borders[border1].append(bb) + else: + borders[border1].append(bb) + + if not border2 in borders: + borders[border2] = [] + borders[border2].append(bb) + else: + borders[border2].append(bb) + +def writeMeshSphere(file, obj): + + blockType = obj.get(BLOCK_TYPE) + + if blockType in [ + 8,35, + 28,30 + ]: + result = getSingleCoundingSphere(obj) + center = result[0] + rad = result[1] + + file.write(struct.pack("<3f", *center)) + file.write(struct.pack(" 0: + for obj in rChild[:-1]: + if obj.get(BLOCK_TYPE) == 10 or obj.get(BLOCK_TYPE) == 9: + curMaxCnt = 2 + elif obj.get(BLOCK_TYPE) == 21: + curMaxCnt = obj[prop(b_21.GroupCnt)] + exportBlock(obj, False, curLevel, curMaxCnt, [0], {}, file) + + file.write(struct.pack(" 0: + file.write(struct.pack(" 0): + for i, ch in enumerate(blChildren[:-1]): + + exportBlock(ch, False, curLevel+1, curMaxCnt, curGroups, extra, file) + + exportBlock(blChildren[-1], True, curLevel+1, curMaxCnt, curGroups, extra, file) + + else: + + file.write(struct.pack("> 8) + + if format & 0b10: + uvCount += 1 + + if format & 0b100000 and format & 0b10000: + useNormals = True + if format & 0b1: + normalSwitch = True + else: + normalSwitch = False + + l_uvs = {} + for li in poly.loop_indices: + vi = mesh.loops[li].vertex_index + l_uvs[vi] = mesh.uv_layers['UVMap'].data[li].uv + + for i, vert in enumerate(poly.vertices): + file.write(struct.pack("> 8) + + if format & 0b10: + uvCount += 1 + + if format & 0b100000 and format & 0b10000: + useNormals = True + if format & 0b1: + normalSwitch = True + else: + normalSwitch = False + + l_uvs = {} + for li in poly.loop_indices: + vi = mesh.loops[li].vertex_index + l_uvs[vi] = mesh.uv_layers['UVMap'].data[li].uv + + for i, vert in enumerate(poly.vertices): + file.write(struct.pack("> 8 + format = formatRaw & 0xFF + + if format == 1 or format == 2: + normalSwitch = True + elif format == 3: + normalSwitch = False + + + + file.write(struct.pack("> 8 + format = formatRaw & 0xFF + + if format == 1 or format == 2: + normalSwitch = True + elif format == 3: + normalSwitch = False + + file.write(struct.pack(" 0): + for i, ch in enumerate(blChildren[:-1]): + + if len(passToMesh) > 0: + l_extra['passToMesh'] = passToMesh + exportBlock(ch, False, curLevel+1, curMaxCnt, curGroups, l_extra, file) + + if len(passToMesh) > 0: + l_extra['passToMesh'] = passToMesh + exportBlock(blChildren[-1], True, curLevel+1, curMaxCnt, curGroups, l_extra, file) + + file.write(struct.pack(" 0: +# mesh_blocks += 1 + +# if len(flat_faces) > 0: +# mesh_blocks += 1 + +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 180): +# object.rotation_euler[0] = (360-(object.rotation_euler[0] * 180/PI)) * PI/180 + +# if ((object.rotation_euler[1] * 180/PI) < -180): +# object.rotation_euler[1] = (360+(object.rotation_euler[1] * 180/PI)) * PI/180 +# elif ((object.rotation_euler[1] * 180/PI) > 180): +# object.rotation_euler[1] = (360-(object.rotation_euler[1] * 180/PI)) * PI/180 + +# if ((object.rotation_euler[2] * 180/PI) < -180): +# object.rotation_euler[2] = (360+(object.rotation_euler[2] * 180/PI)) * PI/180 +# elif ((object.rotation_euler[2] * 180/PI) > 180): +# object.rotation_euler[2] = (360-(object.rotation_euler[2] * 180/PI)) * PI/180 +# """ + +# #file.write(struct.pack(" 0: +# mesh_blocks += 1 + +# if len(flat_faces) > 0: +# mesh_blocks += 1 + +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 0: +# mesh_blocks += 1 + +# if len(flat_faces) > 0: +# mesh_blocks += 1 + +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 0: +# file.write(struct.pack(" 0: + A = 0b11111111 + else: + A = 0 + colors.extend([B, G, R, A]) + return colors + + +def MSKtoTGA32(filepath): + outpath = os.path.splitext(filepath)[0] + ".tga" + log.info("Converting " + outpath) + indexes = [] + colorsAfter = [] + with open(filepath, "rb") as mskFile: + magic = struct.unpack("<4s", mskFile.read(4)) + width = struct.unpack(" 127): + for i in range(curBit-128): + indexes.append(0) + else: + indexes.extend(list(struct.unpack("<"+str(curBit)+"B", mskFile.read(curBit)))) + curBit = mskFile.read(1) + colorsAfter = paletteToColors(palette, indexes) + header = [None]*12 + header[0] = 0 #IDLength + header[1] = 0 #ColorMapType + header[2] = 2 #ImageType + header[3] = 0 #FirstIndexEntry + header[4] = 0 #ColorMapLength + header[5] = 32 #ColorMapEntrySize + header[6] = 0 #XOrigin + header[7] = 0 #YOrigin + header[8] = width #Width + header[9] = height #Height + header[10] = 32 #PixelDepth + header[11] = 32 #ImageDescriptor + + with open(outpath, "wb") as tgaFile: + headerPack = struct.pack("<3b2hb4h2b", *header) + colorsPack = struct.pack("<"+str(colorsSize*4)+"B", *colorsAfter) + tgaFile.write(headerPack) + tgaFile.write(colorsPack) + + +def TXR565toTGA8888(filepath): + + outpath = os.path.splitext(filepath)[0] + ".tga" + with open(filepath, "rb") as txrFile: + colorsAfter = [] + header = list(struct.unpack("<3b2hb4h2b"+"4s2i", txrFile.read(30))) + header[5] = 32 #ColorMapEntrySize + header[10] = 32 #PixelDepth + width = header[8] + height = header[9] + colorsSize = height*width + colorsBefore = list(struct.unpack("<"+str(colorsSize)+"H", txrFile.read(colorsSize*2))) + + footerIdentifier = txrFile.read(4) + footerSize = struct.unpack("> Runmask[2]) << (8-Runmask[1]) + G = ((color & Gmsk) >> Gunmask[2]) << (8-Gunmask[1]) + B = ((color & Bmsk) >> Bunmask[2]) << (8-Bunmask[1]) + A = ((color & Amsk) >> Aunmask[2]) << (8-Aunmask[1]) + colorsAfter.extend([B, G, R, A]) + else: + for color in colorsBefore: + R = ((color & Rmsk) >> Runmask[2]) << (8-Runmask[1]) + G = ((color & Gmsk) >> Gunmask[2]) << (8-Gunmask[1]) + B = ((color & Bmsk) >> Bunmask[2]) << (8-Bunmask[1]) + if (R | B | G) > 0: + A = 0b11111111 + else: + A = 0 + colorsAfter.extend([B, G, R, A]) + footer[0] = 0xFF000000 + footer[1] = 0x00FF0000 + footer[2] = 0x0000FF00 + footer[3] = 0x000000FF + with open(outpath, "wb") as tgaFile: + headerPack = struct.pack("<3b2hb4h2b"+"4s2i", *header) + colorsPack = struct.pack("<"+str(colorsSize*4)+"B", *colorsAfter) + footerPack = struct.pack("<4I"+str(footerSize-16)+"B", *footer) + tgaFile.write(headerPack) + tgaFile.write(colorsPack) + tgaFile.write(footerIdentifier) + tgaFile.write(struct.pack(" 0: + A = 0b11111111 + else: + A = 0 + colorsAfter.extend([B, G, R, A]) + + with open(outpath, "wb") as tgaFile: + headerPack = struct.pack("<3b2hb4h2b", *header) + colorsPack = struct.pack("<"+str(colorsSize*4)+"B", *colorsAfter) + tgaFile.write(headerPack) + tgaFile.write(colorsPack) + + return + +def TXRtoTGA32(filepath): + outpath = os.path.splitext(filepath)[0] + ".tga" + log.info("Converting " + outpath) + imageType = "" + with open(filepath, "rb") as txrFile: + txrFile.seek(2, 0) + imageType = struct.unpack("> 3) << 11) | ((G >> 2) << 5) | (B >> 3) + colorsAfter.append(color) + footer = tgaFile.read() + with open(outpath, "wb") as tgaFile: + headerPack = struct.pack("<3b2hb4h2b"+"4s2i", *header) + colorsPack = struct.pack("<"+str(colorsSize)+"H", *colorsAfter) + tgaFile.write(headerPack) + tgaFile.write(colorsPack) + tgaFile.write(footer) + + return \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/b3d/import_b3d.py b/src/2.80/addons/b3d_tools/b3d/import_b3d.py new file mode 100644 index 0000000..843eb36 --- /dev/null +++ b/src/2.80/addons/b3d_tools/b3d/import_b3d.py @@ -0,0 +1,2725 @@ +import enum +from hashlib import new +import struct +import sys +import time +import timeit +import datetime +import threading +import pdb +import logging +from pathlib import Path + +from .class_descr import ( + b_1, + b_2, + # b_3, + b_4, + b_5, + b_6, + b_7, + # b_8, + b_9, + b_10, + b_11, + b_12, + b_13, + b_14, + b_15, + b_16, + b_17, + b_18, + b_20, + b_21, + b_22, + b_23, + b_24, + b_25, + b_26, + b_27, + b_28, + b_29, + b_30, + b_31, + b_33, + b_34, + b_35, + b_36, + b_37, + b_39, + b_40, + pfb_8, + pfb_28, + pfb_35, + pvb_8, + pvb_35 +) + +from ..consts import ( + EMPTY_NAME, + BLOCK_TYPE, + BORDER_COLLECTION +) + +from .scripts import ( + prop, + createCustomAttribute +) + + +from .imghelp import TXRtoTGA32 +from .imghelp import parsePLM +from .common import ( + createMaterials, + getColPropertyByName, + getColPropertyIndexByName, + getUsedFaces, + getUsedFace, + getUsedVerticesAndTransform, + getUserVertices, + readCString, + transformVertices, + getPolygonsBySelectedVertices, + recalcToLocalCoord, + getCenterCoord +) + +import bpy +import mathutils +import os.path +import os +from threading import Lock +from bpy.props import * +from bpy_extras.image_utils import load_image +from ast import literal_eval as make_tuple + +from math import sqrt +from math import atan2 + +import re + +import bmesh + +from ..common import log + + +def thread_import_b3d(self, files, context): + for b3dfile in files: + filepath = os.path.join(self.directory, b3dfile.name) + + print('Importing file', filepath) + t = time.mktime(datetime.datetime.now().timetuple()) + with open(filepath, 'rb') as file: + read(file, context, self, filepath) + t = time.mktime(datetime.datetime.now().timetuple()) - t + print('Finished importing in', t, 'seconds') + +class ChunkType(enum.Enum): + END_CHUNK = 0 + END_CHUNKS = 1 + BEGIN_CHUNK = 2 + GROUP_CHUNK = 3 + +def openclose(file): + oc = file.read(4) + if (oc == (b'\x4D\x01\x00\x00')): # Begin_Chunk(111) + return ChunkType.BEGIN_CHUNK + elif oc == (b'\x2B\x02\x00\x00'): # End_Chunk(555) + return ChunkType.END_CHUNK + elif oc == (b'\xbc\x01\x00\x00'): # Group_Chunk(444) + return ChunkType.GROUP_CHUNK + elif oc == (b'\xde\x00\00\00'): # End_Chunks(222) + return ChunkType.END_CHUNKS + else: + raise Exception() + +def onebyte(file): + return (file.read(1)) + +def readName(file): + objName = file.read(32) + if (objName[0] == 0): + objName = EMPTY_NAME + #objname = "Untitled_0x" + str(hex(pos-36)) + else: + objName = (objName.decode("cp1251").rstrip('\0')) + return objName + + +def Triangulate(faces): + faces_new = [] + for t in range(len(faces)-2): + faces_new.extend([faces[t],faces[t+1],faces[t+2]]) + # if t%2 ==0: + # faces_new.extend([faces[t],faces[t+1],faces[t+2]]) + # else: + # faces_new.extend([faces[t+2],faces[t+1],faces[t]]) + + + # if ((format == 0) or (format == 16) or (format == 1)): + # faces_new.extend([faces[t+2],faces[t+1],faces[0]]) + # else: + # faces_new.extend([faces[t+2],faces[t+1],faces[t]]) + return faces_new + + +def MakePolyOk(faces): + faces1 = [] + face = [] + for j in range(len(faces)): + if (j%2 == 0): + faces1.append(faces[j]) + else: + face.append(faces[j]) + + faces = face + faces1.reverse() + faces.extend(faces1) + return faces + +def parse_plm(input_file, color_format): + with open (input_file,'r+b') as file: + struct.unpack(' 10 + material_colors.append(str(colors_list[colNum])) + else: + material_colors.append("[]") + + + ##### Параметры материалов (tex %d) ##### + material_textures = [] + + for i in range(materials_num): + texNum_str = str(re.findall(r't\wx\s+\d+', materials[i])) # t\wx - так как помимо tex, ещё бывает ttx + texNum = 0 + if (texNum_str != "[]"): + texNum_final = str(re.findall(r'\d+', texNum_str[2:-2]))[2:-2] + texNum = int(texNum_final) #[5:-2] нужно чтобы убрать ['tex и '], например: ['tex 10'] -> 10 + material_textures.append(texturefiles[texNum-1]) + else: + material_textures.append(material_colors[i]) + + #for k in range(materials_num): + + return material_textures + +def parseRAW(file, context, op, filepath): + + basename1 = os.path.basename(filepath)[:-4] + basename2 = basename1+"_2" + basename1 = basename1+"_1" + + vertexes = [] + faces1 = [] + faces2 = [] + + counter = 0 + + width = 257 + + for i in range(width): + for j in range(width): + vertexes.append((i*10,j*10,struct.unpack(' 0 and os.path.exists(self.res_location): + resPath = self.res_location + else: + resPath = os.path.join(noextPath + ".res") + + res_basepath = os.path.dirname(resPath) + res_basename = os.path.basename(resPath)[:-4] #cut extension + + commonResPath = bpy.context.preferences.addons['b3d_tools'].preferences.COMMON_RES_Path + + resIndex = getColPropertyIndexByName(resModules, res_basename) + resModule = None + print(resIndex) + if resIndex >= 0: + resModule = resModules[resIndex] + if resModule: + #delete old materials + for m in [cn.value for cn in resModule.materials]: + mat = bpy.data.materials.get("{}_{}".format(res_basename, m)) + if mat: + bpy.data.materials.remove(mat) + #delete RES module + print("Removing resModule " + str(resIndex)) + resModules.remove(resIndex) + commonResIndex = getColPropertyIndexByName(resModules, "common") + resModules.remove(commonResIndex) + + resModule = resModules.add() + resModule.value = res_basename + + if self.to_import_textures: + if self.to_unpack_res: + unpackRES(resModule, resPath, True) + else: + unpackRES(resModule, resPath, False) + + if self.to_import_textures and not os.path.exists(commonResPath): + self.report({'ERROR'}, "Common.res path is wrong or is not set. Textures weren't imported! Please, set path to Common.res in addon preferences.") + self.to_import_textures = False + + if self.to_import_textures and os.path.exists(commonResPath): + # commonPath = os.path.join(r"D:\_PROJECTS\DB2\Hard Truck 2", r"COMMON") + # commonResPath = os.path.join(commonPath, r"COMMON.RES") + commonPath = os.path.join(os.path.dirname(commonResPath)) + palettePath = os.path.join(commonPath, r"COMMON_unpack/PALETTEFILES/common.plm") + unpackPath = os.path.join(res_basepath, res_basename + r"_unpack") + materialsPath = os.path.join(unpackPath, r"MATERIALS", "MATERIALS.txt") + colorsPath = os.path.join(unpackPath, r"COLORS", "COLORS.txt") + texturePath = os.path.join(unpackPath, r"TEXTUREFILES") + maskfiles = os.path.join(unpackPath, r"MASKFILES") + + #1. Получить общую для большинства палитру Common.plm + if os.path.exists(commonResPath): + # commonResModule = getColPropertyByName(resModules, "common") + # if not commonResModule: + commonResModule = resModules.add() + commonResModule.value = "common" + unpackRES(commonResModule, commonResPath) + commonPalette = parsePLM(palettePath) + else: + log.warning("Failed to unpack COMMON.RES") + + #2. Распаковка .RES и конвертация .txr и .msk в .tga 32bit + # txrFolder = os.path.join(texturePath, "txr") + if self.to_convert_txr: + folder_content = os.listdir(texturePath) + reTXR = re.compile(r'\.txr') + for path in folder_content: + fullpath = os.path.join(texturePath, path) + if reTXR.search(path): + TXRtoTGA32(fullpath) + + #3. Парсинг и добавление материалов + createMaterials(resModule, commonPalette, texturePath, self.textures_format) + +def read(file, context, self, filepath): + if file.read(3) == b'b3d': + log.info("correct file") + else: + log.error("b3d error") + + commonResPath = bpy.context.preferences.addons['b3d_tools'].preferences.COMMON_RES_Path + + scene = context.scene + mytool = scene.my_tool + + #skip to materials list + file.seek(21,1) + Imgs = [] + math = [] + materials = [] + + resModules = getattr(mytool, "resModules") + + importTextures(filepath, resModules, self) + + noextPath, ext = os.path.splitext(filepath) + basepath = os.path.dirname(filepath) + basename = os.path.basename(filepath)[:-4] #cut extension + + + #Пути + resPath = '' + if len(self.res_location) > 0 and os.path.exists(self.res_location): + resPath = self.res_location + else: + resPath = os.path.join(noextPath + ".res") + + res_basepath = os.path.dirname(resPath) + res_basename = os.path.basename(resPath)[:-4] #cut extension + + resModule = getColPropertyByName(resModules, res_basename) + + # commonPath = os.path.join(bpy.context.preferences.addons['import_b3d'].preferences.COMMON_RES_Path, r"COMMON") + + blocksToImport = self.blocks_to_import + + usedBlocks = {} + for block in blocksToImport: + usedBlocks[block.name] = block.state + + # Parsing b3d + material_textures = [] + materials_count = struct.unpack(' 0: + + levelGroups[lvl] +=1 + parentObj = context.scene.objects.get(objString[-1]) + + if parentObj.name[:6] == 'GROUP_': + del objString[-1] + parentObj = context.scene.objects.get(objString[-1]) + + if levelGroups[lvl] > 0: + + if len(parentObj.children) == 0: + groupObjName = 'GROUP_{}'.format(0) + + b3dObj = bpy.data.objects.new(groupObjName, None) + b3dObj[BLOCK_TYPE] = 444 + + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) + # objString.append(b3dObj.name) + + groupObjName = 'GROUP_{}'.format(levelGroups[lvl]) + + b3dObj = bpy.data.objects.new(groupObjName, None) + b3dObj[BLOCK_TYPE] = 444 + + b3dObj.parent = context.scene.objects.get(objString[-1]) + context.collection.objects.link(b3dObj) + + objString.append(b3dObj.name) + + continue + elif ex == ChunkType.BEGIN_CHUNK: + lvl+=1 + if len(levelGroups) <= lvl: + for i in range(lvl+1-len(levelGroups)): + levelGroups.append(0) + + t1 = time.perf_counter() + + parentObj = context.scene.objects.get(objString[-1]) + + if parentObj.get(BLOCK_TYPE) in [9, 10, 21]: + + groupObjName = 'GROUP_{}'.format(0) + + b3dObj = bpy.data.objects.new(groupObjName, None) + b3dObj[BLOCK_TYPE] = 444 + + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) + + objString.append(b3dObj.name) + + objString.append("") + + onlyName = readName(file) + type = struct.unpack("> 8) #ah + triangulateOffset = format & 0b10000000 + + if format & 0b10: + uvCount += 1 + + poly_block_uvs = [{}] + + poly_block_len = len(poly_block_uvs) + + for i in range(uvCount - poly_block_len): + poly_block_uvs.append({}) + + unkF = struct.unpack(" len(overriden_uvs): + for i in range(len(poly_block_uvs)-len(overriden_uvs)): + overriden_uvs.append([]) + + #Triangulating faces + for t in range(len(faces)-2): + if not triangulateOffset: + if t%2 == 0: + faces_new.append([faces[t-1],faces[t],faces[t+1]]) + else: + faces_new.append([faces[t],faces[t+1],faces[t+2]]) + else: + if t%2 == 0: + faces_new.append([faces[t],faces[t+1],faces[t+2]]) + else: + faces_new.append([faces[t],faces[t+2],faces[t+1]]) + + for u, over_uv in enumerate(poly_block_uvs): + if over_uv: + overriden_uvs[u].append((over_uv[faces_new[t][0]], over_uv[faces_new[t][1]], over_uv[faces_new[t][2]])) + + uv_indexes.append(faces_new[t]) + formats.append(formatRaw) + unkFs.append(unkF) + unkInts.append(unkInt) + + faces_all.extend(faces_new) + + if not usedBlocks[str(type)]: + continue + + b3dMesh = (bpy.data.meshes.new(objName)) + + curVertexes, curFaces, indices, oldNewTransf, newOldTransf = getUsedVerticesAndTransform(vertexes, faces_all) + + curNormals = [] + curNormalsOff = [] + for i in range(len(curVertexes)): + curNormals.append(l_normals[newOldTransf[i]]) + curNormalsOff.append(l_normals_off[newOldTransf[i]]) + + curVertexes = recalcToLocalCoord(bounding_sphere[:3], curVertexes) + + b3dMesh.from_pydata(curVertexes,[],curFaces) + + # Ev = threading.Event() + # Tr = threading.Thread(target=b3dMesh.from_pydata, args = (curVertexes,[],curFaces)) + # Tr.start() + # Ev.set() + # Tr.join() + + # newIndices = getUserVertices(curFaces) + + if len(normals) > 0: + b3dMesh.use_auto_smooth = True + normalList = [] + for i,vert in enumerate(b3dMesh.vertices): + normalList.append(normals[newOldTransf[i]]) + # b3dMesh.vertices[idx].normal = normals[idx] + b3dMesh.normals_split_custom_set_from_vertices(normalList) + + #Setup UV + # customUV = b3dMesh.uv_layers.new() + # customUV.name = "UVmap" + + # for k, texpoly in enumerate(b3dMesh.polygons): + # for j,loop in enumerate(texpoly.loop_indices): + # uvsMesh = (uvs[k][j][0], 1-uvs[k][j][1]) + # customUV.data[loop].uv = uvsMesh + + # for simplicity + # for u, uvMap in enumerate(vertex_block_uvs): + + # customUV = b3dMesh.uv_layers.new() + # customUV.name = "UVmapVert{}".format(u) + # uvsMesh = [] + + # for i, texpoly in enumerate(b3dMesh.polygons): + # for j,loop in enumerate(texpoly.loop_indices): + # uvsMesh = (uvMap[uv_indexes[i][j]][0],1 - uvMap[uv_indexes[i][j]][1]) + # customUV.data[loop].uv = uvsMesh + + for u, uvOver in enumerate(overriden_uvs): + + customUV = b3dMesh.uv_layers.new() + customUV.name = "UVMap" + uvsMesh = [] + + for i, texpoly in enumerate(b3dMesh.polygons): + for j,loop in enumerate(texpoly.loop_indices): + uvsMesh = [uvOver[i][j][0],1 - uvOver[i][j][1]] + customUV.data[loop].uv = uvsMesh + + createCustomAttribute(b3dMesh, formats, pfb_8, pfb_8.Format_Flags) + + # those are usually consts in all objects + # createCustomAttribute(b3dMesh, unkFs, pfb_8, pfb_8.Unk_Float1) + # createCustomAttribute(b3dMesh, unkInts, pfb_8, pfb_8.Unk_Int2) + + # cancel for now, maybe find workaround later + # createCustomAttribute(b3dMesh, curNormalsOff, pvb_8, pvb_8.Normal_Switch) + # createCustomAttribute(b3dMesh, curNormals, pvb_8, pvb_8.Custom_Normal) + + #Create Object + + b3dObj = bpy.data.objects.new(objName, b3dMesh) + b3dObj[BLOCK_TYPE] = type + b3dObj.location = bounding_sphere[0:3] + # b3dObj[prop(b_8.XYZ)] = bounding_sphere[0:3] + # b3dObj[prop(b_8.R)] = bounding_sphere[3] + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) + realName = b3dObj.name + objString[-1] = b3dObj.name + + if self.to_import_textures: + #For assignMaterialByVertices just-in-case + # bpy.ops.object.mode_set(mode = 'OBJECT') + #Set appropriate meaterials + if len(texnums.keys()) > 1: + for texnum in texnums: + mat = bpy.data.materials["{}_{}".format(resModule.value, resModule.materials[int(texnum)].value)] + b3dMesh.materials.append(mat) + lastIndex = len(b3dMesh.materials)-1 + + for vertArr in texnums[texnum]: + newVertArr = getUsedFace(vertArr, oldNewTransf) + # self.lock.acquire() + assignMaterialByVertices(b3dObj, newVertArr, lastIndex) + # self.lock.release() + else: + for texnum in texnums: + mat = bpy.data.materials["{}_{}".format(resModule.value, resModule.materials[int(texnum)].value)] + b3dMesh.materials.append(mat) + + + + elif (type == 9 or type == 22): + + bounding_sphere = struct.unpack("<4f",file.read(16)) + unknown_sphere = struct.unpack("<4f",file.read(16)) + childCnt = struct.unpack(" 16*sys.float_info.epsilon): + z_d = atan2(m12, m22) + x_d = atan2(- m32, var_cy) + y_d = atan2(m31, m33) + # log.debug("MORE than 16") + else: + z_d = atan2(- m21, m11) + x_d = atan2(- m32, var_cy) + y_d = 0 + # log.debug("LESS than 16") + + rot_x = ((x_d * 180) / PI) + rot_y = ((y_d * 180) / PI) + rot_z = ((z_d * 180) / PI) + + + + b3dObj = bpy.data.objects.new(objName, None) + b3dObj[BLOCK_TYPE] = type + b3dObj.rotation_euler[0] = x_d + b3dObj.rotation_euler[1] = y_d + b3dObj.rotation_euler[2] = z_d + b3dObj.location = sp_pos + b3dObj.empty_display_type = 'ARROWS' + b3dObj[prop(b_24.Flag)] = flag + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) + realName = b3dObj.name + objString[-1] = b3dObj.name + + elif (type == 25): #copSiren????/ контейнер + + unknown1 = struct.unpack("<3i",file.read(12)) + + name = readName(file) + unknown_sphere1 = struct.unpack("<3f",file.read(12)) + unknown_sphere2 = struct.unpack("<3f",file.read(12)) + unknown2 = struct.unpack("<5f",file.read(20)) + + if not usedBlocks[str(type)]: + continue + + b3dObj = bpy.data.objects.new(objName, None) + b3dObj[BLOCK_TYPE] = type + b3dObj[prop(b_25.XYZ)] = unknown1 + b3dObj[prop(b_25.Name)] = name + b3dObj[prop(b_25.Unk_XYZ1)] = unknown_sphere1 + b3dObj[prop(b_25.Unk_XYZ2)] = unknown_sphere2 + b3dObj[prop(b_25.Unk_Float1)] = unknown2[0] + b3dObj[prop(b_25.Unk_Float2)] = unknown2[1] + b3dObj[prop(b_25.Unk_Float3)] = unknown2[2] + b3dObj[prop(b_25.Unk_Float4)] = unknown2[3] + b3dObj[prop(b_25.Unk_Float5)] = unknown2[4] + + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) + realName = b3dObj.name + objString[-1] = b3dObj.name + + elif (type == 26): + + bounding_sphere = struct.unpack("<4f",file.read(16)) + + unknown_sphere1 = struct.unpack("<3f",file.read(12)) + unknown_sphere2 = struct.unpack("<3f",file.read(12)) + unknown_sphere3 = struct.unpack("<3f",file.read(12)) + + childCnt = struct.unpack("> 8) + 1 #ah + triangulateOffset = format & 0b10000000 + + unkF = struct.unpack(" len(overriden_uvs): + for i in range(len(poly_block_uvs)-len(overriden_uvs)): + overriden_uvs.append([]) + + #Triangulating faces + for t in range(len(l_faces)-2): + if not triangulateOffset: + if t%2 == 0: + faces_new.append([l_faces[t-1],l_faces[t],l_faces[t+1]]) + else: + faces_new.append([l_faces[t],l_faces[t+1],l_faces[t+2]]) + else: + if t%2 == 0: + faces_new.append([l_faces[t],l_faces[t+1],l_faces[t+2]]) + else: + faces_new.append([l_faces[t],l_faces[t+2],l_faces[t+1]]) + + for u, over_uv in enumerate(poly_block_uvs): + if over_uv: #not empty + overriden_uvs[u].append((over_uv[faces_new[t][0]], over_uv[faces_new[t][1]], over_uv[faces_new[t][2]])) + + # store face indexes for setting uv coords later + uv_indexes.append(faces_new[t]) + formats.append(formatRaw) + unkFs.append(unkF) + unkInts.append(unkInt) + + l_faces_all.extend(faces_new) + + + if not usedBlocks[str(type)]: + continue + + b3dMesh = (bpy.data.meshes.new(objName)) + + l_vertexes = recalcToLocalCoord(bounding_sphere[:3], l_vertexes) + + b3dMesh.from_pydata(l_vertexes,[],l_faces_all) + + # Ev = threading.Event() + # Tr = threading.Thread(target=b3dMesh.from_pydata, args = (l_vertexes,[],l_faces_all)) + # Tr.start() + # Ev.set() + # Tr.join() + + + # for simplicity + # for u, uvMap in enumerate(vertex_block_uvs): + # if len(uvMap): + # customUV = b3dMesh.uv_layers.new() + # customUV.name = "UVmapVert{}".format(u) + # uvsMesh = [] + + # for i, texpoly in enumerate(b3dMesh.polygons): + # for j,loop in enumerate(texpoly.loop_indices): + # uvsMesh = (uvMap[uv_indexes[i][j]][0],1 - uvMap[uv_indexes[i][j]][1]) + # customUV.data[loop].uv = uvsMesh + + for u, uvOver in enumerate(overriden_uvs): + if len(uvOver): + customUV = b3dMesh.uv_layers.new() + customUV.name = "UVMap" + uvsMesh = [] + + for i, texpoly in enumerate(b3dMesh.polygons): + for j,loop in enumerate(texpoly.loop_indices): + uvsMesh = [uvOver[i][j][0],1 - uvOver[i][j][1]] + customUV.data[loop].uv = uvsMesh + + if self.to_import_textures: + #For assignMaterialByVertices just-in-case + # bpy.ops.object.mode_set(mode = 'OBJECT') + #Set appropriate meaterials + if len(texnums.keys()) > 1: + for texnum in texnums: + mat = bpy.data.materials["{}_{}".format(resModule.value, resModule.materials[int(texnum)].value)] + b3dMesh.materials.append(mat) + lastIndex = len(b3dMesh.materials)-1 + + for vertArr in texnums[texnum]: + # self.lock.acquire() + assignMaterialByVertices(b3dObj, vertArr, lastIndex) + # self.lock.release() + else: + for texnum in texnums: + mat = bpy.data.materials["{}_{}".format(resModule.value, resModule.materials[int(texnum)].value)] + b3dMesh.materials.append(mat) + + createCustomAttribute(b3dMesh, formats, pfb_28, pfb_28.Format_Flags) + + # those are usually consts in all objects + # createCustomAttribute(b3dMesh, unkFs, pfb_28, pfb_28.Unk_Float1) + # createCustomAttribute(b3dMesh, unkInts, pfb_28, pfb_28.Unk_Int2) + + b3dObj = bpy.data.objects.new(objName, b3dMesh) + b3dObj[BLOCK_TYPE] = type + # b3dObj[prop(b_28.XYZ)] = bounding_sphere[0:3] + # b3dObj[prop(b_28.R)] = bounding_sphere[3] + b3dObj[prop(b_28.Sprite_Center)] = sprite_center + b3dObj.location = bounding_sphere[0:3] + + b3dObj.parent = parentObj + context.collection.objects.link(b3dObj) #добавляем в сцену обьект + realName = b3dObj.name + objString[-1] = b3dObj.name + + elif (type == 29): + + bounding_sphere = struct.unpack("<4f",file.read(16)) + num0 = struct.unpack(" 0: + f = struct.unpack("<"+str(num0)+"f",file.read(4*num0)) + + childCnt = struct.unpack("> 8) #ah + triangulateOffset = format & 0b10000000 + + if format & 0b10: + uvCount += 1 + + poly_block_uvs = [{}] + + poly_block_len = len(poly_block_uvs) + + for i in range(uvCount - poly_block_len): + poly_block_uvs.append({}) + + unkF = struct.unpack(" len(overriden_uvs): + for i in range(len(poly_block_uvs)-len(overriden_uvs)): + overriden_uvs.append([]) + + for u, over_uv in enumerate(poly_block_uvs): + if over_uv: + overriden_uvs[u].append((over_uv[faces[0]], over_uv[faces[1]], over_uv[faces[2]])) + + # store face indexes for setting uv coords later + uv_indexes.append((faces[0], faces[1], faces[2])) + + #https://docs.blender.org/api/current/bpy.types.bpy_prop_collection.html#bpy.types.bpy_prop_collection + #seq must be uni-dimensional + # normals_set.extend([normals[faces[0]], normals[faces[1]], normals[faces[2]]]) + # normals_set.extend(list(normals[faces[0]])) + # normals_set.extend(list(normals[faces[1]])) + # normals_set.extend(list(normals[faces[2]])) + # intens_set.extend(list(intencities[faces[0]])) + # intens_set.extend(list(intencities[faces[1]])) + # intens_set.extend(list(intencities[faces[2]])) + # intens_set.extend([intencities[faces[0]], intencities[faces[1]], intencities[faces[2]]]) + + if not usedBlocks[str(type)]: + continue + + # log.debug(vertex_block_uvs) + # log.debug(overriden_uvs) + + b3dMesh = (bpy.data.meshes.new(objName)) + curVertexes, curFaces, indices, oldNewTransf, newOldTransf = getUsedVerticesAndTransform(vertexes, faces_all) + + curNormals = [] + curNormalsOff = [] + for i in range(len(curVertexes)): + curNormals.append(l_normals[newOldTransf[i]]) + curNormalsOff.append(l_normals_off[newOldTransf[i]]) + + curVertexes = recalcToLocalCoord(bounding_sphere[:3], curVertexes) + + b3dMesh.from_pydata(curVertexes,[],curFaces) + + # Ev = threading.Event() + # Tr = threading.Thread(target=b3dMesh.from_pydata, args = (curVertexes,[],curFaces)) + # Tr.start() + # Ev.set() + # Tr.join() + + # newIndices = getUserVertices(curFaces) + + # blender generates his own normals, so imported vertex normals doesn't affect + # them too much. + if len(normals) > 0: + b3dMesh.use_auto_smooth = True + normalList = [] + for i,vert in enumerate(b3dMesh.vertices): + normalList.append(normals[newOldTransf[i]]) + # b3dMesh.vertices[idx].normal = normals[idx] + # log.debug(normalList) + b3dMesh.normals_split_custom_set_from_vertices(normalList) + + # if len(normals) > 0: + # for i,idx in enumerate(newIndices): + # b3dMesh.vertices[idx].normal = normals[idx] + + + # for simplicity + # for u, uvMap in enumerate(vertex_block_uvs): + + # customUV = b3dMesh.uv_layers.new() + # customUV.name = "UVmapVert{}".format(u) + # uvsMesh = [] + + # for i, texpoly in enumerate(b3dMesh.polygons): + # for j,loop in enumerate(texpoly.loop_indices): + # uvsMesh = (uvMap[uv_indexes[i][j]][0],1 - uvMap[uv_indexes[i][j]][1]) + # customUV.data[loop].uv = uvsMesh + + for u, uvOver in enumerate(overriden_uvs): + + customUV = b3dMesh.uv_layers.new() + customUV.name = "UVMap" + uvsMesh = [] + + for i, texpoly in enumerate(b3dMesh.polygons): + for j,loop in enumerate(texpoly.loop_indices): + uvsMesh = [uvOver[i][j][0],1 - uvOver[i][j][1]] + customUV.data[loop].uv = uvsMesh + + + # b3dMesh.attributes["my_normal"].data.foreach_set("vector", normals_set) + + createCustomAttribute(b3dMesh, formats, pfb_35, pfb_35.Format_Flags) + # those are usually consts in all objects + # createCustomAttribute(b3dMesh, unkFs, pfb_35, pfb_35.Unk_Float1) + # createCustomAttribute(b3dMesh, unkInts, pfb_35, pfb_35.Unk_Int2) + + # cancel for now, maybe find workaround later + # createCustomAttribute(b3dMesh, curNormalsOff, pvb_35, pvb_35.Normal_Switch) + # createCustomAttribute(b3dMesh, curNormals, pvb_35, pvb_35.Custom_Normal) + + if self.to_import_textures: + mat = bpy.data.materials["{}_{}".format(resModule.value, resModule.materials[int(texNum)].value)] + b3dMesh.materials.append(mat) + + + b3dObj = bpy.data.objects.new(objName, b3dMesh) + b3dObj.parent = parentObj + b3dObj[BLOCK_TYPE] = type + b3dObj.location = bounding_sphere[0:3] + # b3dObj[prop(b_35.XYZ)] = bounding_sphere[0:3] + # b3dObj[prop(b_35.R)] = bounding_sphere[3] + b3dObj[prop(b_35.MType)] = mType + b3dObj[prop(b_35.TexNum)] = texNum + # b3dObj['FType'] = 0 + # try: + # b3dObj['SType'] = b3dObj.parent['SType'] + # except: + # b3dObj['SType'] = 2 + # b3dObj['BType'] = 35 + realName = b3dObj.name + # for face in b3dMesh.polygons: + # face.use_smooth = True + context.collection.objects.link(b3dObj) #добавляем в сцену обьект + objString[-1] = b3dObj.name + + elif (type == 36): + + vertexes = [] + normals = [] + normals_off = [] + uv = [] + vertex_block_uvs = [] + + bounding_sphere = struct.unpack("<4f",file.read(16)) + name1 = readName(file) + name2 = readName(file) + + formatRaw = struct.unpack("> 8 + format = formatRaw & 0xFF + + vertex_block_uvs.append([]) + for i in range(uvCount): + vertex_block_uvs.append([]) + + vertexCount = struct.unpack("> 8 + format = formatRaw & 0xFF + + vertex_block_uvs.append([]) + for i in range(uvCount): + vertex_block_uvs.append([]) + + + vertexCount = struct.unpack(" 0: + + if format == 0: + pass + else: + for i in range(vertexCount): + vertexes.append(struct.unpack("<3f",file.read(12))) + vertex_block_uvs[0].append(struct.unpack("<2f",file.read(8))) + for j in range(uvCount): + vertex_block_uvs[j+1].append(struct.unpack("<2f",file.read(8))) + if format == 1 or format == 2: #Vertex with normals + normals.append(struct.unpack("<3f",file.read(12))) + normals_off.append(1.0) + elif format == 3: #отличается от шаблона 010Editor + normals.append((0.0, 0.0, 0.0)) + normals_off.append(struct.unpack("1): + del objString[-1] + type = grom + elif(line[0:4] == 'MNAM'): + mnam_c = int(line[5]) + type = mnam + + + + + + + + +def assignMaterialByVertices(obj, vertIndexes, matIndex): + bpy.context.view_layer.objects.active = obj + # bpy.ops is slow https://blender.stackexchange.com/questions/2848/why-avoid-bpy-ops#comment11187_2848 + # bpy.ops.object.mode_set(mode = 'EDIT') + # bpy.ops.mesh.select_mode(type= 'VERT') + # bpy.ops.mesh.select_all(action = 'DESELECT') + # bpy.ops.object.mode_set(mode = 'OBJECT') + vert = obj.data.vertices + face = obj.data.polygons + edge = obj.data.edges + for i in face: + i.select=False + for i in edge: + i.select=False + for i in vert: + i.select=False + + for idx in vertIndexes: + obj.data.vertices[idx].select = True + selectedPolygons = getPolygonsBySelectedVertices(bpy.context.view_layer.objects.active) + for poly in selectedPolygons: + poly.material_index = matIndex + +def getRESFolder(filepath): + basename = os.path.basename(filepath) + basepath = os.path.dirname(filepath) + return os.path.join(basepath, "{}_unpack".format(basename[:-4])) + +def saveMaterial(resModule, materialStr): + nameSplit = materialStr.split(" ") + name = nameSplit[0] + params = nameSplit[1:] + + material = resModule.materials.add() + material.value = name + i = 0 + while i < len(params): + paramName = params[i].replace('"', '') + if paramName in ["col", "att", "msk", "power", "coord", "tex", "ttx", "itx"]: + setattr(material, "is_" + paramName, True) + setattr(material, paramName, int(params[i+1])) + i+=1 + elif paramName in ["reflect", "specular", "transp", "rot"]: + setattr(material, paramName, float(params[i+1])) + i+=1 + elif paramName in ["noz", "nof", "notile", "notiveu", "notilev", \ + "alphamirr", "bumpcoord", "usecol", "wave"]: + setattr(material, "is_" + paramName, True) + i+=1 + elif paramName in ["RotPoint", "move"]: + setattr(material, "is_" + paramName, True) + setattr(material, paramName, [float(params[i+1]), float(params[i+2])]) + i+=2 + elif paramName[0:3] == "env": + setattr(material, "is_env", True) + envid = paramName[3:] + setattr(material, "env", [float(params[i+1]), float(params[i+2])]) + i+=2 + if len(envid) > 0: + setattr(material, "envId", int(envid)) + i+=1 + +def saveMaskfile(resModule, maskfileStr): + nameSplit = maskfileStr.split(" ") + rawName = nameSplit[0] + params = nameSplit[1:] + + name = rawName.split("\\")[-1][:-4] + + maskfile = resModule.maskfiles.add() + maskfile.value = name + i = 0 + while i < len(params): + paramName = params[i].replace('"', '') + if paramName == "noload": + setattr(maskfile, "is_noload", True) + else: + someInt = None + try: + someInt = int(someInt) + except: + pass + if someInt: + setattr(maskfile, "is_someint", True) + setattr(maskfile, "someint", someInt) + i+=1 + +def saveTexture(resModule, textueStr): + nameSplit = textueStr.split(" ") + rawName = nameSplit[0] + params = nameSplit[1:] + + name = rawName.split("\\")[-1][:-4] + log.debug(name) + + texture = resModule.textures.add() + texture.value = name + i = 0 + while i < len(params): + paramName = params[i].replace('"', '') + if paramName in ["noload", "bumpcoord", "memfix"]: + setattr(texture, "is_" + paramName, True) + else: + someInt = None + try: + someInt = int(someInt) + except: + pass + if someInt: + setattr(texture, "is_someint", True) + setattr(texture, "someint", someInt) + i+=1 + + +def unpackRES(resModule, filepath, saveOnDisk = True, ): + log.info("Unpacking {}:".format(filepath)) + + # filename, resExtension = os.path.splitext(filepath) + # basename = os.path.basename(filepath)[:-4] + # basepath = os.path.dirname(filepath) + resdir = getRESFolder(filepath) + curfolder = resdir + if not os.path.exists(resdir): + os.mkdir(resdir) + + with open(filepath, "rb") as resFile: + while 1: + category = readCString(resFile) + if(len(category) > 0): + log.info(category) + resSplit = category.split(" ") + id = resSplit[0] + cnt = int(resSplit[1]) + log.info("Reading category {}".format(id)) + log.info("Element count in category is {}.".format(cnt)) + binfilePath = None + if cnt > 0: + elements = [] + log.info("Start processing...") + curfolder = os.path.join(resdir,id) + if id == "COLORS" or id == "MATERIALS" or id == "SOUNDS": + if saveOnDisk: + binfilePath = os.path.join(curfolder, "{}.txt".format(id)) + binfileBase = os.path.dirname(binfilePath) + binfileBase = Path(binfileBase) + binfileBase.mkdir(exist_ok=True, parents=True) + rawStrings = [] + for i in range(cnt): + rawString = readCString(resFile) + rawStrings.append(rawString) + if id == "MATERIALS": + if saveOnDisk: + with open(binfilePath, "wb") as outFile: + for rawString in rawStrings: + saveMaterial(resModule, rawString) + # nameSplit = rawString.split(" ") + # name = nameSplit[0] + # params = " ".join(nameSplit[1:]) + # unpacked.materials.append(name) + # unpacked.materialParams.append(params) + outFile.write((rawString+"\n").encode("UTF-8")) + else: + for rawString in rawStrings: + saveMaterial(resModule, rawString) + # nameSplit = rawString.split(" ") + # name = nameSplit[0] + # params = " ".join(nameSplit[1:]) + # unpacked.materials.append(name) + # unpacked.materialParams.append(params) + elif id == "COLORS": + if saveOnDisk: + with open(binfilePath, "wb") as outFile: + for rawString in rawStrings: + # unpacked.colors.append(rawString) + outFile.write((rawString+"\n").encode("UTF-8")) + else: + for rawString in rawStrings: + pass + # unpacked.colors.append(rawString) + else: + if saveOnDisk: + with open(binfilePath, "wb") as outFile: + for rawString in rawStrings: + outFile.write((rawString+"\n").encode("UTF-8")) + else: #TEXTUREFILES and MASKFILES + rawStrings = [] + for i in range(cnt): + rawString = readCString(resFile) + rawStrings.append(rawString) + + nameSplit = rawString.split(" ") + fullname = nameSplit[0] + lastpath = fullname.split("\\") + name = "\\".join(lastpath[len(lastpath)-1:]) + sectionSize = struct.unpack(" 0)] + + res = other + res.extend(roots) + + return res + + +def processLOD(root, state, explevel = 0, curlevel = -1): + + stack = [[root, curlevel, False]] + + while stack: + blChildren = [] + block, clevel, isActive = stack.pop() + + if block[BLOCK_TYPE] == 18: + refObj = bpy.data.objects.get(block[prop(b_18.Add_Name)]) + if refObj is not None: + stack.append([refObj, clevel, isActive]) + if isActive: + block.hide_set(state) + + if block[BLOCK_TYPE] == 10: + clevel += 1 + + if block[BLOCK_TYPE] in [9, 10, 21]: + for ch in block.children: + blChildren.extend(ch.children) + else: + blChildren = block.children + + if isActive and isMeshBlock(block): + block.hide_set(state) + + for directChild in blChildren: + + # if directChild[BLOCK_TYPE] == 444: + # log.debug("Skipping {}".format(directChild.name)) + # for ch in directChild.children: + # stack.append([ch, clevel, isActive]) + # continue + + if clevel == explevel: + # if directChild[LEVEL_GROUP] == 1: + if getLevelGroup(directChild) == 1: + stack.append([directChild, clevel, True]) + else: + stack.append([directChild, -1, isActive]) + elif clevel > explevel: + stack.append([directChild, clevel, True]) + else: + stack.append([directChild, clevel, isActive]) + + +def showLOD(root): + if root.parent is None: + hroots = getHierarchyRoots(root) + for hroot in hroots: + obj = bpy.data.objects.get(hroot) + if obj is not None: + processLOD(obj, False, 0, -1) + else: + processLOD(root, False, 0, -1) + +def hideLOD(root): + if root.parent is None: + hroots = getHierarchyRoots(root) + for hroot in hroots: + obj = bpy.data.objects.get(hroot) + if obj is not None: + processLOD(obj, True, 0, -1) + else: + processLOD(root, True, 0, -1) + + +def processCond(root, group, state): + + curlevel = 0 + globlevel = 0 + + stack = [[root, curlevel, globlevel, 0, False]] + + while stack: + blChildren = [] + block, clevel, glevel, groupMax, isActive = stack.pop() + + l_group = group + + if block[BLOCK_TYPE] == 18: + refObj = bpy.data.objects.get(block[prop(b_18.Add_Name)]) + if refObj is not None: + stack.append([refObj, clevel+1, glevel, groupMax, isActive]) + if isActive: + block.hide_set(state) + + if block[BLOCK_TYPE] == 21: + glevel += 1 + clevel = 1 + groupMax = block[prop(b_21.GroupCnt)] + if l_group > groupMax-1: + l_group = groupMax-1 + + + if block[BLOCK_TYPE] in [9, 10, 21]: + for ch in block.children: + blChildren.extend(ch.children) + else: + blChildren = block.children + + if isActive and isMeshBlock(block): + block.hide_set(state) + + for directChild in blChildren: + log.debug("Processing {}".format(directChild.name)) + nextState = False + if glevel == 1: + nextState = True + elif glevel > 1: + nextState = isActive and True + if clevel == 1: + # if directChild[LEVEL_GROUP] == l_group or l_group == -1: + if getLevelGroup(directChild) == l_group or l_group == -1: + stack.append([directChild, clevel+1, glevel, groupMax, nextState]) + else: + stack.append([directChild, clevel+1, glevel, groupMax, False]) + elif clevel > 1: + stack.append([directChild, clevel+1, glevel, groupMax, isActive]) + else: + stack.append([directChild, clevel+1, glevel, groupMax, False]) + +def showConditionals(root, group): + + if root.parent is None: + hroots = getHierarchyRoots(root) + for hroot in hroots: + obj = bpy.data.objects.get(hroot) + if obj is not None: + processCond(obj, group, False) + else: + processCond(root, group, False) + + +def hideConditionals(root, group): + + if root.parent is None: + hroots = getHierarchyRoots(root) + for hroot in hroots: + obj = bpy.data.objects.get(hroot) + if obj is not None: + processCond(obj, group, True) + else: + processCond(root, group, True) + +def createCenterDriver(srcObj, bname, pname): + d = None + for i in range(3): + + d = srcObj.driver_add('location', i).driver + + v = d.variables.new() + v.name = 'location{}'.format(i) + # v.targets[0].id_type = 'SCENE' + # v.targets[0].id = bpy.context.scene + # v.targets[0].data_path = 'my_tool.{}.{}[{}]'.format(bname, pname, i) + v.targets[0].id = bpy.context.object + v.targets[0].data_path = '["{}"][{}]'.format(pname, i) + + d.expression = v.name + +def createRadDriver(srcObj, bname, pname): + d = srcObj.driver_add('empty_display_size').driver + + v1 = d.variables.new() + v1.name = 'rad' + # v1.targets[0].id_type = 'SCENE' + # v1.targets[0].id = bpy.context.scene + # v1.targets[0].data_path = 'my_tool.{}.{}'.format(bname, pname) + v1.targets[0].id = bpy.context.object + v1.targets[0].data_path = '["{}"]'.format(pname) + + d.expression = v1.name + +def showHideSphere(context, root, pname): + + transfCollection = bpy.data.collections.get(TEMP_COLLECTION) + if transfCollection is None: + transfCollection = bpy.data.collections.new(TEMP_COLLECTION) + bpy.context.scene.collection.children.link(transfCollection) + + objName = "{}||{}||{}".format(root.name, pname, 'temp') + + b3dObj = bpy.data.objects.get(objName) + + if b3dObj is not None: + bpy.data.objects.remove(b3dObj, do_unlink=True) + else: + + bnum = root.get(BLOCK_TYPE) + + centerName = "{}_center".format(pname) + radName = "{}_rad".format(pname) + + center = root.get(centerName) + rad = root.get(radName) + + # creating object + + b3dObj = bpy.data.objects.new(objName, None) + b3dObj.empty_display_type = 'SPHERE' + b3dObj.empty_display_size = rad + b3dObj.location = center + b3dObj.parent = root.parent + + transfCollection.objects.link(b3dObj) + + # center driver + bname = getMytoolBlockName('b', bnum) + + createCenterDriver(b3dObj, bname, centerName) + + # rad driver + createRadDriver(b3dObj, bname, radName) + +def drawCommon(l_self, obj): + block_type = None + level_group = None + if BLOCK_TYPE in obj: + block_type = obj[BLOCK_TYPE] + + level_group = getLevelGroup(obj) + + lenStr = str(len(obj.children)) + + box = l_self.layout.box() + box.label(text="Block type: " + str(block_type)) + box.label(text="Children block count: " + lenStr) + box.label(text="Block group: " + str(level_group)) + +def drawAllFieldsByType(l_self, context, zclass, multipleEdit = True): + drawFieldsByType(l_self, context, zclass, multipleEdit) + +def drawFieldsByType(l_self, context, zclass, multipleEdit = True): + + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + boxes = {} + for attr in attrs: + obj = zclass.__dict__[attr] + + bname, bnum = getMytoolBlockNameByClass(zclass, multipleEdit) + + ftype = obj.get('type') + subtype = obj.get('subtype') + curGroupName = obj.get('group') + propText = obj.get('name') + pname = obj.get('prop') + mytool = context.scene.my_tool + curLayout = l_self.layout + + if curGroupName is not None: + if boxes.get(curGroupName) is None: + boxes[curGroupName] = l_self.layout.box() + curLayout = boxes[curGroupName] + + + if ftype == fieldType.SPHERE_EDIT: + if not multipleEdit: # sphere edit available only in single object edit + box = curLayout.box() + col = box.column() + + props = col.operator("wm.show_hide_sphere_operator") + props.pname = pname + + elif ftype == fieldType.STRING \ + or ftype == fieldType.COORD \ + or ftype == fieldType.RAD \ + or ftype == fieldType.INT \ + or ftype == fieldType.FLOAT \ + or ftype == fieldType.ENUM \ + or ftype == fieldType.ENUM_DYN \ + or ftype == fieldType.LIST: + + box = curLayout.box() + if multipleEdit: + box.prop(getattr(mytool, bname), "show_"+pname) + + col = box.column() + if ftype in [ + fieldType.STRING, + fieldType.COORD, + fieldType.RAD, + fieldType.INT, + fieldType.FLOAT + ]: + if multipleEdit: # getting from my_tool + col.prop(getattr(mytool, bname), pname) + else: + col.prop(context.object, '["{}"]'.format(pname), text=propText) + + elif ftype in [fieldType.ENUM_DYN, fieldType.ENUM]: + + col.prop(getattr(mytool, bname), '{}_switch'.format(pname)) + + if(getattr(getattr(mytool, bname), '{}_switch'.format(pname))): + col.prop(getattr(mytool, bname), '{}_enum'.format(pname)) + else: + if multipleEdit: + col.prop(getattr(mytool, bname), pname) + else: + col.prop(context.object, '["{}"]'.format(pname), text=propText) + + elif ftype == fieldType.LIST: + collect = getattr(mytool, bname) + + scn = bpy.context.scene + + rows = 2 + + row = box.row() + props = row.operator("wm.get_prop_value_operator") + props.pname = pname + props = row.operator("wm.set_prop_value_operator") + props.pname = pname + row = box.row() + row.template_list("CUSTOM_UL_items", "", collect, pname, scn, "custom_index", rows=rows) + + col = row.column(align=True) + props = col.operator("custom.list_action", icon='ADD', text="") + props.action = 'ADD' + props.bname = bname + props.pname = pname + props.customindex = "custom_index" + props = col.operator("custom.list_action", icon='REMOVE', text="") + props.action = 'REMOVE' + props.bname = bname + props.pname = pname + props.customindex = "custom_index" + col.separator() + props = col.operator("custom.list_action", icon='TRIA_UP', text="") + props.action = 'UP' + props.bname = bname + props.pname = pname + props.customindex = "custom_index" + props = col.operator("custom.list_action", icon='TRIA_DOWN', text="") + props.action = 'DOWN' + props.bname = bname + props.pname = pname + props.customindex = "custom_index" + + if multipleEdit: + if getattr(getattr(mytool, bname), "show_"+pname): + col.enabled = True + else: + col.enabled = False + + elif ftype == fieldType.V_FORMAT: + if multipleEdit: + box = curLayout.box() + box.prop(getattr(mytool, bname), "show_{}".format(pname)) + + col1 = box.column() + col1.prop(getattr(mytool, bname), "{}_{}".format(pname, 'triang_offset')) + col1.prop(getattr(mytool, bname), "{}_{}".format(pname, 'use_uvs')) + col1.prop(getattr(mytool, bname), "{}_{}".format(pname, 'use_normals')) + col1.prop(getattr(mytool, bname), "{}_{}".format(pname, 'normal_flag')) + + if getattr(getattr(mytool, bname), "show_"+pname): + col1.enabled = True + else: + col1.enabled = False + + +def getObjByProp(context, object, zclass, pname): + + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + bname, bnum = getMytoolBlockNameByClass(zclass, True) + + mytool = context.scene.my_tool + for property in attrs: + obj = zclass.__dict__[property] + + if obj['prop'] == pname: + + if obj['type'] == fieldType.LIST: + + col = getattr(getattr(mytool, bname), obj['prop']) + + col.clear() + for i, obj in enumerate(object[obj['prop']]): + item = col.add() + item.index = i + item.value = obj + +def getAllObjsByType(context, object, zclass): + getObjsByType(context, object, zclass) + +def getObjsByType(context, object, zclass): + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + bname, bnum = getMytoolBlockNameByClass(zclass) + + mytool = context.scene.my_tool + for property in attrs: + obj = zclass.__dict__[property] + + if obj['type'] != fieldType.SPHERE_EDIT: + if getattr(getattr(mytool, bname), "show_"+obj['prop']) is not None \ + and getattr(getattr(mytool, bname), "show_"+obj['prop']): + + if obj['type'] == fieldType.FLOAT \ + or obj['type'] == fieldType.RAD: + setattr( + getattr(mytool, bname), + obj['prop'], + float(object[obj['prop']]) + ) + + elif obj['type'] == fieldType.INT: + setattr( + getattr(mytool, bname), + obj['prop'], + int(object[obj['prop']]) + ) + + elif obj['type'] == fieldType.STRING: + getattr(mytool, bname)[obj['prop']] = str(object[obj['prop']]) + + elif obj['type'] == fieldType.ENUM \ + or obj['type'] == fieldType.ENUM_DYN: + setattr( + getattr(mytool, bname), + obj['prop'], + str(object[obj['prop']]) + ) + + elif obj['type'] == fieldType.LIST: + + col = getattr(getattr(mytool, bname), obj['prop']) + + col.clear() + for i, obj in enumerate(object[obj['prop']]): + item = col.add() + item.index = i + item.value = obj + + else: + setattr( + getattr(mytool, bname), + obj['prop'], + object[obj['prop']] + ) + +def setObjByProp(context, object, zclass, pname): + + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + bname, bnum = getMytoolBlockNameByClass(zclass, True) + + mytool = context.scene.my_tool + for property in attrs: + obj = zclass.__dict__[property] + + if obj['prop'] == pname: + + if obj['type'] == fieldType.LIST: + collect = getattr(getattr(mytool, bname), obj['prop']) + + arr = [] + for item in list(collect): + arr.append(item.value) + + object[obj['prop']] = arr + +def setAllObjsByType(context, object, zclass): + setObjsByType(context, object, zclass) + +# def setObjsDefaultByType(context, object, zclass): + +def setObjsByType(context, object, zclass): + attrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass) + mytool = context.scene.my_tool + for property in attrs: + obj = zclass.__dict__[property] + if obj['type'] != fieldType.SPHERE_EDIT: + # if getattr(getattr(mytool, bname), "show_"+obj['prop']) is not None \ + # and getattr(getattr(mytool, bname), "show_"+obj['prop']): + + if obj['type'] == fieldType.FLOAT or obj['type'] == fieldType.RAD: + object[obj['prop']] = float(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['type'] == fieldType.INT: + object[obj['prop']] = int(getattr(getattr(mytool, bname), obj['prop'])) + + # elif obj['type'] == fieldType.MATERIAL_IND: + # object[obj['prop']] = int(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['type'] == fieldType.STRING: + object[obj['prop']] = str(getattr(getattr(mytool, bname), obj['prop'])) + + # elif obj['type'] == fieldType.SPACE_NAME \ + # or obj['type'] == fieldType.REFERENCEABLE: + # object[obj['prop']] = str(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['type'] == fieldType.ENUM: + + if obj['subtype'] == fieldType.INT: + object[obj['prop']] = int(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['subtype'] == fieldType.STRING: + object[obj['prop']] = str(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['subtype'] == fieldType.FLOAT: + object[obj['prop']] = float(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['type'] == fieldType.ENUM_DYN: + object[obj['prop']] = str(getattr(getattr(mytool, bname), obj['prop'])) + + elif obj['type'] == fieldType.COORD: + xyz = getattr(getattr(mytool, bname), obj['prop']) + object[obj['prop']] = (xyz[0],xyz[1],xyz[2]) + + elif obj['type'] == fieldType.LIST: + collect = getattr(getattr(mytool, bname), obj['prop']) + + arr = [] + for item in list(collect): + arr.append(item.value) + + object[obj['prop']] = arr + + else: + object[obj['prop']] = getattr(getattr(mytool, bname), obj['prop']) + +def getFromAttributes(context, obj, attrs, bname, index): + + mytool = context.scene.my_tool + + if getattr(getattr(mytool, bname), "show_"+obj['prop']) is not None \ + and getattr(getattr(mytool, bname), "show_"+obj['prop']): + + if obj['type'] == fieldType.FLOAT: + getattr(mytool, bname)[obj['prop']] = float(getattr(attrs[index], "value")) + + elif obj['type'] == fieldType.COORD: + getattr(mytool, bname)[obj['prop']] = getattr(attrs[index], "vector") + + elif obj['type'] == fieldType.INT: + getattr(mytool, bname)[obj['prop']] = int(getattr(attrs[index], "value")) + + elif obj['type'] == fieldType.V_FORMAT: + format = getattr(attrs[index], "value") ^ 1 + triangOffset = format & 0b10000000 + useUV = format & 0b10 + useNormals = format & 0b10000 and format & 0b100000 + normalFlag = format & 1 + + getattr(mytool, bname)["{}_{}".format(obj['prop'],'triang_offset')] = triangOffset + getattr(mytool, bname)["{}_{}".format(obj['prop'],'use_uvs')] = useUV + getattr(mytool, bname)["{}_{}".format(obj['prop'],'use_normals')] = useNormals + getattr(mytool, bname)["{}_{}".format(obj['prop'],'normal_flag')] = normalFlag + +def setFromAttributes(context, obj, attrs, bname, index): + + mytool = context.scene.my_tool + + if getattr(getattr(mytool, bname), "show_"+obj['prop']) is not None \ + and getattr(getattr(mytool, bname), "show_"+obj['prop']): + + if obj['type'] == fieldType.FLOAT: + attrs[index].value = getattr(mytool, bname)[obj['prop']] + + elif obj['type'] == fieldType.INT: + attrs[index].value = getattr(mytool, bname)[obj['prop']] + + elif obj['type'] == fieldType.COORD: + attrs[index].vector = getattr(mytool, bname)[obj['prop']] + + elif obj['type'] == fieldType.V_FORMAT: + value = 0 + triangOffset = getattr(mytool, bname)['{}_{}'.format(obj['prop'], 'triang_offset')] + useUV = getattr(mytool, bname)['{}_{}'.format(obj['prop'], 'use_uvs')] + useNormals = getattr(mytool, bname)['{}_{}'.format(obj['prop'], 'use_normals')] + normalFlag = getattr(mytool, bname)['{}_{}'.format(obj['prop'], 'normal_flag')] + + if triangOffset: + value = value ^ 0b10000000 + if useUV: + value = value ^ 0b10 + if useNormals: + value = value ^ 0b110000 + if normalFlag: + value = value ^ 0b1 + + value = value ^ 1 + + attrs[index].value = value + +def getPerFaceByType(context, object, zclass): + zattrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass) + + mesh = object.data + bpy.ops.object.mode_set(mode = 'OBJECT') + polygons = getPolygonsBySelectedVertices(object) + indexes = [cn.index for cn in polygons] + + if len(indexes) > 0: + index = indexes[0] + for property in zattrs: + obj = zclass.__dict__[property] + attrs = mesh.attributes[obj['prop']].data + getFromAttributes(context, obj, attrs, bname, index) + + bpy.ops.object.mode_set(mode = 'EDIT') + +def getPerVertexByType(context, object, zclass): + zattrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass) + + mesh = object.data + bpy.ops.object.mode_set(mode = 'OBJECT') + vertices = getSelectedVertices(object) + indexes = [cn.index for cn in vertices] + + if len(indexes) > 0: + index = indexes[0] + for property in zattrs: + obj = zclass.__dict__[property] + attrs = mesh.attributes[obj['prop']].data + getFromAttributes(context, obj, attrs, bname, index) + + bpy.ops.object.mode_set(mode = 'EDIT') + +def setPerVertexByType(context, object, zclass): + zattrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass) + + mesh = object.data + + bpy.ops.object.mode_set(mode = 'OBJECT') + vertices = getSelectedVertices(object) + indexes = [cn.index for cn in vertices] + + for index in indexes: + for property in zattrs: + obj = zclass.__dict__[property] + attrs = mesh.attributes[obj['prop']].data + setFromAttributes(context, obj, attrs, bname, index) + + bpy.ops.object.mode_set(mode = 'EDIT') + +def setPerFaceByType(context, object, zclass): + zattrs = [obj for obj in zclass.__dict__.keys() if not obj.startswith('__')] + + bname, bnum = getMytoolBlockNameByClass(zclass) + + mesh = object.data + + bpy.ops.object.mode_set(mode = 'OBJECT') + polygons = getPolygonsBySelectedVertices(object) + indexes = [cn.index for cn in polygons] + + for index in indexes: + for property in zattrs: + obj = zclass.__dict__[property] + attrs = mesh.attributes[obj['prop']].data + setFromAttributes(context, obj, attrs, bname, index) + + bpy.ops.object.mode_set(mode = 'EDIT') + +def createCustomAttribute(mesh, values, zclass, zobj): + ctype, btype = zclass.__name__.split('_') + domain = '' + if ctype == 'pvb': + domain = 'POINT' + elif ctype == 'pfb': + domain = 'FACE' + + if zobj['type'] == fieldType.FLOAT: + ztype = 'FLOAT' + mesh.attributes.new(name=zobj['prop'], type=ztype, domain=domain) + attr = mesh.attributes[zobj['prop']].data + for i in range(len(attr)): + setattr(attr[i], "value", values[i]) + + elif zobj['type'] == fieldType.COORD: + ztype = 'FLOAT_VECTOR' + mesh.attributes.new(name=zobj['prop'], type=ztype, domain=domain) + attr = mesh.attributes[zobj['prop']].data + for i in range(len(attr)): + setattr(attr[i], "vector", values[i]) + + elif zobj['type'] == fieldType.INT: + ztype = 'INT' + mesh.attributes.new(name=zobj['prop'], type=ztype, domain=domain) + attr = mesh.attributes[zobj['prop']].data + for i in range(len(attr)): + setattr(attr[i], "value", values[i]) + + elif zobj['type'] == fieldType.V_FORMAT: + ztype = 'INT' + mesh.attributes.new(name=zobj['prop'], type=ztype, domain=domain) + attr = mesh.attributes[zobj['prop']].data + for i in range(len(attr)): + setattr(attr[i], "value", values[i]) + diff --git a/src/2.80/addons/b3d_tools/common.py b/src/2.80/addons/b3d_tools/common.py new file mode 100644 index 0000000..dba8261 --- /dev/null +++ b/src/2.80/addons/b3d_tools/common.py @@ -0,0 +1,16 @@ +import bpy + +import logging +import sys + + +# https://blenderartists.org/t/solved-adding-a-tab-in-blender-2-8/1133119/3 +def getRegion(): + if bpy.app.version < (2,80,0): + return "TOOLS" + else: + return "UI" + + +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) +log = logging.getLogger("common") \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/consts.py b/src/2.80/addons/b3d_tools/consts.py new file mode 100644 index 0000000..3cef23f --- /dev/null +++ b/src/2.80/addons/b3d_tools/consts.py @@ -0,0 +1,197 @@ + +EMPTY_NAME = '~' + +LEVEL_GROUP = 'level_group' + +BLOCK_TYPE = 'block_type' + +TRANSF_COLLECTION = 'Transformed objects' + +TEMP_COLLECTION = 'Temporary objects' + +BORDER_COLLECTION = 'Border objects' + +blockTypeList = [ + ('111', "b3d root", "b3d"), + ('444', "Group", "Grouping object"), + ('00', "00(Unk?)", "2D background"), + ('01', "01(Camera)", "Camera"), + ('02', "02(Unk?)", "Unknown block"), + ('03', "03(Container)", "Container"), + ('04', "04(Container)", "Container containing other blocks"), + ('05', "05(Container)", "Container containing other blocks"), + ('06', "06(Vertexes)", "Vertex block (HT1)"), + ('07', "07(Vertexes)", "Vertex block (HT2)"), + # ('08', "08(Polygons)", "Polygon block"), + ('09', "09(Trigger)", "Trigger"), + ('10', "10(LOD)", "LOD"), + ('11', "11(Trigger)", "Unknown block"), + ('12', "12(Trigger)", "Unknown trigger"), + ('13', "13(Trigger)", "Unknown trigger(map load)"), + ('14', "14(Trigger)", "Unknown trigger(car related)"), + ('15', "15(Trigger)", "Unknown block"), + ('16', "16(Trigger)", "Unknown block"), + ('17', "17(Trigger)", "Unknown block"), + ('18', "18(Connector)", "Connector between space block(24) and root container block. Exmaple: FiatWheel0Space and Single0Wheel14"), + ('19', "19(Room)", "Room container"), + # ('20', "20(2D collision)", "Flat vertical collision"), + ('21', "21(Event)", "Container with event handling"), + ('22', "22(Trigger)", "Locator?"), + # ('23', "23(3D collision)", "3D collision"), + ('24', "24(Space)", "Space"), + ('25', "25(Sound)", "Sound"), + ('26', "26(Unk?)", "Locator?"), + ('27', "27(Unk?)", "Locator?"), + ('28', "28(2D Sprite)", "2D Sprite"), + ('29', "29(Unk?)", "Unknown block"), + ('30', "30(Portal)", "Portal"), + ('31', "31(Unk?)", "Unknown block"), + ('33', "33(Light)", "Light source"), + ('34', "34(Unk?)", "Unknown block"), + # ('35', "35(Polygons)", "Polygon block"), + ('36', "36(Vertexes)", "Vertex block (HT1)"), + ('37', "37(Vertexes)", "Vertex block (HT2)"), + ('39', "39(Unk?)", "Locator?"), + ('40', "40(Generator)", "Object generator"), +] + +collisionTypeList = [ + ('0', "standart", ""), + ('1', "asphalt", ""), + ('2', "grass", ""), + ('3', "swamp (hard to pass)", ""), + ('4', "swamp (easy to pass)", ""), + ('5', "wet asphalt", ""), + ('7', "ice", ""), + ('8', "water (destroys truck)", ""), + ('10', "sand", ""), + ('11', "desert", ""), + ('13', "spikes", ""), + ('16', "ice (no tire marks)", "") +] + + + +vTypeList = [ + ('0', '0(No normals)', "No normals"), + ('1', '1(With normals)', "With normals"), + ('2', '2(With normals)', "With normals"), + ('3', '3(Normal switch)', "Normal switch") +] + + +sTypeList = [ + ('2', "Type 2, coords + UV + normals", "Is used on common models"), + ('3', "Type 3, coords + UV", "Is used on headlight models"), + ('258', "Type 258, coords + UV + normals + 2 float", "Is used on asphalt road models"), + ('514', "Type 514, coords + UV + normals + 4 float", "Is used on water surface"), + ('515', "Type 515, coords + UV + normals + 2 float", "Same as 258. Is used on asphalt road models") +] + +fTypeList = [ + ('0', "Type 0", "With normals"), + ('1', "Type 1", "No normals"), + ('2', "Type 2", "With normals, breakable UV"), + ('128', "Type 128", ""), + ('144', "Type 144", "") +] + + +mTypeList = [ + ('1', "Type 1, Breakable UV", "Each polygon saves its own UV."), + ('3', "Type 3, Unbroken UV", "Can be used for models, that use reflection texture.") +] + +b24FlagList = [ + ('0', "0", "Attached object won't be shown"), + ('1', "1", "Attached object will be shown") +] + +b14Enum = [ + ('car', "For car", ""), + ('trl', "For trailer", ""), + ('trl_cyc', "For tank-trailer", ""), + ('clk', "For collision", "") +] + +triggerTypeList = [ + ('loader', "Loader", "Map part loader"), + ('radar0', "Radar 0", "Event 0"), + ('radar1', "Radar 1", "Event 1") +] + +generatorTypeList = [ + ('$SeaGenerator', "$SeaGenerator", ""), + ('$$TreeGenerator1', "$$TreeGenerator1", ""), + ('$$TreeGenerator', "$$TreeGenerator", ""), + ('$$CollisionOfTerrain', "$$CollisionOfTerrain", ""), + ('$$GeneratorOfTerrain', "$$GeneratorOfTerrain", ""), + ('$$People', "$$People", ""), + ('$$WeldingSparkles', "$$WeldingSparkles", ""), + ('$$DynamicGlow', "$$DynamicGlow", ""), + ('$$StaticGlow', "$$StaticGlow", "") +] + +conditionBlockTypeList = [ + ('GeometryKey', "GeometryKey", ""), + ('CollisionKey', "CollisionKey", ""), + ('GlassKey', "GlassKey", ""), + ('RD_LampKey', "RD_LampKey", ""), + ('RD_Key', "RD_Key", ""), + ('RedSvetKey', "RedSvetKey", ""), + ('GreenSvetKey', "GreenSvetKey", ""), + ('TrafficLightKey0', "TrafficLightKey0", ""), + ('TrafficLightKey1', "TrafficLightKey1", ""), + ('ILLUM_LEVEL_00', "ILLUM_LEVEL_00", ""), + ('ILLUM_LEVEL_01', "ILLUM_LEVEL_01", ""), + ('ILLUM_LEVEL_02', "ILLUM_LEVEL_02", ""), + ('ILLUM_LEVEL_03', "ILLUM_LEVEL_03", ""), + ('ILLUM_LEVEL_04', "ILLUM_LEVEL_04", ""), + ('ILLUM_LEVEL_05', "ILLUM_LEVEL_05", ""), + ('ILLUM_LEVEL_06', "ILLUM_LEVEL_06", ""), + ('ILLUM_LEVEL_07', "ILLUM_LEVEL_07", ""), + ('ILLUM_LEVEL_08', "ILLUM_LEVEL_08", ""), + ('ILLUM_LEVEL_09', "ILLUM_LEVEL_09", ""), + ('ILLUM_LEVEL_10', "ILLUM_LEVEL_10", ""), + ('ILLUM_LEVEL_11', "ILLUM_LEVEL_11", ""), + ('ILLUM_LEVEL_12', "ILLUM_LEVEL_12", ""), + ('ILLUM_LEVEL_13', "ILLUM_LEVEL_13", ""), + ('ILLUM_LEVEL_14', "ILLUM_LEVEL_14", ""), + ('ILLUM_LEVEL_15', "ILLUM_LEVEL_15", ""), + ('Damage1Key', "Damage1Key", ""), + ('DamageFRKey', "DamageFRKey", ""), + ('DamageFCKey', "DamageFCKey", ""), + ('DamageFLKey', "DamageFLKey", ""), + ('DamageRKey', "DamageRKey", ""), + ('DamageLKey', "DamageLKey", ""), + ('DamageBRKey', "DamageBRKey", ""), + ('DamageBCKey', "DamageBCKey", ""), + ('DamageBLKey', "DamageBLKey", ""), + ('DamageWheel0Key', "DamageWheel0Key", ""), + ('DamageWheel1Key', "DamageWheel1Key", ""), + ('DamageWheel2Key', "DamageWheel2Key", ""), + ('DamageWheel3Key', "DamageWheel3Key", ""), + ('DamageWheel4Key', "DamageWheel4Key", ""), + ('DamageWheel5Key', "DamageWheel5Key", ""), + ('DamageWheel6Key', "DamageWheel6Key", ""), + ('DamageWheel7Key', "DamageWheel7Key", ""), + ('HeadLightKey', "HeadLightKey", ""), + ('BackFaraKeyR', "BackFaraKeyR", ""), + ('StopFaraKeyR', "StopFaraKeyR", ""), + ('BackFaraKeyL', "BackFaraKeyL", ""), + ('StopFaraKeyL', "StopFaraKeyL", ""), + ('IconKey', "IconKey", ""), + ('SizeLightKey', "SizeLightKey", ""), + ('HornKey', "HornKey", ""), + ('SupportKey', "SupportKey", ""), + ('CopSirenKey', "CopSirenKey", ""), + ('CopLightKey', "CopLightKey", ""), + ('GunRightKey', "GunRightKey", ""), + ('GunLeftKey', "GunLeftKey", ""), + ('StaticLightKey', "StaticLightKey", ""), + ('BlinkLightKey', "BlinkLightKey", ""), + ('SearchLightKey', "SearchLightKey", "") +] + + + diff --git a/src/2.80/addons/b3d_tools/custom_UIList.py b/src/2.80/addons/b3d_tools/custom_UIList.py new file mode 100644 index 0000000..a9b6445 --- /dev/null +++ b/src/2.80/addons/b3d_tools/custom_UIList.py @@ -0,0 +1,463 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# https://blender.stackexchange.com/questions/30444/create-an-interface-which-is-similar-to-the-material-list-box +# https://gist.github.com/p2or/5acad9e29ddb071096f9f004ae6cace7 + +bl_info = { + "name": "object-pointer-uilist-dev", + "description": "", + "author": "p2or", + "version": (0, 1), + "blender": (2, 80, 0), + "location": "Text Editor", + "warning": "", # used for warning icon and text in addons panel + "wiki_url": "", + "tracker_url": "", + "category": "Development" +} + +import bpy + +from bpy.props import (IntProperty, + BoolProperty, + StringProperty, + CollectionProperty, + PointerProperty) + +from bpy.types import (Operator, + Panel, + PropertyGroup, + UIList) + +# ------------------------------------------------------------------- +# Operators +# ------------------------------------------------------------------- + +def action_invoke(self, context, event, arr_bname = False): + scn = context.scene + idx = getattr(scn, self.customindex) + mytool = scn.my_tool + + if arr_bname: + param = getattr(getattr(mytool, self.bname)[self.bindex], self.pname) + else: + param = getattr(getattr(mytool, self.bname), self.pname) + + try: + item = param[idx] + except IndexError: + pass + else: + if self.action == 'DOWN' and idx < len(param) - 1: + item_next = param[idx+1].name + param.move(idx, idx+1) + idx += 1 + info = 'Item "%s" moved to position %d' % (item.name, idx + 1) + self.report({'INFO'}, info) + + elif self.action == 'UP' and idx >= 1: + item_prev = param[idx-1].name + param.move(idx, idx-1) + idx -= 1 + info = 'Item "%s" moved to position %d' % (item.name, idx + 1) + self.report({'INFO'}, info) + + elif self.action == 'REMOVE': + info = 'Item "%s" removed from list' % (param[idx].name) + idx -= 1 + param.remove(idx) + self.report({'INFO'}, info) + + if self.action == 'ADD': + if context.object: + item = param.add() + # item.name = context.object.name + # item.obj = context.object + item['value'] = 0.0 + idx = len(param)-1 + # info = '"%s" added to list' % (item.name) + # self.report({'INFO'}, info) + else: + pass + # self.report({'INFO'}, "Nothing selected in the Viewport") + return {"FINISHED"} + +class CUSTOM_OT_actions_arrbname(Operator): + """Move items up and down, add and remove""" + bl_idname = "custom.list_action_arrbname" + bl_label = "List Actions" + bl_description = "Move items up and down, add and remove" + bl_options = {'REGISTER'} + + action: bpy.props.EnumProperty( + items=( + ('UP', "Up", ""), + ('DOWN', "Down", ""), + ('REMOVE', "Remove", ""), + ('ADD', "Add", ""))) + + bname: bpy.props.StringProperty() + + bindex: bpy.props.IntProperty(default=-1) + + pname: bpy.props.StringProperty() + + customindex: bpy.props.StringProperty() + + def invoke(self, context, event): + return action_invoke(self, context, event, True) + + +class CUSTOM_OT_actions(Operator): + """Move items up and down, add and remove""" + bl_idname = "custom.list_action" + bl_label = "List Actions" + bl_description = "Move items up and down, add and remove" + bl_options = {'REGISTER'} + + action: bpy.props.EnumProperty( + items=( + ('UP', "Up", ""), + ('DOWN', "Down", ""), + ('REMOVE', "Remove", ""), + ('ADD', "Add", ""))) + + bname: bpy.props.StringProperty() + + pname: bpy.props.StringProperty() + + customindex: bpy.props.StringProperty() + + def invoke(self, context, event): + return action_invoke(self, context, event, False) + + +class CUSTOM_OT_addViewportSelection(Operator): + """Add all items currently selected in the viewport""" + bl_idname = "custom.add_viewport_selection" + bl_label = "Add Viewport Selection to List" + bl_description = "Add all items currently selected in the viewport" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + scn = context.scene + selected_objs = context.selected_objects + if selected_objs: + new_objs = [] + for i in selected_objs: + item = scn.custom.add() + item.name = i.name + item.obj = i + new_objs.append(item.name) + info = ', '.join(map(str, new_objs)) + self.report({'INFO'}, 'Added: "%s"' % (info)) + else: + self.report({'INFO'}, "Nothing selected in the Viewport") + return{'FINISHED'} + + +class CUSTOM_OT_printItems(Operator): + """Print all items and their properties to the console""" + bl_idname = "custom.print_items" + bl_label = "Print Items to Console" + bl_description = "Print all items and their properties to the console" + bl_options = {'REGISTER', 'UNDO'} + + reverse_order: BoolProperty( + default=False, + name="Reverse Order") + + @classmethod + def poll(cls, context): + return bool(context.scene.custom) + + def execute(self, context): + scn = context.scene + if self.reverse_order: + for i in range(scn.custom_index, -1, -1): + ob = scn.custom[i].obj + print ("Object:", ob,"-",ob.name, ob.type) + else: + for item in scn.custom: + ob = item.obj + print ("Object:", ob,"-",ob.name, ob.type) + return{'FINISHED'} + + +class CUSTOM_OT_clearList(Operator): + """Clear all items of the list""" + bl_idname = "custom.clear_list" + bl_label = "Clear List" + bl_description = "Clear all items of the list" + bl_options = {'INTERNAL'} + + @classmethod + def poll(cls, context): + return bool(context.scene.custom) + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + def execute(self, context): + if bool(context.scene.custom): + context.scene.custom.clear() + self.report({'INFO'}, "All items removed") + else: + self.report({'INFO'}, "Nothing to remove") + return{'FINISHED'} + + +class CUSTOM_OT_removeDuplicates(Operator): + """Remove all duplicates""" + bl_idname = "custom.remove_duplicates" + bl_label = "Remove Duplicates" + bl_description = "Remove all duplicates" + bl_options = {'INTERNAL'} + + def find_duplicates(self, context): + """find all duplicates by name""" + name_lookup = {} + for c, i in enumerate(context.scene.custom): + name_lookup.setdefault(i.obj.name, []).append(c) + duplicates = set() + for name, indices in name_lookup.items(): + for i in indices[1:]: + duplicates.add(i) + return sorted(list(duplicates)) + + @classmethod + def poll(cls, context): + return bool(context.scene.custom) + + def execute(self, context): + scn = context.scene + removed_items = [] + # Reverse the list before removing the items + for i in self.find_duplicates(context)[::-1]: + scn.custom.remove(i) + removed_items.append(i) + if removed_items: + scn.custom_index = len(scn.custom)-1 + info = ', '.join(map(str, removed_items)) + self.report({'INFO'}, "Removed indices: %s" % (info)) + else: + self.report({'INFO'}, "No duplicates") + return{'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + +class CUSTOM_OT_selectItems(Operator): + """Select Items in the Viewport""" + bl_idname = "custom.select_items" + bl_label = "Select Item(s) in Viewport" + bl_description = "Select Items in the Viewport" + bl_options = {'REGISTER', 'UNDO'} + + select_all = BoolProperty( + default=False, + name="Select all Items of List", + options={'SKIP_SAVE'}) + + @classmethod + def poll(cls, context): + return bool(context.scene.custom) + + def execute(self, context): + scn = context.scene + idx = scn.custom_index + + try: + item = scn.custom[idx] + except IndexError: + self.report({'INFO'}, "Nothing selected in the list") + return{'CANCELLED'} + + obj_error = False + bpy.ops.object.select_all(action='DESELECT') + if not self.select_all: + name = scn.custom[idx].obj.name + obj = scn.objects.get(name, None) + if not obj: + obj_error = True + else: + obj.select_set(True) + info = '"%s" selected in Vieport' % (obj.name) + else: + selected_items = [] + unique_objs = set([i.obj.name for i in scn.custom]) + for i in unique_objs: + obj = scn.objects.get(i, None) + if obj: + obj.select_set(True) + selected_items.append(obj.name) + + if not selected_items: + obj_error = True + else: + missing_items = unique_objs.difference(selected_items) + if not missing_items: + info = '"%s" selected in Viewport' \ + % (', '.join(map(str, selected_items))) + else: + info = 'Missing items: "%s"' \ + % (', '.join(map(str, missing_items))) + if obj_error: + info = "Nothing to select, object removed from scene" + self.report({'INFO'}, info) + return{'FINISHED'} + + +class CUSTOM_OT_deleteObject(Operator): + """Delete object from scene""" + bl_idname = "custom.delete_object" + bl_label = "Remove Object from Scene" + bl_description = "Remove object from scene" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + return bool(context.scene.custom) + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + def execute(self, context): + scn = context.scene + selected_objs = context.selected_objects + idx = scn.custom_index + try: + item = scn.custom[idx] + except IndexError: + pass + else: + ob = scn.objects.get(item.obj.name) + if not ob: + self.report({'INFO'}, "No object of that name found in scene") + return {"CANCELLED"} + else: + bpy.ops.object.select_all(action='DESELECT') + ob.select_set(True) + bpy.ops.object.delete() + + info = ' Item "%s" removed from Scene' % (len(selected_objs)) + scn.custom_index -= 1 + scn.custom.remove(idx) + self.report({'INFO'}, info) + return{'FINISHED'} + + +# ------------------------------------------------------------------- +# Drawing +# ------------------------------------------------------------------- + +class CUSTOM_UL_items(UIList): + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # obj = item.obj + # custom_icon = "OUTLINER_OB_%s" % obj.type + split = layout.split(factor=0.3) + split.label(text="Index: %d" % (index)) + split.prop(item, "value", text="", emboss=False, translate=False) + # split.prop(obj, "name", text="", emboss=False, translate=False, icon=custom_icon) + + def invoke(self, context, event): + pass + +class CUSTOM_PT_objectList(Panel): + """Adds a custom panel to the TEXT_EDITOR""" + bl_idname = 'TEXT_PT_my_panel' + bl_space_type = "TEXT_EDITOR" + bl_region_type = "UI" + bl_label = "Custom Object List Demo" + + def draw(self, context): + layout = self.layout + scn = bpy.context.scene + + rows = 2 + row = layout.row() + row.template_list("CUSTOM_UL_items", "float_list", scn, "custom", scn, "custom_index", rows=rows) + + col = row.column(align=True) + col.operator("custom.list_action", icon='ADD', text="").action = 'ADD' + col.operator("custom.list_action", icon='REMOVE', text="").action = 'REMOVE' + col.separator() + col.operator("custom.list_action", icon='TRIA_UP', text="").action = 'UP' + col.operator("custom.list_action", icon='TRIA_DOWN', text="").action = 'DOWN' + + +# ------------------------------------------------------------------- +# Collection +# ------------------------------------------------------------------- + +class CUSTOM_PG_objectCollection(PropertyGroup): + #name: StringProperty() -> Instantiated by default + obj: PointerProperty( + name="Object", + type=bpy.types.Object) + + +# ------------------------------------------------------------------- +# Register & Unregister +# ------------------------------------------------------------------- + +_classes = [ + CUSTOM_OT_actions, + CUSTOM_OT_actions_arrbname, + CUSTOM_OT_addViewportSelection, + CUSTOM_OT_printItems, + CUSTOM_OT_clearList, + CUSTOM_OT_removeDuplicates, + CUSTOM_OT_selectItems, + CUSTOM_OT_deleteObject, + CUSTOM_UL_items, + CUSTOM_PT_objectList, + CUSTOM_PG_objectCollection, +] + +def register(): + from bpy.utils import register_class + for cls in _classes: + register_class(cls) + + # Custom scene properties + bpy.types.Scene.custom = CollectionProperty(type=CUSTOM_PG_objectCollection) + bpy.types.Scene.custom_index = IntProperty() + bpy.types.Scene.textures_index = IntProperty() + bpy.types.Scene.materials_index = IntProperty() + bpy.types.Scene.maskfiles_index = IntProperty() + + +def unregister(): + from bpy.utils import unregister_class + for cls in _classes[::-1]: + unregister_class(cls) + + del bpy.types.Scene.custom + del bpy.types.Scene.custom_index + del bpy.types.Scene.textures_index + del bpy.types.Scene.materials_index + del bpy.types.Scene.maskfiles_index + + + +if __name__ == "__main__": + register() \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/tch/__init__.py b/src/2.80/addons/b3d_tools/tch/__init__.py new file mode 100644 index 0000000..e3964a9 --- /dev/null +++ b/src/2.80/addons/b3d_tools/tch/__init__.py @@ -0,0 +1,284 @@ +import bpy +from mathutils import Matrix +from math import radians + +from mathutils import Vector +from math import sqrt + +import bmesh + + +def read_tch(context, filepath, use_some_setting): + print("running read_tch...") + #file = open(filepath, 'r', encoding='utf-8') + #data = f.read() + + + file = open(filepath, 'r') + + CollisionPlane = [] + + CPlanes_num = 0 + + field_ind = "" + field_x = "" + field_y = "" + field_z = "" + field_offset = "" + + for line in file: + ColPlane_line = "" + if "CollisionPlane" in line: + CollisionPlane.append(line) + #CPlanes_num += 1 + file.close() + + #print(str(CollisionPlane)) + ColPlane_vector = [] + + + for i in range(len(CollisionPlane)):#(CPlanes_num): + if i < 10: + ColPlane_vector.append((CollisionPlane[i])[16:].split(" ")) + else: + ColPlane_vector.append((CollisionPlane[i])[17:].split(" ")) + #print(str(i)) + + + print(str(ColPlane_vector)) + # would normally load the data here + #print(data) + + return {'FINISHED'} + +def export_tch(context, filepath): + print("exporting tch...") + file = open(filepath, 'w') + + global global_matrix + global_matrix = axis_conversion(to_forward="-Z", + to_up="Y", + ).to_4x4() * Matrix.Scale(1, 4) + + global CollisionPlane_num + CollisionPlane_num = 0 + + forChild(bpy.data.objects['tch'],True, file, CollisionPlane_num) + + return {'FINISHED'} + + + +def forChild(object, root, file, CollisionPlane_num): + if (not root): + if object.name == "Corner_Mark": + verticesL = [] + uvs = [] + faces = [] + + bm = bmesh.new() + bm.from_mesh(object.data) + bm.verts.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + bm.transform(global_matrix * object.matrix_world) + + bm.to_mesh(object.data) + + + for v in bm.verts: + verticesL.append((v.co)) + + meshdata = object.data + + for i, polygon in enumerate(meshdata.polygons): + for i1, loopindex in enumerate(polygon.loop_indices): + meshloop = meshdata.loops[loopindex] + faces.append(meshloop.vertex_index) + + vLen = len(verticesL) + + #for i,vert in enumerate(verticesL): + #file.write("CornerMark_" + str(i) + "=" + str(vert[0][0]) + " " + str(-vert[0][2]) + " " + str(vert[0][1])) + #file.write(str(object.data.vert[i][0][0])) + + v_num = 0 + + for vert in object.data.vertices: + v_num += 1 + file.write("Corner_Mark_" + str(v_num) + "=" + str(round(vert.co.x, 6)) + " " + str(round(-vert.co.z, 6)) + " " + str(round(vert.co.y, 6)) + "\n") + #file.write(struct.pack(" 15): + text_len = 20 + + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): + text_len = 16 + + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): + text_len = 12 + + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): + text_len = 8 + + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): + text_len = 4 + + bytes_len = 8 + text_len + + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'RNOD': + bytes_len = 8 + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'NNAM': + text_len = 0 + + if (len(str(object['str'])) > 15): + text_len = 20 + + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): + text_len = 16 + + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): + text_len = 12 + + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): + text_len = 8 + + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): + text_len = 4 + + bytes_len = 8 + text_len + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'GROM': + bytes_len = 8 + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'GDAT': + bytes_len = 8 + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'MNAM': + text_len = 0 + + if (len(str(object['str'])) > 15): + text_len = 20 + + elif (11 < len(str(object['str'])) < 15 or len(str(object['str'])) == 15): + text_len = 16 + + elif (7 < len(str(object['str'])) < 11 or len(str(object['str'])) == 11): + text_len = 12 + + elif (3 < len(str(object['str'])) < 7 or len(str(object['str'])) == 7): + text_len = 8 + + elif (len(str(object['str'])) < 3 or len(str(object['str'])) == 3): + text_len = 4 + + bytes_len = 8 + text_len + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'WTWR': + bytes_len = 8 + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + + elif object.name[0:4] == 'FLAG': + bytes_len = 12 + object['bytes_len'] = bytes_len + object['c_bytes_len'] = 0 + #object['flag'] = 1 + + for child in object.children: + SetBytesLength(child,False) + + +def SetCBytesLength(object, root): + varCB = 0 + if (not root): + if object.name[0:4] == 'RSEG': + bytes_len = 8 + + elif object.name[0:4] == 'RNOD': + for x in range (len(object.children)): + varCB += object.children[x]['bytes_len'] + + object['c_bytes_len'] = varCB + + for child in object.children: + SetCBytesLength(child,False) + +def SetRS(object, root): + varRS = 0 + if (not root): + if object.name[0:4] == 'RSEG': + for r in range (len(object.children)): + varRS += object.children[r]['bytes_len'] + object.children[r]['c_bytes_len'] + + object['c_bytes_len'] = varRS + + for child in object.children: + SetRS(child, False) + +def SetGR(object, root): + varGR = 0 + if (not root): + if object.name[0:4] == 'GROM': + for j in range (len(object.children)): + varGR += object.children[j]['bytes_len'] + object.children[j]['c_bytes_len'] + + object['c_bytes_len'] = varGR + + for child in object.children: + SetGR(child, False) + +def SetGD(object, root): + variable = 0 + if (not root): + if object.name[0:4] == 'GDAT': + for n in range (len(object.children)): + variable += object.children[n]['bytes_len'] + object.children[n]['c_bytes_len'] + + object['c_bytes_len'] = variable + + for child in object.children: + SetGD(child, False) + +def SetMN(object, root): + varMN = 0 + if (not root): + if object.name[0:4] == 'MNAM': + for i in range (len(object.children)): + varMN = object.children[i]['bytes_len'] + object.children[i]['c_bytes_len'] + + object['c_bytes_len'] = varMN + + for child in object.children: + SetMN(child, False) + +def SetWT(object, root): + varWT = 0 + if (not root): + if object.name[0:4] == 'WTWR': + for i in range (len(object.children)): + varWT = object.children[i]['bytes_len'] + object.children[i]['c_bytes_len'] + + object['c_bytes_len'] = varWT + + for child in object.children: + SetWT(child, False) + + +def GetCBytesLength(object, root): + var1 = 0 + if (not root): + #for object in bpy.data.objects: + if (object.children): + for i in range (len(object.children)): + var1 = object.children[i]['bytes_len'] + object['bytes_len'] + #object['c_bytes_len'] = var1 + object['c_bytes_len'] = var1 + object['c_bytes_len'] + print(str(var1)) + + for child in object.children: + GetCBytesLength(child,False) + +def SetRSEGBytes(object, root): + if (not root): + if object['type'] == 'rseg': + for subcurve in object.data.splines: + object['bytes_len'] = 68 + len(subcurve.points) * 24 + + room = object.parent + room['c_bytes_len'] += object['bytes_len'] + + for child in object.children: + SetRSEGBytes(child, False) + +def SetNodeBytes(object, root): + var_node = 0 + print("SetNodeBytes") + try: + if object['type'] == 'rnod' or object['type'] == 'ortn': + object_name = object.name.split(".")[0] + text_len1 = 0 + if (len(str(object_name)) > 15): + text_len1 = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + text_len1 = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + text_len1 = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + text_len1 = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + text_len1 = 4 + + var_node = text_len1 + 16 + 32 + 12 + if object['type'] == "ortn": + var_node += 104 + object['bytes_len'] = var_node + + #object.parent['c_bytes_len'] += var_node + room = object.parent + room['c_bytes_len'] += var_node + + for child in object.children: + SetNodeBytes(child, False) + except: + pass + +def SetRoomBytes(object, root): + var_r = 0 + if object['type'] == 'room': + object_name = object.name.split(".")[0] + text_len1 = 0 + if (len(str(object_name)) > 15): + text_len1 = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + text_len1 = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + text_len1 = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + text_len1 = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + text_len1 = 4 + + var_r = text_len1 + 16 + + object['bytes_len'] = var_r #+ 8 + + #way = object.parent + #way['c_bytes_len'] += object['bytes_len'] + + for child in object.children: + SetRoomBytes(child, False) + +def SetWayBytes(object, root): + if object['type'] == 'room': + way = object.parent + print(object['bytes_len']) + way['c_bytes_len'] = way['c_bytes_len'] + object['c_bytes_len'] + object['bytes_len'] + + for child in object.children: + SetWayBytes(child, False) + +def SetWayBytes1(object, root): + if object['type'] == 'way': + object['bytes_len'] = 20 + object['c_bytes_len'] + + for child in object.children: + SetWayBytes1(child, False) + +def ClearBytes(object, root): + if object['type'] == 'room': + object['bytes_len'] = 0 + object['c_bytes_len'] = 0 + + if object['type'] == 'way': + object['bytes_len'] = 0 + object['c_bytes_len'] = 0 + + for child in object.children: + ClearBytes(child, False) + +def writeWTWR(object, file): + file.write(str.encode('WTWR')) + file.write(struct.pack(" 15): + text_len1 = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + text_len1 = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + text_len1 = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + text_len1 = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + text_len1 = 4 + + object['c_bytes_len'] = text_len1 + 8 + 32 + 12 + + if object['type'] == "ortn": + object['c_bytes_len'] += 104 + + file.write(struct.pack(" 15): + file.write(str.encode(object_name)+bytearray(b'\x00'*(20-len(str(object_name))))) + text_len = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + file.write(str.encode(object_name)+bytearray(b'\x00'*(16-len(str(object_name))))) + text_len = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + file.write(str.encode(object_name)+bytearray(b'\x00'*(12-len(str(object_name))))) + text_len = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + file.write(str.encode(object_name)+bytearray(b'\x00'*(8-len(str(object_name))))) + text_len = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + file.write(str.encode(object_name)+bytearray(b'\x00'*(4-len(str(object_name))))) + text_len = 4 + + #bytes_len = 8 + text_len + + file.write(str.encode('POSN')) + file.write(struct.pack(" 15): + text_len1 = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + text_len1 = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + text_len1 = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + text_len1 = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + text_len1 = 4 + + object['bytes_len'] = 8 + text_len1 + object['c_bytes_len'] + object['c_bytes_len'] = object['c_bytes_len'] + 20 + + file.write(struct.pack(" 15): + file.write(str.encode(object_name)+bytearray(b'\x00'*(20-len(str(object_name))))) + text_len = 20 + + elif (11 < len(str(object_name)) < 15 or len(str(object_name)) == 15): + file.write(str.encode(object_name)+bytearray(b'\x00'*(16-len(str(object_name))))) + text_len = 16 + + elif (7 < len(str(object_name)) < 11 or len(str(object_name)) == 11): + file.write(str.encode(object_name)+bytearray(b'\x00'*(12-len(str(object_name))))) + text_len = 12 + + elif (3 < len(str(object_name)) < 7 or len(str(object_name)) == 7): + file.write(str.encode(object_name)+bytearray(b'\x00'*(8-len(str(object_name))))) + text_len = 8 + + elif (len(str(object_name)) < 3 or len(str(object_name)) == 3): + file.write(str.encode(object_name)+bytearray(b'\x00'*(4-len(str(object_name))))) + text_len = 4 + + for child in object.children: + forChild(child,False,file) + +# ExportHelper is a helper class, defines filename and +# invoke() function which calls the file selector. +from bpy_extras.io_utils import ExportHelper +from bpy.props import StringProperty, BoolProperty, EnumProperty +from bpy.types import Operator + +class ImportSomeData(Operator, ExportHelper): + """This appears in the tooltip of the operator and in the generated docs""" + bl_idname = "import_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed + bl_label = "Import WAY" + + # ExportHelper mixin class uses this + filename_ext = '.way' + + filter_glob : StringProperty(default='*.way',options={'HIDDEN'}) # Max internal buffer length, longer would be clamped. + + def execute(self, context): + from . import import_way + with open(self.filepath, 'rb') as file: + import_way.read(file, context, self.filepath) + return {'FINISHED'} + +class ExportSomeData(Operator, ExportHelper): + """This appears in the tooltip of the operator and in the generated docs""" + bl_idname = "export_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed + bl_label = "Export *.way" + + # ExportHelper mixin class uses this + filename_ext = '.way' + + filter_glob : StringProperty(default='*.way', options={'HIDDEN'}) # Max internal buffer length, longer would be clamped. + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + use_setting = BoolProperty( + name="Example Boolean", + description="Example Tooltip", + default=True, + ) + + type = EnumProperty( + name="Example Enum", + description="Choose between two items", + items=(('OPT_A', "First Option", "Description one"), + ('OPT_B', "Second Option", "Description two")), + default='OPT_A', + ) + + def execute(self, context): + return export(context, self.filepath, self.use_setting) + + +# Only needed if you want to add into a dynamic menu +def menu_func_export(self, context): + pass + # self.layout.operator(ExportSomeData.bl_idname, text="KOTR WAY (.way") + +def menu_func_import(self, context): + self.layout.operator(ImportSomeData.bl_idname, text="KOTR WAY (.way)") + +_classes = [ + PanelSettings1, + AddOperator1, + SetValuesOperator1, + AddOperator11, + OBJECT_PT_way_add_panel, + GetValuesOperator1, + OBJECT_PT_way_edit_panel, + OBJECT_PT_way_misc_panel, + OBJECT_PT_blocks_panel, + ImportSomeData, + ExportSomeData +] + + +def register(): + menus.register() + for cls in _classes: + bpy.utils.register_class(cls) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.types.Scene.way_tool = bpy.props.PointerProperty(type=PanelSettings1) + +def unregister(): + del bpy.types.Scene.way_tool + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + for cls in _classes: + bpy.utils.unregister_class(cls) + menus.unregister() + +# def register(): +# bpy.utils.register_class(ExportSomeData) +# bpy.types.TOPBAR_MT_file_export.append(menu_func_export) + +# bpy.utils.register_class(ImportSomeData) +# bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + +# bpy.utils.register_module(__name__) +# bpy.types.Scene.way_tool = PointerProperty(type=PanelSettings1) + + +# def unregister(): +# bpy.utils.unregister_class(ExportSomeData) +# bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) + +# bpy.utils.unregister_class(ImportSomeData) +# bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + +# bpy.utils.unregister_module(__name__) +# del bpy.types.Scene.way_tool + diff --git a/src/2.80/addons/b3d_tools/way/import_way.py b/src/2.80/addons/b3d_tools/way/import_way.py new file mode 100644 index 0000000..cd9fe26 --- /dev/null +++ b/src/2.80/addons/b3d_tools/way/import_way.py @@ -0,0 +1,234 @@ +import bpy +import struct +import os +import sys + +def readName(file, text_len): + # text_len = 0 + str_len = 0 + + rem = text_len % 4 + fill_len = (text_len + 4 - rem if rem else text_len) - text_len + + # if (text_len > 15): + # str_len = 20 + + # elif (11 < text_len < 15 or text_len == 15): + # str_len = 16 + + # elif (7 < text_len < 11 or text_len == 11): + # str_len = 12 + + # elif (3 < text_len < 7 or text_len == 7): + # str_len = 8 + + # elif (text_len < 3 or text_len == 3): + # str_len = 4 + + # string = file.read(str_len).decode("utf-8") + + string = file.read(text_len).decode("cp1251") + if fill_len > 0: + file.seek(fill_len, 1) + return string + +def openclose(file, path): + if file.tell() == os.path.getsize(path): + print ('EOF') + return 1 + else: + return 2 + print(str(os.path.getsize(path))) + +def read(file, context, filepath): + # file_path = file + # file = open(file, 'rb') + + ex = 0 + rootName = os.path.basename(filepath) + rootObject = bpy.data.objects.new(rootName, None) + rootObject.location=(0,0,0) + context.collection.objects.link(rootObject) + + while ex!=1: + ex = openclose(file, filepath) + if ex == 1: + file.close() + break + + elif ex == 2: + type = file.read(4).decode("cp1251") + + global r + + if type == "WTWR": #WTWR + rootObject['wtwr_size'] = struct.unpack(" 0: + subtype = file.read(4).decode("cp1251") + subtypeSize = struct.unpack(" 0: + subtype = file.read(4).decode("cp1251") + subtypeSize = struct.unpack(" 0: + + cur_pos = object_matrix[3] + object = bpy.ops.mesh.primitive_ico_sphere_add(radius=0.2, calc_uvs=True, location=cur_pos) + object = bpy.context.selected_objects[0] + object.parent = r + else: + print("Unknown type: " + type + " on position " + str(file.tell())) + #sys.exit() + #except: + #print(str(file.tell())) + #print('error') + + return {'FINISHED'} + +def bak(): + if type == "FLAG": + object_name = type + object = bpy.data.objects.new(object_name, None) + object.location=(0,0,0) + object['flag'] = waytool.flag_int + bpy.context.collection.objects.link(object) + + if type == "RNAM": + object_name = type + object = bpy.data.objects.new(object_name, None) + object.location=(0,0,0) + object['str'] = waytool.name_string + bpy.context.collection.objects.link(object) + + if type == "NNAM": + object_name = type + object = bpy.data.objects.new(object_name, None) + object.location=(0,0,0) + object['str'] = waytool.name_string + bpy.context.collection.objects.link(object) + + if type == "ORTN": + object = bpy.ops.mesh.primitive_ico_sphere_add(radius=0.05, calc_uvs=True, location=(0.0,0.0,0.0)) + object = bpy.context.selected_objects[0] + object.name = type + object.location=cursor_pos1 + + if type == "POSN": + object = bpy.ops.mesh.primitive_ico_sphere_add(radius=0.05, calc_uvs=True, location=(0.0,0.0,0.0)) + object = bpy.context.selected_objects[0] + object.name =type + object.location=cursor_pos1 + return {'FINISHED'} \ No newline at end of file diff --git a/src/2.80/addons/b3d_tools/way/menus.py b/src/2.80/addons/b3d_tools/way/menus.py new file mode 100644 index 0000000..6391a00 --- /dev/null +++ b/src/2.80/addons/b3d_tools/way/menus.py @@ -0,0 +1,78 @@ + +import time +import datetime +from threading import Lock, Thread +import os +import math +import bpy + +from bpy.props import ( + StringProperty, + EnumProperty, + BoolProperty, + CollectionProperty, + FloatProperty +) +from bpy_extras.io_utils import ( + ImportHelper, + ExportHelper +) +from bpy.types import ( + OperatorFileListElement, + Operator, + AddonPreferences +) + + + + +class ImportWayTxt(Operator, ImportHelper): + '''Import from txt file format (.txt)''' + bl_idname = 'import_scene.kotr_way_txt' + bl_label = 'Import way_txt' + + filename_ext = '.txt' + filter_glob = StringProperty(default='*.txt', options={'HIDDEN'}) + + use_image_search = BoolProperty(name='Image Search', + description='Search subdirectories for any associated'\ + 'images', default=True) + + def execute(self, context): + from . import import_b3d + print('Importing file', self.filepath) + t = time.mktime(datetime.datetime.now().timetuple()) + with open(self.filepath, 'r') as file: + import_b3d.readWayTxt(file, context, self, self.filepath) + t = time.mktime(datetime.datetime.now().timetuple()) - t + print('Finished importing in', t, 'seconds') + return {'FINISHED'} + + +def menu_func_import(self, context): + self.layout.operator(ImportWayTxt.bl_idname, text='KOTR WAY (.txt)') + + +def menu_func_export(self, context): + pass + + +_classes = [ + ImportWayTxt +] + + +def register(): + print("registering addon") + for cls in _classes: + bpy.utils.register_class(cls) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export) + + +def unregister(): + print("unregistering addon") + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) + for cls in _classes[::-1]: #reversed + bpy.utils.unregister_class(cls) \ No newline at end of file diff --git a/utils/b3dSqlite.py b/utils/b3dSqlite.py new file mode 100644 index 0000000..b31f755 --- /dev/null +++ b/utils/b3dSqlite.py @@ -0,0 +1,1108 @@ +import sqlite3 +import struct +import logging +import sys +import os +import enum + +filename = '' +outdir = '' + + +# dbName = r'.\testHT1.db' +dbName = r'.\testHT2.db' + +# reloadTables = True +reloadTables = False + +args = sys.argv + +if len(args) == 2: + filename = args[1] + outdir = os.path.dirname(filename) +else: + print(args) + print("Wrong number of parameters") + print("") + print('Usage: python b3dSqlite.py ') + print(' - (obligatory) Path to .b3d file to fill SQLite') + print('Tested with Python v.3.9.13') + sys.exit() + + +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) +log = logging.getLogger("sqliteb3d") +log.setLevel(logging.DEBUG) + +b3dBlocks = [ + 1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,33,34,35,36,37,39,40 +] + +class ChunkType(enum.Enum): + END_CHUNK = 0 + END_CHUNKS = 1 + BEGIN_CHUNK = 2 + GROUP_CHUNK = 3 + +def openclose(file): + oc = file.read(4) + if (oc == (b'\x4D\x01\x00\x00')): # Begin_Chunk(111) + return ChunkType.BEGIN_CHUNK + elif oc == (b'\x2B\x02\x00\x00'): # End_Chunk(555) + return ChunkType.END_CHUNK + elif oc == (b'\xbc\x01\x00\x00'): # Group_Chunk(444) + return ChunkType.GROUP_CHUNK + elif oc == (b'\xde\x00\00\00'): # End_Chunks(222) + return ChunkType.END_CHUNKS + else: + log.debug(oc) + log.debug(file.tell()) + raise Exception() + +def readName(file): + objName = file.read(32) + if (objName[0] == 0): + objName = '' + #objname = "Untitled_0x" + str(hex(pos-36)) + else: + objName = (objName.decode("cp1251").rstrip('\0')) + return objName + +def tabSphere(name, isInt = False): + ctype = "INT" if isInt else "FLOAT" + return """ + {name}_x {ctype}, + {name}_y {ctype}, + {name}_z {ctype}, + {name}_r {ctype}""".format(name=name, ctype=ctype) + +def tabPoint(name, isInt = False): + ctype = "INT" if isInt else "FLOAT" + return """ + {name}_x {ctype}, + {name}_y {ctype}, + {name}_z {ctype}""".format(name=name, ctype=ctype) + +def getBlockColumnByType(blockType, noTypes = False): + blockColumns = "" + if blockType == 1: + blockColumns = """ + name1 VARCHAR(32), + name2 VARCHAR(32) + """ + elif blockType == 2: + blockColumns = """ + {}, + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere") + ) + elif blockType == 3: + blockColumns = """ + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 4: + blockColumns = """ + {}, + name1 VARCHAR(32), + name2 VARCHAR(32), + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 5: + blockColumns = """ + {}, + name1 VARCHAR(32), + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 6: + blockColumns = """ + {}, + name1 VARCHAR(32), + name2 VARCHAR(32), + vertex_cnt INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 7: + blockColumns = """ + {}, + name1 VARCHAR(32), + vertex_cnt INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 8: + blockColumns = """ + {}, + poly_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType in [9,10,22]: + blockColumns = """ + {}, + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere") + ) + elif blockType == 11: + blockColumns = """ + {}, + {}, + {}, + float1 FLOAT, + float2 FLOAT, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabPoint("unk_point1"), + tabPoint("unk_point2") + ) + elif blockType in [12, 14]: + blockColumns = """ + {}, + {}, + int1 INT, + int2 INT, + unk_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere") + ) + elif blockType in [13, 15]: + blockColumns = """ + {}, + int1 INT, + int2 INT, + unk_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere") + ) + elif blockType in [16, 17]: + blockColumns = """ + {}, + {}, + {}, + float1 FLOAT, + float2 FLOAT, + int1 INT, + int2 INT, + poly_cnt INT + """.format( + tabSphere("bound_sphere"), + tabPoint("point1"), + tabPoint("point2"), + ) + elif blockType == 18: + blockColumns = """ + {}, + space_name VARCHAR(32), + add_name VARCHAR(32) + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 19: + blockColumns = """ + child_cnt INT + """ + elif blockType == 20: + blockColumns = """ + {}, + vertex_cnt INT, + int1 INT, + int2 INT, + unk_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 21: + blockColumns = """ + {}, + int1 INT, + int2 INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 23: + blockColumns = """ + int1 INT, + surface INT, + unk_cnt INT, + poly_cnt INT + """ + elif blockType == 24: + blockColumns = """ + {}, + {}, + {}, + {}, + flag INT, + child_cnt INT + """.format( + tabPoint("transf1"), + tabPoint("transf2"), + tabPoint("transf3"), + tabPoint("pos") + ) + elif blockType == 25: + blockColumns = """ + {}, + name1 VARCHAR(32), + {}, + {}, + float1 FLOAT, + float2 FLOAT, + float3 FLOAT, + float4 FLOAT, + float5 FLOAT + """.format( + tabPoint("point1", True), + tabPoint("point2"), + tabPoint("point3") + ) + elif blockType == 26: + blockColumns = """ + {}, + {}, + {}, + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("point1"), + tabSphere("point2"), + tabSphere("point3") + ) + elif blockType == 27: + blockColumns = """ + {}, + flag1 INT, + {}, + material INT + """.format( + tabSphere("bound_sphere"), + tabPoint("unk_point"), + ) + elif blockType == 28: + blockColumns = """ + {}, + {}, + vertex_cnt INT + """.format( + tabSphere("bound_sphere"), + tabPoint("sprite_center"), + ) + elif blockType == 29: + blockColumns = """ + {}, + unk_cnt INT, + int2 INT, + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere"), + ) + elif blockType == 30: + blockColumns = """ + {}, + room_name VARCHAR(32), + {}, + {} + """.format( + tabSphere("bound_sphere"), + tabPoint("point1"), + tabPoint("point2"), + ) + elif blockType == 31: + blockColumns = """ + {}, + unk_cnt INT, + {}, + int1 INT, + {} + """.format( + tabSphere("bound_sphere"), + tabSphere("unk_sphere"), + tabPoint("unk_point"), + ) + elif blockType == 33: + blockColumns = """ + {}, + use_lights INT, + light_type INT, + flag1 INT, + {}, + {}, + float1 FLOAT, + float2 FLOAT, + light_r FLOAT, + intensity FLOAT, + float3 FLOAT, + float4 FLOAT, + {}, + child_cnt INT + """.format( + tabSphere("bound_sphere"), + tabPoint("unk_sphere1"), + tabPoint("unk_sphere2"), + tabPoint("rgb"), + ) + elif blockType == 34: + blockColumns = """ + {}, + int1 INT, + unk_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 35: + blockColumns = """ + {}, + mtype INT, + texnum INT, + poly_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 36: + blockColumns = """ + {}, + name1 VARCHAR(32), + name2 VARCHAR(32), + format INT, + vertex_cnt INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 37: + blockColumns = """ + {}, + name1 VARCHAR(32), + format INT, + vertex_cnt INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 39: + blockColumns = """ + {}, + color_r INT, + float1 FLOAT, + fog_start FLOAT, + fog_end FLOAT, + color_id INT, + child_cnt INT + """.format( + tabSphere("bound_sphere") + ) + elif blockType == 40: + blockColumns = """ + {}, + name1 VARCHAR(32), + name2 VARCHAR(32), + int1 INT, + int2 INT, + unk_cnt INT + """.format( + tabSphere("bound_sphere") + ) + + if noTypes: + blockColumns = blockColumns.replace(" INT", "") + blockColumns = blockColumns.replace(" FLOAT", "") + blockColumns = blockColumns.replace(" VARCHAR(32)", "") + + return blockColumns + +insertColumns = {} +for blockType in b3dBlocks: + insertColumns[blockType] = getBlockColumnByType(blockType, True) + +def createTableByType(con, blockType): + + cur = con.cursor() + + sqlStatement = """ + CREATE TABLE IF NOT EXISTS b_{}( + id INTEGER PRIMARY KEY AUTOINCREMENT, + b3dmodule VARCHAR(32), + b3dname VARCHAR(32), + {} + ) + """ + blockColumns = getBlockColumnByType(blockType) + + cur.execute(sqlStatement.format(blockType, blockColumns)) + +def getPlaceholders(cnt): + if cnt > 0: + arr = ['?'] * cnt + return ",".join(arr) + return "" + +def insertByType(con, blockType, row): + + # log.debug("inserting {}".format(blockType)) + + cur = con.cursor() + + count = (insertColumns[blockType]).count(",")+1+2 + + sqlStatement = """ + INSERT INTO b_{}(b3dmodule, b3dname, {}) + VALUES ({}) + """.format(blockType, insertColumns[blockType], getPlaceholders(count)) + + cur.execute(sqlStatement, row) + con.commit() + +def readb3d(con, file): + + resBasename = os.path.basename(file.name)[:-4] #cut extension + + if file.read(3) == b'b3d': + log.info("correct file") + else: + log.error("b3d error") + + file.seek(1,1) + filesize = struct.unpack("> 8 #ah + + if format & 0b10: + uvCount += 1 + + unkF = struct.unpack("> 8) + 1 #ah + unknown3 = struct.unpack(" 0: + f = struct.unpack("<"+str(num0)+"f",file.read(4*num0)) + + childCnt = struct.unpack("> 8 #ah + + if format & 0b10: + uvCount += 1 + + unkF = struct.unpack("> 8 + format = formatRaw & 0xFF + + vertexCount = struct.unpack("> 8 + format = formatRaw & 0xFF + vertexCount = struct.unpack(" 0: + if format == 0: + # objString[len(objString)-1] = objString[-2] + pass + else: + for i in range(vertexCount): + vertex = struct.unpack("<3f",file.read(12)) + uv = struct.unpack("<2f",file.read(8)) + for j in range(uvCount): + uv = struct.unpack("<2f",file.read(8)) + if format == 1 or format == 2: + normal = struct.unpack("<3f",file.read(12)) + elif format == 3: + normal_off = struct.unpack(" ') + print(' - (obligatory) Path to .b3d file to export meshes') + print(' - (optional) Path to .txt file with mesh root names. 1 line = 1 name') + print('if not defined or file is empty, script search for roots automatically') + print('Tested with Python v.3.9.13') + sys.exit() + +blocksToExtract = [] + +if rootsFromFile: + if os.path.exists(rootsFile): + with open(rootsFile) as f: + for line in f: + l = line.rstrip() + if len(l) > 0: + blocksToExtract.append(l) + + +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) +log = logging.getLogger("splitb3d") +log.setLevel(logging.DEBUG) + +def getHierarchyRoots(refObjs): + + graph = {} + + for key in refObjs.keys(): + graph[key] = [cn['add_name'] for cn in refObjs[key]] + + zgraph = Graph(graph) + + visited = zgraph.DFS() + + roots = [cn for cn in visited.keys() if (visited[cn]["in"] == 0) and (visited[cn]["out"] > 0)] + + return roots + +def readName(file): + objName = file.read(32) + if (objName[0] == 0): + objName = EMPTY_NAME + #objname = "Untitled_0x" + str(hex(pos-36)) + else: + objName = (objName.decode("cp1251").rstrip('\0')) + return objName + +class ChunkType(enum.Enum): + END_CHUNK = 0 + END_CHUNKS = 1 + BEGIN_CHUNK = 2 + GROUP_CHUNK = 3 + +def openclose(file): + oc = file.read(4) + if (oc == (b'\x4D\x01\x00\x00')): # Begin_Chunk(111) + return ChunkType.BEGIN_CHUNK + elif oc == (b'\x2B\x02\x00\x00'): # End_Chunk(555) + return ChunkType.END_CHUNK + elif oc == (b'\xbc\x01\x00\x00'): # Group_Chunk(444) + return ChunkType.GROUP_CHUNK + elif oc == (b'\xde\x00\00\00'): # End_Chunks(222) + return ChunkType.END_CHUNKS + else: + # log.debug(file.tell()) + raise Exception() + +class Graph: + + def __init__(self, graph): + self.graph = graph + + def DFSUtil(self, val, visited): + + visited[val]["in"] += 1 + for v in self.graph[val]: + if self.graph.get(v) is not None: + visited[val]["out"] += 1 + self.DFSUtil(v, visited) + + def DFS(self, start=None): + V = len(self.graph) #total vertices + + visited = {} + for val in self.graph.keys(): + visited[val] = { + "in": 0, + "out": 0 + } + + searchIn = [] + if start is not None: + searchIn.append(start.name) + else: + searchIn = self.graph.keys() + + for val in searchIn: + for v in self.graph[val]: + if self.graph.get(v) is not None: + visited[val]["out"] += 1 + self.DFSUtil(v, visited) + + return visited + +def read(file): + if file.read(3) == b'b3d': + log.info("correct file") + else: + log.error("b3d error") + + file.seek(1,1) + filesize = struct.unpack("> 8 #ah + + if format & 0b10: + uvCount += 1 + + unkF = struct.unpack("> 8) + 1 #ah + unknown3 = struct.unpack(" 0: + f = struct.unpack("<"+str(num0)+"f",file.read(4*num0)) + + childCnt = struct.unpack("> 8 #ah + + if format & 0b10: + uvCount += 1 + + unkF = struct.unpack("> 8 + format = formatRaw & 0xFF + + vertexCount = struct.unpack("> 8 + format = formatRaw & 0xFF + vertexCount = struct.unpack(" 0: + if format == 0: + # objString[len(objString)-1] = objString[-2] + pass + else: + for i in range(vertexCount): + vertex = struct.unpack("<3f",file.read(12)) + uv = struct.unpack("<2f",file.read(8)) + for j in range(uvCount): + uv = struct.unpack("<2f",file.read(8)) + if format == 1 or format == 2: + normal = struct.unpack("<3f",file.read(12)) + elif format == 3: + normal_off = struct.unpack(" 0: + for add in curLevel: + for block in blocks18[add]: + # log.debug(block) + g_spaces.add(block['space_name']) + g_objs.add(block['add_name']) + objs.add(block['add_name']) + curLevel = list(objs) + objs = set() + + spaces = [cn for cn in list(g_spaces) if cn != EMPTY_NAME] + objs = list(g_objs) + + spaces.sort() + objs.sort(reverse=True) + + + outfilename = os.path.join(outdir, '{}.b3d'.format(extBlock)) + with open(outfilename, 'wb') as outFile: + outFile.write(b'b3d\x00') + outFile.write(struct.pack("