Skip to content
This repository has been archived by the owner on Jul 21, 2024. It is now read-only.

Commit

Permalink
ean proxyengine
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdmgl committed Jan 10, 2019
1 parent fddd3a3 commit 678a746
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ max-bool-expr=5
max-branches=12

# Maximum number of locals for function / method body.
max-locals=15
max-locals=20

# Maximum number of parents for a class (see R0901).
max-parents=7
Expand Down
164 changes: 72 additions & 92 deletions proxyengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@


import os
import time

import mathutils
import bpy
Expand All @@ -25,6 +24,11 @@


class ProxyEngine:
__slots__ = ('has_data', 'data_path', 'templates_library', 'assets_models', 'assets_path')

corrective_modifier_name = "mbastlab_proxy_smooth_modifier"
# mask_modifier_name = "mbastlab_mask_modifier"
proxy_armature_modifier = "mbastlab_proxy_armature"

def __init__(self):
self.has_data = False
Expand All @@ -34,9 +38,6 @@ def __init__(self):
self.assets_path = os.path.join(self.data_path, "assets")
self.assets_models = algorithms.generate_items_list(self.assets_path, "blend")

self.corrective_modifier_name = "mbastlab_proxy_smooth_modifier"
#self.mask_modifier_name = "mbastlab_mask_modifier"
self.proxy_armature_modifier = "mbastlab_proxy_armature"

def update_assets_models(self):
scn = bpy.context.scene
Expand All @@ -48,15 +49,16 @@ def update_assets_models(self):
self.assets_models = algorithms.generate_items_list(self.assets_path, "blend")

def load_asset(self, assetname):
scn = bpy.context.scene
asset_path = os.path.join(self.assets_path, assetname+".blend")
algorithms.append_object_from_library(asset_path, [assetname])

def transfer_weights(self, body, proxy):
@staticmethod
def transfer_weights(body, proxy):

body_kd_tree = algorithms.kdtree_from_mesh_vertices(body.data)

fit_shapekey = algorithms.get_shapekey(proxy, "mbastlab_proxyfit")

if fit_shapekey:
proxy_vertices = fit_shapekey.data
else:
Expand All @@ -68,33 +70,28 @@ def transfer_weights(self, body, proxy):
for idx, w_data in enumerate(body_verts_weights):
try:
w_data.append([grp.name, grp.weight(idx)])
except:
except AttributeError:
pass # TODO: idx in grp.weight

for p_idx, proxy_vert in enumerate(proxy_vertices):

proxy_vert_weights = {}
nearest_body_vert = body_kd_tree.find(proxy_vert.co)
min_dist = nearest_body_vert[2]
min_dist = body_kd_tree.find(proxy_vert.co)[2]

nearest_body_verts = body_kd_tree.find_range(proxy_vert.co, min_dist*2)

for nearest_body_vert_data in nearest_body_verts:

body_vert_idx = nearest_body_vert_data[1]
body_vert_dist = nearest_body_vert_data[2]

if body_vert_dist != 0:
magnitude = min_dist/body_vert_dist
else:
magnitude = 1
magnitude = min_dist / body_vert_dist if body_vert_dist != 0 else 1

group_data = body_verts_weights[body_vert_idx]
group_data = body_verts_weights[nearest_body_vert_data[1]]

for g_data in group_data:
if len(g_data) > 0:
group_name = g_data[0]
vert_weight = g_data[1]
if not g_data:
continue
group_name, vert_weight = g_data[:2]

if group_name in proxy_vert_weights:
proxy_vert_weights[group_name] += vert_weight*magnitude
Expand All @@ -107,16 +104,15 @@ def transfer_weights(self, body, proxy):
weights_sum += vert_weight

for group_name, vert_weight in proxy_vert_weights.items():
proxy_vert_weights[group_name] = vert_weight/weights_sum

for group_name, vert_weight in proxy_vert_weights.items():
proxy_vert_weights[group_name] = vert_weight / weights_sum

if group_name not in proxy.vertex_groups:
proxy.vertex_groups.new(name=group_name)

g = proxy.vertex_groups[group_name]
g.add([p_idx], vert_weight, 'REPLACE')


