many text work and scripts

This commit is contained in:
sShemet
2025-12-08 20:28:56 +05:00
parent 37dca7ee15
commit c09c3db56c
697 changed files with 12308 additions and 9392 deletions

250
p2coll_importer.py Normal file
View 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()