Newer
Older
"version": (2, 0, 1),
"blender": (2, 77, 3),
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/bprojection",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
from bpy.app.handlers import persistent
from bpy.types import (
Panel,
Operator,
PropertyGroup,
AddonPreferences,
)
from bpy.props import (
IntProperty,
FloatProperty,
BoolProperty,
StringProperty,
FloatVectorProperty,
CollectionProperty,
)
from math import (
radians,
sqrt,
pi,
)
from mathutils import (
Quaternion,
Vector,
)
BProjection_Empty = 'Empty for BProjection'
BProjection_Material = 'Material for BProjection'
BProjection_Texture = 'Texture for BProjection'
# Main function for align the plan to view
def align_to_view(context):
last_mouse = Vector((0, 0))
CoDEmanX
committed
em = bpy.data.objects[BProjection_Empty]
rotation = em.custom_rotation
scale = em.custom_scale
z = em.custom_location.z
pos = [em.custom_location.x, em.custom_location.y]
CoDEmanX
committed
sd = context.space_data
r3d = sd.region_3d
r3d.update()
vr = r3d.view_rotation
quat = Quaternion((0.0, 0.0, 1.0), radians(float(rotation)))
v = Vector((pos[0], pos[1], z))
v.rotate(vr)
em = bpy.data.objects[BProjection_Empty]
img = bpy.data.textures[BProjection_Texture].image
if img and img.size[1] != 0:
prop = img.size[0] / img.size[1]
else:
prop = 1
CoDEmanX
committed
if em.custom_linkscale:
em.scale = Vector((prop * scale[0], scale[0], 1))
em.scale = Vector((prop * scale[0], scale[1], 1))
pos_cur = em.location - sd.cursor_location
rot_cur1 = em.rotation_euler.to_quaternion()
em.location = v + ob.location
em.rotation_euler = Quaternion.to_euler(vr * quat)
if em.custom_c3d:
if em.custom_old_scale != em.custom_scale:
CoDEmanX
committed
pos_cur = em.location - sd.cursor_location
rot_cur2 = em.rotation_euler.to_quaternion()
rot_cur1.invert()
pos_cur.rotate(rot_cur1)
pos_cur.rotate(rot_cur2)
v = em.location - pos_cur
sd.cursor_location = v
CoDEmanX
committed
def applyimage(context):
img = bpy.data.textures[BProjection_Texture].image
ob = context.object
CoDEmanX
committed
face = ob.data.polygons
uvdata = ob.data.uv_textures.active.data
CoDEmanX
committed
for f, d in zip(face, uvdata):
if f.select:
d.image = img
align_to_view(context)
ob.data.update()
CoDEmanX
committed
# Function to update the properties
CoDEmanX
committed
def update_Location(self, context):
def find_uv(context):
obj = context.object
me = obj.data.vertices
vg = obj.vertex_groups
CoDEmanX
committed
index_uv = 0
for face in obj.data.polygons:
x = len(face.vertices)
for vertex in face.vertices:
if (len(me[vertex]. groups) > 0):
for g in me[vertex].groups:
if vg[g.group].name == 'texture plane':
CoDEmanX
committed
l.append([index_uv, len(face. vertices)])
index_uv += len(face.vertices)
return l
# Function to update the scaleUV
def update_UVScale(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
v = Vector((em.custom_offsetuv[0] / 10 + 0.5, em.custom_offsetuv[1] / 10 + 0.5))
l = Vector((0.0, 0.0))
CoDEmanX
committed
os = em.custom_old_scaleuv
for i, j in l:
d = ob.data.uv_layers.active.data[i + t]
vres = v - d.uv
d.uv.x = v.x - vres.x / os[0] * s[0]
d.uv.y = v.y - vres.y / os[1] * s[1]
CoDEmanX
committed
em.custom_old_scaleuv = s
def update_PropUVScale(self, context):
em = bpy.data.objects[BProjection_Empty]
if em.custom_linkscaleuv:
em.custom_scaleuv = [em.custom_propscaleuv, em.custom_propscaleuv]
def update_LinkUVScale(self, context):
em = bpy.data.objects[BProjection_Empty]
if em.custom_linkscaleuv:
em.custom_propscaleuv = em.custom_scaleuv.x
update_PropUVScale(self, context)
else:
CoDEmanX
committed
update_UVScale(self, context)
# Function to update the offsetUV
def update_UVOffset(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
o = em.custom_offsetuv
CoDEmanX
committed
oo = em.custom_old_offsetuv
for i, j in l:
d = ob.data.uv_layers.active.data[i + t]
d.uv = [d.uv[0] - oo[0] / 10 + o[0] / 10, d.uv[1] - oo[1] / 10 + o[1] / 10]
CoDEmanX
committed
# Function to update the flip horizontal
def update_FlipUVX(self, context):
l = find_uv(context)
for i, j in l:
d = context.object.data.uv_layers.active.data[i + t]
x = d.uv.x
d.uv.x = 1 - x
CoDEmanX
committed
# Function to update the flip vertical
def update_FlipUVY(self, context):
l = find_uv(context)
for i, j in l:
d = context.object.data.uv_layers.active.data[i + t]
y = d.uv[1]
d.uv[1] = 1 - y
CoDEmanX
committed
CoDEmanX
committed
def update_Rotation(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
if em.custom_rotc3d:
angle = em.custom_rotation - em.custom_old_rotation
sd = context.space_data
CoDEmanX
committed
vr = sd.region_3d.view_rotation.copy()
c = sd.cursor_location - ob.location
e = bpy.data.objects[BProjection_Empty].location - ob.location
vo = Vector((0.0, 0.0, 1.0))
vo.rotate(vr)
quat = Quaternion(vo, radians(angle))
v = e - c
v.rotate(quat)
vr.invert()
v.rotate(vr)
c.rotate(vr)
em.custom_location = c + v
CoDEmanX
committed
else:
CoDEmanX
committed
em.custom_old_rotation = em.custom_rotation
CoDEmanX
committed
def update_Scale(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
CoDEmanX
committed
if em.custom_scac3d:
sd = context.space_data
r3d = sd.region_3d
vr = r3d.view_rotation.copy()
vr.invert()
e = em.location - ob.location
c = sd.cursor_location - ob.location
ce = e - c
CoDEmanX
committed
s = em.custom_scale
os = em.custom_old_scale
c.rotate(vr)
ce.rotate(vr)
CoDEmanX
committed
v = Vector((s.x * ce.x / os.x, s.y * ce.y / os.y, 0.0))
em.custom_location = c + v
CoDEmanX
committed
else:
CoDEmanX
committed
em.custom_old_scale = em.custom_scale
def update_PropScale(self, context):
em = bpy.data.objects[BProjection_Empty]
if em.custom_linkscale:
em.custom_scale = [em.custom_propscale, em.custom_propscale]
CoDEmanX
committed
def update_LinkScale(self, context):
em = bpy.data.objects[BProjection_Empty]
if em.custom_linkscale:
em.custom_propscale = em.custom_scale.x
update_PropScale(self, context)
else:
CoDEmanX
committed
update_Scale(self, context)
def update_activeviewname(self, context):
em = bpy.data.objects[BProjection_Empty]
if self.custom_active:
em.custom_active_view = self.custom_active_view
CoDEmanX
committed
def update_style_clone(self, context):
km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
for kmi in km.keymap_items:
CoDEmanX
committed
if self.custom_style_clone:
if kmi.idname == 'paint.image_paint':
kmi.idname = 'paint.bp_paint'
else:
if kmi.idname == 'paint.bp_paint':
CoDEmanX
committed
kmi.idname = 'paint.image_paint'
CoDEmanX
committed
class custom_props(PropertyGroup):
custom_fnlevel: IntProperty(
name="Fast navigate level",
description="Increase or decrease the SubSurf level, decrease make navigation faster",
default=0,
)
custom_location: FloatVectorProperty(
name="Location",
description="Location of the plane",
default=(1.0, 0, -1.0),
subtype='XYZ',
soft_min=-10,
soft_max=10,
step=0.1,
size=3,
)
custom_rotation: FloatProperty(
name="Rotation",
description="Rotate the plane",
min=-180, max=180,
default=0
)
custom_scale: FloatVectorProperty(
name="Scales",
description="Scale the planes",
default=(1.0, 1.0),
subtype='XYZ',
min=0.1,
max=10,
soft_min=0.1,
soft_max=10,
step=0.1,
size=2,
)
custom_propscale: FloatProperty(
name="PropScale",
description="Scale the Plane",
default=1.0,
min=0.1,
max=10,
soft_min=0.1,
soft_max=10,
step=0.1
)
custom_linkscale: BoolProperty(
name="linkscale",
default=True
)
custom_scaleuv: FloatVectorProperty(
name="ScaleUV",
description="Scale the texture's UV",
default=(1.0, 1.0),
min=0.01,
subtype='XYZ',
size=2
)
custom_propscaleuv: FloatProperty(
name="PropScaleUV",
description="Scale the texture's UV",
default=1.0,
min=0.01
)
custom_offsetuv: FloatVectorProperty(
name="OffsetUV",
description="Decal the texture's UV",
default=(0.0, 0.0),
subtype='XYZ',
size=2
)
custom_linkscaleuv: BoolProperty(
name="linkscaleUV",
default=True)
custom_flipuvx: BoolProperty(
name="flipuvx",
default=False
)
custom_flipuvy: BoolProperty(
name="flipuvy",
default=False
)
custom_active: BoolProperty(
name="custom_active",
default=True
)
custom_expand: BoolProperty(
name="expand",
default=False
)
custom_style_clone: BoolProperty(
name="custom_style_clone",
default=False
)
custom_active_view: StringProperty(
name="custom_active_view",
default="View",
update=update_activeviewname
)
custom_image: StringProperty(
name="custom_image",
default=""
)
custom_index: IntProperty()
# Function to create custom properties
def createcustomprops(context):
Ob = bpy.types.Object
CoDEmanX
committed
Ob.custom_fnlevel = IntProperty(
name="Fast navigate level",
description="Increase or decrease the SubSurf level, decrease make navigation faster",
default=0
)
CoDEmanX
committed
# plane properties
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
Ob.custom_location = FloatVectorProperty(
name="Location",
description="Location of the plane",
default=(1.0, 0, -1.0),
subtype='XYZ',
size=3,
step=0.5,
soft_min=-10,
soft_max=10,
update=update_Location
)
Ob.custom_rotation = FloatProperty(
name="Rotation",
description="Rotate the plane",
min=-180,
max=180,
default=0,
update=update_Rotation
)
Ob.custom_old_rotation = FloatProperty(
name="old_Rotation",
description="Old Rotate the plane",
min=-180,
max=180,
default=0
)
Ob.custom_scale = FloatVectorProperty(
name="Scales",
description="Scale the planes",
subtype='XYZ',
default=(1.0, 1.0),
min=0.1,
max=10,
soft_min=0.1,
soft_max=10,
size=2,
step=0.5,
update=update_Scale
)
Ob.custom_propscale = FloatProperty(
name="PropScale",
description="Scale the Plane",
default=1.0,
min=0.1,
soft_min=0.1,
soft_max=10,
step=0.5,
update=update_PropScale
)
Ob.custom_old_scale = FloatVectorProperty(
name="old_Scales",
description="Old Scale the planes",
subtype='XYZ',
default=(1.0, 1.0),
min=0.1,
size=2
)
Ob.custom_linkscale = BoolProperty(
name="linkscale",
default=True,
update=update_LinkScale
)
Ob.custom_sub = IntProperty(
name="Subdivide",
description="Number of subdivision of the plane",
min=0,
max=20,
default=0
)
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
Ob.custom_scaleuv = FloatVectorProperty(
name="ScaleUV",
description="Scale the texture's UV",
default=(1.0, 1.0),
soft_min=0.01,
soft_max=100,
min=0.01,
subtype='XYZ',
size=2,
update=update_UVScale
)
Ob.custom_propscaleuv = FloatProperty(
name="PropScaleUV",
description="Scale the texture's UV",
default=1.0,
soft_min=0.01,
soft_max=100,
min=0.01,
update=update_PropUVScale
)
Ob.custom_old_scaleuv = FloatVectorProperty(
name="old_ScaleUV",
description="Scale the texture's UV",
default=(1.0, 1.0),
min=0.01,
subtype='XYZ',
size=2
)
Ob.custom_offsetuv = FloatVectorProperty(
name="OffsetUV",
description="Decal the texture's UV",
default=(0.0, 0.0),
subtype='XYZ',
size=2,
update=update_UVOffset
)
Ob.custom_old_offsetuv = FloatVectorProperty(
name="old_OffsetUV",
description="Decal the texture's UV",
default=(0.0, 0.0),
subtype='XYZ',
size=2
)
Ob.custom_linkscaleuv = BoolProperty(
name="linkscaleUV",
default=True,
update=update_LinkUVScale
)
Ob.custom_flipuvx = BoolProperty(
name="flipuvx",
default=False,
update=update_FlipUVX
)
Ob.custom_flipuvy = BoolProperty(
name="flipuvy",
default=False,
update=update_FlipUVY
)
CoDEmanX
committed
# other properties
Ob.custom_c3d = BoolProperty(
name="c3d",
default=True
)
Ob.custom_rotc3d = BoolProperty(
name="rotc3d",
default=False
)
Ob.custom_scac3d = BoolProperty(
name="scac3d",
default=False
)
Ob.custom_expand = BoolProperty(
name="expand",
default=True
)
Ob.custom_style_clone = BoolProperty(
name="custom_style_clone",
default=False,
update=update_style_clone
)
Ob.custom_active_view = StringProperty(
name="custom_active_view",
default="View"
)
Ob.custom_active_object = StringProperty(
name="custom_active_object",
default=context.object.name
)
Ob.custom_active_object = StringProperty(
name="custom_active_object",
default='debut'
)
Ob.custom_props = CollectionProperty(type=custom_props)
# Function to remove custom properties
CoDEmanX
committed
def removecustomprops():
list_prop = ['custom_location', 'custom_rotation',
'custom_old_rotation', 'custom_scale',
'custom_old_scale', 'custom_c3d',
'custom_rotc3d', 'custom_scaleuv',
'custom_flipuvx', 'custom_flipuvy',
'custom_linkscale', 'custom_linkscaleuv',
'custom_old_scaleuv', 'custom_offsetuv',
'custom_old_offsetuv', 'custom_scac3d',
'custom_sub', 'custom_expand',
'custom_style_clone', 'custom_active_view',
'custom_propscaleuv', 'custom_props', 'custom_propscale']
for prop in list_prop:
try:
del bpy.data.objects[BProjection_Empty][prop]
except:
pass
CoDEmanX
committed
CoDEmanX
committed
em = bpy.data.objects[BProjection_Empty]
em.custom_scale = [1, 1]
em.custom_scaleuv = [1.0, 1.0]
em.custom_offsetuv = [0.0, 0.0]
em.custom_propscaleuv = 1.0
em.custom_propscale = 1.0
if em.custom_flipuvx is True:
if em.custom_flipuvy is True:
em.custom_flipuvy = False
# Operator Class to create view
class CreateView(Operator):
bl_idname = "object.create_view"
bl_label = "Create a new view"
CoDEmanX
committed
def execute(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
CoDEmanX
committed
new_props = em.custom_props.add()
em.custom_active_view = new_props.custom_active_view
ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = True
bpy.ops.object.shape_key_add(from_mix=False)
ob.data.shape_keys.key_blocks[ob.active_shape_key_index].value = 1.0
new_props.custom_index = len(em.custom_props) - 1
bpy.ops.object.active_view(index=new_props.custom_index)
# Operator Class to copy view
class SaveView(Operator):
bl_idname = "object.save_view"
bl_label = "copy the view"
CoDEmanX
committed
index: IntProperty(default=0)
CoDEmanX
committed
def execute(self, context):
em = bpy.data.objects[BProjection_Empty]
CoDEmanX
committed
prop = em.custom_props[self.index]
prop.custom_rotation = em.custom_rotation
prop.custom_scale = em.custom_scale
prop.custom_linkscale = em.custom_linkscale
prop.custom_scaleuv = em.custom_scaleuv
prop.custom_propscale = em.custom_propscale
prop.custom_offsetuv = em.custom_offsetuv
prop.custom_linkscaleuv = em.custom_linkscaleuv
prop.custom_propscaleuv = em.custom_propscaleuv
prop.custom_flipuvx = em.custom_flipuvx
prop.custom_flipuvy = em.custom_flipuvy
try:
prop.custom_image = bpy.data.textures[BProjection_Texture].image.name
except:
pass
CoDEmanX
committed
# Operator Class to copy view
class PasteView(Operator):
bl_idname = "object.paste_view"
bl_label = "paste the view"
CoDEmanX
committed
index: IntProperty(default=0)
CoDEmanX
committed
def execute(self, context):
em = bpy.data.objects[BProjection_Empty]
tmp_scac3d = em.custom_scac3d
tmp_rotc3d = em.custom_rotc3d
em.custom_scac3d = False
em.custom_rotc3d = False
prop = em.custom_props[self.index]
em.custom_linkscale = prop.custom_linkscale
em.custom_offsetuv = prop.custom_offsetuv
em.custom_linkscaleuv = prop.custom_linkscaleuv
em.custom_scaleuv = prop.custom_scaleuv
CoDEmanX
committed
em.custom_propscaleuv = prop.custom_propscaleuv
em.custom_rotation = prop.custom_rotation
em.custom_scale = prop.custom_scale
CoDEmanX
committed
em.custom_propscale = prop.custom_propscale
if prop.custom_image != '':
if bpy.data.textures[BProjection_Texture].image.name != prop.custom_image:
bpy.data.textures[BProjection_Texture].image = bpy.data.images[prop.custom_image]
applyimage(context)
if em.custom_flipuvx != prop.custom_flipuvx:
em.custom_flipuvx = prop.custom_flipuvx
if em.custom_flipuvy != prop.custom_flipuvy:
em.custom_flipuvy = prop.custom_flipuvy
em.custom_scac3d = tmp_scac3d
CoDEmanX
committed
em.custom_rotc3d = tmp_rotc3d
# Operator Class to remove view
class RemoveView(Operator):
bl_idname = "object.remove_view"
bl_label = "Rmeove the view"
CoDEmanX
committed
index: IntProperty(default=0)
CoDEmanX
committed
def execute(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
CoDEmanX
committed
ob.active_shape_key_index = self.index + 1
bpy.ops.object.shape_key_remove()
CoDEmanX
committed
if em.custom_props[self.index].custom_active:
if len(em.custom_props) > 0:
bpy.ops.object.active_view(index=self.index - 1)
if self.index == 0 and len(em.custom_props) > 1:
bpy.ops.object.active_view(index=1)
CoDEmanX
committed
em.custom_props.remove(self.index)
CoDEmanX
committed
if len(em.custom_props) == 0:
clear_props(context)
CoDEmanX
committed
bpy.ops.object.create_view()
for item in em.custom_props:
CoDEmanX
committed
item.custom_index = i
for item in (item for item in em.custom_props if item.custom_active):
ob.active_shape_key_index = item.custom_index + 1
CoDEmanX
committed
# Operator Class to copy view
class ActiveView(Operator):
bl_idname = "object.active_view"
bl_label = "Active the view"
CoDEmanX
committed
index: IntProperty(default=0)
CoDEmanX
committed
def execute(self, context):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
for item in (item for item in em.custom_props if item.custom_active is True):
bpy.ops.object.save_view(index=item.custom_index)
em.custom_props[self.index].custom_active = True
CoDEmanX
committed
em.custom_active_view = em.custom_props[self.index].custom_active_view
ob.active_shape_key_index = self.index + 1
CoDEmanX
committed
for i in ob.data.shape_keys.key_blocks:
i.mute = True
CoDEmanX
committed
ob.data.shape_keys.key_blocks[ob.active_shape_key_index].mute = False
CoDEmanX
committed
bpy.ops.object.paste_view(index=self.index)
CoDEmanX
committed
# Draw Class to show the panel
class BProjection(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_label = "BProjection"
@classmethod
def poll(cls, context):
return (context.image_paint_object or context.sculpt_object)
def draw(self, context):
CoDEmanX
committed
layout = self.layout
if BProjection_Empty in [ob.name for ob in bpy.data.objects]:
tex = bpy.data.textures[BProjection_Texture]
ob = context.object
em = bpy.data.objects[BProjection_Empty]
CoDEmanX
committed
if ob == bpy.data.objects[em.custom_active_object]:
col = layout.column(align=True)
CoDEmanX
committed
col.operator("object.removebprojectionplane", text="Remove BProjection plane")
try:
matBProjection = bpy.data.materials[BProjection_Material]
except:
matBProjection = None
CoDEmanX
committed
box = layout.box()
row = box.row()
if not em.custom_expand:
row.prop(em, "custom_expand", text="", icon="TRIA_RIGHT", emboss=False)
row.label(text='Paint Object: ' + ob.name)
row.prop(em, "custom_expand", text="", icon="TRIA_DOWN", emboss=False)
row.label(text='Paint Object: ' + ob.name)
CoDEmanX
committed
if ob == bpy.data.objects[em.custom_active_object]:
col = box.column(align=True)
col.template_ID(tex, "image", open="image.open")
row = box.row(align=True)
row.operator('object.applyimage', text="Apply image", icon='FILE_TICK')
row.prop(em, "custom_c3d", text="", icon='PIVOT_CURSOR')
row = box.row(align=True)
row.label(text="Location:")
row = box.row(align=True)
row.prop(em, 'custom_location', text='')
row = box.row(align=True)
row.prop(em, 'custom_rotation')
row.prop(em, 'custom_rotc3d', text="", icon='MANIPUL')
row = box.row(align=True)
row = box.row(align=True)
if em.custom_linkscale:
row.prop(em, "custom_propscale", text="")
row.prop(em, "custom_linkscale", text="", icon='LINKED')
CoDEmanX
committed
else:
row.prop(em, 'custom_scale', text='')
row.prop(em, "custom_linkscale", text="", icon='UNLINKED')
row.prop(em, 'custom_scac3d', text="", icon='MANIPUL')
row = box.row(align=True)
row.label(text="UV's Offset:")
row = box.row(align=True)
row.prop(em, 'custom_offsetuv', text='')
row.prop(em, "custom_flipuvx", text="", icon='ARROW_LEFTRIGHT')
row.prop(em, "custom_flipuvy", text="", icon='FULLSCREEN_ENTER')
row = box.row(align=True)
row.label(text="UV's Scale:")
row = box.row(align=True)
row.prop(em, 'custom_propscaleuv', text='')
row.prop(em, "custom_linkscaleuv", text="", icon='LINKED')
CoDEmanX
committed
else:
row.prop(em, 'custom_scaleuv', text='')
row.prop(em, "custom_linkscaleuv", text="", icon='UNLINKED')
CoDEmanX
committed
if (context.scene.game_settings.material_mode == 'GLSL' and
context.space_data.viewport_shade == 'TEXTURED'):
row = box.column(align=True)
row.prop(matBProjection, 'alpha', slider=True)
row = box.column(align=True)
row.prop(ob, "custom_fnlevel")
row = box.column(align=True)
row.prop(em, "custom_style_clone", text="Style Clone Normal", icon='RENDERLAYERS')
row.prop(em, "custom_style_clone", text="Style Clone New", icon='RENDERLAYERS')
row = box.column(align=True)
CoDEmanX
committed
if ob == bpy.data.objects[em.custom_active_object]:
for item in em.custom_props:
box = layout.box()
row = box.row()
if item.custom_active:
row.operator("object.active_view", text="",
icon='RADIOBUT_ON', emboss=False).index = item.custom_index
row.operator("object.active_view", text="",
icon='RADIOBUT_OFF', emboss=False).index = item.custom_index
CoDEmanX
committed
row.prop(item, "custom_active_view", text="")
row.operator('object.remove_view', text="",
icon='PANEL_CLOSE', emboss=False).index = item.custom_index
row.operator('object.create_view', text="Create View", icon='RENDER_STILL')
col = box.column(align=True)
CoDEmanX
committed
col.operator("object.change_object", text="Change Object")
else:
ob = context.object
col = layout.column(align=True)
CoDEmanX
committed
if ob.active_material is None:
col.label(text="Add a material first!", icon="ERROR")
elif ob.data.uv_textures.active is None:
col.label(text="Create UVMap first!!", icon="ERROR")
else:
col.operator("object.addbprojectionplane", text="Add BProjection plane")
col = layout.column(align=True)
col.prop(ob, "custom_sub", text="Subdivision level")
CoDEmanX
committed
# Operator Class to apply the image to the plane
class ApplyImageOB(Operator):
bl_idname = "object.applyimage"
bl_label = "Apply image"
CoDEmanX
committed
def execute(self, context):
CoDEmanX
committed
# Operator Class to make the 4 or 6 point and scale the plan
class IntuitiveScale(Operator):
bl_idname = "object.intuitivescale"
bl_label = "Draw lines"
def invoke(self, context, event):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
x = event.mouse_region_x
CoDEmanX
committed
y = event.mouse_region_y
draw_stroke = {"name": "", "pen_flip": False,
"is_start": True, "location": (0, 0, 0),
"mouse": (x, y), "pressure": 1,
"size": 0.5, "time": 0}
CoDEmanX
committed
if len(ob.grease_pencil.layers.active.frames) == 0:
bpy.ops.gpencil.draw(mode='DRAW', stroke=[draw_stroke])
else:
if em.custom_linkscale:
nb_point = 4
else:
nb_point = 6
CoDEmanX
committed
if len(ob.grease_pencil.layers.active.frames[0].strokes) < nb_point:
bpy.ops.gpencil.draw(mode='DRAW', stroke=[draw_stroke])
CoDEmanX
committed
if len(ob.grease_pencil.layers.active.frames[0].strokes) == nb_point:
s = ob.grease_pencil.layers.active.frames[0]
v1 = s.strokes[1].points[0].co - s.strokes[0].points[0].co
if not em.custom_linkscale:
v2 = s.strokes[4].points[0].co - s.strokes[3].points[0].co
else:
v2 = s.strokes[3].points[0].co - s.strokes[2].points[0].co
if (v1.x and v2.x) != 0:
propx = v1.x / v2.x
em.custom_scale[0] *= abs(propx)
CoDEmanX
committed
if not em.custom_linkscale:
v1 = s.strokes[2].points[0].co - s.strokes[0].points[0].co
v2 = s.strokes[5].points[0].co - s.strokes[3].points[0].co
if (v1.y and v2.y) != 0:
propy = v1.y / v2.y
em.custom_scale[1] *= abs(propy)
bpy.ops.gpencil.active_frame_delete()
CoDEmanX
committed
# Operator Class to configure all what's needed
class AddBProjectionPlane(Operator):
bl_idname = "object.addbprojectionplane"
bl_label = "Configure"
CoDEmanX
committed
def creatematerial(self, context):
if 'Material for BProjection' not in [mat.name for mat in bpy.data.materials]:
bpy.data.textures.new(name='Texture for BProjection', type='IMAGE')
CoDEmanX
committed
bpy.data.materials.new(name='Material for BProjection')
CoDEmanX
committed
matBProjection = bpy.data.materials['Material for BProjection']
matBProjection.texture_slots.add()
matBProjection.use_shadeless = True
matBProjection.use_transparency = True
matBProjection.active_texture = bpy.data.textures['Texture for BProjection']
CoDEmanX
committed
index = matBProjection.active_texture_index
matBProjection.texture_slots[index].texture_coords = 'UV'
CoDEmanX
committed
ob = context.object
old_index = ob.active_material_index
bpy.ops.object.material_slot_add()
index = ob.active_material_index
ob.material_slots[index].material = bpy.data.materials['Material for BProjection']
bpy.ops.object.material_slot_assign()
ob.active_material_index = old_index
ob.data.update()
CoDEmanX
committed
def execute(self, context):
if BProjection_Empty not in [ob.name for ob in bpy.data.objects]:
CoDEmanX
committed
cm = bpy.context.object.mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
CoDEmanX
committed
context.space_data.show_relationship_lines = False
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.object.add()
em = context.object
em.name = BProjection_Empty
CoDEmanX
committed
context.view_layer.objects.active = ob
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
bpy.ops.mesh.primitive_plane_add()
# fix the vertex group creation
bpy.ops.object.vertex_group_assign_new()
CoDEmanX
committed
ob.vertex_groups.active.name = 'texture plane'
CoDEmanX
committed
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.vertex_group_select()
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
for i in range(4):
ob.data.edges[len(ob.data.edges) - 1 - i].crease = 1
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
em.custom_sub = ob.custom_sub
if em.custom_sub > 0:
bpy.ops.mesh.subdivide(number_cuts=em.custom_sub)
CoDEmanX
committed
bpy.ops.object.hook_add_selob()
CoDEmanX
committed
em.hide = True
self.creatematerial(context)
CoDEmanX
committed
# fix the grease pencil for 2.78
bpy.ops.gpencil.data_add()
bpy.context.scene.tool_settings.grease_pencil_source = 'OBJECT'
bpy.context.tool_settings.gpencil_stroke_placement_view3d = 'VIEW'
bpy.ops.gpencil.layer_add()
bpy.ops.gpencil.palette_add()
palette = bpy.context.active_gpencil_palette
palette_color = palette.colors.new()
palette_color.color = (1.0, 0, 0)
bpy.context.space_data.show_grease_pencil = True
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
bpy.ops.object.shape_key_add(from_mix=False)
CoDEmanX
committed
bpy.ops.object.create_view()
CoDEmanX
committed
km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
l = ['view3d.rotate', 'view3d.move', 'view3d.zoom', 'view3d.viewnumpad', 'paint.bp_paint',
'MOUSE', 'KEYBOARD', 'LEFT', 'MIDDLEMOUSE', 'WHEELINMOUSE', 'WHEELOUTMOUSE', 'NUMPAD_1',
'NUMPAD_3', 'NUMPAD_7']
for kmi in km.keymap_items:
if kmi.idname in l and kmi.map_type in l and kmi.type in l:
try:
p = kmi.properties.delta
if p == -1 or p == 1:
kmi.idname = 'view3d.zoom_view3d'
kmi.properties.delta = p
except:
try:
p = kmi.properties.type
if kmi.shift is False:
kmi.idname = 'view3d.preset_view3d'
kmi.properties.view = p
except:
if kmi.idname == 'view3d.rotate':
CoDEmanX
committed
kmi.idname = 'view3d.rotate_view3d'
if kmi.idname == 'view3d.move':
kmi.idname = 'view3d.pan_view3d'
CoDEmanX
committed
km = context.window_manager.keyconfigs.default.keymaps['Image Paint']
CoDEmanX
committed
kmi = km.keymap_items.new("object.intuitivescale", 'LEFTMOUSE', 'PRESS', shift=True)
kmi = km.keymap_items.new("object.bp_grab", 'G', 'PRESS')
CoDEmanX
committed
kmi = km.keymap_items.new("object.bp_rotate", 'R', 'PRESS')
kmi = km.keymap_items.new("object.bp_scale", 'S', 'PRESS', ctrl=True)
kmi = km.keymap_items.new("object.bp_scaleuv", 'U', 'PRESS')
kmi = km.keymap_items.new("object.bp_offsetuv", 'Y', 'PRESS')
kmi = km.keymap_items.new("object.bp_clear_prop", 'C', 'PRESS')
kmi = km.keymap_items.new("object.bp_toggle_alpha", 'Q', 'PRESS')
CoDEmanX
committed
context.space_data.cursor_location = em.location
CoDEmanX
committed
bpy.ops.object.mode_set(mode=cm, toggle=False)
bpy.data.objects[BProjection_Empty].custom_active_object = context.object.name
CoDEmanX
committed
# Operator Class to remove what is no more needed
class RemoveBProjectionPlane(Operator):
bl_idname = "object.removebprojectionplane"
bl_label = "Configure"
def removematerial(self, context):
CoDEmanX
committed
ob = context.object
i = 0
for ms in ob.material_slots:
if ms.name == BProjection_Material:
index = i
CoDEmanX
committed
ob.active_material_index = index
bpy.ops.object.material_slot_remove()
CoDEmanX
committed
def execute(self, context):
CoDEmanX
committed
try:
cm = bpy.context.object.mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
CoDEmanX
committed
context.space_data.show_relationship_lines = True
CoDEmanX
committed
bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
CoDEmanX
committed
self.removematerial(context)
ob = context.object
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.mesh.select_all(action='DESELECT')
ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
bpy.ops.object.vertex_group_select()
bpy.ops.mesh.delete()
bpy.ops.object.vertex_group_remove()
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
CoDEmanX
committed
em = bpy.data.objects[BProjection_Empty]
context.view_layer.objects.active = em
CoDEmanX
committed
context.view_layer.objects.active = ob
CoDEmanX
committed
CoDEmanX
committed
ob = context.object
for i in ob.data.shape_keys.key_blocks:
bpy.ops.object.shape_key_remove()
bpy.ops.object.shape_key_remove()
CoDEmanX
committed
bpy.ops.object.mode_set(mode=cm, toggle=False)
CoDEmanX
committed
CoDEmanX
committed
def reinitkey():
km = bpy.data.window_managers['WinMan'].keyconfigs['Blender'].keymaps['3D View']
l = ['view3d.zoom_view3d', 'view3d.preset_view3d', 'view3d.rotate_view3d',
'view3d.pan_view3d', 'MOUSE', 'KEYBOARD', 'MIDDLEMOUSE', 'WHEELINMOUSE',
'WHEELOUTMOUSE', 'NUMPAD_1', 'NUMPAD_3', 'NUMPAD_7']
for kmi in km.keymap_items:
if kmi.idname in l and kmi.map_type in l and kmi.type in l:
try:
p = kmi.properties.delta
if p == -1 or p == 1:
kmi.idname = 'view3d.zoom'
kmi.properties.delta = p
except:
try:
p = kmi.properties.view
if kmi.shift is False:
kmi.idname = 'view3d.viewnumpad'
kmi.properties.type = p
except:
if kmi.idname == 'view3d.rotate_view3d':
CoDEmanX
committed
kmi.idname = 'view3d.rotate'
if kmi.idname == 'view3d.pan_view3d':
CoDEmanX
committed
kmi.idname = 'view3d.move'
km = bpy.context.window_manager.keyconfigs.default.keymaps['Image Paint']
CoDEmanX
committed
for kmi in km.keymap_items:
if kmi.idname == 'paint.bp_paint':
kmi.idname = 'paint.image_paint'
CoDEmanX
committed
for kmi in (kmi for kmi in km.keymap_items if kmi.idname in
{"object.intuitivescale", "object.bp_grab", "object.bp_rotate",
"object.bp_scale", "object.bp_scaleuv", "object.bp_clear_prop",
"object.bp_offsetuv", "object.bp_toggle_alpha", }):
km.keymap_items.remove(kmi)
# Operator Class to remove what is no more needed
class ChangeObject(Operator):
bl_idname = "object.change_object"
bl_label = "Change Object"
def removematerial(self, context):
CoDEmanX
committed
ob = context.object
i = 0
for ms in ob.material_slots:
if ms.name == BProjection_Material:
index = i
CoDEmanX
committed
ob.active_material_index = index
bpy.ops.object.material_slot_remove()
CoDEmanX
committed
def execute(self, context):
new_ob = context.object
CoDEmanX
committed
em = bpy.data.objects[BProjection_Empty]
context.view_layer.objects.active = bpy.data.objects[em.custom_active_object]
ob = context.object
if ob != new_ob:
cm = bpy.context.object.mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
CoDEmanX
committed
bpy.ops.object.modifier_remove(modifier="Hook-Empty for BProjection")
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.mesh.select_all(action='DESELECT')
ob.vertex_groups.active_index = ob.vertex_groups['texture plane'].index
bpy.ops.object.vertex_group_select()
lo_b = [ob for ob in bpy.data.objects if ob.type == 'MESH']
bpy.ops.mesh.separate(type='SELECTED')
lo_a = [ob for ob in bpy.data.objects if ob.type == 'MESH']
bpy.ops.object.vertex_group_remove()
CoDEmanX
committed
for i in lo_b:
lo_a.remove(i)
bplane = lo_a[0]
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
CoDEmanX
committed
self.removematerial(context)
CoDEmanX
committed
bpy.ops.object.mode_set(mode=cm, toggle=False)
CoDEmanX
committed
shape_index = ob.active_shape_key_index
CoDEmanX
committed
for i in ob.data.shape_keys.key_blocks:
bpy.ops.object.shape_key_remove()
bpy.ops.object.shape_key_remove()
CoDEmanX
committed
CoDEmanX
committed
context.view_layer.objects.active = bplane
for ms in (ms for ms in bplane.material_slots if ms.name != BProjection_Material):
bplane.active_material = ms.material
bpy.ops.object.material_slot_remove()
CoDEmanX
committed
for gs in (gs for gs in bplane.vertex_groups if gs.name != 'texture plane'):
bplane.vertex_groups.active_index = gs.index
bpy.ops.object.vertex_group_remove()
CoDEmanX
committed
context.view_layer.objects.active = new_ob
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
CoDEmanX
committed
em.select_set(True)
new_ob.select_set(False)
bpy.ops.object.location_clear()
bpy.ops.object.rotation_clear()
bpy.ops.object.scale_clear()
context.view_layer.objects.active = new_ob
CoDEmanX
committed
bpy.ops.object.editmode_toggle()
bpy.ops.object.hook_add_selob()
bpy.ops.object.editmode_toggle()
em.hide = True
em.select_set(False)
new_ob.select_set(True)
em.custom_active_object = new_ob.name
tmp = em.custom_c3d
em.custom_c3d = False
bpy.ops.object.active_view(index=shape_index - 1)
bpy.ops.object.mode_set(mode=cm, toggle=False)
CoDEmanX
committed
CoDEmanX
committed
r3d = sd.region_3d
vr = r3d.view_rotation.copy()
vr.invert()
CoDEmanX
committed
ob_loc = ob.location.copy()
new_ob_loc = new_ob.location.copy()
ob_loc.rotate(vr)
new_ob_loc.rotate(vr)
em.custom_location += Vector((ob_loc.x - new_ob_loc.x, ob_loc.y - new_ob_loc.y, 0.0))
em.custom_c3d = tmp
CoDEmanX
committed
CoDEmanX
committed
# Paint from the bp_plan
last_mouse = Vector((0, 0))
def move_bp(self, context, cm, fm):
em = bpy.data.objects['Empty for BProjection']
CoDEmanX
committed
deltax = cm.x - round(fm.x)
deltay = cm.y - round(fm.y)
CoDEmanX
committed
sd = context.space_data
l = sd.region_3d
vr = l.view_rotation.copy()
vr.invert()
CoDEmanX
committed
v_init = Vector((0.0, 0.0, 1.0))
CoDEmanX
committed
pos = [-deltax, -deltay]
v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
pos = [0, 0]
CoDEmanX
committed
vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
loc = vbl - v
CoDEmanX
committed
CoDEmanX
committed
CoDEmanX
committed
class BP_Paint(Operator):
bl_idname = "paint.bp_paint"
bl_label = "Paint BProjection Plane"
first_mouse = Vector((0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
global last_mouse
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
if event.type == 'MOUSEMOVE': # 'INBETWEEN_MOUSEMOVE':
step_act = Vector((event.mouse_region_x, event.mouse_region_y)) - self.step_prev
if (step_act.length >= context.scene.tool_settings.unified_paint_settings.size *
bpy.data.brushes['Clone'].spacing / 100 or bpy.data.brushes['Clone'].use_airbrush):
move_bp(self, context, Vector((event.mouse_region_x, event.mouse_region_y)) - self.v_offset,
self.first_mouse)
CoDEmanX
committed
bpy.ops.paint.image_paint(stroke=[{"name": "", "location": (0, 0, 0),
"mouse": (event.mouse_region_x, event.mouse_region_y),
"pressure": 1, "pen_flip": False, "time": 0, "is_start": False}])
self.step_prev = Vector((event.mouse_region_x, event.mouse_region_y))
CoDEmanX
committed
if event.type == 'LEFTMOUSE':
em.custom_c3d = True
bpy.data.materials['Material for BProjection'].alpha = self.alpha
em.custom_location = self.first_location
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_c3d = True
bpy.data.materials['Material for BProjection'].alpha = self.alpha
em.custom_location = self.first_location
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
CoDEmanX
committed
sd = context.space_data
l = sd.region_3d
v_init = Vector((0.0, 0.0, 1.0))
context.scene.cursor_location = view3d_utils.region_2d_to_location_3d(
context.region, l,
[event.mouse_region_x,
event.mouse_region_y], v_init
)
CoDEmanX
committed
self.first_location = em.custom_location.copy()
CoDEmanX
committed
self.v_offset = Vector((context.region.width, context.region.height)) - Vector((event.mouse_region_x,
event.mouse_region_y))
move_bp(self, context, Vector((event.mouse_region_x, event.mouse_region_y)) - self.v_offset, self.first_mouse)
CoDEmanX
committed
em.custom_c3d = False
self.alpha = bpy.data.materials['Material for BProjection'].alpha
CoDEmanX
committed
CoDEmanX
committed
bpy.data.materials['Material for BProjection'].alpha = 0
CoDEmanX
committed
bpy.ops.paint.image_paint(stroke=[{"name": "", "location": (0, 0, 0),
"mouse": (event.mouse_region_x, event.mouse_region_y),
"pressure": 1, "pen_flip": False,
"time": 0, "size": 1,
"is_start": False}])
CoDEmanX
committed
self.step_prev = Vector((event.mouse_region_x, event.mouse_region_y))
return {'RUNNING_MODAL'}
# Operator Class toggle the alpha of the plane
Geo Kgeo
committed
temp_alpha = 0
class ApplyImage(Operator):
bl_idname = "object.bp_toggle_alpha"
bl_label = "Toggle Alpha of the BP"
CoDEmanX
committed
def execute(self, context):
global temp_alpha
CoDEmanX
committed
bpy.data.materials['Material for BProjection'].alpha = temp_alpha
temp_alpha = 0
else:
temp_alpha = bpy.data.materials['Material for BProjection'].alpha
CoDEmanX
committed
bpy.data.materials['Material for BProjection'].alpha = 0
CoDEmanX
committed
# reinit the values of the bp_plane
class BP_Clear_Props(Operator):
bl_idname = "object.bp_clear_prop"
bl_label = "Clear Props BProjection Plane"
CoDEmanX
committed
def execute(self, context):
CoDEmanX
committed
CoDEmanX
committed
# Move the UV of the bp_plane
class BP_OffsetUV(Operator):
bl_idname = "object.bp_offsetuv"
bl_label = "OffsetUV BProjection Plane"
CoDEmanX
committed
axe_x = True
axe_y = True
first_mouse = Vector((0, 0))
first_offsetuv = Vector((0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
if event.shift:
fac = 0.1
else:
fac = 1
if event.type == 'X' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
elif self.axe_x is False and self.axe_y is True:
CoDEmanX
committed
self.axe_x = True
if event.type == 'Y' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is False and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
self.axe_y = True
self.axe_x = False
CoDEmanX
committed
deltax = (event.mouse_region_x - self.first_mouse.x) * fac
deltay = (event.mouse_region_y - self.first_mouse.y) * fac
if event.type == 'MOUSEMOVE':
if not self.axe_y:
deltay = 0
if not self.axe_x:
CoDEmanX
committed
deltax = 0
em.custom_offsetuv = [ouv[0] - deltax / 50, ouv[1] - deltay / 50]
CoDEmanX
committed
self.first_mouse.x = event.mouse_region_x
CoDEmanX
committed
self.first_mouse.y = event.mouse_region_y
if event.type == 'LEFTMOUSE':
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_offsetuv = self.first_offsetuv
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
self.first_offsetuv = em.custom_offsetuv.copy()
CoDEmanX
committed
return {'RUNNING_MODAL'}
# Scale the UV of the bp_plane
class BP_ScaleUV(Operator):
bl_idname = "object.bp_scaleuv"
bl_label = "ScaleUV BProjection Plane"
CoDEmanX
committed
axe_x = True
axe_y = True
first_mouse = Vector((0, 0))
first_scaleuv = Vector((0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
if event.shift:
fac = 0.1
else:
fac = 1
if event.type == 'X' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
elif self.axe_x is False and self.axe_y is True:
CoDEmanX
committed
self.axe_x = True
if event.type == 'Y' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is False and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
self.axe_y = True
self.axe_x = False
CoDEmanX
committed
deltax = (event.mouse_region_x - self.first_mouse.x) * fac
deltay = (event.mouse_region_y - self.first_mouse.y) * fac
if event.type == 'MOUSEMOVE':
if not self.axe_y:
deltay = 0
if not self.axe_x:
CoDEmanX
committed
deltax = 0
if self.axe_x and self.axe_y:
fac = em.custom_scaleuv[1] / em.custom_scaleuv[0]
deltay = deltax * fac
else:
CoDEmanX
committed
em.custom_linkscaleuv = False
em.custom_scaleuv = [s[0] + deltax / 50, s[1] + deltay / 50]
CoDEmanX
committed
self.first_mouse.x = event.mouse_region_x
CoDEmanX
committed
self.first_mouse.y = event.mouse_region_y
if event.type == 'LEFTMOUSE':
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_scaleuv = self.first_scaleuv
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
self.first_scaleuv = em.custom_scaleuv.copy()
CoDEmanX
committed
CoDEmanX
committed
# Scale the bp_plane
class BP_Scale(Operator):
bl_idname = "object.bp_scale"
bl_label = "Scale BProjection Plane"
CoDEmanX
committed
axe_x = True
axe_y = True
first_mouse = Vector((0, 0))
first_scale = Vector((0, 0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
center = view3d_utils.location_3d_to_region_2d(context.region, sd.region_3d, em.location)
CoDEmanX
committed
vec_init = self.first_mouse - center
vec_act = Vector((event.mouse_region_x, event.mouse_region_y)) - center
scale_fac = vec_act.length / vec_init.length
CoDEmanX
committed
scale_fac = round(scale_fac, 1)
if event.type == 'X' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
elif self.axe_x is False and self.axe_y is True:
CoDEmanX
committed
self.axe_x = True
if event.type == 'Y' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is False and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
self.axe_y = True
self.axe_x = False
CoDEmanX
committed
if event.type == 'MOUSEMOVE':
CoDEmanX
committed
em.custom_scale = [self.first_scale[0] * scale_fac, self.first_scale[1]]
em.custom_scale = [self.first_scale[0], self.first_scale[1] * scale_fac]
CoDEmanX
committed
if self.axe_x and self.axe_y:
em.custom_scale = [self.first_scale[0] * scale_fac, self.first_scale[1] * scale_fac]
CoDEmanX
committed
em.custom_linkscale = False
if event.type == 'LEFTMOUSE':
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_scale = self.first_scale
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
self.first_scale = em.custom_scale.copy()
CoDEmanX
committed
CoDEmanX
committed
# Rotate the bp_plan
class BP_Rotate(Operator):
bl_idname = "object.bp_rotate"
bl_label = "Rotate BProjection Plane"
CoDEmanX
committed
first_mouse = Vector((0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
CoDEmanX
committed
center = view3d_utils.location_3d_to_region_2d(context.region, sd.region_3d, em.location if
em.custom_rotc3d else context.scene.cursor_location)
CoDEmanX
committed
vec_init = self.first_mouse - center
vec_act = Vector((event.mouse_region_x, event.mouse_region_y)) - center
rot = -vec_init.angle_signed(vec_act) * 180 / pi
CoDEmanX
committed
CoDEmanX
committed
rot = int(rot / 5) * 5
CoDEmanX
committed
if event.type == 'MOUSEMOVE':
CoDEmanX
committed
em.custom_rotation = self.first_rotation + rot
if event.type == 'LEFTMOUSE':
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_rotation = self.first_rotation
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
self.first_rotation = em.custom_rotation
CoDEmanX
committed
return {'RUNNING_MODAL'}
class BP_Grab(Operator):
bl_idname = "object.bp_grab"
bl_label = "Grab BProjection Plane"
CoDEmanX
committed
axe_x = True
axe_y = True
axe_z = False
first_mouse = Vector((0, 0))
first_location = Vector((0, 0, 0))
CoDEmanX
committed
@classmethod
def poll(cls, context):
return 1
def modal(self, context, event):
CoDEmanX
committed
em = bpy.data.objects['Empty for BProjection']
if event.shift:
fac = 0.1
else:
fac = 1
if event.type == 'X' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
elif self.axe_x is False and self.axe_y is True:
self.axe_y = False
self.axe_x = True
if self.axe_z is True:
CoDEmanX
committed
self.axe_x = True
if event.type == 'Y' and event.value == 'PRESS':
if self.axe_x is True and self.axe_y is True:
elif self.axe_x is False and self.axe_y is True:
elif self.axe_x is True and self.axe_y is False:
self.axe_y = True
self.axe_x = False
if self.axe_z is True:
self.axe_z = False
self.axe_y = True
CoDEmanX
committed
if event.type == 'Z' and event.value == 'PRESS':
if self.axe_z is False:
self.axe_z = True
self.axe_x = False
self.axe_y = False
elif self.axe_z is True:
self.axe_z = False
self.axe_x = True
self.axe_y = True
CoDEmanX
committed
deltax = event.mouse_region_x - round(self.first_mouse.x)
deltay = event.mouse_region_y - round(self.first_mouse.y)
CoDEmanX
committed
if event.type == 'MOUSEMOVE':
CoDEmanX
committed
sd = context.space_data
l = sd.region_3d
vr = l.view_rotation.copy()
vr.invert()
CoDEmanX
committed
v_init = Vector((0.0, 0.0, 1.0))
CoDEmanX
committed
pos = [-deltax, -deltay]
v = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
pos = [0, 0]
CoDEmanX
committed
vbl = view3d_utils.region_2d_to_location_3d(context.region, l, pos, v_init)
loc = vbl - v
if not self.axe_y:
loc.y = loc.z = 0
if not self.axe_x:
loc.x = loc.z = 0
if self.axe_z:
loc.z = deltax / 10
CoDEmanX
committed
em.custom_location += loc * fac
CoDEmanX
committed
self.first_mouse.x = event.mouse_region_x
CoDEmanX
committed
self.first_mouse.y = event.mouse_region_y
if event.type == 'LEFTMOUSE':
return {'FINISHED'}
CoDEmanX
committed
if event.type == 'ESC' or event.type == 'RIGHTMOUSE':
em.custom_location = self.first_location
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
em = bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
self.first_location = em.custom_location.copy()
CoDEmanX
committed
return {'RUNNING_MODAL'}
Geo Kgeo
committed
# Operator Class to rotate the view3D
class RotateView3D(Operator):
bl_idname = "view3d.rotate_view3d"
bl_label = "Rotate the View3D"
CoDEmanX
committed
first_mouse = Vector((0, 0))
CoDEmanX
committed
first_time = True
tmp_level = -1
def vect_sphere(self, context, mx, my):
width = context.region.width
height = context.region.height
CoDEmanX
committed
ratio = height / width
CoDEmanX
committed
x = 2 * mx / width
y = 2 * ratio * my / height
CoDEmanX
committed
x = x - 1
y = y - ratio
else:
ratio = width / height
CoDEmanX
committed
x = 2 * ratio * mx / width
y = 2 * my / height
CoDEmanX
committed
x = x - ratio
y = y - 1
CoDEmanX
committed
z2 = 1 - x * x - y * y
if z2 > 0:
z = sqrt(z2)
else:
z = 0
CoDEmanX
committed
p = Vector((x, y, z))
p.normalize()
return p
CoDEmanX
committed
def tracball(self, context, mx, my, origine):
sd = context.space_data
CoDEmanX
committed
vr_b = sd.region_3d.view_rotation.copy()
vr_b.invert()
pos_init = sd.region_3d.view_location - origine
sd.region_3d.view_location = origine
CoDEmanX
committed
v1 = self.vect_sphere(context, self.first_mouse.x, self.first_mouse.y)
v2 = self.vect_sphere(context, mx, my)
CoDEmanX
committed
axis = Vector.cross(v1, v2)
angle = Vector.angle(v1, v2)
CoDEmanX
committed
q = Quaternion(axis, -2 * angle)
CoDEmanX
committed
sd.region_3d.view_rotation *= q
CoDEmanX
committed
vr_a = sd.region_3d.view_rotation.copy()
pos_init.rotate(vr_a * vr_b)
sd.region_3d.view_location = pos_init + origine
CoDEmanX
committed
self.first_mouse = Vector((mx, my))
Geo Kgeo
committed
def turnable(self, context, mx, my, origine):
sd = context.space_data
CoDEmanX
committed
width = context.region.width
height = context.region.height
CoDEmanX
committed
vr_b = sd.region_3d.view_rotation.copy()
vr_b.invert()
pos_init = sd.region_3d.view_location - origine
sd.region_3d.view_location = origine
CoDEmanX
committed
vz = Vector((0, 0, 1))
qz = Quaternion(vz, -8 * (mx - self.first_mouse.x) / width)
Geo Kgeo
committed
sd.region_3d.view_rotation.rotate(qz)
sd.region_3d.update()
vx = Vector((1, 0, 0))
CoDEmanX
committed
vx.rotate(sd.region_3d.view_rotation)
qx = Quaternion(vx, 8 * (my - self.first_mouse.y) / height)
CoDEmanX
committed
sd.region_3d.view_rotation.rotate(qx)
Geo Kgeo
committed
sd.region_3d.update()
CoDEmanX
committed
vr_a = sd.region_3d.view_rotation.copy()
pos_init.rotate(vr_a * vr_b)
sd.region_3d.view_location = pos_init + origine
CoDEmanX
committed
Geo Kgeo
committed
self.first_mouse = Vector((mx, my))
CoDEmanX
committed
def modal(self, context, event):
CoDEmanX
committed
if event.value == 'RELEASE':
if self.tmp_level > -1:
for sub in context.object.modifiers:
if sub.type in ['SUBSURF', 'MULTIRES']:
CoDEmanX
committed
sub.levels = self.tmp_level
CoDEmanX
committed
if event.type == 'MOUSEMOVE':
if context.preferences.inputs.view_rotate_method == 'TRACKBALL':
self.tracball(context, event.mouse_region_x, event.mouse_region_y, ob.location)
self.turnable(context, event.mouse_region_x, event.mouse_region_y, ob.location)
# to unlock the camera
CoDEmanX
committed
if self.first_time:
rot_ang = context.preferences.view.rotation_angle
context.preferences.view.rotation_angle = 0
bpy.ops.view3d.view_orbit(type='ORBITLEFT')
context.preferences.view.rotation_angle = rot_ang
CoDEmanX
committed
bpy.ops.view3d.view_persportho()
bpy.ops.view3d.view_persportho()
self.first_time = False
CoDEmanX
committed
CoDEmanX
committed
def execute(self, context):
align_to_view(context)
bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse = Vector((event.mouse_region_x, event.mouse_region_y))
self.first_time = True
for sub in context.object.modifiers:
if sub.type in ['SUBSURF', 'MULTIRES']:
self.tmp_level = sub.levels
if sub.levels - self.tmp_level < 0:
sub.levels = 0
else:
sub.levels += bpy.context.object.custom_fnlevel
return {'RUNNING_MODAL'}
# Operator Class to pan the view3D
class PanView3D(Operator):
bl_idname = "view3d.pan_view3d"
bl_label = "Pan View3D"
CoDEmanX
committed
first_mouse = Vector((0, 0))
tmp_level = -1
def modal(self, context, event):
em = bpy.data.objects[BProjection_Empty]
deltax = event.mouse_region_x - self.first_mouse.x
CoDEmanX
committed
deltay = event.mouse_region_y - self.first_mouse.y
sd = context.space_data
r3d = sd.region_3d
vr = r3d.view_rotation.copy()
vr.invert()
CoDEmanX
committed
v_init = Vector((0.0, 0.0, 1.0))
CoDEmanX
committed
pos = [deltax, deltay]
v = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
pos = [0, 0]
CoDEmanX
committed
vbl = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
loc = vbl - v
sd.region_3d.view_location += loc
Geo Kgeo
committed
if not em.custom_style_clone:
em.custom_location += loc
self.first_mouse.x = event.mouse_region_x
self.first_mouse.y = event.mouse_region_y
if event.type == 'MIDDLEMOUSE'and event.value == 'RELEASE':
if self.tmp_level > -1:
for sub in context.object.modifiers:
if sub.type in ['SUBSURF', 'MULTIRES']:
sub.levels = self.tmp_level
return {'FINISHED'}
CoDEmanX
committed
CoDEmanX
committed
def invoke(self, context, event):
CoDEmanX
committed
bpy.data.objects['Empty for BProjection']
context.window_manager.modal_handler_add(self)
self.first_mouse.x = event.mouse_region_x
CoDEmanX
committed
self.first_mouse.y = event.mouse_region_y
for sub in context.object.modifiers:
if sub.type in ['SUBSURF', 'MULTIRES']:
self.tmp_level = sub.levels
if sub.levels - self.tmp_level < 0:
CoDEmanX
committed
sub.levels += bpy.context.object.custom_fnlevel
return {'RUNNING_MODAL'}
CoDEmanX
committed
def execute(self, context):
align_to_view(context)
# Operator Class to zoom the view3D
class ZoomView3D(Operator):
bl_idname = "view3d.zoom_view3d"
bl_label = "Zoom View3D"
name="delta",
description="Delta",
min=-1.0, max=1,
default=1.0)
CoDEmanX
committed
def invoke(self, context, event):
ob = context.object
em = bpy.data.objects[BProjection_Empty]
sd = context.space_data
width = context.region.width
CoDEmanX
committed
height = context.region.height
r3d = sd.region_3d
v_init = Vector((0.0, 0.0, 1.0))
pos = [width, height]
vtr_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
pos = [0, 0]
vbl_b = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
len_b = vtr_b - vbl_b
CoDEmanX
committed
bpy.ops.view3d.zoom(delta=self.delta)
pos = [width, height]
vtr_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
pos = [0, 0]
CoDEmanX
committed
vbl_a = view3d_utils.region_2d_to_location_3d(context.region, r3d, pos, v_init)
CoDEmanX
committed
fac = len_a.length / len_b.length
r3d.view_location -= ob.location
r3d.view_location *= fac
r3d.view_location += ob.location
vres = Vector((em. custom_location. x * fac, em.custom_location. y * fac, em.custom_location.z))
CoDEmanX
committed
Geo Kgeo
committed
if not em.custom_style_clone:
em.custom_location = vres
CoDEmanX
committed
CoDEmanX
committed
CoDEmanX
committed
def execute(self, context):
CoDEmanX
committed
# Operator Class to use numpad shortcut
class PresetView3D(Operator):
bl_idname = "view3d.preset_view3d"
bl_label = "Preset View3D"
view: StringProperty(name="View", description="Select the view", default='TOP')
CoDEmanX
committed
def invoke(self, context, event):
ob = context.object
origine = ob.location
sd = context.space_data
vr_b = sd.region_3d.view_rotation.copy()
vr_b.invert()
pos_init = sd.region_3d.view_location - origine
sd.region_3d.view_location = origine
tmp = context.preferences.view.smooth_view
context.preferences.view.smooth_view = 0
bpy.ops.view3d.viewnumpad(type=self.view)
CoDEmanX
committed
align_to_view(context)
context.preferences.view.smooth_view = tmp
CoDEmanX
committed
vr_a = sd.region_3d.view_rotation.copy()
pos_init.rotate(vr_a * vr_b)
sd.region_3d.view_location = pos_init + origine
CoDEmanX
committed
class Shortcuts_Pref(AddonPreferences):
bl_idname = __name__
def draw(self, context):
layout = self.layout
scene = context.scene
layout.prop(context.scene, "bp_shortcuts", text="Hot Keys", icon="KEYINGSET")
if scene.bp_shortcuts:
row = layout.row()
col = row.column()
col.label(text="Hotkey List:")
col.label(text="Shift + left mouse - Intuitive Scale")
col.label(text="G + mouse move to translate the projection plane")
col.label(text="R + mouse move to rotate the projection plane")
col.label(text="Ctrl + S + mouse move to scale the projection plane")
col.label(text="U + mouse move to scale the projection plane UV")
col.label(text="Y + mouse move to offset the projection plane UV")
col.label(text="C - clear all")
col.label(text="Q - toggle alpha of the projection plane")
@persistent
def load_handler(dummy):
reinitkey()
bpy.types.Scene.bp_shortcuts = BoolProperty(
default=False,
description='Show shortcuts for BP Projection'
)
createcustomprops(bpy.context)
bpy.app.handlers.load_post.append(load_handler)
del bpy.types.Scene.bp_shortcuts
register()