def disable_extra_armature_modfr(self, proxy):
for modfr in proxy.modifiers:
if modfr.type == 'ARMATURE':
Expand All @@ -137,7 +133,8 @@ def add_proxy_armature_modfr(self, proxy, armat):
# parameters = {"vertex_group":mask_name,"invert_vertex_group":True}
# algorithms.new_modifier(body, mask_name, 'MASK', parameters)

def calibrate_proxy_object(self, proxy):
@staticmethod
def calibrate_proxy_object(proxy):
if proxy is not None:
old_version_sk = algorithms.get_shapekey(proxy, "Fitted")
if old_version_sk:
Expand All @@ -155,10 +152,11 @@ def remove_fitting(self):
proxy.matrix_world.identity()
self.remove_body_mask(body, mask_name)

def get_proxy_template_design(self, proxy_obj):
@staticmethod
def get_proxy_template_design(proxy_obj):

g_identifiers1 = ["girl", "woman", "female"]
g_identifiers2 = ["boy", "man", "male"]
g_identifiers1 = ("girl", "woman", "female")
g_identifiers2 = ("boy", "man", "male")

if "anime" in proxy_obj.name.lower():
for g_id in g_identifiers1:
Expand All @@ -182,18 +180,16 @@ def validate_assets_compatibility(self, proxy_obj, reference_obj):
proxy_template = self.get_proxy_template_design(proxy_obj)
id_template = algorithms.get_template_model(reference_obj)

if proxy_template is not None:
if id_template is not None:
if proxy_template in id_template:
return "OK"
else:
return "WARNING"
if proxy_template is not None and id_template is not None:
if proxy_template in id_template:
return "OK"
return "WARNING"

return "NO_SPECIFIED"

def get_proxy_fitting_ingredients(self):
@staticmethod
def get_proxy_fitting_ingredients():
scn = bpy.context.scene
status = 'OK'

if scn.mblab_proxy_name != "NO_PROXY_FOUND":

Expand All @@ -211,7 +207,8 @@ def get_proxy_fitting_ingredients(self):

return ["PROXY_NOT_FOUND", None, None]

