From f8a178bdf0707488dcaf3de4ed7f1d26380ff165 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 11 Sep 2024 12:52:54 +0200 Subject: [PATCH 01/35] Reloading improvements --- Arma3ObjectBuilder/__init__.py | 26 ++++++++++++------------ Arma3ObjectBuilder/io/__init__.py | 26 ++++++++++++++++++++++++ Arma3ObjectBuilder/props/__init__.py | 9 ++++++++ Arma3ObjectBuilder/ui/__init__.py | 24 ++++++++++++++++++++++ Arma3ObjectBuilder/utilities/__init__.py | 20 ++++++++++++++++++ 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index fb0e3bf..12a3b6b 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -13,19 +13,19 @@ if "bpy" in locals(): - import importlib - - importlib.reload(props) - importlib.reload(ui) - importlib.reload(flagutils) + io.reload() + props.reload() + ui.reload() + utilities.reload() -else: - from . import props - from . import ui - from .utilities import flags as flagutils import bpy +from . import io +from . import props +from . import ui +from . import utilities + def outliner_enable_update(self, context): if self.outliner == 'ENABLED' and ui.tool_outliner.depsgraph_update_post_handler not in bpy.app.handlers.depsgraph_update_post: @@ -124,13 +124,13 @@ def poll(cls, context): def invoke(self, context, event): prefs = context.preferences.addons[__package__].preferences - flagutils.set_flag_vertex(self, prefs.flag_vertex) + utilities.flags.set_flag_vertex(self, prefs.flag_vertex) return context.window_manager.invoke_props_dialog(self) def execute(self, context): prefs = context.preferences.addons[__package__].preferences - prefs.flag_vertex = flagutils.get_flag_vertex(self) + prefs.flag_vertex = utilities.flags.get_flag_vertex(self) return {'FINISHED'} @@ -176,13 +176,13 @@ def poll(cls, context): def invoke(self, context, event): prefs = context.preferences.addons[__package__].preferences - flagutils.set_flag_face(self, prefs.flag_face) + utilities.flags.set_flag_face(self, prefs.flag_face) return context.window_manager.invoke_props_dialog(self) def execute(self, context): prefs = context.preferences.addons[__package__].preferences - prefs.flag_face = flagutils.get_flag_face(self) + prefs.flag_face = utilities.flags.get_flag_face(self) return {'FINISHED'} diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index 033507d..7cc664b 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -1,4 +1,5 @@ from . import binary_handler +from . import compression from . import data_asc from . import data_p3d from . import data_rap @@ -8,8 +9,33 @@ from . import export_mcfg from . import export_p3d from . import export_rtm +from . import export_tbcsv from . import import_armature from . import import_asc from . import import_mcfg from . import import_p3d from . import import_rtm +from . import import_tbcsv + + +def reload(): + import importlib + + importlib.reload(binary_handler) + importlib.reload(compression) + importlib.reload(data_asc) + importlib.reload(data_p3d) + importlib.reload(data_rap) + importlib.reload(data_rtm) + importlib.reload(data_tbcsv) + importlib.reload(export_asc) + importlib.reload(export_mcfg) + importlib.reload(export_p3d) + importlib.reload(export_rtm) + importlib.reload(export_tbcsv) + importlib.reload(import_armature) + importlib.reload(import_asc) + importlib.reload(import_mcfg) + importlib.reload(import_p3d) + importlib.reload(import_rtm) + importlib.reload(import_tbcsv) diff --git a/Arma3ObjectBuilder/props/__init__.py b/Arma3ObjectBuilder/props/__init__.py index 4a423a7..68f3a50 100644 --- a/Arma3ObjectBuilder/props/__init__.py +++ b/Arma3ObjectBuilder/props/__init__.py @@ -2,3 +2,12 @@ from . import material from . import object from . import scene + + +def reload(): + import importlib + + importlib.reload(action) + importlib.reload(material) + importlib.reload(object) + importlib.reload(scene) diff --git a/Arma3ObjectBuilder/ui/__init__.py b/Arma3ObjectBuilder/ui/__init__.py index 6a07273..798443a 100644 --- a/Arma3ObjectBuilder/ui/__init__.py +++ b/Arma3ObjectBuilder/ui/__init__.py @@ -17,3 +17,27 @@ from . import tool_scripts from . import tool_utilities from . import tool_validation + + +def reload(): + import importlib + + importlib.reload(import_export_armature) + importlib.reload(import_export_asc) + importlib.reload(import_export_mcfg) + importlib.reload(import_export_p3d) + importlib.reload(import_export_rtm) + importlib.reload(import_export_tbcsv) + importlib.reload(props_action) + importlib.reload(props_material) + importlib.reload(props_object_mesh) + importlib.reload(tool_outliner) + importlib.reload(tool_hitpoint) + importlib.reload(tool_mass) + importlib.reload(tool_materials) + importlib.reload(tool_paths) + importlib.reload(tool_proxies) + importlib.reload(tool_rigging) + importlib.reload(tool_scripts) + importlib.reload(tool_utilities) + importlib.reload(tool_validation) diff --git a/Arma3ObjectBuilder/utilities/__init__.py b/Arma3ObjectBuilder/utilities/__init__.py index ebd8615..9b9e6e3 100644 --- a/Arma3ObjectBuilder/utilities/__init__.py +++ b/Arma3ObjectBuilder/utilities/__init__.py @@ -13,3 +13,23 @@ from . import rigging from . import structure from . import validator + + +def reload(): + import importlib + + importlib.reload(clouds) + importlib.reload(colors) + importlib.reload(compat) + importlib.reload(data) + importlib.reload(flags) + importlib.reload(generic) + importlib.reload(lod) + importlib.reload(logger) + importlib.reload(masses) + importlib.reload(outliner) + importlib.reload(proxy) + importlib.reload(renaming) + importlib.reload(rigging) + importlib.reload(structure) + importlib.reload(validator) From 064cff6418b0351c68d3b2a47be3d390cc12a5ce Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 17 Sep 2024 01:00:15 +0200 Subject: [PATCH 02/35] Small fixes and improvements to P3D IO --- Arma3ObjectBuilder/io/data_p3d.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index fd787d7..e81e7e6 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -40,12 +40,16 @@ def __init__(self): def read(cls, file, length = 0): output = cls() - for i in range(length // 8): - point_1, point_2 = binary.read_ulongs(file, 2) - + count_values = length // 4 + data = binary.read_ulongs(file, count_values) + + for i in range(0, count_values, 2): + point_1 = data[i] + point_2 = data[i + 1] + if point_1 == point_2: continue - + output.edges.append((point_1, point_2)) return output @@ -98,8 +102,7 @@ def length(self): return len(self.masses) * 4 def write(self, file): - for value in self.masses.values(): - binary.write_float(file, value) + binary.write_float(file, *self.masses.values()) class P3D_TAGG_DataUVSet(): @@ -110,10 +113,10 @@ def __init__(self): @classmethod def read(cls, file, length = 0): output = cls() - count_values = (length - 4) // 4 output.id = binary.read_ulong(file) + count_values = (length - 4) // 4 data = binary.read_floats(file, count_values) - output.uvs = {i: (data[i * 2], 1 - data[i * 2 + 1]) for i in range(count_values // 2)} + output.uvs = {i // 2: (data[i], 1 - data[i + 1]) for i in range(0, count_values, 2)} return output @@ -209,17 +212,19 @@ def read(cls, file, count_verts, count_faces): output.name = binary.read_asciiz(file) length = binary.read_ulong(file) - if output.name == "#EndOfFile#": if length != 0: raise P3D_Error("Invalid EOF") output.active = False elif output.name == "#SharpEdges#": + if length % 8 != 0: + raise P3D_Error("Invalid sharp edges length: %d" % length) + output.data = P3D_TAGG_DataSharpEdges.read(file, length) elif output.name == "#Property#": if length != 128: - raise P3D_Error("Invalid name property length: %d" % length) + raise P3D_Error("Invalid named property length: %d" % length) output.data = P3D_TAGG_DataProperty.read(file) elif output.name == "#Mass#": @@ -252,7 +257,7 @@ def is_proxy(self): if not self.name.startswith("proxy:"): return False - regex_proxy = "proxy:.*\.\d+" + regex_proxy = r"proxy:.*\.\d+" return re.match(regex_proxy, self.name) def is_selection(self): @@ -652,7 +657,7 @@ def get_sections_merged(self, materials): def renumber_components(self): counter = 1 for tagg in self.taggs: - if not re.match("component\d+", tagg.name, re.IGNORECASE): + if not re.match(r"component\d+", tagg.name, re.IGNORECASE): continue tagg.name = "component%02d" % counter @@ -665,7 +670,7 @@ def renumber_components(self): # they have to be replaced by formatted placeholders and later looked up # from a dictionary when needed. def proxies_to_placeholders(self): - regex_proxy = "proxy:(.*)\.(\d+)" + regex_proxy = r"proxy:(.*)\.(\d+)" lookup = {} From 61cb7fbbdba8073f30ff1a8b8f043b772548fd4e Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 00:32:22 +0200 Subject: [PATCH 03/35] Added type hints to base reader functions --- Arma3ObjectBuilder/io/binary_handler.py | 73 +++++++++++++------------ Arma3ObjectBuilder/io/compression.py | 20 ++++--- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/Arma3ObjectBuilder/io/binary_handler.py b/Arma3ObjectBuilder/io/binary_handler.py index 1b78e38..bbd1337 100644 --- a/Arma3ObjectBuilder/io/binary_handler.py +++ b/Arma3ObjectBuilder/io/binary_handler.py @@ -4,42 +4,43 @@ import struct +from io import BufferedReader, BufferedWriter -def read_byte(file): +def read_byte(file: BufferedReader) -> int: return struct.unpack('B', file.read(1))[0] -def read_bytes(file, count = 1): +def read_bytes(file: BufferedReader, count: int = 1) -> list[int]: return [read_byte(file) for i in range(count)] -def read_bool(file): +def read_bool(file: BufferedReader) -> bool: return read_byte(file) != 0 -def read_short(file): +def read_short(file: BufferedReader) -> int: return struct.unpack(' tuple[int]: return struct.unpack('<%dh' % count, file.read(2 * count)) -def read_ushort(file): +def read_ushort(file: BufferedReader) -> int: return struct.unpack(' tuple[int]: return struct.unpack('<%dH' % count, file.read(2 * count)) -def read_long(file): +def read_long(file: BufferedReader) -> int: return struct.unpack(' tuple[int]: return struct.unpack('<%di' % count, file.read(4 * count)) -def read_ulong(file): +def read_ulong(file: BufferedReader) -> int: return struct.unpack(' int: return struct.unpack('<%dI' % count, file.read(4 * count)) -def read_compressed_uint(file): +def read_compressed_uint(file: BufferedReader) -> int: output = read_byte(file) extra = output @@ -51,32 +52,32 @@ def read_compressed_uint(file): return output -def read_half(file): +def read_half(file: BufferedReader) -> int: return struct.unpack(' tuple[int]: return struct.unpack('<%de' % count, file.read(2 * count)) -def read_float(file): +def read_float(file: BufferedReader) -> float: return struct.unpack(' tuple[float]: return struct.unpack('<%df' % count, file.read(4 * count)) -def read_double(file): +def read_double(file: BufferedReader) -> float: return struct.unpack(' float: return struct.unpack('<%dd' % count, file.read(8 * count)) -def read_char(file, count = 1): +def read_char(file: BufferedReader, count: int = 1) -> str: chars = struct.unpack('%ds' % count, file.read(count))[0] return chars.decode('ascii') # In theory all strings in BI files should be strictly ASCII, # but on the off chance that a corrupt character is present, the method would fail. # Therefore using UTF-8 decoding is more robust, and gives the same result for valid ASCII values. -def read_asciiz(file): +def read_asciiz(file: BufferedReader) -> str: res = b'' while True: @@ -88,7 +89,7 @@ def read_asciiz(file): return res.decode('utf8', errors="replace") -def read_asciiz_field(file, field_len): +def read_asciiz_field(file: BufferedReader, field_len: int) -> str: field = file.read(field_len) if len(field) < field_len: raise EOFError("ASCIIZ field ran into unexpected EOF") @@ -104,7 +105,7 @@ def read_asciiz_field(file, field_len): return result.decode('utf8', errors="replace") -def read_lascii(file): +def read_lascii(file: BufferedReader) -> str: length = read_byte(file) value = file.read(length) if len(value) != length: @@ -112,25 +113,25 @@ def read_lascii(file): return value.decode('utf8', errors="replace") -def write_byte(file, *args): +def write_byte(file: BufferedWriter, *args) -> None: file.write(struct.pack('%dB' % len(args), *args)) -def write_bool(file, value): +def write_bool(file: BufferedWriter, value: bool) -> None: write_byte(file, value) -def write_short(file, *args): +def write_short(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%dh' % len(args), *args)) -def write_ushort(file, *args): +def write_ushort(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%dH' % len(args), *args)) -def write_long(file, *args): +def write_long(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%di' % len(args), *args)) -def write_ulong(file, *args): +def write_ulong(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%dI' % len(args), *args)) -def write_compressed_uint(file, value): +def write_compressed_uint(file: BufferedWriter, value: int) -> None: temp = value while True: if temp < 128: @@ -140,28 +141,28 @@ def write_compressed_uint(file, value): write_byte(file, (temp & 127) + 128) temp = temp >> 7 -def write_half(file, *args): +def write_half(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%de' % len(args), *args)) -def write_float(file, *args): +def write_float(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%df' % len(args), *args)) -def write_double(file, *args): +def write_double(file: BufferedWriter, *args: int) -> None: file.write(struct.pack('<%dd' % len(args), *args)) -def write_chars(file, values): +def write_chars(file: BufferedWriter, values: str) -> None: file.write(struct.pack('<%ds' % len(values), values.encode('ascii'))) -def write_asciiz(file, value): +def write_asciiz(file: BufferedWriter, value: str) -> None: file.write(struct.pack('<%ds' % (len(value) + 1), value.encode('ascii'))) -def write_asciiz_field(file, value, field_len): +def write_asciiz_field(file: BufferedWriter, value: str, field_len: int) -> None: if (len(value) + 1) > field_len: raise ValueError("ASCIIZ value is longer (%d + 1) than field length (%d)" % (len(value), field_len)) file.write(struct.pack('<%ds' % field_len, value.encode('ascii'))) -def write_lascii(file, value): +def write_lascii(file: BufferedWriter, value: str) -> None: length = len(value) if length > 255: raise ValueError("LASCII string cannot be longer than 255 characters") diff --git a/Arma3ObjectBuilder/io/compression.py b/Arma3ObjectBuilder/io/compression.py index e411cb3..9616a54 100644 --- a/Arma3ObjectBuilder/io/compression.py +++ b/Arma3ObjectBuilder/io/compression.py @@ -2,6 +2,8 @@ import struct +from io import BufferedReader +from collections import Iterable class LZO_Error(Exception): @@ -16,34 +18,34 @@ def __str__(self): # https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/lzo.c # The original LZO implementations as defined by Markus F.X.J. Oberhumer: # https://www.oberhumer.com/opensource/lzo/ -def lzo1x_decompress(file, expected): +def lzo1x_decompress(file: BufferedReader, expected: int) -> tuple[int, bytearray]: state = 0 start = file.tell() output = bytearray() - def check_free_space(length): + def check_free_space(length: int) -> None: free_space = expected - len(output) if free_space < length: raise LZO_Error("Output overrun (free buffer: %d, match length: %d)" % (free_space, length)) - def read1(): + def read1() -> int: return struct.unpack('B', file.read(1))[0] - def read(size): + def read(size: int) -> tuple[int]: return struct.unpack('%dB' % size, file.read(size)) - def read_le16(): + def read_le16() -> int: return struct.unpack(' None: nonlocal output output.extend(items) - def copy_literal(length): + def copy_literal(length: int) -> None: check_free_space(length) extend(read(length)) - def copy_match(distance, length): + def copy_match(distance: int, length: int) -> None: output_length = len(output) if output_length < distance: @@ -59,7 +61,7 @@ def copy_match(distance, length): extend(output[start:] * (length // distance)) # copy as many whole chunks as possible extend(output[start:(start + (length % distance))]) # copy remainder - def get_length(x, mask): + def get_length(x: int, mask: int) -> None: length = x & mask if not length: while True: From 2f8b57486291529164f36b75e4908adfa544803d Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 00:51:53 +0200 Subject: [PATCH 04/35] Mass data structure update Changed the internal mass storage to be a simple list instead of dictionary --- Arma3ObjectBuilder/io/compression.py | 3 +-- Arma3ObjectBuilder/io/data_p3d.py | 6 +++--- Arma3ObjectBuilder/io/export_p3d.py | 3 +-- Arma3ObjectBuilder/io/import_p3d.py | 1 + 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Arma3ObjectBuilder/io/compression.py b/Arma3ObjectBuilder/io/compression.py index 9616a54..5eead2f 100644 --- a/Arma3ObjectBuilder/io/compression.py +++ b/Arma3ObjectBuilder/io/compression.py @@ -3,7 +3,6 @@ import struct from io import BufferedReader -from collections import Iterable class LZO_Error(Exception): @@ -37,7 +36,7 @@ def read(size: int) -> tuple[int]: def read_le16() -> int: return struct.unpack(' None: + def extend(items: tuple[int]) -> None: nonlocal output output.extend(items) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index e81e7e6..1f41e4d 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -89,12 +89,12 @@ def write(self, file): class P3D_TAGG_DataMass(): def __init__(self): - self.masses = {} + self.masses = () @classmethod def read(cls, file, count_verts): output = cls() - output.masses = {i: value for i, value in enumerate(binary.read_floats(file, count_verts))} + output.masses = binary.read_floats(file, count_verts) return output @@ -102,7 +102,7 @@ def length(self): return len(self.masses) * 4 def write(self, file): - binary.write_float(file, *self.masses.values()) + binary.write_float(file, *self.masses) class P3D_TAGG_DataUVSet(): diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index 38a34e6..651835f 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -530,8 +530,7 @@ def process_tagg_mass(bm, layer): output.name = "#Mass#" output.data = p3d.P3D_TAGG_DataMass() - for vert in bm.verts: - output.data.masses[vert.index] = vert[layer] + output.data.masses = [vert[layer] for vert in bm.verts] return output diff --git a/Arma3ObjectBuilder/io/import_p3d.py b/Arma3ObjectBuilder/io/import_p3d.py index 77db8b4..fb40ecc 100644 --- a/Arma3ObjectBuilder/io/import_p3d.py +++ b/Arma3ObjectBuilder/io/import_p3d.py @@ -152,6 +152,7 @@ def process_mass(bm, lod): for tagg in lod.taggs: if tagg.name == "#Mass#": data = tagg.data + break if not data: return From 8796f51e587d851dca20bcc0d40efbb6bb23a408 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 01:08:07 +0200 Subject: [PATCH 05/35] UV set data structure update --- Arma3ObjectBuilder/io/data_p3d.py | 8 ++++---- Arma3ObjectBuilder/io/export_p3d.py | 8 +------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 1f41e4d..0caae4c 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -108,7 +108,7 @@ def write(self, file): class P3D_TAGG_DataUVSet(): def __init__(self): self.id = 0 - self.uvs = {} + self.uvs = [] @classmethod def read(cls, file, length = 0): @@ -116,7 +116,7 @@ def read(cls, file, length = 0): output.id = binary.read_ulong(file) count_values = (length - 4) // 4 data = binary.read_floats(file, count_values) - output.uvs = {i // 2: (data[i], 1 - data[i + 1]) for i in range(0, count_values, 2)} + output.uvs = [(data[i], 1 - data[i + 1]) for i in range(0, count_values, 2)] return output @@ -125,8 +125,8 @@ def length(self): def write(self, file): binary.write_ulong(file, self.id) - for value in self.uvs.values(): - binary.write_float(file, value[0], 1 - value[1]) + for u, v in self.uvs: + binary.write_float(file, u, 1 - v) class P3D_TAGG_DataSelection(): diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index 651835f..d7f1d03 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -504,13 +504,7 @@ def process_tagg_uvset(bm, layer): output = p3d.P3D_TAGG() output.name = "#UVSet#" output.data = p3d.P3D_TAGG_DataUVSet() - uvs = {} - - for face in bm.faces: - for loop in face.loops: - uvs[loop.index] = (loop[layer].uv[0], loop[layer].uv[1]) - - output.data.uvs = dict(sorted(uvs.items())) + output.data.uvs = [(loop[layer].uv[0], loop[layer].uv[1]) for face in bm.faces for loop in face.loops] return output From 6e50bc2a355b324d64b0749cdf25e679979aee21 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 01:30:17 +0200 Subject: [PATCH 06/35] Vertex data structure update --- Arma3ObjectBuilder/io/data_p3d.py | 18 +++++++++--------- Arma3ObjectBuilder/io/export_p3d.py | 9 ++------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 0caae4c..3667cf0 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -421,7 +421,7 @@ def __init__(self): self.flags = 0x00000000 self.resolution = P3D_LOD_Resolution() - self.verts = {} + self.verts = [] self.normals = {} self.faces = {} self.taggs = [] @@ -437,7 +437,7 @@ def read_vert(file): return x, y, z, flag def read_verts(self, file, count_verts): - self.verts = {i: self.read_vert(file) for i in range(count_verts)} + self.verts = [self.read_vert(file) for i in range(count_verts)] @staticmethod def read_normal(file): @@ -516,8 +516,8 @@ def write_vert(self, file, vert): file.write(struct.pack(' Date: Wed, 18 Sep 2024 01:42:34 +0200 Subject: [PATCH 07/35] Normals data structure update --- Arma3ObjectBuilder/io/data_p3d.py | 17 ++++++++++------- Arma3ObjectBuilder/io/export_p3d.py | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 3667cf0..1687c34 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -422,7 +422,7 @@ def __init__(self): self.resolution = P3D_LOD_Resolution() self.verts = [] - self.normals = {} + self.normals = [] self.faces = {} self.taggs = [] @@ -445,7 +445,7 @@ def read_normal(file): return -x, -y, -z def read_normals(self, file, count_normals): - self.normals = {i: self.read_normal(file) for i in range(count_normals)} + self.normals = [self.read_normal(file) for i in range(count_normals)] @staticmethod def read_face(file): @@ -523,8 +523,8 @@ def write_normal(self, file, normal): file.write(struct.pack(' Date: Wed, 18 Sep 2024 01:49:56 +0200 Subject: [PATCH 08/35] Face data structure update --- Arma3ObjectBuilder/io/data_p3d.py | 34 +++++++++++++---------------- Arma3ObjectBuilder/io/export_p3d.py | 4 ++-- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 1687c34..b699157 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -423,7 +423,7 @@ def __init__(self): self.verts = [] self.normals = [] - self.faces = {} + self.faces = [] self.taggs = [] def __eq__(self, other): @@ -470,7 +470,7 @@ def read_face(file): return [vertices, normals, uvs, texture, material, flag] def read_faces(self, file, count_faces): - self.faces = {i: self.read_face(file) for i in range(count_faces)} + self.faces = [self.read_face(file) for i in range(count_faces)] @classmethod def read(cls, file): @@ -541,8 +541,8 @@ def write_face(self, file, face): binary.write_asciiz(file, face[4]) def write_faces(self, file): - for i in self.faces: - self.write_face(file, self.faces[i]) + for face in self.faces: + self.write_face(file, face) def write(self, file): @@ -591,7 +591,7 @@ def renormalize_normals(self): def pydata(self): verts = [vert[0:3] for vert in self.verts] - faces = [self.faces[idx][0] for idx in self.faces] + faces = [face[0] for face in self.faces] return verts, [], faces @@ -602,8 +602,7 @@ def clean_taggs(self): # from the parent MLOD object, so the dictionary is edited in place, not # returned. def get_materials(self, materials = {}): - for item in self.faces: - face = self.faces[item] + for face in self.faces: texture = face[3] material = face[4] @@ -619,8 +618,7 @@ def get_sections(self, materials): last_idx = None face_idx = 0 - for i in self.faces: - face = self.faces[i] + for face in self.faces: texture = face[3] material = face[4] @@ -642,8 +640,7 @@ def get_sections_merged(self, materials): slot_indices = [] material_slot_indices = {} - for i in self.faces: - face = self.faces[i] + for face in self.faces: texture = face[3] material = face[4] @@ -709,7 +706,7 @@ def placeholders_to_proxies(self, lookup): # of all UVSets, unique by ID. If UVSet 0 is also found as a TAGG, the TAGG # data takes precedence over the embedded values. def uvsets(self): - sets = {0: [uv for idx in self.faces for uv in self.faces[idx][2]]} + sets = {0: [uv for face in self.faces for uv in face[2]]} for tagg in self.taggs: if tagg.name != "#UVSet#": continue @@ -721,7 +718,7 @@ def uvsets(self): # Generate loop normals list that can be directly used by the Blender API # mesh.normals_split_custom_set() function def loop_normals(self): - return [self.normals[item] for face in self.faces.values() for item in face[1]] + return [self.normals[item] for face in self.faces for item in face[1]] # Collect and group the used vertex flag values for setting up # the flag data layer and flag groups object data. @@ -744,23 +741,22 @@ def flag_groups_vertex(self): # the flag data layer and flag groups object data. def flag_groups_face(self): groups = {} - values = {} + values = [] - for idx in self.faces: - flag = self.faces[idx][5] + for face in self.faces: + flag = face[5] group = groups.get(flag) if group is None: group = len(groups) groups[flag] = group - values[idx] = group + values.append(group) return list(groups.keys()), values # Change every file path, and selection name to lower case for a uniform output. def force_lowercase(self): - for idx in self.faces: - face = self.faces[idx] + for face in self.faces: face[3] = face[3].lower() face[4] = face[4].lower() diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index 013478b..e3f693b 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -438,7 +438,7 @@ def process_materials(obj, relative): # Produce the face data dictionary from the obj and bmesh data. # {face 0: ([vert 0, vert 1, vert 2], [normal 0, normal 1, normal 2], [(uv 0 0, uv 0 1), (...), ...], texture, material, flag), ...} def process_faces(obj, bm, normals_lookup, relative): - output = {} + output = [] # Materials need to be precompiled to speed up the face access. materials = process_materials(obj, relative) @@ -458,7 +458,7 @@ def process_faces(obj, bm, normals_lookup, relative): normals.append(normals_lookup[loop.index]) uvs.append((loop[uv_layer].uv[0], 1 - loop[uv_layer].uv[1]) if uv_layer else (0, 0)) - output[face.index] = [verts, normals, uvs, *materials[face.material_index], face[flag_layer]] + output.append([verts, normals, uvs, *materials[face.material_index], face[flag_layer]]) return output From 045668473892ee43020b7c1f2d625c9d091ea694 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 14:32:10 +0200 Subject: [PATCH 09/35] Rolled back typehints Turns out that the Python 3.7.7 that Blender 2.90 comes with did not yet have the subscriptable type syntax yet --- Arma3ObjectBuilder/io/binary_handler.py | 73 ++++++++++++------------- Arma3ObjectBuilder/io/compression.py | 19 +++---- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/Arma3ObjectBuilder/io/binary_handler.py b/Arma3ObjectBuilder/io/binary_handler.py index bbd1337..1b78e38 100644 --- a/Arma3ObjectBuilder/io/binary_handler.py +++ b/Arma3ObjectBuilder/io/binary_handler.py @@ -4,43 +4,42 @@ import struct -from io import BufferedReader, BufferedWriter -def read_byte(file: BufferedReader) -> int: +def read_byte(file): return struct.unpack('B', file.read(1))[0] -def read_bytes(file: BufferedReader, count: int = 1) -> list[int]: +def read_bytes(file, count = 1): return [read_byte(file) for i in range(count)] -def read_bool(file: BufferedReader) -> bool: +def read_bool(file): return read_byte(file) != 0 -def read_short(file: BufferedReader) -> int: +def read_short(file): return struct.unpack(' tuple[int]: +def read_shorts(file, count = 1): return struct.unpack('<%dh' % count, file.read(2 * count)) -def read_ushort(file: BufferedReader) -> int: +def read_ushort(file): return struct.unpack(' tuple[int]: +def read_ushorts(file, count = 1): return struct.unpack('<%dH' % count, file.read(2 * count)) -def read_long(file: BufferedReader) -> int: +def read_long(file): return struct.unpack(' tuple[int]: +def read_longs(file, count = 1): return struct.unpack('<%di' % count, file.read(4 * count)) -def read_ulong(file: BufferedReader) -> int: +def read_ulong(file): return struct.unpack(' int: +def read_ulongs(file, count = 1): return struct.unpack('<%dI' % count, file.read(4 * count)) -def read_compressed_uint(file: BufferedReader) -> int: +def read_compressed_uint(file): output = read_byte(file) extra = output @@ -52,32 +51,32 @@ def read_compressed_uint(file: BufferedReader) -> int: return output -def read_half(file: BufferedReader) -> int: +def read_half(file): return struct.unpack(' tuple[int]: +def read_halfs(file, count = 1): return struct.unpack('<%de' % count, file.read(2 * count)) -def read_float(file: BufferedReader) -> float: +def read_float(file): return struct.unpack(' tuple[float]: +def read_floats(file, count = 1): return struct.unpack('<%df' % count, file.read(4 * count)) -def read_double(file: BufferedReader) -> float: +def read_double(file): return struct.unpack(' float: +def read_doubles(file, count = 1): return struct.unpack('<%dd' % count, file.read(8 * count)) -def read_char(file: BufferedReader, count: int = 1) -> str: +def read_char(file, count = 1): chars = struct.unpack('%ds' % count, file.read(count))[0] return chars.decode('ascii') # In theory all strings in BI files should be strictly ASCII, # but on the off chance that a corrupt character is present, the method would fail. # Therefore using UTF-8 decoding is more robust, and gives the same result for valid ASCII values. -def read_asciiz(file: BufferedReader) -> str: +def read_asciiz(file): res = b'' while True: @@ -89,7 +88,7 @@ def read_asciiz(file: BufferedReader) -> str: return res.decode('utf8', errors="replace") -def read_asciiz_field(file: BufferedReader, field_len: int) -> str: +def read_asciiz_field(file, field_len): field = file.read(field_len) if len(field) < field_len: raise EOFError("ASCIIZ field ran into unexpected EOF") @@ -105,7 +104,7 @@ def read_asciiz_field(file: BufferedReader, field_len: int) -> str: return result.decode('utf8', errors="replace") -def read_lascii(file: BufferedReader) -> str: +def read_lascii(file): length = read_byte(file) value = file.read(length) if len(value) != length: @@ -113,25 +112,25 @@ def read_lascii(file: BufferedReader) -> str: return value.decode('utf8', errors="replace") -def write_byte(file: BufferedWriter, *args) -> None: +def write_byte(file, *args): file.write(struct.pack('%dB' % len(args), *args)) -def write_bool(file: BufferedWriter, value: bool) -> None: +def write_bool(file, value): write_byte(file, value) -def write_short(file: BufferedWriter, *args: int) -> None: +def write_short(file, *args): file.write(struct.pack('<%dh' % len(args), *args)) -def write_ushort(file: BufferedWriter, *args: int) -> None: +def write_ushort(file, *args): file.write(struct.pack('<%dH' % len(args), *args)) -def write_long(file: BufferedWriter, *args: int) -> None: +def write_long(file, *args): file.write(struct.pack('<%di' % len(args), *args)) -def write_ulong(file: BufferedWriter, *args: int) -> None: +def write_ulong(file, *args): file.write(struct.pack('<%dI' % len(args), *args)) -def write_compressed_uint(file: BufferedWriter, value: int) -> None: +def write_compressed_uint(file, value): temp = value while True: if temp < 128: @@ -141,28 +140,28 @@ def write_compressed_uint(file: BufferedWriter, value: int) -> None: write_byte(file, (temp & 127) + 128) temp = temp >> 7 -def write_half(file: BufferedWriter, *args: int) -> None: +def write_half(file, *args): file.write(struct.pack('<%de' % len(args), *args)) -def write_float(file: BufferedWriter, *args: int) -> None: +def write_float(file, *args): file.write(struct.pack('<%df' % len(args), *args)) -def write_double(file: BufferedWriter, *args: int) -> None: +def write_double(file, *args): file.write(struct.pack('<%dd' % len(args), *args)) -def write_chars(file: BufferedWriter, values: str) -> None: +def write_chars(file, values): file.write(struct.pack('<%ds' % len(values), values.encode('ascii'))) -def write_asciiz(file: BufferedWriter, value: str) -> None: +def write_asciiz(file, value): file.write(struct.pack('<%ds' % (len(value) + 1), value.encode('ascii'))) -def write_asciiz_field(file: BufferedWriter, value: str, field_len: int) -> None: +def write_asciiz_field(file, value, field_len): if (len(value) + 1) > field_len: raise ValueError("ASCIIZ value is longer (%d + 1) than field length (%d)" % (len(value), field_len)) file.write(struct.pack('<%ds' % field_len, value.encode('ascii'))) -def write_lascii(file: BufferedWriter, value: str) -> None: +def write_lascii(file, value): length = len(value) if length > 255: raise ValueError("LASCII string cannot be longer than 255 characters") diff --git a/Arma3ObjectBuilder/io/compression.py b/Arma3ObjectBuilder/io/compression.py index 5eead2f..e411cb3 100644 --- a/Arma3ObjectBuilder/io/compression.py +++ b/Arma3ObjectBuilder/io/compression.py @@ -2,7 +2,6 @@ import struct -from io import BufferedReader class LZO_Error(Exception): @@ -17,34 +16,34 @@ def __str__(self): # https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/lzo.c # The original LZO implementations as defined by Markus F.X.J. Oberhumer: # https://www.oberhumer.com/opensource/lzo/ -def lzo1x_decompress(file: BufferedReader, expected: int) -> tuple[int, bytearray]: +def lzo1x_decompress(file, expected): state = 0 start = file.tell() output = bytearray() - def check_free_space(length: int) -> None: + def check_free_space(length): free_space = expected - len(output) if free_space < length: raise LZO_Error("Output overrun (free buffer: %d, match length: %d)" % (free_space, length)) - def read1() -> int: + def read1(): return struct.unpack('B', file.read(1))[0] - def read(size: int) -> tuple[int]: + def read(size): return struct.unpack('%dB' % size, file.read(size)) - def read_le16() -> int: + def read_le16(): return struct.unpack(' None: + def extend(items): nonlocal output output.extend(items) - def copy_literal(length: int) -> None: + def copy_literal(length): check_free_space(length) extend(read(length)) - def copy_match(distance: int, length: int) -> None: + def copy_match(distance, length): output_length = len(output) if output_length < distance: @@ -60,7 +59,7 @@ def copy_match(distance: int, length: int) -> None: extend(output[start:] * (length // distance)) # copy as many whole chunks as possible extend(output[start:(start + (length % distance))]) # copy remainder - def get_length(x: int, mask: int) -> None: + def get_length(x, mask): length = x & mask if not length: while True: From cb5a3ff779ca3b355c4d7f342fdae33f529b22cb Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 15:28:41 +0200 Subject: [PATCH 10/35] ASCIIZ reading improvements --- Arma3ObjectBuilder/io/binary_handler.py | 26 ++++++++----------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Arma3ObjectBuilder/io/binary_handler.py b/Arma3ObjectBuilder/io/binary_handler.py index 1b78e38..1307f3f 100644 --- a/Arma3ObjectBuilder/io/binary_handler.py +++ b/Arma3ObjectBuilder/io/binary_handler.py @@ -4,6 +4,8 @@ import struct +import functools +import itertools def read_byte(file): @@ -76,33 +78,21 @@ def read_char(file, count = 1): # In theory all strings in BI files should be strictly ASCII, # but on the off chance that a corrupt character is present, the method would fail. # Therefore using UTF-8 decoding is more robust, and gives the same result for valid ASCII values. +# https://stackoverflow.com/a/32775270 def read_asciiz(file): - res = b'' - - while True: - a = file.read(1) - if a == b'\x00' or a == b'': - break - - res += a - - return res.decode('utf8', errors="replace") + eof = iter(functools.partial(file.read, 1), "") + return b"".join(itertools.takewhile(b"\x00".__ne__, eof)).decode('utf8', errors="replace") def read_asciiz_field(file, field_len): field = file.read(field_len) if len(field) < field_len: raise EOFError("ASCIIZ field ran into unexpected EOF") - result = bytearray() - for value in field: - if value == 0: - break - - result.append(value) - else: + parts = field.split(b"\x00") + if len(parts) < 2: raise ValueError("ASCIIZ field length overflow") - return result.decode('utf8', errors="replace") + return parts[0].decode('utf8', errors="replace") def read_lascii(file): length = read_byte(file) From 2e4e54665a88dfd2dece3c256042369b13da65be Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 18 Sep 2024 15:29:41 +0200 Subject: [PATCH 11/35] Selection weights data update --- Arma3ObjectBuilder/io/data_p3d.py | 14 +++++++------- Arma3ObjectBuilder/io/export_p3d.py | 4 ++-- Arma3ObjectBuilder/io/import_p3d.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index b699157..8b9d602 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -133,8 +133,8 @@ class P3D_TAGG_DataSelection(): def __init__(self): self.count_verts = 0 self.count_faces = 0 - self.weight_verts = {} - self.weight_faces = {} + self.weight_verts = [] + self.weight_faces = [] @classmethod def decode_weight(cls, weight): @@ -169,7 +169,7 @@ def read(cls, file, count_verts, count_faces): data_verts = bytearray(file.read(count_verts)) - output.weight_verts = {i: cls.decode_weight(value) for i, value in enumerate(data_verts) if value > 0} + output.weight_verts = [(i, cls.decode_weight(value)) for i, value in enumerate(data_verts) if value > 0] file.read(count_faces) return output @@ -179,12 +179,12 @@ def length(self): def write(self, file): bytes_verts = bytearray(self.count_verts) - for idx in self.weight_verts: - bytes_verts[idx] = self.encode_weight(self.weight_verts[idx]) + for idx, weight in self.weight_verts: + bytes_verts[idx] = self.encode_weight(weight) bytes_faces = bytearray(self.count_faces) - for idx in self.weight_faces: - bytes_faces[idx] = self.encode_weight(self.weight_faces[idx]) + for idx, weight in self.weight_faces: + bytes_faces[idx] = self.encode_weight(weight) file.write(bytes_verts) file.write(bytes_faces) diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index e3f693b..e5e8f5a 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -540,7 +540,7 @@ def process_taggs_selections(obj, bm): for vert in bm.verts: for idx in vert[layer].keys(): - output[idx].data.weight_verts[vert.index] = vert[layer][idx] + output[idx].data.weight_verts.append((vert.index, vert[layer][idx])) # If all vertices of a face belong to a selection, then the face belongs to the # selection as well. @@ -549,7 +549,7 @@ def process_taggs_selections(obj, bm): unique = set(indices) for idx in unique: if indices.count(idx) == len(face.loops): - output[idx].data.weight_faces[face.index] = 1 + output[idx].data.weight_faces.append((face.index, 1)) return output.values() diff --git a/Arma3ObjectBuilder/io/import_p3d.py b/Arma3ObjectBuilder/io/import_p3d.py index fb40ecc..332e0cd 100644 --- a/Arma3ObjectBuilder/io/import_p3d.py +++ b/Arma3ObjectBuilder/io/import_p3d.py @@ -122,8 +122,8 @@ def process_selections(bm, lod): continue weights = tagg.data.weight_verts - for idx in weights: - bm.verts[idx][layer][count_selections] = weights[idx] + for idx, weight in weights: + bm.verts[idx][layer][count_selections] = weight count_selections += 1 From 161b500524a95c7d2fc276e14aab0d268759dd40 Mon Sep 17 00:00:00 2001 From: MrClock Date: Thu, 19 Sep 2024 00:32:22 +0200 Subject: [PATCH 12/35] Selection weight decoding fixes Simplification of logic and correction of conversion formulas --- Arma3ObjectBuilder/__init__.py | 4 ++-- Arma3ObjectBuilder/blender_manifest.toml | 2 +- Arma3ObjectBuilder/io/data_p3d.py | 19 ++++++------------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index 12a3b6b..a6d5a83 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -2,10 +2,10 @@ "name": "Arma 3 Object Builder", "description": "Collection of tools for editing Arma 3 content", "author": "MrClock (present add-on), Hans-Joerg \"Alwarren\" Frieden (original ArmaToolbox add-on)", - "version": (2, 4, 0), + "version": (2, 4, 1, "dev"), "blender": (2, 90, 0), "location": "Object Builder panels", - "warning": "", + "warning": "Development", "doc_url": "https://mrcmodding.gitbook.io/arma-3-object-builder/home", "tracker_url": "https://github.com/MrClock8163/Arma3ObjectBuilder/issues", "category": "Import-Export" diff --git a/Arma3ObjectBuilder/blender_manifest.toml b/Arma3ObjectBuilder/blender_manifest.toml index f8f8ef2..c0dd479 100644 --- a/Arma3ObjectBuilder/blender_manifest.toml +++ b/Arma3ObjectBuilder/blender_manifest.toml @@ -5,7 +5,7 @@ type = "add-on" id = "Arma3ObjectBuilder" name = "Arma 3 Object Builder" tagline = "Comprehensive add-on for modding Arma 3" -version = "2.4.0" +version = "2.4.1-dev" blender_version_min = "4.2.0" website = "https://mrcmodding.gitbook.io/arma-3-object-builder/home" tags = ["Import-Export", "Game Engine", "Object"] diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 8b9d602..34bf58e 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -138,25 +138,17 @@ def __init__(self): @classmethod def decode_weight(cls, weight): - if weight == 0 or weight == 1: + if weight in (0, 1): return weight - - value = (256 - weight) / 255 - if value > 1: - return 0 - - return value + return (255 - weight) / 254 @classmethod def encode_weight(cls, weight): - if weight == 0 or weight == 1: + if weight in (0, 1): return int(weight) - value = round(256 - 255 * weight) - - if value == 256: - return 0 + value = round(255 - 254 * weight) return value @@ -168,9 +160,10 @@ def read(cls, file, count_verts, count_faces): output.count_faces = count_faces data_verts = bytearray(file.read(count_verts)) - output.weight_verts = [(i, cls.decode_weight(value)) for i, value in enumerate(data_verts) if value > 0] file.read(count_faces) + # data_faces = bytearray(file.read(count_faces)) + # output.weight_faces = [(i, cls.decode_weight(value)) for i, value in enumerate(data_faces) if value > 0] return output From f270e33d624dfd7b62b74316eee25c4806266a12 Mon Sep 17 00:00:00 2001 From: MrClock Date: Thu, 19 Sep 2024 23:54:16 +0200 Subject: [PATCH 13/35] LOD signature handling simplification --- Arma3ObjectBuilder/io/data_p3d.py | 154 +++++++++--------- .../scripts/convert_atbx_to_a3ob.py | 2 +- Arma3ObjectBuilder/utilities/data.py | 15 +- Arma3ObjectBuilder/utilities/validator.py | 2 +- 4 files changed, 83 insertions(+), 90 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 34bf58e..f59132b 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -7,7 +7,6 @@ import struct import math import re -from decimal import Decimal, Context from . import binary_handler as binary @@ -274,7 +273,7 @@ class P3D_LOD_Resolution(): HITPOINTS = 13 VIEW_GEOMETRY = 14 FIRE_GEOMETRY = 15 - VIEW_CARGO_GEOMERTRY = 16 + VIEW_CARGO_GEOMETRY = 16 VIEW_CARGO_FIRE_GEOMETRY = 17 VIEW_COMMANDER = 18 VIEW_COMMANDER_GEOMETRY = 19 @@ -288,57 +287,47 @@ class P3D_LOD_Resolution(): SHADOW_VIEW_PILOT = 27 SHADOW_VIEW_GUNNER = 28 WRECKAGE = 29 - UNDERGROUND = 30 # Geometry PhysX Old for Arma 3 + UNDERGROUND = 30 GROUNDLAYER = 31 NAVIGATION = 32 - # SHADOWBUFFER = ... UNKNOWN = -1 - INDEX_MAP = { - (0.0, 0): VISUAL, # Visual - (1.0, 3): VIEW_GUNNER, # View Gunner - (1.1, 3): VIEW_PILOT, # View Pilot - (1.2, 3): VIEW_CARGO, # View Cargo - (1.0, 4): SHADOW, # Shadow - # (1.1, 4): SHADOWBUFFER, - (1.3, 4): GROUNDLAYER, # GroundLayer (VBS) - (2.0, 4): EDIT, # Edit - (1.0, 13): GEOMETRY, # Geometry - (2.0, 13): GEOMETRY_BUOY, # Geometry Buoyancy - (3.0, 13): UNDERGROUND, # Underground (VBS), Geometry PhysX (old) for Arma 3 - (4.0, 13): GEOMETRY_PHYSX, # Geometry PhysX - (5.0, 13): NAVIGATION, # Navigation (VBS) - (1.0, 15): MEMORY, # Memory - (2.0, 15): LANDCONTACT, # Land Contact - (3.0, 15): ROADWAY, # Roadway - (4.0, 15): PATHS, # Paths - (5.0, 15): HITPOINTS, # Hit Points - (6.0, 15): VIEW_GEOMETRY, # View Geometry - (7.0, 15): FIRE_GEOMETRY, # Fire Geometry - (8.0, 15): VIEW_CARGO_GEOMERTRY, # View Cargo Geometry - (9.0, 15): VIEW_CARGO_FIRE_GEOMETRY, # View Cargo Fire Geometry - (1.0, 16): VIEW_COMMANDER, # View Commander - (1.1, 16): VIEW_COMMANDER_GEOMETRY, # View Commander Geometry - (1.2, 16): VIEW_COMMANDER_FIRE_GEOMETRY, # View Commander Fire Geometry - (1.3, 16): VIEW_PILOT_GEOMETRY, # View Pilot Geometry - (1.4, 16): VIEW_PILOT_FIRE_GEOMETRY, # View Pilot Fire Geometry - (1.5, 16): VIEW_GUNNER_GEOMETRY, # View Gunner Geometry - (1.6, 16): VIEW_GUNNER_FIRE_GEOMETRY, # View Gunner Fire Geometry - (1.7, 16): SUBPARTS, # Sub Parts - (1.8, 16): SHADOW_VIEW_CARGO, # Cargo View Shadow Volume - (1.9, 16): SHADOW_VIEW_PILOT, # Pilot View Shadow Volume - (2.0, 16): SHADOW_VIEW_GUNNER, # Gunner View Shadow Volume - (2.1, 16): WRECKAGE, # Wreckage - (-1.0, 0): UNKNOWN # Unknown - } - - RESOLUTION_POS = { # decimal places in normalized format - VIEW_CARGO: 3, - SHADOW: 4, - # SHADOWBUFFER: 4, - EDIT: 4, - VIEW_CARGO_GEOMERTRY: 2, - SHADOW_VIEW_CARGO: 3 + SIGNATURE_MAP = { + # "0.000e+00": VISUAL, # (+ resolution) + "1.000e+03": VIEW_GUNNER, + "1.100e+03": VIEW_PILOT, + # "1.200e+03": VIEW_CARGO, # (+ resolution) + # "1.000e+04": SHADOW, # (+ resolution) + # "1.100e+04": SHADOWBUFFER, + "1.300e+04": GROUNDLAYER, # Shadow Volume 3000 for Arma 3 + # "2.000e+04": EDIT, # (+ resolution) + "1.000e+13": GEOMETRY, + "2.000e+13": GEOMETRY_BUOY, + "3.000e+13": UNDERGROUND, # Geometry PhysX (old) for Arma 3 + "4.000e+13": GEOMETRY_PHYSX, + "5.000e+13": NAVIGATION, # Resolution 5e13 for Arma 3 + "1.000e+15": MEMORY, + "2.000e+15": LANDCONTACT, + "3.000e+15": ROADWAY, + "4.000e+15": PATHS, + "5.000e+15": HITPOINTS, + "6.000e+15": VIEW_GEOMETRY, + "7.000e+15": FIRE_GEOMETRY, + # "8.000e+15": VIEW_CARGO_GEOMETRY, (+ resolution * 1e13) + "9.000e+15": VIEW_CARGO_FIRE_GEOMETRY, + "1.000e+16": VIEW_COMMANDER, + "1.100e+16": VIEW_COMMANDER_GEOMETRY, + "1.200e+16": VIEW_COMMANDER_FIRE_GEOMETRY, + "1.300e+16": VIEW_PILOT_GEOMETRY, + "1.400e+16": VIEW_PILOT_FIRE_GEOMETRY, + "1.500e+16": VIEW_GUNNER_GEOMETRY, + "1.600e+16": VIEW_GUNNER_FIRE_GEOMETRY, + "1.700e+16": SUBPARTS, + # "1.800e+16": SHADOW_VIEW_CARGO, # (+ resolution * 1e13) + "1.900e+16": SHADOW_VIEW_PILOT, + "2.000e+16": SHADOW_VIEW_GUNNER, + "2.100e+16": WRECKAGE, + # (-1.0, 0): UNKNOWN } def __init__(self, lod = 0, res = 0): @@ -354,40 +343,49 @@ def __float__(self): @classmethod def encode(cls, lod, resolution): - if lod == cls.VISUAL or lod == cls.UNKNOWN: - return resolution - - lookup = {v: k for k, v in cls.INDEX_MAP.items()} - - coef, exp = lookup[lod] - pos = cls.RESOLUTION_POS.get(lod, None) - - resolution_sign = (resolution * 10**(exp - pos)) if pos is not None else 0 - - return coef * 10**exp + resolution_sign - + if lod in (cls.VISUAL, cls.UNKNOWN): + return resolution + + lookup = {v: k for k, v in cls.SIGNATURE_MAP.items()} + signature = lookup.get(lod) + if signature is not None: + return float(signature) + + if lod == cls.VIEW_CARGO: + return 1.2e3 + resolution + elif lod == cls.SHADOW: + return 1.0e4 + resolution + elif lod == cls.EDIT: + return 2.0e4 + resolution + elif lod == cls.VIEW_CARGO_GEOMETRY: + return 8.0e15 + resolution * 1e13 + elif lod == cls.SHADOW_VIEW_CARGO: + return 1.8e16 + resolution * 1e13 + @classmethod def decode(cls, signature): if signature < 1e3: return cls.VISUAL, round(signature) - elif 1e4 <= signature < 1.2e4: + elif 1.2e3 <= signature < 1.3e3: + return cls.VIEW_CARGO, round(signature - 1.2e3) + elif 1.0e4 <= signature < 1.2e4: return cls.SHADOW, round(signature - 1e4) - - num = Decimal(signature) - exp = num.normalize(Context(2)).adjusted() - - coef = float((num / 10**exp)) - base = round(coef, 1) if exp in (3, 4, 16) else round(coef) - - lod = cls.INDEX_MAP.get((base, exp), cls.UNKNOWN) - pos = cls.RESOLUTION_POS.get(lod, None) - - if lod == cls.UNKNOWN: - return lod, round(signature) - - resolution = int(round((coef - base) * 10**pos, pos)) if pos is not None else 0 - - return lod, resolution + elif 2e4 <= signature < 3e4: + return cls.EDIT, round(signature - 2e4) + + string = "%.3e" % signature + lod = cls.SIGNATURE_MAP.get(string) + if lod is not None: + return lod, 0 + + exp = int(string[-2:]) + if exp == 15: + return cls.VIEW_CARGO_GEOMETRY, int(string[2:4]) + elif exp == 16: + return cls.SHADOW_VIEW_CARGO, int(string[2:5]) + + print(signature, string) + return cls.UNKNOWN, round(signature) @classmethod def from_float(cls, value): diff --git a/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py b/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py index f31a72b..b33bb8f 100644 --- a/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py +++ b/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py @@ -75,7 +75,7 @@ class Settings: '5.000e+15': str(LOD.HITPOINTS), '6.000e+15': str(LOD.VIEW_GEOMETRY), '7.000e+15': str(LOD.FIRE_GEOMETRY), - '8.000e+15': str(LOD.VIEW_CARGO_GEOMERTRY), + '8.000e+15': str(LOD.VIEW_CARGO_GEOMETRY), '9.000e+15': str(LOD.VIEW_CARGO_FIRE_GEOMETRY), '1.000e+16': str(LOD.VIEW_COMMANDER), '1.100e+16': str(LOD.VIEW_COMMANDER_GEOMETRY), diff --git a/Arma3ObjectBuilder/utilities/data.py b/Arma3ObjectBuilder/utilities/data.py index b23a16d..edc31b3 100644 --- a/Arma3ObjectBuilder/utilities/data.py +++ b/Arma3ObjectBuilder/utilities/data.py @@ -71,9 +71,8 @@ LOD.VISUAL, LOD.VIEW_CARGO, LOD.SHADOW, - # LOD.SHADOWBUFFER, LOD.EDIT, - LOD.VIEW_CARGO_GEOMERTRY, + LOD.VIEW_CARGO_GEOMETRY, LOD.SHADOW_VIEW_CARGO } @@ -90,7 +89,6 @@ lod_shadows = { LOD.SHADOW, - # LOD.SHADOWBUFFER, LOD.SHADOW_VIEW_CARGO, LOD.SHADOW_VIEW_PILOT, LOD.SHADOW_VIEW_GUNNER @@ -103,7 +101,7 @@ LOD.GEOMETRY_PHYSX, LOD.VIEW_GEOMETRY, LOD.FIRE_GEOMETRY, - LOD.VIEW_CARGO_GEOMERTRY, + LOD.VIEW_CARGO_GEOMETRY, LOD.VIEW_CARGO_FIRE_GEOMETRY, LOD.VIEW_COMMANDER_GEOMETRY, LOD.VIEW_COMMANDER_FIRE_GEOMETRY, @@ -127,7 +125,6 @@ LOD.VIEW_PILOT: ("View - Pilot", "First person view"), LOD.VIEW_CARGO: ("View - Cargo", "Passenger first person view"), LOD.SHADOW: ("Shadow Volume", "Shadow casting geometry"), - # LOD.SHADOWBUFFER: ("Shadow Buffer", "Shadow buffer geometry"), LOD.EDIT: ("Edit", "Temporary layer"), LOD.GEOMETRY: ("Geometry", "Object collision geometry and occluders"), LOD.GEOMETRY_BUOY: ("Geometry Buoyancy", "Buoyant object geometry (Displacement for VBS)"), @@ -139,7 +136,7 @@ LOD.HITPOINTS: ("Hit-points", "Hit point cloud"), LOD.VIEW_GEOMETRY: ("View Geometry", "View occlusion for AI"), LOD.FIRE_GEOMETRY: ("Fire Geometry", "Hitbox geometry"), - LOD.VIEW_CARGO_GEOMERTRY: ("View - Cargo Geometry", "Passenger view collision geometry and occluders"), + LOD.VIEW_CARGO_GEOMETRY: ("View - Cargo Geometry", "Passenger view collision geometry and occluders"), LOD.VIEW_CARGO_FIRE_GEOMETRY: ("View - Cargo Fire Geometry", "Passenger view hitbox geometry"), LOD.VIEW_COMMANDER: ("View - Commander", "Commander first person view"), LOD.VIEW_COMMANDER_GEOMETRY: ("View - Commander Geometry", "Commander view collision geometry and occluders"), @@ -174,7 +171,6 @@ **dict.fromkeys( [ LOD.SHADOW, - # LOD.SHADOWBUFFER, LOD.SHADOW_VIEW_CARGO, LOD.SHADOW_VIEW_PILOT, LOD.SHADOW_VIEW_GUNNER @@ -188,7 +184,7 @@ LOD.GEOMETRY_PHYSX, LOD.VIEW_GEOMETRY, LOD.FIRE_GEOMETRY, - LOD.VIEW_CARGO_GEOMERTRY, + LOD.VIEW_CARGO_GEOMETRY, LOD.VIEW_CARGO_FIRE_GEOMETRY, LOD.VIEW_COMMANDER_GEOMETRY, LOD.VIEW_COMMANDER_FIRE_GEOMETRY, @@ -236,7 +232,7 @@ LOD.VIEW_GUNNER, LOD.VIEW_PILOT, LOD.VIEW_CARGO, - LOD.VIEW_CARGO_GEOMERTRY, + LOD.VIEW_CARGO_GEOMETRY, LOD.VIEW_CARGO_FIRE_GEOMETRY, LOD.VIEW_COMMANDER, LOD.VIEW_COMMANDER_GEOMETRY, @@ -254,7 +250,6 @@ **dict.fromkeys( [ LOD.SHADOW, - # LOD.SHADOWBUFFER, LOD.GEOMETRY ], "General" diff --git a/Arma3ObjectBuilder/utilities/validator.py b/Arma3ObjectBuilder/utilities/validator.py index f9083e8..515fa71 100644 --- a/Arma3ObjectBuilder/utilities/validator.py +++ b/Arma3ObjectBuilder/utilities/validator.py @@ -719,7 +719,7 @@ def setup_lod_specific(self): str(LOD.GEOMETRY_PHYSX), str(LOD.VIEW_GEOMETRY), str(LOD.FIRE_GEOMETRY), - str(LOD.VIEW_CARGO_GEOMERTRY), + str(LOD.VIEW_CARGO_GEOMETRY), str(LOD.VIEW_CARGO_FIRE_GEOMETRY), str(LOD.VIEW_COMMANDER_GEOMETRY), str(LOD.VIEW_COMMANDER_FIRE_GEOMETRY), From 2920c926d84dac9a870530f6357f71111fe7deb6 Mon Sep 17 00:00:00 2001 From: MrClock Date: Fri, 20 Sep 2024 12:47:00 +0200 Subject: [PATCH 14/35] Serious typo fix --- Arma3ObjectBuilder/io/data_p3d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index f59132b..75db1ac 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -382,7 +382,7 @@ def decode(cls, signature): if exp == 15: return cls.VIEW_CARGO_GEOMETRY, int(string[2:4]) elif exp == 16: - return cls.SHADOW_VIEW_CARGO, int(string[2:5]) + return cls.SHADOW_VIEW_CARGO, int(string[3:5]) print(signature, string) return cls.UNKNOWN, round(signature) From e08775e088e2f31ddc1f6716a5f5cb0ded760334 Mon Sep 17 00:00:00 2001 From: MrClock Date: Fri, 20 Sep 2024 14:25:08 +0200 Subject: [PATCH 15/35] Update to chunk skipping in P3D Changed the consuming reads to relative position seeks --- Arma3ObjectBuilder/io/data_p3d.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 75db1ac..58aaf76 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -21,7 +21,7 @@ def __str__(self): class P3D_TAGG_DataEmpty(): @classmethod def read(cls, file, length): - file.read(length) + file.seek(length, 1) return cls() def length(self): @@ -160,7 +160,7 @@ def read(cls, file, count_verts, count_faces): data_verts = bytearray(file.read(count_verts)) output.weight_verts = [(i, cls.decode_weight(value)) for i, value in enumerate(data_verts) if value > 0] - file.read(count_faces) + file.seek(count_faces, 1) # skip face selection data # data_faces = bytearray(file.read(count_faces)) # output.weight_faces = [(i, cls.decode_weight(value)) for i, value in enumerate(data_faces) if value > 0] @@ -223,11 +223,10 @@ def read(cls, file, count_verts, count_faces): output.data = P3D_TAGG_DataMass.read(file, count_verts) elif output.name == "#UVSet#": output.data = P3D_TAGG_DataUVSet.read(file, length) - elif not output.name.startswith("#") and not output.name.endswith("#"): + elif output.is_selection(): output.data = P3D_TAGG_DataSelection.read(file, count_verts, count_faces) else: - # Consume unneeded TAGGs - file.read(length) + file.seek(length, 1) # Skip unneeded TAGG data output.active = False return output @@ -452,7 +451,7 @@ def read_face(file): uvs.append((u, 1 - v)) if count_sides < 4: - file.read(16) + file.seek(16, 1) flag = binary.read_ulong(file) texture = binary.read_asciiz(file) From db5325908ce912616a9ee86dc49b53e05fd31c98 Mon Sep 17 00:00:00 2001 From: MrClock Date: Fri, 20 Sep 2024 14:31:49 +0200 Subject: [PATCH 16/35] Added LOD property lowercasing The named properties were mistakenyl not lowercased if the Force Lowercasse option was enabled --- Arma3ObjectBuilder/io/data_p3d.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index 58aaf76..d66f4e2 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -753,6 +753,9 @@ def force_lowercase(self): for tagg in self.taggs: if tagg.is_selection(): tagg.name = tagg.name.lower() + elif type(tagg.data) is P3D_TAGG_DataProperty: + tagg.data.key = tagg.data.key.lower() + tagg.data.value = tagg.data.value.lower() class P3D_MLOD(): From 61db5a855501587e43b8d2b9a62a0df9b4be34ae Mon Sep 17 00:00:00 2001 From: MrClock Date: Fri, 20 Sep 2024 15:28:40 +0200 Subject: [PATCH 17/35] Update CHANGELOG.md --- Arma3ObjectBuilder/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Arma3ObjectBuilder/CHANGELOG.md b/Arma3ObjectBuilder/CHANGELOG.md index edea7f3..4e92927 100644 --- a/Arma3ObjectBuilder/CHANGELOG.md +++ b/Arma3ObjectBuilder/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v2.4.1-dev (Blender 2.90 -> 4.2) + +### Changes + +- internal handling of the P3D data was simplified and improved +- named properties are now properly lowercased with the Force Lowercase export option + ## [v2.4.0](https://github.com/MrClock8163/Arma3ObjectBuilder/releases/tag/v2.4.0) (Blender 2.90 -> 4.2) ### Added From 53150a88806349cfcb0fe6aedd64cce12f34b9d6 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:02:20 +0200 Subject: [PATCH 18/35] Moved simplified export manager to IO module --- Arma3ObjectBuilder/io/__init__.py | 39 +++++++++++++++++++ Arma3ObjectBuilder/utilities/generic.py | 50 ------------------------- 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index 7cc664b..eb0712d 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -1,3 +1,6 @@ +import os +from datetime import datetime + from . import binary_handler from . import compression from . import data_asc @@ -18,6 +21,42 @@ from . import import_tbcsv +class ExportFile(): + def __init__(self, filepath, mode, backup, preserve_faulty): + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + self.filepath = filepath + self.temppath = "%s.%s.temp" % (filepath, timestamp) + self.mode = mode + self.file = None + self.backup_old = backup + self.preserve_faulty = preserve_faulty + + def __enter__(self): + file = open(self.temppath, self.mode) + self.file = file + + return file + + def __exit__(self, exc_type, exc_value, traceback): + self.file.close() + + if exc_type is None: + if os.path.isfile(self.filepath) and self.backup_old: + self.force_rename(self.filepath, self.filepath + ".bak") + + self.force_rename(self.temppath, self.filepath) + + elif not self.preserve_faulty: + os.remove(self.temppath) + + @staticmethod + def force_rename(old, new): + if os.path.isfile(new): + os.remove(new) + + os.rename(old, new) + + def reload(): import importlib diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index e395929..b3c33e8 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -3,7 +3,6 @@ import os import json -from datetime import datetime from contextlib import contextmanager import bpy @@ -98,55 +97,6 @@ def query_bmesh(obj): bm.free() -class OutputManager(): - def __init__(self, filepath, mode = "w"): - timestamp = datetime.now().strftime("%Y%m%d%H%M%S") - self.filepath = filepath - self.temppath = "%s.%s.temp" % (filepath, timestamp) - self.backup = "%s.%s.bak" % (filepath, timestamp) - self.mode = mode - self.file = None - self.success = False - - def __enter__(self): - file = open(self.temppath, self.mode) - self.file = file - - return file - - def __exit__(self, exc_type, exc_value, exc_tb): - self.file.close() - addon_prefs = get_addon_preferences() - - if self.success: - if os.path.isfile(self.filepath) and addon_prefs.create_backups: - self.force_rename(self.filepath, self.filepath + ".bak") - - self.force_rename(self.temppath, self.filepath) - - elif not addon_prefs.preserve_faulty_output: - os.remove(self.temppath) - - return False - - @staticmethod - def force_rename(old, new): - if os.path.isfile(new): - os.remove(new) - - os.rename(old, new) - - # Very dirty check, but works for now - @staticmethod - def can_access_path(path): - try: - open(path, "ab").close() - os.rename(path, path) - return True - except: - return False - - def get_loose_components(obj): mesh = obj.data mesh.calc_loop_triangles() From 950d989d18fecf3d176f535ad3dcc74630871d38 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:03:30 +0200 Subject: [PATCH 19/35] Simplified P3D export handling --- Arma3ObjectBuilder/ui/import_export_p3d.py | 44 +++++++++------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/Arma3ObjectBuilder/ui/import_export_p3d.py b/Arma3ObjectBuilder/ui/import_export_p3d.py index 02d6322..cf1dcff 100644 --- a/Arma3ObjectBuilder/ui/import_export_p3d.py +++ b/Arma3ObjectBuilder/ui/import_export_p3d.py @@ -4,7 +4,7 @@ import bpy import bpy_extras -from ..io import import_p3d, export_p3d +from ..io import import_p3d, export_p3d, ExportFile from ..utilities import generic as utils @@ -328,33 +328,25 @@ class A3OB_OP_export_p3d(bpy.types.Operator, bpy_extras.io_utils.ExportHelper): def draw(self, context): pass - def execute(self, context): - if not utils.OutputManager.can_access_path(self.filepath): - utils.op_report(self, {'ERROR'}, "Cannot write to target file (file likely in use by another blocking process)") + def execute(self, context): + if not export_p3d.can_export(self, context): + utils.op_report(self, {'ERROR'}, "There are no LODs to export") return {'FINISHED'} - if export_p3d.can_export(self, context): - output = utils.OutputManager(self.filepath, "wb") - temp_collection = export_p3d.create_temp_collection(context) - with output as file: - try: - lod_count, exported_count = export_p3d.write_file(self, context, file, temp_collection) - if lod_count == exported_count: - utils.op_report(self, {'INFO'}, "Successfully exported all %d LODs (check the logs in the system console)" % exported_count) - else: - utils.op_report(self, {'WARNING'}, "Only exported %d/%d LODs (check the logs in the system console)" % (exported_count, lod_count)) - output.success = True - except export_p3d.p3d.P3D_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() - - if not utils.get_addon_preferences().preserve_preprocessed_lods: - export_p3d.cleanup_temp_collection(temp_collection) - - else: - utils.op_report(self, {'ERROR'}, "There are no LODs to export") + addon_prefs = utils.get_addon_preferences() + backup = addon_prefs.create_backups + preserve = addon_prefs.preserve_faulty_output + temp_collection = export_p3d.create_temp_collection(context) + + with ExportFile(self.filepath, "wb", backup, preserve) as file: + lod_count, exported_count = export_p3d.write_file(self, context, file, temp_collection) + if lod_count == exported_count: + utils.op_report(self, {'INFO'}, "Successfully exported all %d LODs (check the logs in the system console)" % exported_count) + else: + utils.op_report(self, {'WARNING'}, "Only exported %d/%d LODs (check the logs in the system console)" % (exported_count, lod_count)) + + if not utils.get_addon_preferences().preserve_preprocessed_lods: + export_p3d.cleanup_temp_collection(temp_collection) return {'FINISHED'} From df4693439d9826e1e5fed9940782df01808dd19a Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:07:03 +0200 Subject: [PATCH 20/35] Simplified P3D import handling --- Arma3ObjectBuilder/ui/import_export_p3d.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Arma3ObjectBuilder/ui/import_export_p3d.py b/Arma3ObjectBuilder/ui/import_export_p3d.py index cf1dcff..05d1390 100644 --- a/Arma3ObjectBuilder/ui/import_export_p3d.py +++ b/Arma3ObjectBuilder/ui/import_export_p3d.py @@ -100,17 +100,8 @@ def draw(self, context): def execute(self, context): with open(self.filepath, "rb") as file: - try: - lod_objects = import_p3d.read_file(self, context, file) - utils.op_report(self, {'INFO'}, "Successfully imported %d LODs (check the logs in the system console)" % len(lod_objects)) - except struct.error as ex: - utils.op_report(self, {'ERROR'}, "Unexpected EndOfFile (check the system console)") - traceback.print_exc() - except import_p3d.p3d.P3D_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + lod_objects = import_p3d.read_file(self, context, file) + utils.op_report(self, {'INFO'}, "Successfully imported %d LODs (check the logs in the system console)" % len(lod_objects)) return {'FINISHED'} From 8b21fba51485a1ba2b21602a4233c67e2a70e190 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:20:06 +0200 Subject: [PATCH 21/35] Added export handler factory function --- Arma3ObjectBuilder/io/__init__.py | 2 +- Arma3ObjectBuilder/ui/import_export_p3d.py | 10 ++-------- Arma3ObjectBuilder/utilities/generic.py | 9 +++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index eb0712d..c3c27a8 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -21,7 +21,7 @@ from . import import_tbcsv -class ExportFile(): +class ExportFileHandler(): def __init__(self, filepath, mode, backup, preserve_faulty): timestamp = datetime.now().strftime("%Y%m%d%H%M%S") self.filepath = filepath diff --git a/Arma3ObjectBuilder/ui/import_export_p3d.py b/Arma3ObjectBuilder/ui/import_export_p3d.py index 05d1390..0f04407 100644 --- a/Arma3ObjectBuilder/ui/import_export_p3d.py +++ b/Arma3ObjectBuilder/ui/import_export_p3d.py @@ -1,10 +1,7 @@ -import traceback -import struct - import bpy import bpy_extras -from ..io import import_p3d, export_p3d, ExportFile +from ..io import import_p3d, export_p3d from ..utilities import generic as utils @@ -324,12 +321,9 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "There are no LODs to export") return {'FINISHED'} - addon_prefs = utils.get_addon_preferences() - backup = addon_prefs.create_backups - preserve = addon_prefs.preserve_faulty_output temp_collection = export_p3d.create_temp_collection(context) - with ExportFile(self.filepath, "wb", backup, preserve) as file: + with utils.get_export_handler(self.filepath, "wb") as file: lod_count, exported_count = export_p3d.write_file(self, context, file, temp_collection) if lod_count == exported_count: utils.op_report(self, {'INFO'}, "Successfully exported all %d LODs (check the logs in the system console)" % exported_count) diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index b3c33e8..fdaa041 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -9,6 +9,7 @@ import bpy_extras.mesh_utils as meshutils import bmesh +from ..io import ExportFileHandler from .. import __package__ as addon_name from . import data @@ -49,6 +50,14 @@ def get_addon_preferences(): return bpy.context.preferences.addons[addon_name].preferences +def get_export_handler(filepath, mode): + addon_prefs = get_addon_preferences() + backup = addon_prefs.create_backups + preserve = addon_prefs.preserve_faulty_output + + return ExportFileHandler(filepath, mode, backup, preserve) + + def is_valid_idx(index, subscriptable): return len(subscriptable) > 0 and 0 <= index < len(subscriptable) From b964d6840a72a51a97e41781f2b191379888d5bb Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:21:49 +0200 Subject: [PATCH 22/35] Simplified ASC and armature IO handling --- .../ui/import_export_armature.py | 8 +---- Arma3ObjectBuilder/ui/import_export_asc.py | 32 ++++--------------- 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/Arma3ObjectBuilder/ui/import_export_armature.py b/Arma3ObjectBuilder/ui/import_export_armature.py index 4845cb2..97e5f26 100644 --- a/Arma3ObjectBuilder/ui/import_export_armature.py +++ b/Arma3ObjectBuilder/ui/import_export_armature.py @@ -1,5 +1,3 @@ -import traceback - import bpy import bpy_extras @@ -49,11 +47,7 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "Invalid skeleton definiton, run skeleton validation for RTM for more info") return {'FINISHED'} - try: - arm.import_armature(self, skeleton) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + arm.import_armature(self, skeleton) return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/import_export_asc.py b/Arma3ObjectBuilder/ui/import_export_asc.py index 7a073ed..f25a7c4 100644 --- a/Arma3ObjectBuilder/ui/import_export_asc.py +++ b/Arma3ObjectBuilder/ui/import_export_asc.py @@ -1,5 +1,3 @@ -import traceback - import bpy import bpy_extras @@ -37,14 +35,8 @@ def draw(self, context): def execute(self, context): with open(self.filepath) as file: - try: - import_asc.read_file(self, context, file) - utils.op_report(self, {'INFO'}, "Successfully imported DEM") - except import_asc.asc.ASC_Error as ex: - utils.op_report(self, {'ERROR'}, ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, ex) - traceback.print_exc() + import_asc.read_file(self, context, file) + utils.op_report(self, {'INFO'}, "Successfully imported DTM") return {'FINISHED'} @@ -119,24 +111,12 @@ def poll(cls, context): def draw(self, context): pass - def execute(self, context): - if not utils.OutputManager.can_access_path(self.filepath): - utils.op_report(self, {'ERROR'}, "Cannot write to target file (file likely in use by another blocking process)") - return {'FINISHED'} - + def execute(self, context): obj = context.active_object - output = utils.OutputManager(self.filepath, "w") - with output as file: - try: - export_asc.write_file(self, context, file, obj) - output.success = True - utils.op_report(self, {'INFO'}, "Successfuly exported DTM") - except export_asc.asc.ASC_Error: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + with utils.get_export_handler(self.filepath, "wt") as file: + export_asc.write_file(self, context, file, obj) + utils.op_report(self, {'INFO'}, "Successfuly exported DTM") return {'FINISHED'} From 4b1040b1ae2e8f71d8da3275426c7e6f73b98a53 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 01:31:16 +0200 Subject: [PATCH 23/35] Simplified remaining IO handling --- Arma3ObjectBuilder/ui/import_export_mcfg.py | 26 ++---------- Arma3ObjectBuilder/ui/import_export_rtm.py | 41 ++++-------------- Arma3ObjectBuilder/ui/import_export_tbcsv.py | 44 ++++++-------------- 3 files changed, 26 insertions(+), 85 deletions(-) diff --git a/Arma3ObjectBuilder/ui/import_export_mcfg.py b/Arma3ObjectBuilder/ui/import_export_mcfg.py index 187c5de..7420b6d 100644 --- a/Arma3ObjectBuilder/ui/import_export_mcfg.py +++ b/Arma3ObjectBuilder/ui/import_export_mcfg.py @@ -1,4 +1,3 @@ -import traceback import os import bpy @@ -41,14 +40,7 @@ def draw(self, context): pass def execute(self, context): - count_skeletons = 0 - try: - count_skeletons = import_mcfg.read_file(self, context) - except import_mcfg.rap.RAP_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + count_skeletons = import_mcfg.read_file(self, context) if count_skeletons > 0: utils.op_report(self, {'INFO'}, "Successfully imported %d skeleton(s)" % count_skeletons) @@ -109,27 +101,17 @@ def poll(cls, context): def draw(self, context): pass - def execute(self, context): - if not utils.OutputManager.can_access_path(self.filepath): - utils.op_report(self, {'ERROR'}, "Cannot write to target file (file likely in use by another blocking process)") - return {'FINISHED'} - + def execute(self, context): scene_props = context.scene.a3ob_rigging skeleton = scene_props.skeletons[self.skeleton_index] - output = utils.OutputManager(self.filepath, "w") validator = Validator(ProcessLoggerNull()) if not validator.validate_skeleton(skeleton, False, True): utils.op_report(self, {'ERROR'}, "Invalid skeleton definiton, run skeleton validation for more info") return {'FINISHED'} - with output as file: - try: - export_mcfg.write_file(self, skeleton, file) - output.success = True - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + with utils.get_export_handler(self.filepath, "w") as file: + export_mcfg.write_file(self, skeleton, file) return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/import_export_rtm.py b/Arma3ObjectBuilder/ui/import_export_rtm.py index e4a4725..9bba26e 100644 --- a/Arma3ObjectBuilder/ui/import_export_rtm.py +++ b/Arma3ObjectBuilder/ui/import_export_rtm.py @@ -1,5 +1,3 @@ -import traceback - import bpy import bpy_extras @@ -83,11 +81,7 @@ def invoke(self, context, event): return super().invoke(context, event) - def execute(self, context): - if not utils.OutputManager.can_access_path(self.filepath): - utils.op_report(self, {'ERROR'}, "Cannot write to target file (file likely in use by another blocking process)") - return {'FINISHED'} - + def execute(self, context): obj = context.object action = None if obj.animation_data: @@ -107,23 +101,13 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "Invalid skeleton definiton, run skeleton validation for RTM for more info") return {'FINISHED'} - output = utils.OutputManager(self.filepath, "wb") - with output as file: - try: - static, frame_count = export_rtm.write_file(self, context, file, obj, action) - - if not self.static_pose and static: - utils.op_report(self, {'INFO'}, "Exported as static pose") - else: - utils.op_report(self, {'INFO'}, "Exported %d frame(s)" % frame_count) - - output.success = True - - except export_rtm.rtm.RTM_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + with utils.get_export_handler(self.filepath, "wb") as file: + static, frame_count = export_rtm.write_file(self, context, file, obj, action) + + if not self.static_pose and static: + utils.op_report(self, {'INFO'}, "Exported as static pose") + else: + utils.op_report(self, {'INFO'}, "Exported %d frame(s)" % frame_count) return {'FINISHED'} @@ -274,15 +258,8 @@ def invoke(self, context, event): return super().invoke(context, event) def execute(self, context): - count_frames = 0 with open(self.filepath, "rb") as file: - try: - count_frames = import_rtm.import_file(self, context, file) - except (import_rtm.rtm.RTM_Error, import_rtm.rtm.BMTR_Error) as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + count_frames = import_rtm.import_file(self, context, file) if count_frames > 0: utils.op_report(self, {'INFO'}, "Successfully imported %d frame(s)" % count_frames) diff --git a/Arma3ObjectBuilder/ui/import_export_tbcsv.py b/Arma3ObjectBuilder/ui/import_export_tbcsv.py index ba8cc50..a0c7252 100644 --- a/Arma3ObjectBuilder/ui/import_export_tbcsv.py +++ b/Arma3ObjectBuilder/ui/import_export_tbcsv.py @@ -1,5 +1,3 @@ -import traceback - import bpy import bpy_extras @@ -60,17 +58,12 @@ def draw(self, context): def execute(self, context): with open(self.filepath, "rt") as file: - try: - count_read, count_found = import_tbcsv.read_file(self, context, file) - if count_found > 0: - utils.op_report(self, {'INFO'}, "Successfully imported %d/%d object positions (check the logs in the system console)" % (count_found, count_read)) - else: - utils.op_report(self, {'WARNING'}, "Could not spawn any objects, template objects were not found (check the logs in the system console)") - except import_tbcsv.tb.TBCSV_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + count_read, count_found = import_tbcsv.read_file(self, context, file) + + if count_found > 0: + utils.op_report(self, {'INFO'}, "Successfully imported %d/%d object positions (check the logs in the system console)" % (count_found, count_read)) + else: + utils.op_report(self, {'WARNING'}, "Could not spawn any objects, template objects were not found (check the logs in the system console)") return {'FINISHED'} @@ -147,25 +140,14 @@ def execute(self, context): if not self.collection and self.name_source == 'COLLECTION': utils.op_report(self, {'ERROR'}, "Collection name can only be used when exporting a collection") return {'FINISHED'} - - if not utils.OutputManager.can_access_path(self.filepath): - utils.op_report(self, {'ERROR'}, "Cannot write to target file (file likely in use by another blocking process)") - return {'FINISHED'} - output = utils.OutputManager(self.filepath, "wt") - with output as file: - try: - count = export_tbcsv.write_file(self, context, file) - if count > 0: - utils.op_report(self, {'INFO'}, "Successfully exported %d object positions (check the logs in the system console)" % count) - else: - utils.op_report(self, {'WARNING'}, "Could not export any object positions (check the logs in the system console)") - output.success = True - except import_tbcsv.tb.TBCSV_Error as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - except Exception as ex: - utils.op_report(self, {'ERROR'}, "%s (check the system console)" % ex) - traceback.print_exc() + with utils.get_export_handler(self.filepath, "wt") as file: + count = export_tbcsv.write_file(self, context, file) + + if count > 0: + utils.op_report(self, {'INFO'}, "Successfully exported %d object positions (check the logs in the system console)" % count) + else: + utils.op_report(self, {'WARNING'}, "Could not export any object positions (check the logs in the system console)") return {'FINISHED'} From 847d4839e4ddc685f99a4b041a33c9dce98f7b73 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 13:04:40 +0200 Subject: [PATCH 24/35] Moved export handler to avoid circular import --- Arma3ObjectBuilder/io/__init__.py | 41 ++---------------------- Arma3ObjectBuilder/io/file_handler.py | 38 ++++++++++++++++++++++ Arma3ObjectBuilder/io/import_armature.py | 3 +- Arma3ObjectBuilder/utilities/generic.py | 2 +- 4 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 Arma3ObjectBuilder/io/file_handler.py diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index c3c27a8..e292063 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -1,8 +1,6 @@ -import os -from datetime import datetime - from . import binary_handler from . import compression +from . import file_handler from . import data_asc from . import data_p3d from . import data_rap @@ -21,47 +19,12 @@ from . import import_tbcsv -class ExportFileHandler(): - def __init__(self, filepath, mode, backup, preserve_faulty): - timestamp = datetime.now().strftime("%Y%m%d%H%M%S") - self.filepath = filepath - self.temppath = "%s.%s.temp" % (filepath, timestamp) - self.mode = mode - self.file = None - self.backup_old = backup - self.preserve_faulty = preserve_faulty - - def __enter__(self): - file = open(self.temppath, self.mode) - self.file = file - - return file - - def __exit__(self, exc_type, exc_value, traceback): - self.file.close() - - if exc_type is None: - if os.path.isfile(self.filepath) and self.backup_old: - self.force_rename(self.filepath, self.filepath + ".bak") - - self.force_rename(self.temppath, self.filepath) - - elif not self.preserve_faulty: - os.remove(self.temppath) - - @staticmethod - def force_rename(old, new): - if os.path.isfile(new): - os.remove(new) - - os.rename(old, new) - - def reload(): import importlib importlib.reload(binary_handler) importlib.reload(compression) + importlib.reload(file_handler) importlib.reload(data_asc) importlib.reload(data_p3d) importlib.reload(data_rap) diff --git a/Arma3ObjectBuilder/io/file_handler.py b/Arma3ObjectBuilder/io/file_handler.py new file mode 100644 index 0000000..661a35c --- /dev/null +++ b/Arma3ObjectBuilder/io/file_handler.py @@ -0,0 +1,38 @@ +import os +from datetime import datetime + + +class ExportFileHandler(): + def __init__(self, filepath, mode, backup, preserve_faulty): + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + self.filepath = filepath + self.temppath = "%s.%s.temp" % (filepath, timestamp) + self.mode = mode + self.file = None + self.backup_old = backup + self.preserve_faulty = preserve_faulty + + def __enter__(self): + file = open(self.temppath, self.mode) + self.file = file + + return file + + def __exit__(self, exc_type, exc_value, traceback): + self.file.close() + + if exc_type is None: + if os.path.isfile(self.filepath) and self.backup_old: + self.force_rename(self.filepath, self.filepath + ".bak") + + self.force_rename(self.temppath, self.filepath) + + elif not self.preserve_faulty: + os.remove(self.temppath) + + @staticmethod + def force_rename(old, new): + if os.path.isfile(new): + os.remove(new) + + os.rename(old, new) diff --git a/Arma3ObjectBuilder/io/import_armature.py b/Arma3ObjectBuilder/io/import_armature.py index ad79e78..bd22755 100644 --- a/Arma3ObjectBuilder/io/import_armature.py +++ b/Arma3ObjectBuilder/io/import_armature.py @@ -6,7 +6,6 @@ from mathutils import Vector from . import data_p3d -from . import import_mcfg as mcfg from ..utilities.logger import ProcessLogger @@ -31,7 +30,7 @@ def extract_pivot_coords(lod): if len(data.weight_verts) < 1: continue - vert_idx = list(data.weight_verts.keys())[0] + vert_idx = data.weight_verts[0][0] vert_co = lod.verts[vert_idx][0:3] pivot_points[tagg.name.lower()] = Vector(vert_co) diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index fdaa041..b07d7ac 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -9,7 +9,7 @@ import bpy_extras.mesh_utils as meshutils import bmesh -from ..io import ExportFileHandler +from ..io.file_handler import ExportFileHandler from .. import __package__ as addon_name from . import data From df5fe981c26f4de044c7ac890ae574d744d4e7b4 Mon Sep 17 00:00:00 2001 From: MrClock Date: Tue, 24 Sep 2024 13:29:39 +0200 Subject: [PATCH 25/35] Moved basic helpers to package root --- Arma3ObjectBuilder/__init__.py | 12 +++++++++++- Arma3ObjectBuilder/io/export_p3d.py | 5 +++-- Arma3ObjectBuilder/props/material.py | 3 ++- Arma3ObjectBuilder/props/object.py | 3 ++- Arma3ObjectBuilder/ui/props_object_mesh.py | 5 +++-- Arma3ObjectBuilder/ui/tool_materials.py | 3 ++- Arma3ObjectBuilder/ui/tool_outliner.py | 5 +++-- Arma3ObjectBuilder/utilities/data.py | 2 +- Arma3ObjectBuilder/utilities/generic.py | 10 +--------- 9 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index a6d5a83..3f6cafe 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -12,15 +12,25 @@ } +import os + if "bpy" in locals(): io.reload() props.reload() ui.reload() utilities.reload() - import bpy + +def get_addon_preferences(): + return bpy.context.preferences.addons[__package__].preferences + + +def get_addon_directory(): + return os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) + + from . import io from . import props from . import ui diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index e5e8f5a..766d868 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -11,6 +11,7 @@ import bmesh from . import data_p3d as p3d +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import flags as flagutils from ..utilities import compat as computils @@ -104,7 +105,7 @@ def bake_flags_vertex(obj): with utils.edit_bmesh(obj) as bm: bm.verts.ensure_lookup_table() - default_flag = utils.get_addon_preferences().flag_vertex + default_flag = get_addon_preferences().flag_vertex layer = flagutils.get_layer_flags_vertex(bm) flags_vertex = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.vertex)} @@ -120,7 +121,7 @@ def bake_flags_face(obj): with utils.edit_bmesh(obj) as bm: bm.faces.ensure_lookup_table() - default_flag = utils.get_addon_preferences().flag_face + default_flag = get_addon_preferences().flag_face layer = flagutils.get_layer_flags_face(bm) flags_face = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.face)} diff --git a/Arma3ObjectBuilder/props/material.py b/Arma3ObjectBuilder/props/material.py index fc78cd4..aee1181 100644 --- a/Arma3ObjectBuilder/props/material.py +++ b/Arma3ObjectBuilder/props/material.py @@ -2,6 +2,7 @@ import bpy +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import data @@ -79,7 +80,7 @@ def from_p3d(self, texture, material, absolute): self.material_path = utils.restore_absolute(material) if absolute else material def to_p3d(self, relative): - addon_prefs = utils.get_addon_preferences() + addon_prefs = get_addon_preferences() texture = "" material = "" diff --git a/Arma3ObjectBuilder/props/object.py b/Arma3ObjectBuilder/props/object.py index 2e65ae6..951a420 100644 --- a/Arma3ObjectBuilder/props/object.py +++ b/Arma3ObjectBuilder/props/object.py @@ -3,6 +3,7 @@ import bpy from bpy.app.handlers import persistent +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import masses as massutils from ..utilities import lod as lodutils @@ -255,7 +256,7 @@ class A3OB_PG_properties_object_proxy(bpy.types.PropertyGroup): ) def to_placeholder(self, relative): - addon_prefs = utils.get_addon_preferences() + addon_prefs = get_addon_preferences() path = utils.format_path(utils.abspath(self.proxy_path), utils.abspath(addon_prefs.project_root), relative, False) if relative and len(path) > 0 and path[0] != "\\": diff --git a/Arma3ObjectBuilder/ui/props_object_mesh.py b/Arma3ObjectBuilder/ui/props_object_mesh.py index ac3393b..cfc6fb4 100644 --- a/Arma3ObjectBuilder/ui/props_object_mesh.py +++ b/Arma3ObjectBuilder/ui/props_object_mesh.py @@ -1,5 +1,6 @@ import bpy +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import data from ..utilities import proxy as proxyutils @@ -269,7 +270,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.vertex.add() item.name = "New Flag Group" - item.set_flag(utils.get_addon_preferences().flag_vertex) + item.set_flag(get_addon_preferences().flag_vertex) flag_props.vertex_index = len(flag_props.vertex) - 1 return {'FINISHED'} @@ -398,7 +399,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.face.add() item.name = "New Flag Group" - item.set_flag(utils.get_addon_preferences().flag_face) + item.set_flag(get_addon_preferences().flag_face) flag_props.face_index = len(flag_props.face) - 1 return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/tool_materials.py b/Arma3ObjectBuilder/ui/tool_materials.py index c49d74a..3700524 100644 --- a/Arma3ObjectBuilder/ui/tool_materials.py +++ b/Arma3ObjectBuilder/ui/tool_materials.py @@ -2,6 +2,7 @@ import bpy +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import materials as matutils @@ -27,7 +28,7 @@ def execute(self, context): path = scene_props.templates[scene_props.templates_index].path template = matutils.RVMATTemplate(path) - success = template.write_output(utils.get_addon_preferences().project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) + success = template.write_output(get_addon_preferences().project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) if success: self.report({'INFO'}, "Successfully generated %s.rvmat" % scene_props.basename) diff --git a/Arma3ObjectBuilder/ui/tool_outliner.py b/Arma3ObjectBuilder/ui/tool_outliner.py index 1605f29..fc87239 100644 --- a/Arma3ObjectBuilder/ui/tool_outliner.py +++ b/Arma3ObjectBuilder/ui/tool_outliner.py @@ -1,6 +1,7 @@ import bpy from bpy.app.handlers import persistent +from .. import get_addon_preferences from ..utilities import generic as utils from ..utilities import outliner as linerutils @@ -108,7 +109,7 @@ class A3OB_PT_outliner(bpy.types.Panel): @classmethod def poll(cls, context): - return utils.get_addon_preferences().outliner == 'ENABLED' + return get_addon_preferences().outliner == 'ENABLED' def draw_header(self, context): utils.draw_panel_header(self) @@ -176,7 +177,7 @@ def register(): for cls in classes: bpy.utils.register_class(cls) - if utils.get_addon_preferences().outliner == 'ENABLED': + if get_addon_preferences().outliner == 'ENABLED': bpy.app.handlers.depsgraph_update_post.append(depsgraph_update_post_handler) print("\t" + "UI: Outliner") diff --git a/Arma3ObjectBuilder/utilities/data.py b/Arma3ObjectBuilder/utilities/data.py index edc31b3..9a2e251 100644 --- a/Arma3ObjectBuilder/utilities/data.py +++ b/Arma3ObjectBuilder/utilities/data.py @@ -732,7 +732,7 @@ def get_rvmat_templates(): import os - from ..utilities.generic import get_addon_directory + from .. import get_addon_directory template_dir = os.path.join(get_addon_directory(), "scripts") diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index b07d7ac..14b4f70 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -9,8 +9,8 @@ import bpy_extras.mesh_utils as meshutils import bmesh +from .. import get_addon_preferences from ..io.file_handler import ExportFileHandler -from .. import __package__ as addon_name from . import data @@ -46,10 +46,6 @@ def strip_extension(path): return os.path.splitext(path)[0] -def get_addon_preferences(): - return bpy.context.preferences.addons[addon_name].preferences - - def get_export_handler(filepath, mode): addon_prefs = get_addon_preferences() backup = addon_prefs.create_backups @@ -235,10 +231,6 @@ def format_path(path, root = "", to_relative = True, extension = True): return path -def get_addon_directory(): - return os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) - - def get_cfg_convert(): return os.path.join(get_addon_preferences().a3_tools, "cfgconvert/cfgconvert.exe") From 1178f0670669730421f44d34e3ef54e8830c13e4 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 25 Sep 2024 01:43:47 +0200 Subject: [PATCH 26/35] Major refactor - Reordered module imports to be by order of dependency - Removed unnecessary functions from generic utils - Decoupled utils from IO entirely - Moved export file handler back to generic utils --- Arma3ObjectBuilder/__init__.py | 35 ++++++-- Arma3ObjectBuilder/io/__init__.py | 40 +++++---- Arma3ObjectBuilder/io/file_handler.py | 38 --------- Arma3ObjectBuilder/io/import_mcfg.py | 7 +- Arma3ObjectBuilder/props/__init__.py | 10 +-- Arma3ObjectBuilder/ui/__init__.py | 40 ++++----- Arma3ObjectBuilder/ui/import_export_asc.py | 2 +- Arma3ObjectBuilder/ui/import_export_mcfg.py | 4 +- Arma3ObjectBuilder/ui/import_export_p3d.py | 2 +- Arma3ObjectBuilder/ui/import_export_rtm.py | 2 +- Arma3ObjectBuilder/ui/import_export_tbcsv.py | 2 +- Arma3ObjectBuilder/utilities/__init__.py | 47 +++++------ Arma3ObjectBuilder/utilities/data.py | 40 ++++++++- Arma3ObjectBuilder/utilities/generic.py | 86 ++++++++------------ Arma3ObjectBuilder/utilities/validator.py | 2 +- 15 files changed, 181 insertions(+), 176 deletions(-) delete mode 100644 Arma3ObjectBuilder/io/file_handler.py diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index 3f6cafe..7dbba3e 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -15,10 +15,10 @@ import os if "bpy" in locals(): + utilities.reload() io.reload() props.reload() ui.reload() - utilities.reload() import bpy @@ -28,13 +28,13 @@ def get_addon_preferences(): def get_addon_directory(): - return os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) + return os.path.abspath(os.path.dirname(os.path.realpath(__file__))) +from . import utilities from . import io from . import props from . import ui -from . import utilities def outliner_enable_update(self, context): @@ -355,9 +355,31 @@ def draw(self, context): ) +def register_icons(): + import bpy.utils.previews + + themes_dir = os.path.abspath(os.path.join(get_addon_directory(), "icons")) + for theme in os.listdir(themes_dir): + theme_icons = bpy.utils.previews.new() + + icons_dir = os.path.join(themes_dir, theme) + for filename in os.listdir(icons_dir): + theme_icons.load(os.path.splitext(os.path.basename(filename))[0].lower(), os.path.join(icons_dir, filename), 'IMAGE') + + utilities.generic.preview_collection[theme.lower()] = theme_icons + + +def unregister_icons(): + import bpy.utils.previews + + for icon in utilities.generic.preview_collection.values(): + bpy.utils.previews.remove(icon) + + utilities.generic.preview_collection.clear() + + def register(): from bpy.utils import register_class - from .utilities import generic print("Registering Arma 3 Object Builder ( '" + __package__ + "' )") @@ -367,18 +389,17 @@ def register(): for mod in modules: mod.register() - generic.register_icons() + register_icons() print("Register done") def unregister(): from bpy.utils import unregister_class - from .utilities import generic print("Unregistering Arma 3 Object Builder ( '" + __package__ + "' )") - generic.unregister_icons() + unregister_icons() for mod in reversed(modules): mod.unregister() diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index e292063..12072ff 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -1,6 +1,5 @@ from . import binary_handler from . import compression -from . import file_handler from . import data_asc from . import data_p3d from . import data_rap @@ -20,24 +19,23 @@ def reload(): - import importlib + from importlib import reload - importlib.reload(binary_handler) - importlib.reload(compression) - importlib.reload(file_handler) - importlib.reload(data_asc) - importlib.reload(data_p3d) - importlib.reload(data_rap) - importlib.reload(data_rtm) - importlib.reload(data_tbcsv) - importlib.reload(export_asc) - importlib.reload(export_mcfg) - importlib.reload(export_p3d) - importlib.reload(export_rtm) - importlib.reload(export_tbcsv) - importlib.reload(import_armature) - importlib.reload(import_asc) - importlib.reload(import_mcfg) - importlib.reload(import_p3d) - importlib.reload(import_rtm) - importlib.reload(import_tbcsv) + reload(binary_handler) + reload(compression) + reload(data_asc) + reload(data_p3d) + reload(data_rap) + reload(data_rtm) + reload(data_tbcsv) + reload(export_asc) + reload(export_mcfg) + reload(export_p3d) + reload(export_rtm) + reload(export_tbcsv) + reload(import_armature) + reload(import_asc) + reload(import_mcfg) + reload(import_p3d) + reload(import_rtm) + reload(import_tbcsv) diff --git a/Arma3ObjectBuilder/io/file_handler.py b/Arma3ObjectBuilder/io/file_handler.py deleted file mode 100644 index 661a35c..0000000 --- a/Arma3ObjectBuilder/io/file_handler.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -from datetime import datetime - - -class ExportFileHandler(): - def __init__(self, filepath, mode, backup, preserve_faulty): - timestamp = datetime.now().strftime("%Y%m%d%H%M%S") - self.filepath = filepath - self.temppath = "%s.%s.temp" % (filepath, timestamp) - self.mode = mode - self.file = None - self.backup_old = backup - self.preserve_faulty = preserve_faulty - - def __enter__(self): - file = open(self.temppath, self.mode) - self.file = file - - return file - - def __exit__(self, exc_type, exc_value, traceback): - self.file.close() - - if exc_type is None: - if os.path.isfile(self.filepath) and self.backup_old: - self.force_rename(self.filepath, self.filepath + ".bak") - - self.force_rename(self.temppath, self.filepath) - - elif not self.preserve_faulty: - os.remove(self.temppath) - - @staticmethod - def force_rename(old, new): - if os.path.isfile(new): - os.remove(new) - - os.rename(old, new) diff --git a/Arma3ObjectBuilder/io/import_mcfg.py b/Arma3ObjectBuilder/io/import_mcfg.py index 381b9a8..a17cda0 100644 --- a/Arma3ObjectBuilder/io/import_mcfg.py +++ b/Arma3ObjectBuilder/io/import_mcfg.py @@ -6,6 +6,7 @@ import tempfile import subprocess +from .. import get_addon_preferences from . import data_rap as rap from ..utilities import generic as utils from ..utilities.logger import ProcessLogger @@ -32,6 +33,10 @@ def to_lowercase(self): return self +def get_cfg_convert(): + return os.path.join(get_addon_preferences().a3_tools, "cfgconvert/cfgconvert.exe") + + # The model.cfg reading is dependent on the import_rap module, # so the model config first needs to be rapified by the Arma 3 Tools. # Binary reading is far more reliable, and less messy than trying to @@ -60,7 +65,7 @@ def cfgconvert(filepath, exepath): # Derapify the previously converted model.cfg. def read_mcfg(filepath): - exepath = utils.get_cfg_convert() + exepath = get_cfg_convert() if not os.path.isfile(exepath): return None diff --git a/Arma3ObjectBuilder/props/__init__.py b/Arma3ObjectBuilder/props/__init__.py index 68f3a50..db25aef 100644 --- a/Arma3ObjectBuilder/props/__init__.py +++ b/Arma3ObjectBuilder/props/__init__.py @@ -5,9 +5,9 @@ def reload(): - import importlib + from importlib import reload - importlib.reload(action) - importlib.reload(material) - importlib.reload(object) - importlib.reload(scene) + reload(action) + reload(material) + reload(object) + reload(scene) diff --git a/Arma3ObjectBuilder/ui/__init__.py b/Arma3ObjectBuilder/ui/__init__.py index 798443a..bb30729 100644 --- a/Arma3ObjectBuilder/ui/__init__.py +++ b/Arma3ObjectBuilder/ui/__init__.py @@ -20,24 +20,24 @@ def reload(): - import importlib + from importlib import reload - importlib.reload(import_export_armature) - importlib.reload(import_export_asc) - importlib.reload(import_export_mcfg) - importlib.reload(import_export_p3d) - importlib.reload(import_export_rtm) - importlib.reload(import_export_tbcsv) - importlib.reload(props_action) - importlib.reload(props_material) - importlib.reload(props_object_mesh) - importlib.reload(tool_outliner) - importlib.reload(tool_hitpoint) - importlib.reload(tool_mass) - importlib.reload(tool_materials) - importlib.reload(tool_paths) - importlib.reload(tool_proxies) - importlib.reload(tool_rigging) - importlib.reload(tool_scripts) - importlib.reload(tool_utilities) - importlib.reload(tool_validation) + reload(import_export_armature) + reload(import_export_asc) + reload(import_export_mcfg) + reload(import_export_p3d) + reload(import_export_rtm) + reload(import_export_tbcsv) + reload(props_action) + reload(props_material) + reload(props_object_mesh) + reload(tool_outliner) + reload(tool_hitpoint) + reload(tool_mass) + reload(tool_materials) + reload(tool_paths) + reload(tool_proxies) + reload(tool_rigging) + reload(tool_scripts) + reload(tool_utilities) + reload(tool_validation) diff --git a/Arma3ObjectBuilder/ui/import_export_asc.py b/Arma3ObjectBuilder/ui/import_export_asc.py index f25a7c4..ab39700 100644 --- a/Arma3ObjectBuilder/ui/import_export_asc.py +++ b/Arma3ObjectBuilder/ui/import_export_asc.py @@ -114,7 +114,7 @@ def draw(self, context): def execute(self, context): obj = context.active_object - with utils.get_export_handler(self.filepath, "wt") as file: + with utils.ExportFileHandler(self.filepath, "wt") as file: export_asc.write_file(self, context, file, obj) utils.op_report(self, {'INFO'}, "Successfuly exported DTM") diff --git a/Arma3ObjectBuilder/ui/import_export_mcfg.py b/Arma3ObjectBuilder/ui/import_export_mcfg.py index 7420b6d..5667a87 100644 --- a/Arma3ObjectBuilder/ui/import_export_mcfg.py +++ b/Arma3ObjectBuilder/ui/import_export_mcfg.py @@ -34,7 +34,7 @@ class A3OB_OP_import_mcfg(bpy.types.Operator, bpy_extras.io_utils.ImportHelper): @classmethod def poll(cls, context): - return os.path.isfile(utils.get_cfg_convert()) + return os.path.isfile(import_mcfg.get_cfg_convert()) def draw(self, context): pass @@ -110,7 +110,7 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "Invalid skeleton definiton, run skeleton validation for more info") return {'FINISHED'} - with utils.get_export_handler(self.filepath, "w") as file: + with utils.ExportFileHandler(self.filepath, "w") as file: export_mcfg.write_file(self, skeleton, file) return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/import_export_p3d.py b/Arma3ObjectBuilder/ui/import_export_p3d.py index 0f04407..fa896b6 100644 --- a/Arma3ObjectBuilder/ui/import_export_p3d.py +++ b/Arma3ObjectBuilder/ui/import_export_p3d.py @@ -323,7 +323,7 @@ def execute(self, context): temp_collection = export_p3d.create_temp_collection(context) - with utils.get_export_handler(self.filepath, "wb") as file: + with utils.ExportFileHandler(self.filepath, "wb") as file: lod_count, exported_count = export_p3d.write_file(self, context, file, temp_collection) if lod_count == exported_count: utils.op_report(self, {'INFO'}, "Successfully exported all %d LODs (check the logs in the system console)" % exported_count) diff --git a/Arma3ObjectBuilder/ui/import_export_rtm.py b/Arma3ObjectBuilder/ui/import_export_rtm.py index 9bba26e..ec2a28c 100644 --- a/Arma3ObjectBuilder/ui/import_export_rtm.py +++ b/Arma3ObjectBuilder/ui/import_export_rtm.py @@ -101,7 +101,7 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "Invalid skeleton definiton, run skeleton validation for RTM for more info") return {'FINISHED'} - with utils.get_export_handler(self.filepath, "wb") as file: + with utils.ExportFileHandler(self.filepath, "wb") as file: static, frame_count = export_rtm.write_file(self, context, file, obj, action) if not self.static_pose and static: diff --git a/Arma3ObjectBuilder/ui/import_export_tbcsv.py b/Arma3ObjectBuilder/ui/import_export_tbcsv.py index a0c7252..41640f9 100644 --- a/Arma3ObjectBuilder/ui/import_export_tbcsv.py +++ b/Arma3ObjectBuilder/ui/import_export_tbcsv.py @@ -141,7 +141,7 @@ def execute(self, context): utils.op_report(self, {'ERROR'}, "Collection name can only be used when exporting a collection") return {'FINISHED'} - with utils.get_export_handler(self.filepath, "wt") as file: + with utils.ExportFileHandler(self.filepath, "wt") as file: count = export_tbcsv.write_file(self, context, file) if count > 0: diff --git a/Arma3ObjectBuilder/utilities/__init__.py b/Arma3ObjectBuilder/utilities/__init__.py index 9b9e6e3..a845f9d 100644 --- a/Arma3ObjectBuilder/utilities/__init__.py +++ b/Arma3ObjectBuilder/utilities/__init__.py @@ -1,35 +1,36 @@ +# In order of dependency +from . import data +from . import logger +from . import compat from . import clouds from . import colors -from . import compat -from . import data -from . import flags +from . import proxy +from . import renaming from . import generic +from . import validator +from . import flags from . import lod -from . import logger from . import masses from . import outliner -from . import proxy -from . import renaming from . import rigging from . import structure -from . import validator def reload(): - import importlib + from importlib import reload - importlib.reload(clouds) - importlib.reload(colors) - importlib.reload(compat) - importlib.reload(data) - importlib.reload(flags) - importlib.reload(generic) - importlib.reload(lod) - importlib.reload(logger) - importlib.reload(masses) - importlib.reload(outliner) - importlib.reload(proxy) - importlib.reload(renaming) - importlib.reload(rigging) - importlib.reload(structure) - importlib.reload(validator) + reload(data) + reload(logger) + reload(compat) + reload(clouds) + reload(colors) + reload(proxy) + reload(renaming) + reload(generic) + reload(validator) + reload(flags) + reload(lod) + reload(masses) + reload(outliner) + reload(rigging) + reload(structure) diff --git a/Arma3ObjectBuilder/utilities/data.py b/Arma3ObjectBuilder/utilities/data.py index 9a2e251..0e61f56 100644 --- a/Arma3ObjectBuilder/utilities/data.py +++ b/Arma3ObjectBuilder/utilities/data.py @@ -1,9 +1,6 @@ # Various hard coded data used in I/O and throughout the UI. -from ..io.data_p3d import P3D_LOD_Resolution as LOD - - flags_vertex_surface = { 'NORMAL': 0x00000000, 'SURFACE_ON': 0x00000001, @@ -67,6 +64,43 @@ flag_face_user_mask = 0xfe000000 +class LOD(): + VISUAL = 0 + VIEW_GUNNER = 1 + VIEW_PILOT = 2 + VIEW_CARGO = 3 + SHADOW = 4 + EDIT = 5 + GEOMETRY = 6 + GEOMETRY_BUOY = 7 + GEOMETRY_PHYSX = 8 + MEMORY = 9 + LANDCONTACT = 10 + ROADWAY = 11 + PATHS = 12 + HITPOINTS = 13 + VIEW_GEOMETRY = 14 + FIRE_GEOMETRY = 15 + VIEW_CARGO_GEOMETRY = 16 + VIEW_CARGO_FIRE_GEOMETRY = 17 + VIEW_COMMANDER = 18 + VIEW_COMMANDER_GEOMETRY = 19 + VIEW_COMMANDER_FIRE_GEOMETRY = 20 + VIEW_PILOT_GEOMETRY = 21 + VIEW_PILOT_FIRE_GEOMETRY = 22 + VIEW_GUNNER_GEOMETRY = 23 + VIEW_GUNNER_FIRE_GEOMETRY = 24 + SUBPARTS = 25 + SHADOW_VIEW_CARGO = 26 + SHADOW_VIEW_PILOT = 27 + SHADOW_VIEW_GUNNER = 28 + WRECKAGE = 29 + UNDERGROUND = 30 + GROUNDLAYER = 31 + NAVIGATION = 32 + UNKNOWN = -1 + + lod_has_resolution = { LOD.VISUAL, LOD.VIEW_CARGO, diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index 14b4f70..d2db518 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -4,30 +4,16 @@ import os import json from contextlib import contextmanager +from datetime import datetime import bpy import bpy_extras.mesh_utils as meshutils import bmesh from .. import get_addon_preferences -from ..io.file_handler import ExportFileHandler from . import data -def print_context(): - print("=======================") - for attr in dir(bpy.context): - print(attr, eval('bpy.context.%s' % attr)) - print("=======================") - - -def show_info_box(message, title = "", icon = 'INFO'): - def draw(self, context): - self.layout.label(text=message) - - bpy.context.window_manager.popup_menu(draw, title=title, icon=icon) - - # For some reason, not all operator reports are printed to the console. The behavior seems to be context dependent, # but not certain. def op_report(operator, mode, message): @@ -42,18 +28,6 @@ def abspath(path): return os.path.abspath(bpy.path.abspath(path)) -def strip_extension(path): - return os.path.splitext(path)[0] - - -def get_export_handler(filepath, mode): - addon_prefs = get_addon_preferences() - backup = addon_prefs.create_backups - preserve = addon_prefs.preserve_faulty_output - - return ExportFileHandler(filepath, mode, backup, preserve) - - def is_valid_idx(index, subscriptable): return len(subscriptable) > 0 and 0 <= index < len(subscriptable) @@ -226,15 +200,11 @@ def format_path(path, root = "", to_relative = True, extension = True): path = make_relative(path, root) if not extension: - path = strip_extension(path) + path = os.path.splitext(path)[0] return path -def get_cfg_convert(): - return os.path.join(get_addon_preferences().a3_tools, "cfgconvert/cfgconvert.exe") - - def load_common_data(scene): prefs = get_addon_preferences() custom_path = abspath(prefs.custom_data) @@ -273,24 +243,38 @@ def get_icon(name): return icon -def register_icons(): - import bpy.utils.previews - - themes_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../icons")) - for theme in os.listdir(themes_dir): - theme_icons = bpy.utils.previews.new() - - icons_dir = os.path.join(themes_dir, theme) - for filename in os.listdir(icons_dir): - theme_icons.load(os.path.splitext(os.path.basename(filename))[0].lower(), os.path.join(icons_dir, filename), 'IMAGE') - - preview_collection[theme.lower()] = theme_icons - +class ExportFileHandler(): + def __init__(self, filepath, mode): + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + self.filepath = filepath + self.temppath = "%s.%s.temp" % (filepath, timestamp) + self.mode = mode + self.file = None + addon_pref = get_addon_preferences() + self.backup_old = addon_pref.create_backups + self.preserve_faulty = addon_pref.preserve_faulty_output -def unregister_icons(): - import bpy.utils.previews - - for icon in preview_collection.values(): - bpy.utils.previews.remove(icon) + def __enter__(self): + file = open(self.temppath, self.mode) + self.file = file + + return file + + def __exit__(self, exc_type, exc_value, traceback): + self.file.close() + + if exc_type is None: + if os.path.isfile(self.filepath) and self.backup_old: + self.force_rename(self.filepath, self.filepath + ".bak") + + self.force_rename(self.temppath, self.filepath) + + elif not self.preserve_faulty: + os.remove(self.temppath) - preview_collection.clear() + @staticmethod + def force_rename(old, new): + if os.path.isfile(new): + os.remove(new) + + os.rename(old, new) diff --git a/Arma3ObjectBuilder/utilities/validator.py b/Arma3ObjectBuilder/utilities/validator.py index 515fa71..6965362 100644 --- a/Arma3ObjectBuilder/utilities/validator.py +++ b/Arma3ObjectBuilder/utilities/validator.py @@ -7,7 +7,7 @@ import bmesh import bpy -from ..io.data_p3d import P3D_LOD_Resolution as LOD +from .data import LOD class ValidatorResult(): From 80bdbf0769b69f9cdfe492717a3660a27ccfc8b7 Mon Sep 17 00:00:00 2001 From: MrClock Date: Wed, 25 Sep 2024 11:48:59 +0200 Subject: [PATCH 27/35] Updated vert, normal and face reading Created "precompiled" struct objects for reading the known length part of the data structures --- Arma3ObjectBuilder/io/data_p3d.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Arma3ObjectBuilder/io/data_p3d.py b/Arma3ObjectBuilder/io/data_p3d.py index d66f4e2..c082fe3 100644 --- a/Arma3ObjectBuilder/io/data_p3d.py +++ b/Arma3ObjectBuilder/io/data_p3d.py @@ -416,36 +416,40 @@ def __init__(self): self.faces = [] self.taggs = [] + struct_vert = struct.Struct(' Date: Fri, 27 Sep 2024 01:31:11 +0200 Subject: [PATCH 28/35] Addon preferences query update Instead of querying the addon preferences by the package key every call, the reference to the addon preferences object, as well as the addon directory path are saved to a new static AddonInfo class during addon registration. --- Arma3ObjectBuilder/__init__.py | 20 +++++++++----------- Arma3ObjectBuilder/io/export_p3d.py | 6 +++--- Arma3ObjectBuilder/io/import_mcfg.py | 4 ++-- Arma3ObjectBuilder/props/material.py | 4 ++-- Arma3ObjectBuilder/props/object.py | 4 ++-- Arma3ObjectBuilder/ui/import_export_p3d.py | 2 +- Arma3ObjectBuilder/ui/props_object_mesh.py | 6 +++--- Arma3ObjectBuilder/ui/tool_materials.py | 4 ++-- Arma3ObjectBuilder/ui/tool_outliner.py | 6 +++--- Arma3ObjectBuilder/ui/tool_scripts.py | 7 ++++--- Arma3ObjectBuilder/ui/tool_utilities.py | 3 ++- Arma3ObjectBuilder/utilities/data.py | 4 ++-- Arma3ObjectBuilder/utilities/generic.py | 12 ++++++------ 13 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index 7dbba3e..f419eb2 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -23,12 +23,9 @@ import bpy -def get_addon_preferences(): - return bpy.context.preferences.addons[__package__].preferences - - -def get_addon_directory(): - return os.path.abspath(os.path.dirname(os.path.realpath(__file__))) +class AddonInfo: + prefs = None + dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) from . import utilities @@ -62,8 +59,7 @@ def execute(self, context): from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER key = OpenKey(HKEY_CURRENT_USER, r"software\bohemia interactive\arma 3 tools") value, _type = QueryValueEx(key, "path") - prefs = context.preferences.addons[__package__].preferences - prefs.a3_tools = value + AddonInfo.prefs.a3_tools = value except Exception: self.report({'ERROR'}, "The Arma 3 Tools installation could not be found, it has to be set manually") @@ -358,7 +354,7 @@ def draw(self, context): def register_icons(): import bpy.utils.previews - themes_dir = os.path.abspath(os.path.join(get_addon_directory(), "icons")) + themes_dir = os.path.abspath(os.path.join(AddonInfo.dir, "icons")) for theme in os.listdir(themes_dir): theme_icons = bpy.utils.previews.new() @@ -380,17 +376,19 @@ def unregister_icons(): def register(): from bpy.utils import register_class - + print("Registering Arma 3 Object Builder ( '" + __package__ + "' )") for cls in classes: register_class(cls) + AddonInfo.prefs = bpy.context.preferences.addons[__package__].preferences + for mod in modules: mod.register() register_icons() - + print("Register done") diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index 766d868..7b3e03d 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -11,7 +11,7 @@ import bmesh from . import data_p3d as p3d -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import flags as flagutils from ..utilities import compat as computils @@ -105,7 +105,7 @@ def bake_flags_vertex(obj): with utils.edit_bmesh(obj) as bm: bm.verts.ensure_lookup_table() - default_flag = get_addon_preferences().flag_vertex + default_flag = AddonInfo.prefs.flag_vertex layer = flagutils.get_layer_flags_vertex(bm) flags_vertex = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.vertex)} @@ -121,7 +121,7 @@ def bake_flags_face(obj): with utils.edit_bmesh(obj) as bm: bm.faces.ensure_lookup_table() - default_flag = get_addon_preferences().flag_face + default_flag = AddonInfo.prefs.flag_face layer = flagutils.get_layer_flags_face(bm) flags_face = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.face)} diff --git a/Arma3ObjectBuilder/io/import_mcfg.py b/Arma3ObjectBuilder/io/import_mcfg.py index a17cda0..c63b124 100644 --- a/Arma3ObjectBuilder/io/import_mcfg.py +++ b/Arma3ObjectBuilder/io/import_mcfg.py @@ -6,7 +6,7 @@ import tempfile import subprocess -from .. import get_addon_preferences +from .. import AddonInfo from . import data_rap as rap from ..utilities import generic as utils from ..utilities.logger import ProcessLogger @@ -34,7 +34,7 @@ def to_lowercase(self): def get_cfg_convert(): - return os.path.join(get_addon_preferences().a3_tools, "cfgconvert/cfgconvert.exe") + return os.path.join(AddonInfo.prefs.a3_tools, "cfgconvert/cfgconvert.exe") # The model.cfg reading is dependent on the import_rap module, diff --git a/Arma3ObjectBuilder/props/material.py b/Arma3ObjectBuilder/props/material.py index aee1181..8b4187e 100644 --- a/Arma3ObjectBuilder/props/material.py +++ b/Arma3ObjectBuilder/props/material.py @@ -2,7 +2,7 @@ import bpy -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import data @@ -80,7 +80,7 @@ def from_p3d(self, texture, material, absolute): self.material_path = utils.restore_absolute(material) if absolute else material def to_p3d(self, relative): - addon_prefs = get_addon_preferences() + addon_prefs = AddonInfo.prefs texture = "" material = "" diff --git a/Arma3ObjectBuilder/props/object.py b/Arma3ObjectBuilder/props/object.py index 951a420..60bbbda 100644 --- a/Arma3ObjectBuilder/props/object.py +++ b/Arma3ObjectBuilder/props/object.py @@ -3,7 +3,7 @@ import bpy from bpy.app.handlers import persistent -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import masses as massutils from ..utilities import lod as lodutils @@ -256,7 +256,7 @@ class A3OB_PG_properties_object_proxy(bpy.types.PropertyGroup): ) def to_placeholder(self, relative): - addon_prefs = get_addon_preferences() + addon_prefs = AddonInfo.prefs path = utils.format_path(utils.abspath(self.proxy_path), utils.abspath(addon_prefs.project_root), relative, False) if relative and len(path) > 0 and path[0] != "\\": diff --git a/Arma3ObjectBuilder/ui/import_export_p3d.py b/Arma3ObjectBuilder/ui/import_export_p3d.py index fa896b6..c481be8 100644 --- a/Arma3ObjectBuilder/ui/import_export_p3d.py +++ b/Arma3ObjectBuilder/ui/import_export_p3d.py @@ -330,7 +330,7 @@ def execute(self, context): else: utils.op_report(self, {'WARNING'}, "Only exported %d/%d LODs (check the logs in the system console)" % (exported_count, lod_count)) - if not utils.get_addon_preferences().preserve_preprocessed_lods: + if not utils.AddonInfo.prefs.preserve_preprocessed_lods: export_p3d.cleanup_temp_collection(temp_collection) return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/props_object_mesh.py b/Arma3ObjectBuilder/ui/props_object_mesh.py index cfc6fb4..928fcc6 100644 --- a/Arma3ObjectBuilder/ui/props_object_mesh.py +++ b/Arma3ObjectBuilder/ui/props_object_mesh.py @@ -1,6 +1,6 @@ import bpy -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import data from ..utilities import proxy as proxyutils @@ -270,7 +270,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.vertex.add() item.name = "New Flag Group" - item.set_flag(get_addon_preferences().flag_vertex) + item.set_flag(AddonInfo.prefs.flag_vertex) flag_props.vertex_index = len(flag_props.vertex) - 1 return {'FINISHED'} @@ -399,7 +399,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.face.add() item.name = "New Flag Group" - item.set_flag(get_addon_preferences().flag_face) + item.set_flag(AddonInfo.prefs.flag_face) flag_props.face_index = len(flag_props.face) - 1 return {'FINISHED'} diff --git a/Arma3ObjectBuilder/ui/tool_materials.py b/Arma3ObjectBuilder/ui/tool_materials.py index 3700524..3da1688 100644 --- a/Arma3ObjectBuilder/ui/tool_materials.py +++ b/Arma3ObjectBuilder/ui/tool_materials.py @@ -2,7 +2,7 @@ import bpy -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import materials as matutils @@ -28,7 +28,7 @@ def execute(self, context): path = scene_props.templates[scene_props.templates_index].path template = matutils.RVMATTemplate(path) - success = template.write_output(get_addon_preferences().project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) + success = template.write_output(AddonInfo.prefs.project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) if success: self.report({'INFO'}, "Successfully generated %s.rvmat" % scene_props.basename) diff --git a/Arma3ObjectBuilder/ui/tool_outliner.py b/Arma3ObjectBuilder/ui/tool_outliner.py index fc87239..4e5ee83 100644 --- a/Arma3ObjectBuilder/ui/tool_outliner.py +++ b/Arma3ObjectBuilder/ui/tool_outliner.py @@ -1,7 +1,7 @@ import bpy from bpy.app.handlers import persistent -from .. import get_addon_preferences +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import outliner as linerutils @@ -109,7 +109,7 @@ class A3OB_PT_outliner(bpy.types.Panel): @classmethod def poll(cls, context): - return get_addon_preferences().outliner == 'ENABLED' + return AddonInfo.prefs.outliner == 'ENABLED' def draw_header(self, context): utils.draw_panel_header(self) @@ -177,7 +177,7 @@ def register(): for cls in classes: bpy.utils.register_class(cls) - if get_addon_preferences().outliner == 'ENABLED': + if AddonInfo.prefs.outliner == 'ENABLED': bpy.app.handlers.depsgraph_update_post.append(depsgraph_update_post_handler) print("\t" + "UI: Outliner") diff --git a/Arma3ObjectBuilder/ui/tool_scripts.py b/Arma3ObjectBuilder/ui/tool_scripts.py index a747d8a..5252241 100644 --- a/Arma3ObjectBuilder/ui/tool_scripts.py +++ b/Arma3ObjectBuilder/ui/tool_scripts.py @@ -2,7 +2,8 @@ import bpy -from ..utilities import generic as utils +from .. import AddonInfo +from ..utilities.generic import get_icon scripts = { @@ -28,7 +29,7 @@ def get_scripts_directory(): - return os.path.join(utils.get_addon_directory(), "scripts") + return os.path.join(AddonInfo.dir, "scripts") def add_operators(layout, files): @@ -92,7 +93,7 @@ def draw(self, context): def draw_scripts_menu(self, context): self.layout.separator() - self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=utils.get_icon("addon")) + self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=get_icon("addon")) def register(): diff --git a/Arma3ObjectBuilder/ui/tool_utilities.py b/Arma3ObjectBuilder/ui/tool_utilities.py index f2b3af0..29e676b 100644 --- a/Arma3ObjectBuilder/ui/tool_utilities.py +++ b/Arma3ObjectBuilder/ui/tool_utilities.py @@ -2,6 +2,7 @@ import bpy +from .. import AddonInfo from ..utilities import structure as structutils from ..utilities import generic as utils from ..utilities import data @@ -238,7 +239,7 @@ def poll(cls, context): return True def execute(self, context): - path = os.path.join(utils.get_addon_directory(), "CHANGELOG.md") + path = os.path.join(AddonInfo.dir, "CHANGELOG.md") bpy.ops.text.open(filepath=path, internal=True) self.report({'INFO'}, "See CHANGELOG.md text block") diff --git a/Arma3ObjectBuilder/utilities/data.py b/Arma3ObjectBuilder/utilities/data.py index 0e61f56..0deb375 100644 --- a/Arma3ObjectBuilder/utilities/data.py +++ b/Arma3ObjectBuilder/utilities/data.py @@ -766,9 +766,9 @@ class LOD(): def get_rvmat_templates(): import os - from .. import get_addon_directory + from .. import AddonInfo - template_dir = os.path.join(get_addon_directory(), "scripts") + template_dir = os.path.join(AddonInfo.dir, "scripts") templates = { "PBR (VBS)": os.path.join(template_dir, "pbr_vbs.rvmat_template"), diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index d2db518..a9604da 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -10,7 +10,7 @@ import bpy_extras.mesh_utils as meshutils import bmesh -from .. import get_addon_preferences +from .. import AddonInfo from . import data @@ -33,7 +33,7 @@ def is_valid_idx(index, subscriptable): def draw_panel_header(panel): - if not get_addon_preferences().show_info_links: + if not AddonInfo.prefs.show_info_links: return row = panel.layout.row(align=True) @@ -161,7 +161,7 @@ def replace_slashes(path): # Attempt to restore absolute paths to the set project root (P drive by default). def restore_absolute(path, extension = ""): path = replace_slashes(path.strip().lower()) - addon_prefs = get_addon_preferences() + addon_prefs = AddonInfo.prefs if path == "": return "" @@ -206,7 +206,7 @@ def format_path(path, root = "", to_relative = True, extension = True): def load_common_data(scene): - prefs = get_addon_preferences() + prefs = AddonInfo.prefs custom_path = abspath(prefs.custom_data) builtin = data.common_data json_data = {} @@ -236,7 +236,7 @@ def load_common_data(scene): def get_icon(name): icon = 0 try: - icon = preview_collection[get_addon_preferences().icon_theme.lower()][name].icon_id + icon = preview_collection[AddonInfo.prefs.icon_theme.lower()][name].icon_id except: pass @@ -250,7 +250,7 @@ def __init__(self, filepath, mode): self.temppath = "%s.%s.temp" % (filepath, timestamp) self.mode = mode self.file = None - addon_pref = get_addon_preferences() + addon_pref = AddonInfo.prefs self.backup_old = addon_pref.create_backups self.preserve_faulty = addon_pref.preserve_faulty_output From 094654af229f63584940a9ef00fcebd7ec5d05a6 Mon Sep 17 00:00:00 2001 From: MrClock Date: Fri, 27 Sep 2024 13:14:51 +0200 Subject: [PATCH 29/35] Update CHANGELOG.md --- Arma3ObjectBuilder/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Arma3ObjectBuilder/CHANGELOG.md b/Arma3ObjectBuilder/CHANGELOG.md index 4e92927..8df1d6d 100644 --- a/Arma3ObjectBuilder/CHANGELOG.md +++ b/Arma3ObjectBuilder/CHANGELOG.md @@ -5,7 +5,12 @@ ### Changes - internal handling of the P3D data was simplified and improved -- named properties are now properly lowercased with the Force Lowercase export option +- internal code structure and dependencies were cleaned up +- improved add-on reloading + +### Fixed + +- named properties were not properly lowercased with the Force Lowercase export option during P3D export ## [v2.4.0](https://github.com/MrClock8163/Arma3ObjectBuilder/releases/tag/v2.4.0) (Blender 2.90 -> 4.2) From 2628dca50a745acc8d4447bdd0314da4a28bcb5b Mon Sep 17 00:00:00 2001 From: MrClock Date: Sun, 29 Sep 2024 21:01:59 +0200 Subject: [PATCH 30/35] Moved custom icons to root module --- Arma3ObjectBuilder/__init__.py | 19 +++++++++++++++---- Arma3ObjectBuilder/ui/props_object_mesh.py | 2 +- Arma3ObjectBuilder/ui/tool_hitpoint.py | 3 ++- Arma3ObjectBuilder/ui/tool_mass.py | 13 +++++++------ Arma3ObjectBuilder/ui/tool_materials.py | 2 +- Arma3ObjectBuilder/ui/tool_paths.py | 9 +++++---- Arma3ObjectBuilder/ui/tool_proxies.py | 15 ++++++++------- Arma3ObjectBuilder/ui/tool_rigging.py | 15 ++++++++------- Arma3ObjectBuilder/ui/tool_scripts.py | 3 +-- Arma3ObjectBuilder/ui/tool_utilities.py | 7 +++---- Arma3ObjectBuilder/ui/tool_validation.py | 3 ++- Arma3ObjectBuilder/utilities/generic.py | 13 ------------- 12 files changed, 53 insertions(+), 51 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index f419eb2..edaca3e 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -26,6 +26,17 @@ class AddonInfo: prefs = None dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + icons = {} + + @classmethod + def get_icon(cls, name): + icon = 0 + try: + icon = cls.icons[cls.prefs.icon_theme.lower()][name].icon_id + except Exception: + pass + + return icon from . import utilities @@ -354,7 +365,7 @@ def draw(self, context): def register_icons(): import bpy.utils.previews - themes_dir = os.path.abspath(os.path.join(AddonInfo.dir, "icons")) + themes_dir = os.path.join(AddonInfo.dir, "icons") for theme in os.listdir(themes_dir): theme_icons = bpy.utils.previews.new() @@ -362,16 +373,16 @@ def register_icons(): for filename in os.listdir(icons_dir): theme_icons.load(os.path.splitext(os.path.basename(filename))[0].lower(), os.path.join(icons_dir, filename), 'IMAGE') - utilities.generic.preview_collection[theme.lower()] = theme_icons + AddonInfo.icons[theme.lower()] = theme_icons def unregister_icons(): import bpy.utils.previews - for icon in utilities.generic.preview_collection.values(): + for icon in AddonInfo.icons.values(): bpy.utils.previews.remove(icon) - utilities.generic.preview_collection.clear() + AddonInfo.icons.clear() def register(): diff --git a/Arma3ObjectBuilder/ui/props_object_mesh.py b/Arma3ObjectBuilder/ui/props_object_mesh.py index 928fcc6..213c070 100644 --- a/Arma3ObjectBuilder/ui/props_object_mesh.py +++ b/Arma3ObjectBuilder/ui/props_object_mesh.py @@ -901,7 +901,7 @@ def draw(self, context): def menu_func(self, context): self.layout.separator() - self.layout.operator(A3OB_OT_proxy_add.bl_idname, text="Arma 3 Proxy", icon_value=utils.get_icon("op_proxy_add")) + self.layout.operator(A3OB_OT_proxy_add.bl_idname, text="Arma 3 Proxy", icon_value=AddonInfo.get_icon("op_proxy_add")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_hitpoint.py b/Arma3ObjectBuilder/ui/tool_hitpoint.py index 65863e7..805a766 100644 --- a/Arma3ObjectBuilder/ui/tool_hitpoint.py +++ b/Arma3ObjectBuilder/ui/tool_hitpoint.py @@ -1,5 +1,6 @@ import bpy +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import clouds as cloudutils @@ -65,7 +66,7 @@ def draw(self, context): col_selection = layout.column(align=True, heading="Selection:") col_selection.prop(scene_props, "selection", text="", icon='MESH_DATA') - layout.operator("a3ob.hitpoints_generate", text="Generate", icon_value=utils.get_icon("op_hitpoints_generate")) + layout.operator("a3ob.hitpoints_generate", text="Generate", icon_value=AddonInfo.get_icon("op_hitpoints_generate")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_mass.py b/Arma3ObjectBuilder/ui/tool_mass.py index 4e7eed4..6151480 100644 --- a/Arma3ObjectBuilder/ui/tool_mass.py +++ b/Arma3ObjectBuilder/ui/tool_mass.py @@ -1,5 +1,6 @@ import bpy +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import masses as massutils @@ -155,14 +156,14 @@ def draw(self, context): col = layout.column(align=True) if scene_props.source == 'MASS': col.prop(scene_props, "mass") - col.operator("a3ob.vertex_mass_set", icon_value=utils.get_icon("op_mass_set")) - col.operator("a3ob.vertex_mass_distribute", icon_value=utils.get_icon("op_mass_distribute")) + col.operator("a3ob.vertex_mass_set", icon_value=AddonInfo.get_icon("op_mass_set")) + col.operator("a3ob.vertex_mass_distribute", icon_value=AddonInfo.get_icon("op_mass_distribute")) elif scene_props.source == 'DENSITY': col.prop(scene_props, "density") - col.operator("a3ob.vertex_mass_set_density", icon_value=utils.get_icon("op_mass_set_density")) + col.operator("a3ob.vertex_mass_set_density", icon_value=AddonInfo.get_icon("op_mass_set_density")) col.separator() - col.operator("a3ob.vertex_mass_clear", icon_value=utils.get_icon("op_mass_clear")) + col.operator("a3ob.vertex_mass_clear", icon_value=AddonInfo.get_icon("op_mass_clear")) class A3OB_PT_vertex_mass_analyze(bpy.types.Panel): @@ -204,8 +205,8 @@ def draw(self, context): row_method = layout.row(align=True) row_method.prop(scene_props, "method", text="Method", expand=True) - layout.operator("a3ob.vertex_mass_visualize", icon_value=utils.get_icon("op_visualize")) - layout.operator("a3ob.vertex_mass_center", icon_value=utils.get_icon("op_mass_center")) + layout.operator("a3ob.vertex_mass_visualize", icon_value=AddonInfo.get_icon("op_visualize")) + layout.operator("a3ob.vertex_mass_center", icon_value=AddonInfo.get_icon("op_mass_center")) layout.label(text="Stats:") col_stats = layout.column(align=True) diff --git a/Arma3ObjectBuilder/ui/tool_materials.py b/Arma3ObjectBuilder/ui/tool_materials.py index 3da1688..dc8a398 100644 --- a/Arma3ObjectBuilder/ui/tool_materials.py +++ b/Arma3ObjectBuilder/ui/tool_materials.py @@ -150,7 +150,7 @@ def draw(self, context): col_list = layout.column(align=True) col_list.template_list("A3OB_UL_materials_templates", "A3OB_materials_templates", scene_props, "templates", scene_props, "templates_index", item_dyntip_propname="path") - col_list.operator("a3ob.materials_templates_reload", icon_value=utils.get_icon("op_refresh")) + col_list.operator("a3ob.materials_templates_reload", icon_value=AddonInfo.get_icon("op_refresh")) layout.prop(scene_props, "folder") layout.prop(scene_props, "basename") diff --git a/Arma3ObjectBuilder/ui/tool_paths.py b/Arma3ObjectBuilder/ui/tool_paths.py index 3909f17..66f7cb2 100644 --- a/Arma3ObjectBuilder/ui/tool_paths.py +++ b/Arma3ObjectBuilder/ui/tool_paths.py @@ -1,5 +1,6 @@ import bpy +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import renaming as renameutils @@ -109,7 +110,7 @@ def draw(self, context): col_list.template_list("A3OB_UL_renamable_paths", "A3OB_bulk_rename", scene_props, "path_list", scene_props, "path_list_index") row_filter = col_list.row(align=True) - row_filter.operator("a3ob.rename_list_refresh", text="", icon_value=utils.get_icon("op_refresh")) + row_filter.operator("a3ob.rename_list_refresh", text="", icon_value=AddonInfo.get_icon("op_refresh")) row_filter.prop(scene_props, "source_filter") if utils.is_valid_idx(scene_props.path_list_index, scene_props.path_list): @@ -117,7 +118,7 @@ def draw(self, context): col_edit.prop(scene_props.path_list[scene_props.path_list_index], "path", text="") col_edit.prop(scene_props, "new_path", text="") col_edit.separator() - col_edit.operator("a3ob.rename_path_item", icon_value=utils.get_icon("op_replace")) + col_edit.operator("a3ob.rename_path_item", icon_value=AddonInfo.get_icon("op_replace")) class A3OB_PT_renaming_paths_root(bpy.types.Panel): @@ -139,7 +140,7 @@ def draw(self, context): row_filter = layout.row(align=True) row_filter.prop(scene_props, "source_filter") - layout.operator("a3ob.rename_path_root", icon_value=utils.get_icon("op_replace")) + layout.operator("a3ob.rename_path_root", icon_value=AddonInfo.get_icon("op_replace")) class A3OB_PT_renaming_vertex_groups(bpy.types.Panel): @@ -158,7 +159,7 @@ def draw(self, context): col_edit.prop(scene_props, "vgroup_old") col_edit.prop(scene_props, "vgroup_new") layout.prop(scene_props, "vgroup_match_whole") - layout.operator("a3ob.rename_vertex_groups", icon_value=utils.get_icon("op_replace")) + layout.operator("a3ob.rename_vertex_groups", icon_value=AddonInfo.get_icon("op_replace")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_proxies.py b/Arma3ObjectBuilder/ui/tool_proxies.py index ad9cf50..48d0a46 100644 --- a/Arma3ObjectBuilder/ui/tool_proxies.py +++ b/Arma3ObjectBuilder/ui/tool_proxies.py @@ -5,6 +5,7 @@ import bpy import mathutils +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import lod as lodutils from ..utilities import compat as computils @@ -335,14 +336,14 @@ def draw(self, context): layout = self.layout col_align = layout.column(align=True) - col_align.operator("a3ob.proxy_align", icon_value=utils.get_icon("op_proxy_align")) - col_align.operator("a3ob.proxy_align_object", icon_value=utils.get_icon("op_proxy_align_object")) - layout.operator("a3ob.proxy_realign_ocs", icon_value=utils.get_icon("op_proxy_realign")) - layout.operator("a3ob.proxy_extract", icon_value=utils.get_icon("op_proxy_extract")) + col_align.operator("a3ob.proxy_align", icon_value=AddonInfo.get_icon("op_proxy_align")) + col_align.operator("a3ob.proxy_align_object", icon_value=AddonInfo.get_icon("op_proxy_align_object")) + layout.operator("a3ob.proxy_realign_ocs", icon_value=AddonInfo.get_icon("op_proxy_realign")) + layout.operator("a3ob.proxy_extract", icon_value=AddonInfo.get_icon("op_proxy_extract")) col_move = layout.column(align=True) - col_move.operator("a3ob.proxy_copy", icon_value=utils.get_icon("op_proxy_copy")) - col_move.operator("a3ob.proxy_copy_all", icon_value=utils.get_icon("op_proxy_copy_all")) - col_move.operator("a3ob.proxy_transfer", icon_value=utils.get_icon("op_proxy_transfer")) + col_move.operator("a3ob.proxy_copy", icon_value=AddonInfo.get_icon("op_proxy_copy")) + col_move.operator("a3ob.proxy_copy_all", icon_value=AddonInfo.get_icon("op_proxy_copy_all")) + col_move.operator("a3ob.proxy_transfer", icon_value=AddonInfo.get_icon("op_proxy_transfer")) class A3OB_UL_lod_objects_selector(bpy.types.UIList): diff --git a/Arma3ObjectBuilder/ui/tool_rigging.py b/Arma3ObjectBuilder/ui/tool_rigging.py index 6c2b6fe..704aa3b 100644 --- a/Arma3ObjectBuilder/ui/tool_rigging.py +++ b/Arma3ObjectBuilder/ui/tool_rigging.py @@ -1,5 +1,6 @@ import bpy +from .. import AddonInfo from ..utilities import generic as utils from ..utilities import rigging as riggingutils from ..utilities import data @@ -635,7 +636,7 @@ def draw(self, context): col_bones_operators.operator("a3ob.rigging_skeletons_bones_move", text="", icon='TRIA_DOWN').direction = 'DOWN' - layout.operator("a3ob.rigging_pivots_from_armature", icon_value=utils.get_icon("op_pivots_from_armature")) + layout.operator("a3ob.rigging_pivots_from_armature", icon_value=AddonInfo.get_icon("op_pivots_from_armature")) class A3OB_PT_rigging_weights(bpy.types.Panel): @@ -655,15 +656,15 @@ def draw(self, context): layout = self.layout col_select = layout.column(align=True) - col_select.operator("a3ob.rigging_weights_select_overdetermined", icon_value=utils.get_icon("op_weights_select_overdetermined")) - col_select.operator("a3ob.rigging_weights_select_unnormalized", icon_value=utils.get_icon("op_weights_select_unnormalized")) + col_select.operator("a3ob.rigging_weights_select_overdetermined", icon_value=AddonInfo.get_icon("op_weights_select_overdetermined")) + col_select.operator("a3ob.rigging_weights_select_unnormalized", icon_value=AddonInfo.get_icon("op_weights_select_unnormalized")) col_edit = layout.column(align=True) - col_edit.operator("a3ob.rigging_weights_prune", icon_value=utils.get_icon("op_weights_prune")) - col_edit.operator("a3ob.rigging_weights_prune_overdetermined", icon_value=utils.get_icon("op_weights_prune_overdetermined")) - col_edit.operator("a3ob.rigging_weights_normalize", icon_value=utils.get_icon("op_weights_normalize")) + col_edit.operator("a3ob.rigging_weights_prune", icon_value=AddonInfo.get_icon("op_weights_prune")) + col_edit.operator("a3ob.rigging_weights_prune_overdetermined", icon_value=AddonInfo.get_icon("op_weights_prune_overdetermined")) + col_edit.operator("a3ob.rigging_weights_normalize", icon_value=AddonInfo.get_icon("op_weights_normalize")) - layout.operator("a3ob.rigging_weights_cleanup", icon_value=utils.get_icon("op_weights_cleanup")) + layout.operator("a3ob.rigging_weights_cleanup", icon_value=AddonInfo.get_icon("op_weights_cleanup")) layout.prop(scene_props, "prune_threshold") diff --git a/Arma3ObjectBuilder/ui/tool_scripts.py b/Arma3ObjectBuilder/ui/tool_scripts.py index 5252241..b852f28 100644 --- a/Arma3ObjectBuilder/ui/tool_scripts.py +++ b/Arma3ObjectBuilder/ui/tool_scripts.py @@ -3,7 +3,6 @@ import bpy from .. import AddonInfo -from ..utilities.generic import get_icon scripts = { @@ -93,7 +92,7 @@ def draw(self, context): def draw_scripts_menu(self, context): self.layout.separator() - self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=get_icon("addon")) + self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=AddonInfo.get_icon("addon")) def register(): diff --git a/Arma3ObjectBuilder/ui/tool_utilities.py b/Arma3ObjectBuilder/ui/tool_utilities.py index 29e676b..d146cb1 100644 --- a/Arma3ObjectBuilder/ui/tool_utilities.py +++ b/Arma3ObjectBuilder/ui/tool_utilities.py @@ -4,7 +4,6 @@ from .. import AddonInfo from ..utilities import structure as structutils -from ..utilities import generic as utils from ..utilities import data @@ -526,19 +525,19 @@ def menu_func(self, context): self.layout.separator() col = self.layout.column() col.ui_units_x = 5.2 - col.menu("A3OB_MT_object_builder", icon_value=utils.get_icon("addon")) + col.menu("A3OB_MT_object_builder", icon_value=AddonInfo.get_icon("addon")) def vertex_groups_func(self, context): layout = self.layout row = layout.row(align=True) row.alignment = 'RIGHT' - row.menu("A3OB_MT_vertex_groups", text="", icon_value=utils.get_icon("addon")) + row.menu("A3OB_MT_vertex_groups", text="", icon_value=AddonInfo.get_icon("addon")) def menu_help_func(self, context): self.layout.separator() - self.layout.menu("A3OB_MT_help", icon_value=utils.get_icon("addon")) + self.layout.menu("A3OB_MT_help", icon_value=AddonInfo.get_icon("addon")) def register(): diff --git a/Arma3ObjectBuilder/ui/tool_validation.py b/Arma3ObjectBuilder/ui/tool_validation.py index e3751cb..a115a19 100644 --- a/Arma3ObjectBuilder/ui/tool_validation.py +++ b/Arma3ObjectBuilder/ui/tool_validation.py @@ -1,5 +1,6 @@ import bpy +from .. import AddonInfo from ..utilities import generic as utils from ..utilities.validator import Validator from ..utilities.logger import ProcessLogger @@ -78,7 +79,7 @@ def draw(self, context): layout.prop(scene_props, "relative_paths") layout.separator() - layout.operator("a3ob.validate_for_lod", text="Validate", icon_value=utils.get_icon("op_validate")) + layout.operator("a3ob.validate_for_lod", text="Validate", icon_value=AddonInfo.get_icon("op_validate")) classes = ( diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index a9604da..a330220 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -230,19 +230,6 @@ def load_common_data(scene): scene_props.items_index = 0 -preview_collection = {} - - -def get_icon(name): - icon = 0 - try: - icon = preview_collection[AddonInfo.prefs.icon_theme.lower()][name].icon_id - except: - pass - - return icon - - class ExportFileHandler(): def __init__(self, filepath, mode): timestamp = datetime.now().strftime("%Y%m%d%H%M%S") From 7fe5a9d1f9538a96bfd18eb98489bff07ae309c9 Mon Sep 17 00:00:00 2001 From: MrClock Date: Sun, 29 Sep 2024 22:41:09 +0200 Subject: [PATCH 31/35] Small update to imports in untility scripts --- Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py | 11 ++++++----- Arma3ObjectBuilder/scripts/convert_bmtr_to_rtm.py | 12 ++++++------ Arma3ObjectBuilder/scripts/import_p3d_batch.py | 9 +++++---- Arma3ObjectBuilder/scripts/import_rtm_batch.py | 9 +++++---- Arma3ObjectBuilder/scripts/ofp2_manskeleton.py | 9 +++++---- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py b/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py index b33bb8f..4208aba 100644 --- a/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py +++ b/Arma3ObjectBuilder/scripts/convert_atbx_to_a3ob.py @@ -37,15 +37,16 @@ class Settings: import bpy name = None -for addon in bpy.context.preferences.addons.keys(): - if addon.endswith("Arma3ObjectBuilder"): - name = addon +for addon in bpy.context.preferences.addons: + if addon.module.endswith("Arma3ObjectBuilder"): + name = addon.module break else: raise Exception("Arma 3 Object Builder could not be found") -a3ob_utils = importlib.import_module(addon).utilities -a3ob_io = importlib.import_module(addon).io +a3ob = importlib.import_module(name) +a3ob_utils = a3ob.utilities +a3ob_io = a3ob.io utils = a3ob_utils.generic structutils = a3ob_utils.structure diff --git a/Arma3ObjectBuilder/scripts/convert_bmtr_to_rtm.py b/Arma3ObjectBuilder/scripts/convert_bmtr_to_rtm.py index d83d861..655259a 100644 --- a/Arma3ObjectBuilder/scripts/convert_bmtr_to_rtm.py +++ b/Arma3ObjectBuilder/scripts/convert_bmtr_to_rtm.py @@ -41,16 +41,16 @@ class Settings: import bpy name = None -for addon in bpy.context.preferences.addons.keys(): - if addon.endswith("Arma3ObjectBuilder"): - name = addon +for addon in bpy.context.preferences.addons: + if addon.module.endswith("Arma3ObjectBuilder"): + name = addon.module break else: raise Exception("Arma 3 Object Builder could not be found") -a3ob_utils = importlib.import_module(addon).utilities -a3ob_io = importlib.import_module(addon).io - +a3ob = importlib.import_module(name) +a3ob_utils = a3ob.utilities +a3ob_io = a3ob.io rtm = a3ob_io.data_rtm data = a3ob_utils.data ProcessLogger = a3ob_utils.logger.ProcessLogger diff --git a/Arma3ObjectBuilder/scripts/import_p3d_batch.py b/Arma3ObjectBuilder/scripts/import_p3d_batch.py index 6664b6e..f46f1c9 100644 --- a/Arma3ObjectBuilder/scripts/import_p3d_batch.py +++ b/Arma3ObjectBuilder/scripts/import_p3d_batch.py @@ -59,14 +59,15 @@ class Settings: import bpy name = None -for addon in bpy.context.preferences.addons.keys(): - if addon.endswith("Arma3ObjectBuilder"): - name = addon +for addon in bpy.context.preferences.addons: + if addon.module.endswith("Arma3ObjectBuilder"): + name = addon.module break else: raise Exception("Arma 3 Object Builder could not be found") -read_file = importlib.import_module(addon).io.import_p3d.read_file +a3ob = importlib.import_module(name) +read_file = a3ob.io.import_p3d.read_file def main(): diff --git a/Arma3ObjectBuilder/scripts/import_rtm_batch.py b/Arma3ObjectBuilder/scripts/import_rtm_batch.py index 6a0fb77..dfe3837 100644 --- a/Arma3ObjectBuilder/scripts/import_rtm_batch.py +++ b/Arma3ObjectBuilder/scripts/import_rtm_batch.py @@ -50,14 +50,15 @@ class Settings: import bpy name = None -for addon in bpy.context.preferences.addons.keys(): - if addon.endswith("Arma3ObjectBuilder"): - name = addon +for addon in bpy.context.preferences.addons: + if addon.module.endswith("Arma3ObjectBuilder"): + name = addon.module break else: raise Exception("Arma 3 Object Builder could not be found") -import_file = importlib.import_module(addon).io.import_rtm.import_file +a3ob = importlib.import_module(name) +import_file = a3ob.io.import_rtm.import_file def main(): diff --git a/Arma3ObjectBuilder/scripts/ofp2_manskeleton.py b/Arma3ObjectBuilder/scripts/ofp2_manskeleton.py index bf145d8..33b39aa 100644 --- a/Arma3ObjectBuilder/scripts/ofp2_manskeleton.py +++ b/Arma3ObjectBuilder/scripts/ofp2_manskeleton.py @@ -27,14 +27,15 @@ class Settings: import bpy name = None -for addon in bpy.context.preferences.addons.keys(): - if addon.endswith("Arma3ObjectBuilder"): - name = addon +for addon in bpy.context.preferences.addons: + if addon.module.endswith("Arma3ObjectBuilder"): + name = addon.module break else: raise Exception("Arma 3 Object Builder could not be found") -data = importlib.import_module(name).utilities.data +a3ob = importlib.import_module(name) +data = a3ob.utilities.data def main(): From 2679f64759afc49ba8f67d987982284100df3f2d Mon Sep 17 00:00:00 2001 From: MrClock Date: Mon, 30 Sep 2024 01:00:43 +0200 Subject: [PATCH 32/35] Partially rolled back addon prefs query change Storing a reference to the addon preferences object as a class variable, then referencing it from the imported type in other modules, is not viable. The reference in the submodules gets messed up during reloading (for some reason after the reload it points to an OBJECT related operator class). --- Arma3ObjectBuilder/__init__.py | 38 ++++++++++++---------- Arma3ObjectBuilder/io/export_p3d.py | 6 ++-- Arma3ObjectBuilder/io/import_mcfg.py | 4 +-- Arma3ObjectBuilder/props/material.py | 4 +-- Arma3ObjectBuilder/props/object.py | 6 ++-- Arma3ObjectBuilder/ui/props_object_mesh.py | 8 ++--- Arma3ObjectBuilder/ui/tool_hitpoint.py | 4 +-- Arma3ObjectBuilder/ui/tool_mass.py | 14 ++++---- Arma3ObjectBuilder/ui/tool_materials.py | 6 ++-- Arma3ObjectBuilder/ui/tool_outliner.py | 6 ++-- Arma3ObjectBuilder/ui/tool_paths.py | 10 +++--- Arma3ObjectBuilder/ui/tool_proxies.py | 16 ++++----- Arma3ObjectBuilder/ui/tool_rigging.py | 16 ++++----- Arma3ObjectBuilder/ui/tool_scripts.py | 6 ++-- Arma3ObjectBuilder/ui/tool_utilities.py | 10 +++--- Arma3ObjectBuilder/ui/tool_validation.py | 4 +-- Arma3ObjectBuilder/utilities/data.py | 4 +-- Arma3ObjectBuilder/utilities/generic.py | 12 +++---- 18 files changed, 86 insertions(+), 88 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index edaca3e..3d271bb 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -23,20 +23,21 @@ import bpy -class AddonInfo: - prefs = None - dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - icons = {} +addon_prefs = None +addon_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) +addon_icons = {} - @classmethod - def get_icon(cls, name): - icon = 0 - try: - icon = cls.icons[cls.prefs.icon_theme.lower()][name].icon_id - except Exception: - pass +def get_icon(name): + icon = 0 + try: + icon = addon_icons[addon_prefs.icon_theme.lower()][name].icon_id + except Exception: + pass + + return icon - return icon +def get_prefs(): + return addon_prefs from . import utilities @@ -70,7 +71,7 @@ def execute(self, context): from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER key = OpenKey(HKEY_CURRENT_USER, r"software\bohemia interactive\arma 3 tools") value, _type = QueryValueEx(key, "path") - AddonInfo.prefs.a3_tools = value + addon_prefs.a3_tools = value except Exception: self.report({'ERROR'}, "The Arma 3 Tools installation could not be found, it has to be set manually") @@ -365,7 +366,7 @@ def draw(self, context): def register_icons(): import bpy.utils.previews - themes_dir = os.path.join(AddonInfo.dir, "icons") + themes_dir = os.path.join(addon_dir, "icons") for theme in os.listdir(themes_dir): theme_icons = bpy.utils.previews.new() @@ -373,16 +374,16 @@ def register_icons(): for filename in os.listdir(icons_dir): theme_icons.load(os.path.splitext(os.path.basename(filename))[0].lower(), os.path.join(icons_dir, filename), 'IMAGE') - AddonInfo.icons[theme.lower()] = theme_icons + addon_icons[theme.lower()] = theme_icons def unregister_icons(): import bpy.utils.previews - for icon in AddonInfo.icons.values(): + for icon in addon_icons.values(): bpy.utils.previews.remove(icon) - AddonInfo.icons.clear() + addon_icons.clear() def register(): @@ -393,7 +394,8 @@ def register(): for cls in classes: register_class(cls) - AddonInfo.prefs = bpy.context.preferences.addons[__package__].preferences + global addon_prefs + addon_prefs = bpy.context.preferences.addons[__package__].preferences for mod in modules: mod.register() diff --git a/Arma3ObjectBuilder/io/export_p3d.py b/Arma3ObjectBuilder/io/export_p3d.py index 7b3e03d..993e37c 100644 --- a/Arma3ObjectBuilder/io/export_p3d.py +++ b/Arma3ObjectBuilder/io/export_p3d.py @@ -11,7 +11,7 @@ import bmesh from . import data_p3d as p3d -from .. import AddonInfo +from .. import get_prefs from ..utilities import generic as utils from ..utilities import flags as flagutils from ..utilities import compat as computils @@ -105,7 +105,7 @@ def bake_flags_vertex(obj): with utils.edit_bmesh(obj) as bm: bm.verts.ensure_lookup_table() - default_flag = AddonInfo.prefs.flag_vertex + default_flag = get_prefs().flag_vertex layer = flagutils.get_layer_flags_vertex(bm) flags_vertex = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.vertex)} @@ -121,7 +121,7 @@ def bake_flags_face(obj): with utils.edit_bmesh(obj) as bm: bm.faces.ensure_lookup_table() - default_flag = AddonInfo.prefs.flag_face + default_flag = get_prefs().flag_face layer = flagutils.get_layer_flags_face(bm) flags_face = {i: item.get_flag() for i, item in enumerate(obj.a3ob_properties_object_flags.face)} diff --git a/Arma3ObjectBuilder/io/import_mcfg.py b/Arma3ObjectBuilder/io/import_mcfg.py index c63b124..1c1d2a3 100644 --- a/Arma3ObjectBuilder/io/import_mcfg.py +++ b/Arma3ObjectBuilder/io/import_mcfg.py @@ -6,7 +6,7 @@ import tempfile import subprocess -from .. import AddonInfo +from .. import get_prefs from . import data_rap as rap from ..utilities import generic as utils from ..utilities.logger import ProcessLogger @@ -34,7 +34,7 @@ def to_lowercase(self): def get_cfg_convert(): - return os.path.join(AddonInfo.prefs.a3_tools, "cfgconvert/cfgconvert.exe") + return os.path.join(get_prefs().a3_tools, "cfgconvert/cfgconvert.exe") # The model.cfg reading is dependent on the import_rap module, diff --git a/Arma3ObjectBuilder/props/material.py b/Arma3ObjectBuilder/props/material.py index 8b4187e..fe799a0 100644 --- a/Arma3ObjectBuilder/props/material.py +++ b/Arma3ObjectBuilder/props/material.py @@ -2,7 +2,7 @@ import bpy -from .. import AddonInfo +from .. import get_prefs from ..utilities import generic as utils from ..utilities import data @@ -80,7 +80,7 @@ def from_p3d(self, texture, material, absolute): self.material_path = utils.restore_absolute(material) if absolute else material def to_p3d(self, relative): - addon_prefs = AddonInfo.prefs + addon_prefs = get_prefs() texture = "" material = "" diff --git a/Arma3ObjectBuilder/props/object.py b/Arma3ObjectBuilder/props/object.py index 60bbbda..1a16751 100644 --- a/Arma3ObjectBuilder/props/object.py +++ b/Arma3ObjectBuilder/props/object.py @@ -3,7 +3,7 @@ import bpy from bpy.app.handlers import persistent -from .. import AddonInfo +from .. import get_prefs from ..utilities import generic as utils from ..utilities import masses as massutils from ..utilities import lod as lodutils @@ -256,9 +256,7 @@ class A3OB_PG_properties_object_proxy(bpy.types.PropertyGroup): ) def to_placeholder(self, relative): - addon_prefs = AddonInfo.prefs - - path = utils.format_path(utils.abspath(self.proxy_path), utils.abspath(addon_prefs.project_root), relative, False) + path = utils.format_path(utils.abspath(self.proxy_path), utils.abspath(get_prefs().project_root), relative, False) if relative and len(path) > 0 and path[0] != "\\": path = "\\" + path diff --git a/Arma3ObjectBuilder/ui/props_object_mesh.py b/Arma3ObjectBuilder/ui/props_object_mesh.py index 213c070..c899866 100644 --- a/Arma3ObjectBuilder/ui/props_object_mesh.py +++ b/Arma3ObjectBuilder/ui/props_object_mesh.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_prefs, get_icon from ..utilities import generic as utils from ..utilities import data from ..utilities import proxy as proxyutils @@ -270,7 +270,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.vertex.add() item.name = "New Flag Group" - item.set_flag(AddonInfo.prefs.flag_vertex) + item.set_flag(get_prefs().flag_vertex) flag_props.vertex_index = len(flag_props.vertex) - 1 return {'FINISHED'} @@ -399,7 +399,7 @@ def execute(self, context): flag_props = obj.a3ob_properties_object_flags item = flag_props.face.add() item.name = "New Flag Group" - item.set_flag(AddonInfo.prefs.flag_face) + item.set_flag(get_prefs().flag_face) flag_props.face_index = len(flag_props.face) - 1 return {'FINISHED'} @@ -901,7 +901,7 @@ def draw(self, context): def menu_func(self, context): self.layout.separator() - self.layout.operator(A3OB_OT_proxy_add.bl_idname, text="Arma 3 Proxy", icon_value=AddonInfo.get_icon("op_proxy_add")) + self.layout.operator(A3OB_OT_proxy_add.bl_idname, text="Arma 3 Proxy", icon_value=get_icon("op_proxy_add")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_hitpoint.py b/Arma3ObjectBuilder/ui/tool_hitpoint.py index 805a766..a744e33 100644 --- a/Arma3ObjectBuilder/ui/tool_hitpoint.py +++ b/Arma3ObjectBuilder/ui/tool_hitpoint.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities import clouds as cloudutils @@ -66,7 +66,7 @@ def draw(self, context): col_selection = layout.column(align=True, heading="Selection:") col_selection.prop(scene_props, "selection", text="", icon='MESH_DATA') - layout.operator("a3ob.hitpoints_generate", text="Generate", icon_value=AddonInfo.get_icon("op_hitpoints_generate")) + layout.operator("a3ob.hitpoints_generate", text="Generate", icon_value=get_icon("op_hitpoints_generate")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_mass.py b/Arma3ObjectBuilder/ui/tool_mass.py index 6151480..de9d337 100644 --- a/Arma3ObjectBuilder/ui/tool_mass.py +++ b/Arma3ObjectBuilder/ui/tool_mass.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities import masses as massutils @@ -156,14 +156,14 @@ def draw(self, context): col = layout.column(align=True) if scene_props.source == 'MASS': col.prop(scene_props, "mass") - col.operator("a3ob.vertex_mass_set", icon_value=AddonInfo.get_icon("op_mass_set")) - col.operator("a3ob.vertex_mass_distribute", icon_value=AddonInfo.get_icon("op_mass_distribute")) + col.operator("a3ob.vertex_mass_set", icon_value=get_icon("op_mass_set")) + col.operator("a3ob.vertex_mass_distribute", icon_value=get_icon("op_mass_distribute")) elif scene_props.source == 'DENSITY': col.prop(scene_props, "density") - col.operator("a3ob.vertex_mass_set_density", icon_value=AddonInfo.get_icon("op_mass_set_density")) + col.operator("a3ob.vertex_mass_set_density", icon_value=get_icon("op_mass_set_density")) col.separator() - col.operator("a3ob.vertex_mass_clear", icon_value=AddonInfo.get_icon("op_mass_clear")) + col.operator("a3ob.vertex_mass_clear", icon_value=get_icon("op_mass_clear")) class A3OB_PT_vertex_mass_analyze(bpy.types.Panel): @@ -205,8 +205,8 @@ def draw(self, context): row_method = layout.row(align=True) row_method.prop(scene_props, "method", text="Method", expand=True) - layout.operator("a3ob.vertex_mass_visualize", icon_value=AddonInfo.get_icon("op_visualize")) - layout.operator("a3ob.vertex_mass_center", icon_value=AddonInfo.get_icon("op_mass_center")) + layout.operator("a3ob.vertex_mass_visualize", icon_value=get_icon("op_visualize")) + layout.operator("a3ob.vertex_mass_center", icon_value=get_icon("op_mass_center")) layout.label(text="Stats:") col_stats = layout.column(align=True) diff --git a/Arma3ObjectBuilder/ui/tool_materials.py b/Arma3ObjectBuilder/ui/tool_materials.py index dc8a398..d827919 100644 --- a/Arma3ObjectBuilder/ui/tool_materials.py +++ b/Arma3ObjectBuilder/ui/tool_materials.py @@ -2,7 +2,7 @@ import bpy -from .. import AddonInfo +from .. import get_prefs, get_icon from ..utilities import generic as utils from ..utilities import materials as matutils @@ -28,7 +28,7 @@ def execute(self, context): path = scene_props.templates[scene_props.templates_index].path template = matutils.RVMATTemplate(path) - success = template.write_output(AddonInfo.prefs.project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) + success = template.write_output(get_prefs().project_root, scene_props.folder, scene_props.basename, scene_props.check_files_exist) if success: self.report({'INFO'}, "Successfully generated %s.rvmat" % scene_props.basename) @@ -150,7 +150,7 @@ def draw(self, context): col_list = layout.column(align=True) col_list.template_list("A3OB_UL_materials_templates", "A3OB_materials_templates", scene_props, "templates", scene_props, "templates_index", item_dyntip_propname="path") - col_list.operator("a3ob.materials_templates_reload", icon_value=AddonInfo.get_icon("op_refresh")) + col_list.operator("a3ob.materials_templates_reload", icon_value=get_icon("op_refresh")) layout.prop(scene_props, "folder") layout.prop(scene_props, "basename") diff --git a/Arma3ObjectBuilder/ui/tool_outliner.py b/Arma3ObjectBuilder/ui/tool_outliner.py index 4e5ee83..2b78ed0 100644 --- a/Arma3ObjectBuilder/ui/tool_outliner.py +++ b/Arma3ObjectBuilder/ui/tool_outliner.py @@ -1,7 +1,7 @@ import bpy from bpy.app.handlers import persistent -from .. import AddonInfo +from .. import get_prefs from ..utilities import generic as utils from ..utilities import outliner as linerutils @@ -109,7 +109,7 @@ class A3OB_PT_outliner(bpy.types.Panel): @classmethod def poll(cls, context): - return AddonInfo.prefs.outliner == 'ENABLED' + return get_prefs().outliner == 'ENABLED' def draw_header(self, context): utils.draw_panel_header(self) @@ -177,7 +177,7 @@ def register(): for cls in classes: bpy.utils.register_class(cls) - if AddonInfo.prefs.outliner == 'ENABLED': + if get_prefs().outliner == 'ENABLED': bpy.app.handlers.depsgraph_update_post.append(depsgraph_update_post_handler) print("\t" + "UI: Outliner") diff --git a/Arma3ObjectBuilder/ui/tool_paths.py b/Arma3ObjectBuilder/ui/tool_paths.py index 66f7cb2..1fb57ab 100644 --- a/Arma3ObjectBuilder/ui/tool_paths.py +++ b/Arma3ObjectBuilder/ui/tool_paths.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities import renaming as renameutils @@ -110,7 +110,7 @@ def draw(self, context): col_list.template_list("A3OB_UL_renamable_paths", "A3OB_bulk_rename", scene_props, "path_list", scene_props, "path_list_index") row_filter = col_list.row(align=True) - row_filter.operator("a3ob.rename_list_refresh", text="", icon_value=AddonInfo.get_icon("op_refresh")) + row_filter.operator("a3ob.rename_list_refresh", text="", icon_value=get_icon("op_refresh")) row_filter.prop(scene_props, "source_filter") if utils.is_valid_idx(scene_props.path_list_index, scene_props.path_list): @@ -118,7 +118,7 @@ def draw(self, context): col_edit.prop(scene_props.path_list[scene_props.path_list_index], "path", text="") col_edit.prop(scene_props, "new_path", text="") col_edit.separator() - col_edit.operator("a3ob.rename_path_item", icon_value=AddonInfo.get_icon("op_replace")) + col_edit.operator("a3ob.rename_path_item", icon_value=get_icon("op_replace")) class A3OB_PT_renaming_paths_root(bpy.types.Panel): @@ -140,7 +140,7 @@ def draw(self, context): row_filter = layout.row(align=True) row_filter.prop(scene_props, "source_filter") - layout.operator("a3ob.rename_path_root", icon_value=AddonInfo.get_icon("op_replace")) + layout.operator("a3ob.rename_path_root", icon_value=get_icon("op_replace")) class A3OB_PT_renaming_vertex_groups(bpy.types.Panel): @@ -159,7 +159,7 @@ def draw(self, context): col_edit.prop(scene_props, "vgroup_old") col_edit.prop(scene_props, "vgroup_new") layout.prop(scene_props, "vgroup_match_whole") - layout.operator("a3ob.rename_vertex_groups", icon_value=AddonInfo.get_icon("op_replace")) + layout.operator("a3ob.rename_vertex_groups", icon_value=get_icon("op_replace")) classes = ( diff --git a/Arma3ObjectBuilder/ui/tool_proxies.py b/Arma3ObjectBuilder/ui/tool_proxies.py index 48d0a46..c9c1df9 100644 --- a/Arma3ObjectBuilder/ui/tool_proxies.py +++ b/Arma3ObjectBuilder/ui/tool_proxies.py @@ -5,7 +5,7 @@ import bpy import mathutils -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities import lod as lodutils from ..utilities import compat as computils @@ -336,14 +336,14 @@ def draw(self, context): layout = self.layout col_align = layout.column(align=True) - col_align.operator("a3ob.proxy_align", icon_value=AddonInfo.get_icon("op_proxy_align")) - col_align.operator("a3ob.proxy_align_object", icon_value=AddonInfo.get_icon("op_proxy_align_object")) - layout.operator("a3ob.proxy_realign_ocs", icon_value=AddonInfo.get_icon("op_proxy_realign")) - layout.operator("a3ob.proxy_extract", icon_value=AddonInfo.get_icon("op_proxy_extract")) + col_align.operator("a3ob.proxy_align", icon_value=get_icon("op_proxy_align")) + col_align.operator("a3ob.proxy_align_object", icon_value=get_icon("op_proxy_align_object")) + layout.operator("a3ob.proxy_realign_ocs", icon_value=get_icon("op_proxy_realign")) + layout.operator("a3ob.proxy_extract", icon_value=get_icon("op_proxy_extract")) col_move = layout.column(align=True) - col_move.operator("a3ob.proxy_copy", icon_value=AddonInfo.get_icon("op_proxy_copy")) - col_move.operator("a3ob.proxy_copy_all", icon_value=AddonInfo.get_icon("op_proxy_copy_all")) - col_move.operator("a3ob.proxy_transfer", icon_value=AddonInfo.get_icon("op_proxy_transfer")) + col_move.operator("a3ob.proxy_copy", icon_value=get_icon("op_proxy_copy")) + col_move.operator("a3ob.proxy_copy_all", icon_value=get_icon("op_proxy_copy_all")) + col_move.operator("a3ob.proxy_transfer", icon_value=get_icon("op_proxy_transfer")) class A3OB_UL_lod_objects_selector(bpy.types.UIList): diff --git a/Arma3ObjectBuilder/ui/tool_rigging.py b/Arma3ObjectBuilder/ui/tool_rigging.py index 704aa3b..82d4956 100644 --- a/Arma3ObjectBuilder/ui/tool_rigging.py +++ b/Arma3ObjectBuilder/ui/tool_rigging.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities import rigging as riggingutils from ..utilities import data @@ -636,7 +636,7 @@ def draw(self, context): col_bones_operators.operator("a3ob.rigging_skeletons_bones_move", text="", icon='TRIA_DOWN').direction = 'DOWN' - layout.operator("a3ob.rigging_pivots_from_armature", icon_value=AddonInfo.get_icon("op_pivots_from_armature")) + layout.operator("a3ob.rigging_pivots_from_armature", icon_value=get_icon("op_pivots_from_armature")) class A3OB_PT_rigging_weights(bpy.types.Panel): @@ -656,15 +656,15 @@ def draw(self, context): layout = self.layout col_select = layout.column(align=True) - col_select.operator("a3ob.rigging_weights_select_overdetermined", icon_value=AddonInfo.get_icon("op_weights_select_overdetermined")) - col_select.operator("a3ob.rigging_weights_select_unnormalized", icon_value=AddonInfo.get_icon("op_weights_select_unnormalized")) + col_select.operator("a3ob.rigging_weights_select_overdetermined", icon_value=get_icon("op_weights_select_overdetermined")) + col_select.operator("a3ob.rigging_weights_select_unnormalized", icon_value=get_icon("op_weights_select_unnormalized")) col_edit = layout.column(align=True) - col_edit.operator("a3ob.rigging_weights_prune", icon_value=AddonInfo.get_icon("op_weights_prune")) - col_edit.operator("a3ob.rigging_weights_prune_overdetermined", icon_value=AddonInfo.get_icon("op_weights_prune_overdetermined")) - col_edit.operator("a3ob.rigging_weights_normalize", icon_value=AddonInfo.get_icon("op_weights_normalize")) + col_edit.operator("a3ob.rigging_weights_prune", icon_value=get_icon("op_weights_prune")) + col_edit.operator("a3ob.rigging_weights_prune_overdetermined", icon_value=get_icon("op_weights_prune_overdetermined")) + col_edit.operator("a3ob.rigging_weights_normalize", icon_value=get_icon("op_weights_normalize")) - layout.operator("a3ob.rigging_weights_cleanup", icon_value=AddonInfo.get_icon("op_weights_cleanup")) + layout.operator("a3ob.rigging_weights_cleanup", icon_value=get_icon("op_weights_cleanup")) layout.prop(scene_props, "prune_threshold") diff --git a/Arma3ObjectBuilder/ui/tool_scripts.py b/Arma3ObjectBuilder/ui/tool_scripts.py index b852f28..be6de91 100644 --- a/Arma3ObjectBuilder/ui/tool_scripts.py +++ b/Arma3ObjectBuilder/ui/tool_scripts.py @@ -2,7 +2,7 @@ import bpy -from .. import AddonInfo +from .. import get_icon, addon_dir scripts = { @@ -28,7 +28,7 @@ def get_scripts_directory(): - return os.path.join(AddonInfo.dir, "scripts") + return os.path.join(addon_dir, "scripts") def add_operators(layout, files): @@ -92,7 +92,7 @@ def draw(self, context): def draw_scripts_menu(self, context): self.layout.separator() - self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=AddonInfo.get_icon("addon")) + self.layout.menu("A3OB_MT_scripts", text="Object Builder", icon_value=get_icon("addon")) def register(): diff --git a/Arma3ObjectBuilder/ui/tool_utilities.py b/Arma3ObjectBuilder/ui/tool_utilities.py index d146cb1..c7212a6 100644 --- a/Arma3ObjectBuilder/ui/tool_utilities.py +++ b/Arma3ObjectBuilder/ui/tool_utilities.py @@ -2,7 +2,7 @@ import bpy -from .. import AddonInfo +from .. import get_icon, addon_dir from ..utilities import structure as structutils from ..utilities import data @@ -238,7 +238,7 @@ def poll(cls, context): return True def execute(self, context): - path = os.path.join(AddonInfo.dir, "CHANGELOG.md") + path = os.path.join(addon_dir, "CHANGELOG.md") bpy.ops.text.open(filepath=path, internal=True) self.report({'INFO'}, "See CHANGELOG.md text block") @@ -525,19 +525,19 @@ def menu_func(self, context): self.layout.separator() col = self.layout.column() col.ui_units_x = 5.2 - col.menu("A3OB_MT_object_builder", icon_value=AddonInfo.get_icon("addon")) + col.menu("A3OB_MT_object_builder", icon_value=get_icon("addon")) def vertex_groups_func(self, context): layout = self.layout row = layout.row(align=True) row.alignment = 'RIGHT' - row.menu("A3OB_MT_vertex_groups", text="", icon_value=AddonInfo.get_icon("addon")) + row.menu("A3OB_MT_vertex_groups", text="", icon_value=get_icon("addon")) def menu_help_func(self, context): self.layout.separator() - self.layout.menu("A3OB_MT_help", icon_value=AddonInfo.get_icon("addon")) + self.layout.menu("A3OB_MT_help", icon_value=get_icon("addon")) def register(): diff --git a/Arma3ObjectBuilder/ui/tool_validation.py b/Arma3ObjectBuilder/ui/tool_validation.py index a115a19..ad86d06 100644 --- a/Arma3ObjectBuilder/ui/tool_validation.py +++ b/Arma3ObjectBuilder/ui/tool_validation.py @@ -1,6 +1,6 @@ import bpy -from .. import AddonInfo +from .. import get_icon from ..utilities import generic as utils from ..utilities.validator import Validator from ..utilities.logger import ProcessLogger @@ -79,7 +79,7 @@ def draw(self, context): layout.prop(scene_props, "relative_paths") layout.separator() - layout.operator("a3ob.validate_for_lod", text="Validate", icon_value=AddonInfo.get_icon("op_validate")) + layout.operator("a3ob.validate_for_lod", text="Validate", icon_value=get_icon("op_validate")) classes = ( diff --git a/Arma3ObjectBuilder/utilities/data.py b/Arma3ObjectBuilder/utilities/data.py index 0deb375..a9ca99c 100644 --- a/Arma3ObjectBuilder/utilities/data.py +++ b/Arma3ObjectBuilder/utilities/data.py @@ -766,9 +766,9 @@ class LOD(): def get_rvmat_templates(): import os - from .. import AddonInfo + from .. import addon_dir - template_dir = os.path.join(AddonInfo.dir, "scripts") + template_dir = os.path.join(addon_dir, "scripts") templates = { "PBR (VBS)": os.path.join(template_dir, "pbr_vbs.rvmat_template"), diff --git a/Arma3ObjectBuilder/utilities/generic.py b/Arma3ObjectBuilder/utilities/generic.py index a330220..8e85c69 100644 --- a/Arma3ObjectBuilder/utilities/generic.py +++ b/Arma3ObjectBuilder/utilities/generic.py @@ -10,7 +10,7 @@ import bpy_extras.mesh_utils as meshutils import bmesh -from .. import AddonInfo +from .. import get_prefs from . import data @@ -33,7 +33,7 @@ def is_valid_idx(index, subscriptable): def draw_panel_header(panel): - if not AddonInfo.prefs.show_info_links: + if not get_prefs().show_info_links: return row = panel.layout.row(align=True) @@ -161,7 +161,6 @@ def replace_slashes(path): # Attempt to restore absolute paths to the set project root (P drive by default). def restore_absolute(path, extension = ""): path = replace_slashes(path.strip().lower()) - addon_prefs = AddonInfo.prefs if path == "": return "" @@ -169,7 +168,7 @@ def restore_absolute(path, extension = ""): if os.path.splitext(path)[1].lower() != extension: path += extension - root = abspath(addon_prefs.project_root).lower() + root = abspath(get_prefs().project_root).lower() if not path.startswith(root): abs_path = os.path.join(root, path) if os.path.exists(abs_path): @@ -206,8 +205,7 @@ def format_path(path, root = "", to_relative = True, extension = True): def load_common_data(scene): - prefs = AddonInfo.prefs - custom_path = abspath(prefs.custom_data) + custom_path = abspath(get_prefs().custom_data) builtin = data.common_data json_data = {} try: @@ -237,7 +235,7 @@ def __init__(self, filepath, mode): self.temppath = "%s.%s.temp" % (filepath, timestamp) self.mode = mode self.file = None - addon_pref = AddonInfo.prefs + addon_pref = get_prefs() self.backup_old = addon_pref.create_backups self.preserve_faulty = addon_pref.preserve_faulty_output From f748285b35bb6fe789be8907ab8b27a04ae03af5 Mon Sep 17 00:00:00 2001 From: MrClock Date: Mon, 30 Sep 2024 01:27:31 +0200 Subject: [PATCH 33/35] Action props unregister fix --- Arma3ObjectBuilder/CHANGELOG.md | 1 + Arma3ObjectBuilder/props/action.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Arma3ObjectBuilder/CHANGELOG.md b/Arma3ObjectBuilder/CHANGELOG.md index 8df1d6d..4a0c5ee 100644 --- a/Arma3ObjectBuilder/CHANGELOG.md +++ b/Arma3ObjectBuilder/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixed - named properties were not properly lowercased with the Force Lowercase export option during P3D export +- animation action property classes were not properly unregistered when the add-on was deactivated or reloaded ## [v2.4.0](https://github.com/MrClock8163/Arma3ObjectBuilder/releases/tag/v2.4.0) (Blender 2.90 -> 4.2) diff --git a/Arma3ObjectBuilder/props/action.py b/Arma3ObjectBuilder/props/action.py index ec619c7..363bf89 100644 --- a/Arma3ObjectBuilder/props/action.py +++ b/Arma3ObjectBuilder/props/action.py @@ -61,5 +61,8 @@ def register(): def unregister(): del bpy.types.Action.a3ob_properties_action + + for cls in reversed(classes): + bpy.utils.unregister_class(cls) print("\t" + "Properties: action") From cae65d78fb81ab52c0bc624a27a923a855108320 Mon Sep 17 00:00:00 2001 From: MrClock Date: Mon, 30 Sep 2024 01:54:57 +0200 Subject: [PATCH 34/35] Finalized module reloading (hopefully) --- Arma3ObjectBuilder/__init__.py | 14 +++-- Arma3ObjectBuilder/io/__init__.py | 64 ++++++++++++++-------- Arma3ObjectBuilder/props/__init__.py | 22 ++++---- Arma3ObjectBuilder/ui/__init__.py | 67 +++++++++++++++--------- Arma3ObjectBuilder/utilities/__init__.py | 55 ++++++++++++------- 5 files changed, 142 insertions(+), 80 deletions(-) diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index 3d271bb..faea582 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -15,10 +15,16 @@ import os if "bpy" in locals(): - utilities.reload() - io.reload() - props.reload() - ui.reload() + from importlib import reload + + if "utilities" in locals(): + reload(utilities) + if "io" in locals(): + reload(io) + if "props" in locals(): + reload(props) + if "ui" in locals(): + reload(ui) import bpy diff --git a/Arma3ObjectBuilder/io/__init__.py b/Arma3ObjectBuilder/io/__init__.py index 12072ff..f44d379 100644 --- a/Arma3ObjectBuilder/io/__init__.py +++ b/Arma3ObjectBuilder/io/__init__.py @@ -1,3 +1,44 @@ +if "binary_handler" in locals(): + from importlib import reload + + if "binary_handler" in locals(): + reload(binary_handler) + if "compression" in locals(): + reload(compression) + if "data_asc" in locals(): + reload(data_asc) + if "data_p3d" in locals(): + reload(data_p3d) + if "data_rap" in locals(): + reload(data_rap) + if "data_rtm" in locals(): + reload(data_rtm) + if "data_tbcsv" in locals(): + reload(data_tbcsv) + if "export_asc" in locals(): + reload(export_asc) + if "export_mcfg" in locals(): + reload(export_mcfg) + if "export_p3d" in locals(): + reload(export_p3d) + if "export_rtm" in locals(): + reload(export_rtm) + if "export_tbcsv" in locals(): + reload(export_tbcsv) + if "import_armature" in locals(): + reload(import_armature) + if "import_asc" in locals(): + reload(import_asc) + if "import_mcfg" in locals(): + reload(import_mcfg) + if "import_p3d" in locals(): + reload(import_p3d) + if "import_rtm" in locals(): + reload(import_rtm) + if "import_tbcsv" in locals(): + reload(import_tbcsv) + + from . import binary_handler from . import compression from . import data_asc @@ -16,26 +57,3 @@ from . import import_p3d from . import import_rtm from . import import_tbcsv - - -def reload(): - from importlib import reload - - reload(binary_handler) - reload(compression) - reload(data_asc) - reload(data_p3d) - reload(data_rap) - reload(data_rtm) - reload(data_tbcsv) - reload(export_asc) - reload(export_mcfg) - reload(export_p3d) - reload(export_rtm) - reload(export_tbcsv) - reload(import_armature) - reload(import_asc) - reload(import_mcfg) - reload(import_p3d) - reload(import_rtm) - reload(import_tbcsv) diff --git a/Arma3ObjectBuilder/props/__init__.py b/Arma3ObjectBuilder/props/__init__.py index db25aef..610ae0c 100644 --- a/Arma3ObjectBuilder/props/__init__.py +++ b/Arma3ObjectBuilder/props/__init__.py @@ -1,13 +1,17 @@ +if "object" in locals(): + from importlib import reload + + if "action" in locals(): + reload(action) + if "material" in locals(): + reload(material) + if "object" in locals(): + reload(object) + if "scene" in locals(): + reload(scene) + + from . import action from . import material from . import object from . import scene - - -def reload(): - from importlib import reload - - reload(action) - reload(material) - reload(object) - reload(scene) diff --git a/Arma3ObjectBuilder/ui/__init__.py b/Arma3ObjectBuilder/ui/__init__.py index bb30729..5653fa3 100644 --- a/Arma3ObjectBuilder/ui/__init__.py +++ b/Arma3ObjectBuilder/ui/__init__.py @@ -1,3 +1,46 @@ +if "props_object_mesh" in locals(): + from importlib import reload + + if "import_export_armature" in locals(): + reload(import_export_armature) + if "import_export_asc" in locals(): + reload(import_export_asc) + if "import_export_mcfg" in locals(): + reload(import_export_mcfg) + if "import_export_p3d" in locals(): + reload(import_export_p3d) + if "import_export_rtm" in locals(): + reload(import_export_rtm) + if "import_export_tbcsv" in locals(): + reload(import_export_tbcsv) + if "props_action" in locals(): + reload(props_action) + if "props_material" in locals(): + reload(props_material) + if "props_object_mesh" in locals(): + reload(props_object_mesh) + if "tool_outliner" in locals(): + reload(tool_outliner) + if "tool_hitpoint" in locals(): + reload(tool_hitpoint) + if "tool_mass" in locals(): + reload(tool_mass) + if "tool_materials" in locals(): + reload(tool_materials) + if "tool_paths" in locals(): + reload(tool_paths) + if "tool_proxies" in locals(): + reload(tool_proxies) + if "tool_rigging" in locals(): + reload(tool_rigging) + if "tool_scripts" in locals(): + reload(tool_scripts) + if "tool_utilities" in locals(): + reload(tool_utilities) + if "tool_validation" in locals(): + reload(tool_validation) + + from . import import_export_armature from . import import_export_asc from . import import_export_mcfg @@ -17,27 +60,3 @@ from . import tool_scripts from . import tool_utilities from . import tool_validation - - -def reload(): - from importlib import reload - - reload(import_export_armature) - reload(import_export_asc) - reload(import_export_mcfg) - reload(import_export_p3d) - reload(import_export_rtm) - reload(import_export_tbcsv) - reload(props_action) - reload(props_material) - reload(props_object_mesh) - reload(tool_outliner) - reload(tool_hitpoint) - reload(tool_mass) - reload(tool_materials) - reload(tool_paths) - reload(tool_proxies) - reload(tool_rigging) - reload(tool_scripts) - reload(tool_utilities) - reload(tool_validation) diff --git a/Arma3ObjectBuilder/utilities/__init__.py b/Arma3ObjectBuilder/utilities/__init__.py index a845f9d..be69e03 100644 --- a/Arma3ObjectBuilder/utilities/__init__.py +++ b/Arma3ObjectBuilder/utilities/__init__.py @@ -1,3 +1,38 @@ +if "data" in locals(): + from importlib import reload + + if "data" in locals(): + reload(data) + if "logger" in locals(): + reload(logger) + if "compat" in locals(): + reload(compat) + if "clouds" in locals(): + reload(clouds) + if "colors" in locals(): + reload(colors) + if "proxy" in locals(): + reload(proxy) + if "renaming" in locals(): + reload(renaming) + if "generic" in locals(): + reload(generic) + if "validator" in locals(): + reload(validator) + if "flags" in locals(): + reload(flags) + if "lod" in locals(): + reload(lod) + if "masses" in locals(): + reload(masses) + if "outliner" in locals(): + reload(outliner) + if "rigging" in locals(): + reload(rigging) + if "structure" in locals(): + reload(structure) + + # In order of dependency from . import data from . import logger @@ -14,23 +49,3 @@ from . import outliner from . import rigging from . import structure - - -def reload(): - from importlib import reload - - reload(data) - reload(logger) - reload(compat) - reload(clouds) - reload(colors) - reload(proxy) - reload(renaming) - reload(generic) - reload(validator) - reload(flags) - reload(lod) - reload(masses) - reload(outliner) - reload(rigging) - reload(structure) From 95744bfabd46e274f2bad105869ac00fcc864d01 Mon Sep 17 00:00:00 2001 From: MrClock Date: Mon, 30 Sep 2024 16:07:25 +0200 Subject: [PATCH 35/35] Version bump --- Arma3ObjectBuilder/CHANGELOG.md | 2 +- Arma3ObjectBuilder/__init__.py | 2 +- Arma3ObjectBuilder/blender_manifest.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Arma3ObjectBuilder/CHANGELOG.md b/Arma3ObjectBuilder/CHANGELOG.md index 4a0c5ee..86356f2 100644 --- a/Arma3ObjectBuilder/CHANGELOG.md +++ b/Arma3ObjectBuilder/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v2.4.1-dev (Blender 2.90 -> 4.2) +## [v2.4.1](https://github.com/MrClock8163/Arma3ObjectBuilder/releases/tag/v2.4.1) (Blender 2.90 -> 4.2) ### Changes diff --git a/Arma3ObjectBuilder/__init__.py b/Arma3ObjectBuilder/__init__.py index faea582..6686a51 100644 --- a/Arma3ObjectBuilder/__init__.py +++ b/Arma3ObjectBuilder/__init__.py @@ -2,7 +2,7 @@ "name": "Arma 3 Object Builder", "description": "Collection of tools for editing Arma 3 content", "author": "MrClock (present add-on), Hans-Joerg \"Alwarren\" Frieden (original ArmaToolbox add-on)", - "version": (2, 4, 1, "dev"), + "version": (2, 4, 1), "blender": (2, 90, 0), "location": "Object Builder panels", "warning": "Development", diff --git a/Arma3ObjectBuilder/blender_manifest.toml b/Arma3ObjectBuilder/blender_manifest.toml index c0dd479..f4e0e5a 100644 --- a/Arma3ObjectBuilder/blender_manifest.toml +++ b/Arma3ObjectBuilder/blender_manifest.toml @@ -5,7 +5,7 @@ type = "add-on" id = "Arma3ObjectBuilder" name = "Arma 3 Object Builder" tagline = "Comprehensive add-on for modding Arma 3" -version = "2.4.1-dev" +version = "2.4.1" blender_version_min = "4.2.0" website = "https://mrcmodding.gitbook.io/arma-3-object-builder/home" tags = ["Import-Export", "Game Engine", "Object"]