diff --git a/.gitignore b/.gitignore
index 8fea2a5..4d2ade5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@ tracks/
# Pycharm
__pycache*
-.idea*
*.spec
*.pyc
*.gch
diff --git a/.idea/abmatt.iml b/.idea/abmatt.iml
new file mode 100644
index 0000000..0931036
--- /dev/null
+++ b/.idea/abmatt.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..36d7e28
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..4bc962a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5362277
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/cmdline.xml b/.idea/runConfigurations/cmdline.xml
new file mode 100644
index 0000000..716165b
--- /dev/null
+++ b/.idea/runConfigurations/cmdline.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/pytest_in_tests.xml b/.idea/runConfigurations/pytest_in_tests.xml
new file mode 100644
index 0000000..385775f
--- /dev/null
+++ b/.idea/runConfigurations/pytest_in_tests.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abmatt/brres/mdl0/material/light.py b/abmatt/brres/mdl0/material/light.py
index 6a0df14..affae44 100644
--- a/abmatt/brres/mdl0/material/light.py
+++ b/abmatt/brres/mdl0/material/light.py
@@ -71,8 +71,12 @@ def __str__(self):
self.alphaLightControl)
def enable_vertex_color(self, enabled=True):
- return self.colorLightControl.enable_vertex_color(enabled) or \
- self.alphaLightControl.enable_vertex_color(enabled)
+ result = False
+ if self.colorLightControl.enable_vertex_color(enabled):
+ result = True
+ if self.alphaLightControl.enable_vertex_color(enabled):
+ result = True
+ return result
def is_vertex_color_enabled(self):
return self.colorLightControl.is_vertex_color_enabled() and \
diff --git a/abmatt/build/config.txt b/abmatt/build/config.txt
index 7162f41..07f79d1 100644
--- a/abmatt/build/config.txt
+++ b/abmatt/build/config.txt
@@ -1,5 +1,5 @@
build_name = main # build output name
build_type = onedir # the type of build, (auto|onefile|onedir)
-version = 1.2.0
+version = 1.3.0
run_integration_tests = False
run_unit_tests = False
diff --git a/abmatt/command.py b/abmatt/command.py
index dd3ea7f..440053e 100644
--- a/abmatt/command.py
+++ b/abmatt/command.py
@@ -176,8 +176,8 @@ def __init__(self, text=None, arg_list=None):
if not x:
raise ParsingException(self.txt, 'Not enough parameters')
self.set_key_val(x[0])
- elif cmd == 'add' and len(x):
- self.set_key_val(x[0])
+ elif cmd == 'add':
+ self.set_add(x)
elif len(x):
if cmd != 'info':
raise ParsingException(self.txt, "Unknown parameter(s) {}".format(x))
@@ -271,6 +271,12 @@ def set_convert(self, params):
raise ParsingException('Unsupported export format {}'.format(self.ext))
self.flags = flags
+ def set_add(self, params):
+ if len(params):
+ self.set_key_val(params[0])
+ if self.type == 'mdl0':
+ self.include = self.exclude = self.model = None
+
def set_key_val(self, keyval):
if ':' not in keyval:
raise ParsingException(self.txt, 'Requires key:value pair')
@@ -827,9 +833,9 @@ def run_convert(self):
brres = self.create_or_open(self.destination)
for file in files:
if not brres:
- converter = klass(self.create_or_open(convert_file_ext(file, '.brres')), file, self.flags)
- else:
- converter = klass(brres, file, self.flags)
+ brres = self.create_or_open(convert_file_ext(file, '.brres'))
+ converter = klass(brres, file, self.flags,
+ include=self.include, exclude=self.exclude)
base_name = os.path.splitext(os.path.basename(converter.brres.name))[0]
model = self.model
if model and len(model) > len(base_name) and model.startswith(base_name + '-'):
diff --git a/abmatt/converters/convert_lib.py b/abmatt/converters/convert_lib.py
index 64cb21b..e5f0603 100644
--- a/abmatt/converters/convert_lib.py
+++ b/abmatt/converters/convert_lib.py
@@ -116,14 +116,21 @@ def _start_loading(self, model_name):
def _before_encoding(self):
if not self.geometries:
raise ConvertError('No geometries found to encode!')
+ self.removed_materials = {}
if self.patch_existing:
replace_names = [x.name for x in self.geometries]
for poly in [x for x in self.replacement_model.objects if x.name in replace_names]:
+ self.removed_materials[poly.material.name] = poly.material
self.replacement_model.remove_polygon(poly)
+ self.material_name_remap = material_name_remap = {}
self.encode_materials()
+ for x in self.geometries:
+ if x.material_name in material_name_remap:
+ x.material_name = material_name_remap[x.material_name]
+
if os.path.exists(self.json_file):
converter = MatsToJsonConverter(self.json_file)
- converter.load_into(self.mdl0.materials)
+ converter.load_into(self.mdl0.materials, material_name_remap)
self.json_polygon_encoding = converter.polygons_by_name
else:
self.json_polygon_encoding = None
@@ -222,7 +229,9 @@ def __add_pat0_images(self):
def _encode_material(self, generic_mat):
m = None
if self.replacement_model:
- m = self.replacement_model.get_material_by_name(generic_mat.name)
+ m = self.removed_materials.get(generic_mat.name)
+ if m is None:
+ m = self.replacement_model.get_material_by_name(generic_mat.name)
if m is None:
if self.material_library:
m = self.material_library.get(generic_mat)
@@ -234,6 +243,8 @@ def _encode_material(self, generic_mat):
mat.paste(m)
else:
mat = generic_mat.encode(self.mdl0)
+ if mat.name != generic_mat.name:
+ self.material_name_remap[generic_mat.name] = mat.name
for x in mat.layers:
self.image_library.add(x.name)
return mat
@@ -317,13 +328,13 @@ def _encode_geometry(self, geometry):
has_uv_mtx = json_data.get('has_uv_matrix')
priority = json_data.get('draw_priority')
elif self.replacement_model:
- replace_geometry = [x for x in self.replacement_model.objects if x.name == geometry.name]
- if replace_geometry:
- replace_geometry = replace_geometry[0]
- has_uv_mtx = [replace_geometry.has_uv_matrix(i) for i in range(8)]
- priority = replace_geometry.priority
- if self.patch_existing:
- self.mdl0.remove_polygon(replace_geometry)
+ for x in self.replacement_model.objects:
+ if x.name == geometry.name:
+ has_uv_mtx = [x.has_uv_matrix(i) for i in range(8)]
+ priority = x.priority
+ if self.patch_existing:
+ self.mdl0.remove_polygon(x)
+ break
encoder = self.encoder.get_encoder(geometry) if self.encoder else None
return geometry.encode(self.mdl0, encoder=encoder,
priority=priority,
diff --git a/abmatt/converters/convert_mats_to_json.py b/abmatt/converters/convert_mats_to_json.py
index 9ddecdd..39de530 100644
--- a/abmatt/converters/convert_mats_to_json.py
+++ b/abmatt/converters/convert_mats_to_json.py
@@ -40,7 +40,7 @@ def load(self):
for x in self.materials_by_name]
return materials
- def load_into(self, materials):
+ def load_into(self, materials, material_mapper=None):
""" Loads json data into materials, if the material name isn't found it is ignored
:param materials: the materials to load data into
:return: materials
@@ -49,6 +49,10 @@ def load_into(self, materials):
self.materials_by_name = json.loads(f.read())
for x in materials:
data = self.materials_by_name.get(x.name)
+ if not data and material_mapper:
+ new_name = material_mapper.get(x.name)
+ if new_name:
+ data = self.materials_by_name.get(new_name)
if data:
self.__load_material(x, data)
diff --git a/abmatt/converters/dae.py b/abmatt/converters/dae.py
index a667007..d717c36 100644
--- a/abmatt/converters/dae.py
+++ b/abmatt/converters/dae.py
@@ -597,7 +597,7 @@ def __initialize_libraries(self, initial_name):
def __initialize_assets(self, root):
asset = XMLNode('asset', parent=root)
contributor = XMLNode('contributor', parent=asset)
- authoring_tool = XMLNode('authoring_tool', 'ABMATT COLLADA exporter v1.2.0', parent=contributor)
+ authoring_tool = XMLNode('authoring_tool', 'ABMATT COLLADA exporter v1.3.0', parent=contributor)
time_stamp = datetime.now()
created = XMLNode('created', str(time_stamp), parent=asset)
modified = XMLNode('modified', str(time_stamp), parent=asset)
diff --git a/abmatt/converters/mesh_edit.py b/abmatt/converters/mesh_edit.py
new file mode 100644
index 0000000..c8398ef
--- /dev/null
+++ b/abmatt/converters/mesh_edit.py
@@ -0,0 +1,144 @@
+import math
+
+import numpy as np
+
+from abmatt.converters.matrix import euler_to_rotation_matrix, IDENTITY, scale_matrix
+
+
+class MeshPoint:
+ def __init__(self, p_group, index):
+ self.p_group = p_group
+ self.index = index
+
+ def set(self, value):
+ self.p_group.data[self.index] = value
+
+ def get(self):
+ return self.p_group.data[self.index]
+
+ def dist(self, point):
+ p1 = np.array(self.get())
+ p2 = np.array(point.get())
+ return np.sqrt(np.sum((p1 - p2)**2, axis=0))
+
+ def __str__(self):
+ return str(self.p_group.data[self.index])
+
+
+class MeshEdit:
+ def __init__(self, mdl0):
+ self.mdl0 = mdl0
+
+ @staticmethod
+ def in_box(point, bound1, bound2):
+ for i in range(len(bound1)):
+ if bound1[i] < bound2[i]:
+ if not bound1[i] <= point[i] <= bound2[i]:
+ return False
+ else:
+ if not bound2[i] <= point[i] <= bound1[i]:
+ return False
+ return True
+
+ def select_verts(self, point1=(-math.inf, -math.inf, -math.inf),
+ point2=(math.inf, math.inf, math.inf),
+ polygons=None):
+ if not polygons:
+ polygons = self.mdl0.objects
+ elif type(polygons[0]) is str:
+ polygons = [x for x in self.mdl0.objects if x.name in polygons]
+ ret = []
+ for x in polygons:
+ vertices = x.get_vertex_group()
+ for i in range(len(vertices)):
+ if self.in_box(vertices.data[i], point1, point2):
+ ret.append(MeshPoint(vertices, i))
+ return ret
+
+ def shift(self, mesh_points, shift):
+ for x in mesh_points:
+ x.set(np.array(x.get()) + shift)
+
+ def get_clusters(self, mesh_points, dist=3000):
+ clusters = []
+ for p in mesh_points:
+ added = False
+ for c in clusters:
+ if type(dist) is not list and type(dist) is not tuple:
+ if p.dist(c[0]) < dist:
+ c.append(p)
+ added = True
+ break
+ else:
+ within = True
+ for i in range(3):
+ x = p.get()
+ y = c[0].get()
+ if abs(y[i] - x[i]) > dist[i]:
+ within = False
+ break
+ if within:
+ c.append(p)
+ added = True
+ break
+ if not added:
+ clusters.append([p])
+ return clusters
+
+ def rotate_all_uvs(self, mesh_points, range=3000):
+ for c in self.get_clusters(mesh_points, range):
+ self.rotate_uv(c)
+
+ def rotate_uv(self, box_points):
+ rotation = len(box_points) // 2
+ original = [x.get() for x in box_points]
+ for i in range(len(box_points)):
+ box_points[i].set(original[(i + rotation) % len(box_points)])
+
+ def rotate_all(self, mesh_points, rotation=(0, 180, 0), range=3000, box_point=None):
+ point_of_origin = None
+ for c in self.get_clusters(mesh_points, range):
+ if box_point:
+ point_of_origin = self.get_box_point(c, box_point)
+ self.rotate(c, rotation, point_of_origin)
+
+ def rotate(self, mesh_points, euler_angles=(0, 180, 0), point_of_origin=None):
+ # todo normals
+ self.transform_about(point_of_origin, mesh_points,
+ lambda x: np.matmul(x, euler_to_rotation_matrix(euler_angles)))
+
+ def transform_about(self, point_of_origin, mesh_points, transform_func):
+ if point_of_origin is None:
+ point_of_origin = self.get_box_point(mesh_points, (0.5, 0, 0.5))
+ points = np.array([x.get() for x in mesh_points])
+ points -= point_of_origin
+ points = transform_func(points)
+ points += point_of_origin
+ for i in range(len(points)):
+ mesh_points[i].set(points[i])
+
+ def scale(self, mesh_points, scale, point_of_origin=None):
+ self.transform_about(point_of_origin, mesh_points,
+ lambda x: np.matmul(x, scale_matrix(IDENTITY, scale)[:3, :3]))
+
+ def get_box(self, mesh_points):
+ base = [x for x in mesh_points[0].get()]
+ top = [x for x in base]
+ width = len(top)
+ for p in mesh_points:
+ v = p.get()
+ for i in range(width):
+ if v[i] < base[i]:
+ base[i] = v[i]
+ if v[i] > top[i]:
+ top[i] = v[i]
+ return np.array(base), np.array(top)
+
+ def get_box_point(self, mesh_points, box_loc):
+ """
+ Get the box point on mesh using box location
+ :param mesh_points: Mesh
+ :param box_loc: ndarray [x, y, z] location on box ranging from 0, 1 where 1 is the uppermost on box
+ """
+ base, top = self.get_box(mesh_points)
+ return base + (top - base) * box_loc
diff --git a/abmatt/converters/obj.py b/abmatt/converters/obj.py
index 8ca50e9..bd26721 100644
--- a/abmatt/converters/obj.py
+++ b/abmatt/converters/obj.py
@@ -93,10 +93,15 @@ def normalize(self, vertices, normals, tex_coords):
AutoFix.warn('Please triangulate your model before importing it!'.format(self.name))
raise ValueError('Normalize triangles failed')
triangles = triangles - 1
- self.triangles = triangles
self.vertices = self.normalize_indices_group(triangles[:, :, 0], vertices)
- self.normals = self.normalize_indices_group(triangles[:, :, -1], normals) if self.has_normals else None
- self.texcoords = self.normalize_indices_group(triangles[:, :, 1], tex_coords) if self.has_texcoords else None
+ tris = [self.vertices.face_indices]
+ if self.has_normals:
+ self.normals = self.normalize_indices_group(triangles[:, :, -1], normals)
+ tris.append(self.normals.face_indices)
+ if self.has_texcoords:
+ self.texcoords = self.normalize_indices_group(triangles[:, :, 1], tex_coords)
+ tris.append(self.texcoords.face_indices)
+ self.triangles = np.stack(tris, axis=-1)
class Obj():
@@ -138,7 +143,7 @@ def save(self):
self.save_obj()
def save_mtllib(self, folder):
- s = '# Wavefront MTL exported with ABMATT v1.2.0'
+ s = '# Wavefront MTL exported with ABMATT v1.3.0'
materials = self.materials
for x in materials:
s += '\n' + materials[x].get_save_str()
@@ -146,7 +151,7 @@ def save_mtllib(self, folder):
f.write(s)
def save_obj(self):
- s = '# Wavefront OBJ exported with ABMATT v1.2.0\n\nmtllib ' + self.mtllib + '\n\n'
+ s = '# Wavefront OBJ exported with ABMATT v1.3.0\n\nmtllib ' + self.mtllib + '\n\n'
vertex_index = 1
normal_index = 1
normal_offset = -1
diff --git a/abmatt/converters/points.py b/abmatt/converters/points.py
index 1a18f1d..e011b1b 100644
--- a/abmatt/converters/points.py
+++ b/abmatt/converters/points.py
@@ -89,7 +89,7 @@ def get_format_divisor(minimum, maximum):
point_min = min(minimum)
is_signed = True if point_min < 0 else False
point_max = max(point_max, abs(point_min))
- max_shift = 16 - math.frexp(point_max)[1] - is_signed
+ max_shift = 15 - math.frexp(point_max)[1] - is_signed
if max_shift <= 13: # guarantee 4 decimals of precision
return 'f', 0 # float
else:
diff --git a/abmatt/dist/install-ubu.txt b/abmatt/dist/install-ubu.txt
index 25f5cfa..667b4eb 100644
--- a/abmatt/dist/install-ubu.txt
+++ b/abmatt/dist/install-ubu.txt
@@ -1,6 +1,6 @@
====================================================================================
ANOOB'S BRRES MATERIAL TOOL
-Version: 1.2.0
+Version: 1.3.0
Author: Robert Nelson
OS: linux
Github: https://github.com/Robert-N7/abmatt
diff --git a/abmatt/dist/install-win.txt b/abmatt/dist/install-win.txt
index a7bb9f7..7ac4e1b 100644
--- a/abmatt/dist/install-win.txt
+++ b/abmatt/dist/install-win.txt
@@ -1,6 +1,6 @@
===================================================================================================
ANOOB'S BRRES MATERIAL TOOL
-Version 1.2.0
+Version 1.3.0
Robert Nelson
OS: Windows 10 64-bit
Github: https://github.com/Robert-N7/abmatt
diff --git a/abmatt/dist/make_installer.nsi b/abmatt/dist/make_installer.nsi
index 446418d..1ff7052 100644
--- a/abmatt/dist/make_installer.nsi
+++ b/abmatt/dist/make_installer.nsi
@@ -1,4 +1,4 @@
-!define VERSION "1.2.0"
+!define VERSION "1.3.0"
!define PROGRAM_NAME "ANoob's Brres Material Tool ${VERSION}"
InstallDir "$Documents\abmatt"
Name "${PROGRAM_NAME}"
diff --git a/abmatt/editable/__init__.py b/abmatt/editable/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/abmatt/editable/geometry.py b/abmatt/editable/geometry.py
new file mode 100644
index 0000000..a285fdd
--- /dev/null
+++ b/abmatt/editable/geometry.py
@@ -0,0 +1,8 @@
+from abmatt.converters.geometry import Geometry
+
+
+class EditGeometry:
+ def __init__(self, polygon):
+ self.polygon = polygon
+ self.geometry = polygon.get_decoded()
+
diff --git a/abmatt/image_converter.py b/abmatt/image_converter.py
index 665ad15..a97ffc2 100644
--- a/abmatt/image_converter.py
+++ b/abmatt/image_converter.py
@@ -161,13 +161,14 @@ def __del__(self):
@staticmethod
def find_file(filename):
+ original = filename
if filename.startswith('file://'):
filename = filename.replace('file://', '')
if not os.path.exists(filename):
from urllib import parse
filename = parse.unquote(filename)
if not os.path.exists(filename):
- raise EncodeError('No such file {}'.format(filename))
+ raise EncodeError('No such file {}'.format(original))
return filename
@staticmethod
diff --git a/abmatt/load_config.py b/abmatt/load_config.py
index 724deb3..c3a8204 100644
--- a/abmatt/load_config.py
+++ b/abmatt/load_config.py
@@ -117,7 +117,7 @@ def load_config(app_dir=None, loudness=None, autofix_level=None):
return conf
-VERSION = '1.2.0'
+VERSION = '1.3.0'
USAGE = "USAGE: abmatt [command_line][--interactive -f -b -d --overwrite]"
diff --git a/setup.py b/setup.py
index 19250bc..7c4f652 100755
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,7 @@
# calling the setup function
setup(name='abmatt',
- version='1.2.0',
+ version='1.3.0',
entry_points={
'console_scripts': [
'abmatt = abmatt.__main__:main'