def reset_proxy_shapekey(self, proxy):
@staticmethod
def reset_proxy_shapekey(proxy):
fit_shapekey = algorithms.get_shapekey(proxy, "mbastlab_proxyfit")
if fit_shapekey:
algorithms.remove_shapekey(proxy, "mbastlab_proxyfit")
Expand All @@ -223,56 +220,51 @@ def fit_distant_vertices(self, basis_proxy, basis_body, proxy_shapekey, current_
# basis_body = body in basis shape, without morphings and armature
# current_body = current body shape, with morphing (but not armature) applied

polygons_file = algorithms.get_template_polygons(current_body)
polygons_path = os.path.join(self.data_path, "pgroups", polygons_file)
valid_polygons_indxs = algorithms.load_json_data(polygons_path, "Subset of polygons for proxy fitting")

basis_proxy_vertices = basis_proxy.data.vertices # In Blender obj.data = basis data
basis_body_polygons = basis_body.data.polygons
current_body_polygons = current_body.data.polygons

involved_body_polygons_idx = []
involved_basis_body_polygons_coords = []
involved_current_body_polygons_coords = []

if len(basis_body_polygons) == len(current_body_polygons):

involved_basis_body_polygons_coords = []
involved_current_body_polygons_coords = []
valid_polygons_indxs = algorithms.load_json_data(
os.path.join(self.data_path, "pgroups", algorithms.get_template_polygons(current_body)),
"Subset of polygons for proxy fitting"
)

basis_body_tree = algorithms.kdtree_from_obj_polygons(basis_body, valid_polygons_indxs)

for i, basis_proxy_vert in enumerate(basis_proxy_vertices):

nearest_body_polygons_data = basis_body_tree.find(basis_proxy_vert.co)
body_polygon_index = nearest_body_polygons_data[1]
involved_body_polygons_idx.append(body_polygon_index)
involved_basis_body_polygons_coords.append(
basis_body_polygons[nearest_body_polygons_data[1]].center)
involved_current_body_polygons_coords.append(
current_body_polygons[nearest_body_polygons_data[1]].center)

for i in involved_body_polygons_idx:
basis_body_polygon = basis_body_polygons[i]
current_body_polygon = current_body_polygons[i]

#current_body_polygon.select = True

involved_basis_body_polygons_coords.append(basis_body_polygon.center)
involved_current_body_polygons_coords.append(current_body_polygon.center)

basis_body_bbox = algorithms.get_bounding_box(involved_basis_body_polygons_coords)
current_body_bbox = algorithms.get_bounding_box(involved_current_body_polygons_coords)

basis_body_center = algorithms.average_center(involved_basis_body_polygons_coords)
current_body_center = algorithms.average_center(involved_current_body_polygons_coords)

scaleX = current_body_bbox[0]/basis_body_bbox[0]
scaleY = current_body_bbox[1]/basis_body_bbox[1]
scaleZ = current_body_bbox[2]/basis_body_bbox[2]

scale_bbox = mathutils.Vector((scaleX, scaleY, scaleZ))
scale_bbox = mathutils.Vector((
current_body_bbox[0]/basis_body_bbox[0],
current_body_bbox[1]/basis_body_bbox[1],
current_body_bbox[2]/basis_body_bbox[2],
))

for i, basis_proxy_vert in enumerate(basis_proxy_vertices):
proxy_shapekey_vert = proxy_shapekey.data[i]
basis_radial_vector = basis_proxy_vert.co-basis_body_center
scaled_radial_vector = mathutils.Vector((basis_radial_vector[0]*scale_bbox[0],
basis_radial_vector[1]*scale_bbox[1],
basis_radial_vector[2]*scale_bbox[2]))

proxy_shapekey_vert.co = current_body_center + scaled_radial_vector
proxy_shapekey.data[i].co = current_body_center + scaled_radial_vector

def fit_near_vertices(self, basis_proxy, basis_body, proxy_shapekey, current_body, proxy_threshold=0.025):

Expand Down Expand Up @@ -310,44 +302,35 @@ def fit_near_vertices(self, basis_proxy, basis_body, proxy_shapekey, current_bod
else:
f_factor = 0

basis_body_verts_coords = algorithms.get_polygon_vertices_coords(basis_body, body_polygon_index)
p1 = basis_body_verts_coords[0]
p2 = basis_body_verts_coords[1]
p3 = basis_body_verts_coords[2]

raw_body_verts_coords = algorithms.get_polygon_vertices_coords(current_body, body_polygon_index)
p4 = raw_body_verts_coords[0]
p5 = raw_body_verts_coords[1]
p6 = raw_body_verts_coords[2]

proxy_shapekey_vert = proxy_shapekey.data[i]
fitted_vert = mathutils.geometry.barycentric_transform(basis_proxy_vert.co, p1, p2, p3, p4, p5, p6)
fitted_vert = mathutils.geometry.barycentric_transform(
basis_proxy_vert.co,
*algorithms.get_polygon_vertices_coords(basis_body, body_polygon_index)[:3],
*algorithms.get_polygon_vertices_coords(current_body, body_polygon_index)[:3]
)

proxy_shapekey_vert.co = proxy_shapekey_vert.co + f_factor*(fitted_vert-proxy_shapekey_vert.co)

def fit_proxy_object(self, proxy_offset=0.0, proxy_threshold=0.5, create_proxy_mask=False, transfer_w=True):
scn = bpy.context.scene
status, proxy, body = self.get_proxy_fitting_ingredients()
if status == "OK":
armat = algorithms.get_linked_armature(body)
self.calibrate_proxy_object(proxy)
self.reset_proxy_shapekey(proxy) # Always after calibration!

proxy.matrix_world = body.matrix_world

template_name = algorithms.get_template_model(body)
mask_name = "mbastlab_mask_" + proxy.name

algorithms.print_log_report("INFO", "Fitting proxy {0}".format(proxy.name))
selected_objs_names = algorithms.get_objects_selected_names()

body_modfs_status = algorithms.get_object_modifiers_visibility(body)
proxy_modfs_status = algorithms.get_object_modifiers_visibility(proxy)

algorithms.disable_object_modifiers(proxy, ['ARMATURE', 'SUBSURF', 'MASK'])
algorithms.disable_object_modifiers(body, ['ARMATURE', 'SUBSURF', 'MASK'])

basis_body = algorithms.import_object_from_lib(self.templates_library, template_name, stop_import=False)
basis_body = algorithms.import_object_from_lib(
self.templates_library,
algorithms.get_template_model(body),
stop_import=False)

proxy_shapekey = algorithms.new_shapekey(proxy, "mbastlab_proxyfit")

Expand All @@ -365,25 +348,23 @@ def fit_proxy_object(self, proxy_offset=0.0, proxy_threshold=0.5, create_proxy_m
#algorithms.remove_mesh(basis_body_mesh, True)
algorithms.remove_object(basis_body, True, True)

armature_mod = self.add_proxy_armature_modfr(proxy, armat)
armature_mod = self.add_proxy_armature_modfr(proxy, algorithms.get_linked_armature(body))

if transfer_w == True:
if transfer_w:
algorithms.remove_vertgroups_all(proxy)
self.transfer_weights(body, proxy)

algorithms.set_object_modifiers_visibility(proxy, proxy_modfs_status)
algorithms.set_object_modifiers_visibility(body, body_modfs_status)
algorithms.set_object_modifiers_visibility(proxy, algorithms.get_object_modifiers_visibility(proxy))
algorithms.set_object_modifiers_visibility(body, algorithms.get_object_modifiers_visibility(body))
self.disable_extra_armature_modfr(proxy)

parameters = {"show_viewport": True}

correct_smooth_mod = algorithms.new_modifier(
proxy, self.corrective_modifier_name, 'CORRECTIVE_SMOOTH', parameters)
proxy, self.corrective_modifier_name, 'CORRECTIVE_SMOOTH', {"show_viewport": True})

for i in range(10):
for _ in range(10):
algorithms.move_up_modifier(proxy, correct_smooth_mod)

for i in range(10):
for _ in range(10):
algorithms.move_up_modifier(proxy, armature_mod)

for obj_name in selected_objs_names:
Expand Down Expand Up @@ -417,17 +398,15 @@ def proxy_offset(self, basis_proxy, basis_body, proxy_shapekey, current_body, of
# raw body vs proxy shapekey
for body_polygons_data in nearest_body_polygons_data:
body_polygon_index = body_polygons_data[1]
body_polygon_dist = body_polygons_data[2] # distance body-proxy
body_polygon = current_body_polygons[body_polygon_index]
body_polygon_normal = body_polygon.normal
body_polygon_center = body_polygon.center
body_normals.append(body_polygon_normal)

offset_vector = mathutils.Vector((0, 0, 0))
for n in body_normals:
offset_vector += n

if len(body_normals) != 0:
if body_normals:
offset_vector = offset_vector/len(body_normals)
proxy_shapekey_vert.co = proxy_shapekey_vert.co + offset_vector*offset_factor

Expand All @@ -451,7 +430,6 @@ def add_body_mask(self, body, proxy_shapekey, mask_name, proxy_threshold=0.025):
for actual_vert in proxy_shapekey.data:

nearest_body_polygon_data = body_tree.find(actual_vert.co)
involved_vertices = set()
dist_proxy_body = nearest_body_polygon_data[2]
body_polygon_idx = nearest_body_polygon_data[1]
body_polygon = body.data.polygons[body_polygon_idx]
Expand All @@ -472,11 +450,13 @@ def add_body_mask(self, body, proxy_shapekey, mask_name, proxy_threshold=0.025):
parameters = {"vertex_group": mask_name, "invert_vertex_group": True}
algorithms.new_modifier(body, mask_name, 'MASK', parameters)

def remove_body_mask(self, body, mask_name):
@staticmethod
def remove_body_mask(body, mask_name):
algorithms.remove_modifier(body, mask_name)
algorithms.remove_vertgroup(body, mask_name)

def calculate_finishing_morph(self, obj, shapekey_name="Fitted", threshold=0.2):
@staticmethod
def calculate_finishing_morph(obj, shapekey_name="Fitted", threshold=0.2):

shape_to_finish = algorithms.get_shapekey(obj, shapekey_name)
if shape_to_finish:
Expand All @@ -492,8 +472,8 @@ def calculate_finishing_morph(self, obj, shapekey_name="Fitted", threshold=0.2):
current_factors = algorithms.polygon_forma(polyg_current_verts)

deformations = []
for idx in range(len(current_factors)):
deformations.append(abs(current_factors[idx]-base_factors[idx]))
for idx, current_factor in enumerate(current_factors):
deformations.append(abs(current_factor - base_factors[idx]))
max_deform = max(deformations)/2.0

if max_deform > threshold:
Expand Down

0 comments on commit 678a746

Please sign in to comment.