many text work and scripts
This commit is contained in:
250
p2coll_importer.py
Normal file
250
p2coll_importer.py
Normal file
@@ -0,0 +1,250 @@
|
||||
import bpy
|
||||
import struct
|
||||
import math
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from bpy.props import StringProperty
|
||||
from bpy.types import Operator
|
||||
|
||||
bl_info = {
|
||||
"name": "Persona2 PSX Scene Collisions Importer",
|
||||
"author": "Sergey Shemet",
|
||||
"version": (1, 0),
|
||||
"blender": (4, 5, 0),
|
||||
"location": "File > Import",
|
||||
"description": "Imports Persona 2 Eternal Punishment PSX 3D scenes Collisions (.p2coll)",
|
||||
"category": "Import-Export",
|
||||
}
|
||||
|
||||
class P2CollisionImporter(Operator, ImportHelper):
|
||||
"""Import Persona 2 PSX Collisions"""
|
||||
bl_idname = "import_scene.p2coll"
|
||||
bl_label = "Import P2Coll"
|
||||
bl_options = {'PRESET', 'UNDO'}
|
||||
|
||||
filename_ext = ".p2coll"
|
||||
filter_glob: StringProperty(
|
||||
default="*.p2coll",
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
return read_p2coll_file(context, self.filepath)
|
||||
|
||||
def create_cylinder_at_location(location, radius, height, vertices=16, name="cylinder"):
|
||||
"""Создает цилиндр с заданными параметрами в указанной позиции"""
|
||||
# Создаем цилиндр
|
||||
bpy.ops.mesh.primitive_cylinder_add(
|
||||
vertices=vertices,
|
||||
radius=radius,
|
||||
depth=height,
|
||||
location=location
|
||||
)
|
||||
|
||||
# Получаем созданный объект
|
||||
cylinder = context.active_object
|
||||
cylinder.name = name
|
||||
|
||||
return cylinder
|
||||
|
||||
def read_p2coll_file(context, filepath):
|
||||
"""Чтение файла .p2coll и создание объектов в Blender"""
|
||||
|
||||
with open(filepath, 'rb') as file:
|
||||
# Чтение всего файла
|
||||
data = file.read()
|
||||
|
||||
# Чтение блоков из заголовка
|
||||
# Первый блок: vertexMap
|
||||
vertex_map_size = struct.unpack_from('<I', data, 0)[0]
|
||||
vertex_map_block_offset = struct.unpack_from('<I', data, 4)[0]
|
||||
|
||||
# Второй блок: collisionLines
|
||||
collision_lines_size = struct.unpack_from('<I', data, 8)[0]
|
||||
collision_lines_block_offset = struct.unpack_from('<I', data, 12)[0]
|
||||
|
||||
# Третий блок: arrow (теперь cylCollision)
|
||||
cyl_collision_size = struct.unpack_from('<I', data, 16)[0]
|
||||
cyl_collision_block_offset = struct.unpack_from('<I', data, 20)[0]
|
||||
|
||||
print(f"Vertex count: {vertex_map_size}")
|
||||
print(f"Wall collisions count: {collision_lines_size}")
|
||||
print(f"Cylinder collisions count: {cyl_collision_size}")
|
||||
|
||||
# Чтение массива вершин
|
||||
vertices = []
|
||||
vertex_data_offset = vertex_map_block_offset
|
||||
|
||||
for i in range(vertex_map_size):
|
||||
offset = vertex_data_offset + i * 8 # 8 bytes per coord struct (4 x short)
|
||||
if offset + 8 <= len(data):
|
||||
x, y, z, d = struct.unpack_from('<4h', data, offset)
|
||||
# Преобразование координат: y=z, z=-y, деление на 100
|
||||
vertices.append((
|
||||
x / 100.0,
|
||||
z / 100.0,
|
||||
-y / 100.0
|
||||
))
|
||||
|
||||
# Создание коллекции
|
||||
coll_collection_name = "PSX_Collisions"
|
||||
|
||||
# Проверяем, существует ли коллекция
|
||||
if coll_collection_name in bpy.data.collections:
|
||||
coll_collection = bpy.data.collections[coll_collection_name]
|
||||
else:
|
||||
coll_collection = bpy.data.collections.new(coll_collection_name)
|
||||
context.scene.collection.children.link(coll_collection)
|
||||
|
||||
# Сохраняем активную коллекцию для возврата
|
||||
current_collection = context.collection
|
||||
|
||||
# Чтение и создание объектов коллизий (стен)
|
||||
collision_data_offset = collision_lines_block_offset
|
||||
|
||||
for i in range(collision_lines_size):
|
||||
offset = collision_data_offset + i * 8 # 8 bytes per collisionObj struct
|
||||
if offset + 8 <= len(data):
|
||||
# Чтение данных коллизии
|
||||
vert_id1 = struct.unpack_from('<H', data, offset)[0]
|
||||
vert_id2 = struct.unpack_from('<H', data, offset + 2)[0]
|
||||
col_id = struct.unpack_from('<H', data, offset + 4)[0]
|
||||
collis_height = struct.unpack_from('<B', data, offset + 6)[0]
|
||||
collis_flag = struct.unpack_from('<B', data, offset + 7)[0]
|
||||
|
||||
print(f"Wall Collision {i}: v1={vert_id1}, v2={vert_id2}, height={collis_height}, flag={collis_flag}")
|
||||
|
||||
# Проверка, что индексы вершин валидны
|
||||
if vert_id1 < len(vertices) and vert_id2 < len(vertices):
|
||||
# Создание меша для стены
|
||||
mesh = bpy.data.meshes.new(f"wall_collision_{i}")
|
||||
obj = bpy.data.objects.new(f"wall_collision_{i}", mesh)
|
||||
|
||||
# Получаем координаты вершин
|
||||
v1 = vertices[vert_id1]
|
||||
v2 = vertices[vert_id2]
|
||||
|
||||
# Высота стены
|
||||
height = collis_height / 100.0
|
||||
|
||||
# Создаём вершины для прямоугольника (стены)
|
||||
verts = [
|
||||
(v1[0], v1[1], v1[2]), # Нижняя левая
|
||||
(v2[0], v2[1], v2[2]), # Нижняя правая
|
||||
(v2[0], v2[1], v2[2] + height), # Верхняя правая
|
||||
(v1[0], v1[1], v1[2] + height), # Верхняя левая
|
||||
]
|
||||
|
||||
# Грани (два треугольника для прямоугольника)
|
||||
faces = [(0, 1, 2, 3)]
|
||||
|
||||
# Создание меша
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
mesh.update()
|
||||
|
||||
# Добавляем метаданные
|
||||
obj["collisHeight"] = collis_height
|
||||
obj["collisFlag"] = collis_flag
|
||||
obj["collisionId"] = col_id
|
||||
obj["vertexId1"] = vert_id1
|
||||
obj["vertexId2"] = vert_id2
|
||||
obj["collisionType"] = "wall"
|
||||
|
||||
# Добавляем объект в коллекцию
|
||||
coll_collection.objects.link(obj)
|
||||
|
||||
obj.update_tag()
|
||||
|
||||
# Отключаем отображение в основной сцене
|
||||
if obj.name in context.scene.collection.objects:
|
||||
context.scene.collection.objects.unlink(obj)
|
||||
else:
|
||||
print(f"Warning: Invalid vertex indices in wall collision {i}: {vert_id1}, {vert_id2}")
|
||||
|
||||
# Чтение и создание цилиндрических коллизий
|
||||
cyl_collision_data_offset = cyl_collision_block_offset
|
||||
|
||||
for i in range(cyl_collision_size):
|
||||
offset = cyl_collision_data_offset + i * 8 # 8 bytes per cylCollisionObj struct
|
||||
if offset + 8 <= len(data):
|
||||
# Чтение данных цилиндрической коллизии
|
||||
coords_vertex = struct.unpack_from('<H', data, offset)[0]
|
||||
radius = struct.unpack_from('<H', data, offset + 2)[0] # Радиус вместо вращения
|
||||
object_id = struct.unpack_from('<H', data, offset + 4)[0]
|
||||
collis_height = struct.unpack_from('<B', data, offset + 6)[0]
|
||||
collis_flag = struct.unpack_from('<B', data, offset + 7)[0]
|
||||
|
||||
print(f"Cylinder Collision {i}: vertex={coords_vertex}, radius={radius}, height={collis_height}, flag={collis_flag}")
|
||||
|
||||
# Проверка, что индекс вершины валиден
|
||||
if coords_vertex < len(vertices):
|
||||
# Устанавливаем активную коллекцию для создания объектов
|
||||
context.view_layer.active_layer_collection = context.view_layer.layer_collection.children[coll_collection.name]
|
||||
|
||||
# Получаем позицию для цилиндра
|
||||
pos = vertices[coords_vertex]
|
||||
|
||||
# Преобразуем радиус и высоту (делим на 100)
|
||||
cyl_radius = radius / 100.0
|
||||
cyl_height = collis_height / 100.0
|
||||
|
||||
# Создаем цилиндр
|
||||
bpy.ops.mesh.primitive_cylinder_add(
|
||||
vertices=16, # Количество вершин для гладкости
|
||||
radius=cyl_radius,
|
||||
depth=cyl_height,
|
||||
location=pos,
|
||||
rotation=(0, 0, 0) # Без вращения
|
||||
)
|
||||
|
||||
# Получаем созданный объект
|
||||
cyl_obj = context.active_object
|
||||
cyl_obj.name = f"cyl_collision_{i}"
|
||||
|
||||
# Смещаем цилиндр так, чтобы его основание было в позиции вершины
|
||||
cyl_obj.location.z += cyl_height / 2.0
|
||||
|
||||
# Добавляем метаданные
|
||||
cyl_obj["collisHeight"] = collis_height
|
||||
cyl_obj["collisFlag"] = collis_flag
|
||||
cyl_obj["objectId"] = object_id
|
||||
cyl_obj["vertexId"] = coords_vertex
|
||||
cyl_obj["radius"] = radius
|
||||
cyl_obj["collisionType"] = "cylinder"
|
||||
|
||||
# Обновляем объект
|
||||
cyl_obj.update_tag()
|
||||
|
||||
# Убеждаемся, что объект в правильной коллекции
|
||||
if coll_collection.name not in cyl_obj.users_collection:
|
||||
# Удаляем из всех текущих коллекций
|
||||
for coll in cyl_obj.users_collection:
|
||||
coll.objects.unlink(cyl_obj)
|
||||
# Добавляем в нашу коллекцию
|
||||
coll_collection.objects.link(cyl_obj)
|
||||
|
||||
else:
|
||||
print(f"Warning: Invalid vertex index in cylinder collision {i}: {coords_vertex}")
|
||||
|
||||
# Возвращаем активную коллекцию
|
||||
context.view_layer.active_layer_collection = context.view_layer.layer_collection
|
||||
|
||||
# Выводим информацию о результате
|
||||
print(f"Импортировано {len(vertices)} вершин")
|
||||
print(f"Создано {collision_lines_size} стен-коллизий")
|
||||
print(f"Создано {cyl_collision_size} цилиндрических коллизий")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(P2CollisionImporter.bl_idname, text="Persona 2 Collisions (.p2coll)")
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(P2CollisionImporter)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(P2CollisionImporter)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Reference in New Issue
Block a user