From 1eaaedfcbd5cbb8b4ebe381047d467534114846e Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Thu, 26 Oct 2023 12:32:08 +0800 Subject: [PATCH] finish ballance element --- bbp_ng/PROP_ballance_element.py | 98 +++++++++++++++++++++++++++++++-- bbp_ng/UTIL_file_io.py | 87 +++++++++++++++++++++++++++++ bbp_ng/UTIL_virtools_types.py | 80 +++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 bbp_ng/UTIL_file_io.py diff --git a/bbp_ng/PROP_ballance_element.py b/bbp_ng/PROP_ballance_element.py index f76c754..990ac25 100644 --- a/bbp_ng/PROP_ballance_element.py +++ b/bbp_ng/PROP_ballance_element.py @@ -1,6 +1,6 @@ import bpy -import os, typing, enum -from . import UTIL_functions, UTIL_blender_mesh +import os, typing, enum, array +from . import UTIL_functions, UTIL_file_io, UTIL_blender_mesh, UTIL_virtools_types #region Raw Elements Operations @@ -86,11 +86,97 @@ def get_ballance_elements() -> bpy.types.CollectionProperty: #endregion -#region +#region Element Loader -def load_element(mesh: bpy.types.Mesh, element_id: int) -> None: +def _save_element(mesh: bpy.types.Mesh, filename: str) -> None: + # todo: if we need add element placeholder save operator, + # write this function and call this function in operator. pass +def _load_element(mesh: bpy.types.Mesh, element_id: int) -> None: + # resolve mesh path + element_name: str | None = get_ballance_element_name(element_id) + if element_name is None: + raise UTIL_functions.BBPException('invalid element id in _load_element()') + + element_filename: str = os.path.join( + os.path.dirname(__file__), + "meshes", + element_name + '.bin' + ) + + # open file and read + with open(element_filename, 'rb') as fmesh: + # prepare container + vpos: array.array = array.array('f') + vnml: array.array = array.array('f') + face: array.array = array.array('L') + + # read data + # position is vector3 + vpos_count = UTIL_file_io.read_uint32(fmesh) + vpos.extend(UTIL_file_io.read_float_array(fmesh, vpos_count * 3)) + # normal is vector3 + vnml_count = UTIL_file_io.read_uint32(fmesh) + vnml.extend(UTIL_file_io.read_float_array(fmesh, vnml_count * 3)) + # each face use 6 uint32 to describe, + # they are: pos1, nml1, pos2, nml2, pos3, nml3. + # each item is a 0 based index refering to corresponding list + face_count = UTIL_file_io.read_uint32(fmesh) + face.extend(UTIL_file_io.read_uint32_array(fmesh, face_count * 6)) + + # open mesh writer and write data + with UTIL_blender_mesh.MeshWriter(mesh) as writer: + # prepare writer essential function + mesh_part: UTIL_blender_mesh.MeshWriter.MeshWriterPartData = UTIL_blender_mesh.MeshWriter.MeshWriterPartData() + def vpos_iterator() -> typing.Iterator[UTIL_virtools_types.VxVector3]: + v: UTIL_virtools_types.VxVector3 = UTIL_virtools_types.VxVector3() + for i in range(vpos_count): + idx: int = i * 3 + v.x = vpos[idx] + v.y = vpos[idx + 1] + v.z = vpos[idx + 2] + yield v + mesh_part.mVertexPosition = vpos_iterator() + def vnml_iterator() -> typing.Iterator[UTIL_virtools_types.VxVector3]: + v: UTIL_virtools_types.VxVector3 = UTIL_virtools_types.VxVector3() + for i in range(vnml_count): + idx: int = i * 3 + v.x = vnml[idx] + v.y = vnml[idx + 1] + v.z = vnml[idx + 2] + yield v + mesh_part.mVertexNormal = vnml_iterator() + def vuv_iterator() -> typing.Iterator[UTIL_virtools_types.VxVector2]: + v: UTIL_virtools_types.VxVector2 = UTIL_virtools_types.VxVector2() + yield v + mesh_part.mVertexUV = vuv_iterator() + def mtl_iterator() -> typing.Iterator[bpy.types.Material]: + pass + mesh_part.mMaterial = mtl_iterator() + def face_iterator() -> typing.Iterator[UTIL_blender_mesh.FaceData]: + # create face data with 3 placeholder + f: UTIL_blender_mesh.FaceData = UTIL_blender_mesh.FaceData([UTIL_blender_mesh.FaceVertexData() for i in range(3)]) + for i in range(face_count): + idx: int = i * 6 + f.mIndices[0].mPosIdx = face[idx] + f.mIndices[0].mNmlIdx = face[idx + 1] + f.mIndices[1].mPosIdx = face[idx + 2] + f.mIndices[1].mNmlIdx = face[idx + 3] + f.mIndices[2].mPosIdx = face[idx + 4] + f.mIndices[2].mNmlIdx = face[idx + 5] + yield f + mesh_part.mFace = face_iterator() + + writer.add_part(mesh_part) + + # end of with writer + # write mesh data + + # end of with fmesh + # close file + + #endregion #region Ballance Elements Operation Help Class & Functions @@ -154,7 +240,7 @@ class BallanceElementsHelper(): raise UTIL_functions.BBPException('invalid element id') new_mesh: bpy.types.Mesh = bpy.data.meshes.new(get_ballance_element_name(element_id)) - load_element(new_mesh, element_id) + _load_element(new_mesh, element_id) self.__mElementMap[element_id] = new_mesh return new_mesh @@ -199,7 +285,7 @@ def reset_ballance_elements() -> None: if eleid is None or item.mesh_ptr is None: invalid_idx.append(index) else: - load_element(item.mesh_ptr, eleid) + _load_element(item.mesh_ptr, eleid) # inc counter index += 1 diff --git a/bbp_ng/UTIL_file_io.py b/bbp_ng/UTIL_file_io.py new file mode 100644 index 0000000..7f18e24 --- /dev/null +++ b/bbp_ng/UTIL_file_io.py @@ -0,0 +1,87 @@ +import bpy, mathutils +import struct, os, io, typing +from . import UTIL_virtools_types + +_FileWriter_t = io.BufferedWriter +_FileReader_t = io.BufferedReader + +#region Writer Functions + +def write_string(fs: _FileWriter_t, strl: str) -> None: + count = len(strl) + write_uint32(fs, count) + fs.write(strl.encode("utf_32_le")) + +def write_uint8(fs: _FileWriter_t, num: int) -> None: + fs.write(struct.pack(" None: + fs.write(struct.pack(" None: + fs.write(struct.pack(" None: + if boolean: + write_uint8(fs, 1) + else: + write_uint8(fs, 0) + +def write_float(fs: _FileWriter_t, fl: float) -> None: + fs.write(struct.pack(" None: + fs.write(struct.pack("<16f", *mat.to_tuple())) + +def write_color(fs: _FileWriter_t, colors: UTIL_virtools_types.VxColor) -> None: + fs.write(struct.pack(" None: + fs.write(struct.pack('<' + str(count) + 'I', *vals)) + +def write_float_array(fs: _FileWriter_t, vals: typing.Iterable[float], count: int) -> None: + fs.write(struct.pack('<' + str(count) + 'f', *vals)) + +#endregion + +#region Reader Functions + +def peek_stream(fs: _FileReader_t) -> bytes: + res = fs.read(1) + fs.seek(-1, os.SEEK_CUR) + return res + +def read_float(fs: _FileReader_t) -> float: + return struct.unpack("f", fs.read(4))[0] + +def read_uint8(fs: _FileReader_t) -> int: + return struct.unpack("B", fs.read(1))[0] + +def read_uint32(fs: _FileReader_t) -> int: + return struct.unpack("I", fs.read(4))[0] + +def read_uint64(fs: _FileReader_t) -> int: + return struct.unpack("Q", fs.read(8))[0] + +def read_string(fs: _FileReader_t) -> str: + count = read_uint32(fs) + return fs.read(count * 4).decode("utf_32_le") + +def read_bool(fs: _FileReader_t) -> None: + return read_uint8(fs) != 0 + +def read_world_materix(fs: _FileReader_t, mat: UTIL_virtools_types.VxMatrix) -> None: + mat.from_tuple(struct.unpack("<16f", fs.read(16 * 4))) + +def read_color(fs: _FileReader_t, target: UTIL_virtools_types.VxColor) -> None: + target.from_tuple_rgb(struct.unpack("fff", fs.read(3 * 4))) + +def read_uint32_array(fs: _FileReader_t, count: int) -> tuple[int, ...]: + fmt: struct.Struct = struct.Struct('<' + str(count) + 'I') + return fmt.unpack(fs.read(fmt.size)) + +def read_float_array(fs: _FileReader_t, count: int) -> tuple[float, ...]: + fmt: struct.Struct = struct.Struct('<' + str(count) + 'f') + return fmt.unpack(fs.read(fmt.size)) + +#endregion diff --git a/bbp_ng/UTIL_virtools_types.py b/bbp_ng/UTIL_virtools_types.py index 8a3067b..8559144 100644 --- a/bbp_ng/UTIL_virtools_types.py +++ b/bbp_ng/UTIL_virtools_types.py @@ -1,3 +1,4 @@ +import mathutils import typing, enum from . import UTIL_functions @@ -100,6 +101,85 @@ class VxColor(): self.g = UTIL_functions.clamp_float(self.g, 0.0, 1.0) self.b = UTIL_functions.clamp_float(self.b, 0.0, 1.0) +class VxMatrix(): + """ + The Matrix representation. + The bracket statement exactly equal with Virtools. + """ + __mData: list[list[float]] + + def __init__(self): + # init array + self.__mData = [[0] * 4 for i in range(4)] + # set to identy + self.reset() + + def from_tuple(self, data: tuple[float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float]) -> None: + ( + self.__mData[0][0], self.__mData[0][1], self.__mData[0][2], self.__mData[0][3], + self.__mData[1][0], self.__mData[1][1], self.__mData[1][2], self.__mData[1][3], + self.__mData[2][0], self.__mData[2][1], self.__mData[2][2], self.__mData[2][3], + self.__mData[3][0], self.__mData[3][1], self.__mData[3][2], self.__mData[3][3] + ) = data + + def from_blender(self, data_: mathutils.Matrix) -> None: + # transposed first + data: mathutils.Matrix = data_.transposed() + ( + self.__mData[0][0], self.__mData[0][1], self.__mData[0][2], self.__mData[0][3], + self.__mData[1][0], self.__mData[1][1], self.__mData[1][2], self.__mData[1][3], + self.__mData[2][0], self.__mData[2][1], self.__mData[2][2], self.__mData[2][3], + self.__mData[3][0], self.__mData[3][1], self.__mData[3][2], self.__mData[3][3] + ) = ( + data[0][0], data[0][1], data[0][2], data[0][3], + data[1][0], data[1][1], data[1][2], data[1][3], + data[2][0], data[2][1], data[2][2], data[2][3], + data[3][0], data[3][1], data[3][2], data[3][3] + ) + + def to_tuple(self) -> tuple[float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float]: + return ( + self.__mData[0][0], self.__mData[0][1], self.__mData[0][2], self.__mData[0][3], + self.__mData[1][0], self.__mData[1][1], self.__mData[1][2], self.__mData[1][3], + self.__mData[2][0], self.__mData[2][1], self.__mData[2][2], self.__mData[2][3], + self.__mData[3][0], self.__mData[3][1], self.__mData[3][2], self.__mData[3][3] + ) + + def to_tuple(self) -> mathutils.Matrix: + data: mathutils.Matrix = mathutils.Matrix( + (self.__mData[0][0], self.__mData[0][1], self.__mData[0][2], self.__mData[0][3]), + (self.__mData[1][0], self.__mData[1][1], self.__mData[1][2], self.__mData[1][3]), + (self.__mData[2][0], self.__mData[2][1], self.__mData[2][2], self.__mData[2][3]), + (self.__mData[3][0], self.__mData[3][1], self.__mData[3][2], self.__mData[3][3]), + ) + # transpose self + data.transpose() + return data + + def reset(self) -> None: + # reset to identy + for i in range(4): + for j in range(4): + self.__mData[i][j] = 0.0 + + self.__mData[0][0] = 1.0 + self.__mData[1][1] = 1.0 + self.__mData[2][2] = 1.0 + self.__mData[3][3] = 1.0 + + def __swap_row_column(self) -> None: + # swap column 1 and 2 + for i in range(4): + self.__mData[i][1], self.__mData[i][2] = self.__mData[i][2], self.__mData[i][1] + # swap row 1 and 2 + for i in range(4): + self.__mData[1][i], self.__mData[2][i] = self.__mData[2][i], self.__mData[1][i] + + def to_virtools_matrix(self) -> None: + self.__swap_row_column() + + def to_blender_matrix(self) -> None: + self.__swap_row_column() class VXTEXTURE_BLENDMODE(enum.IntEnum): """!