Skip to content

Instantly share code, notes, and snippets.

@havchr
Last active March 9, 2020 09:17
Show Gist options
  • Save havchr/a9ab0bd413599928694736563801c633 to your computer and use it in GitHub Desktop.
Save havchr/a9ab0bd413599928694736563801c633 to your computer and use it in GitHub Desktop.
Small tool to help set up texture projectors for quick block-outs in blender.
import bpy
import math
bl_info = {
"name": "Easy setup of UV Projection",
"blender": (2, 80, 0),
"category": "UV",
}
uvProjectorsCollectionName="UVProjectors"
defaultUVProjectorCollectionName="UVProjectorDefault"
projectorObjectName="UVProjector"
def addUVProjModifier(context):
#First add UVProject if not allready in modifiers
uvProjIndex = context.active_object.modifiers.find("UVProject")
if uvProjIndex == -1:
bpy.ops.object.modifier_add(type='UV_PROJECT')
uvProjIndex = context.active_object.modifiers.find("UVProject")
uvProj = context.active_object.modifiers[uvProjIndex]
return uvProj
def createOrFindUvProjectorsCollection():
if uvProjectorsCollectionName not in bpy.data.collections:
uvProjectorsCollection = bpy.data.collections.new(name=uvProjectorsCollectionName)
bpy.context.scene.collection.children.link(uvProjectorsCollection)
else:
uvProjectorsCollection = bpy.data.collections[uvProjectorsCollectionName]
return uvProjectorsCollection
def setProjectorsForModifier(uvProjModifier,projectorDad):
if projectorDad is not None:
uvProjModifier.projector_count = len(projectorDad.children)
for index,obChild in enumerate(projectorDad.children):
uvProjModifier.projectors[index].object = obChild
def createProjectorSystem(name=projectorObjectName,
collectionName=defaultUVProjectorCollectionName,
distanceFromOrigo=10.0,
offset=(0.0,0.0,0.0)):
#create default projector
print("creating projector system " + name)
deg90InRads = math.radians(90.0)
projectorData = [
((0.0 + offset[0],-distanceFromOrigo + offset[1],0.0 + offset[2]),(deg90InRads,0.0,deg90InRads*0.0),("Projector_Neg_Y")), #near to frontproject
((distanceFromOrigo + offset[0],0 + offset[1],0 + offset[2]),(deg90InRads,0,deg90InRads*1),("Projector_Pos_X")), #left projection
((0 + offset[0],distanceFromOrigo + offset[1],0 + offset[2]),(deg90InRads,0,deg90InRads*2),("Projector_Pos_Y")), # far to back projection
((-distanceFromOrigo + offset[0],0 + offset[1],0 + offset[2]),(deg90InRads,0,deg90InRads*3),("Projector_Neg_X")), #right projection
((0 + offset[0],0 + offset[1] ,-distanceFromOrigo + offset[2]),(deg90InRads*2.0,0,0),("Projector_Neg_Z")), #top down projection
((0 + offset[0],0 + offset[1],distanceFromOrigo + offset[2]),(deg90InRads*0.0,0,0),("Projector_Pos_Z")) #bottom up projection
]
projDefCollection = bpy.data.collections.new(name=collectionName)
bpy.context.scene.collection.children.link(projDefCollection)
bpy.ops.object.empty_add(type="CUBE",location=offset,radius=distanceFromOrigo*0.6)
bpy.context.active_object.name = name
projectorDad = bpy.context.active_object
projDefCollection.objects.link(bpy.context.active_object)
bpy.context.scene.collection.objects.unlink(bpy.context.active_object)
bpy.context.selected_objects.clear()
for data in projectorData:
bpy.ops.object.empty_add(type="ARROWS",location=data[0],rotation=data[1],radius=distanceFromOrigo*0.5)
projDefCollection.objects.link(bpy.context.active_object)
bpy.context.active_object.name = data[2]
bpy.context.scene.collection.objects.unlink(bpy.context.active_object)
bpy.context.active_object.select_set(state=True)
bpy.context.view_layer.objects.active = projectorDad
bpy.ops.object.parent_set(keep_transform=True)
return (projDefCollection,projectorDad)
class OBJECT_OT_texture_project_setup(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.texture_project_setup"
bl_label = "Texture Project default setup"
@classmethod
def poll(cls, context):
return True
canDo = True
if context.active_object is None:
canDo = False
elif context.active_object.type is not 'MESH':
canDo = False
elif context.mode is not 'OBJECT':
canDo = False
return canDo
def execute(self, context):
if context.mode != 'OBJECT':
self.report(type={'OPERATOR','WARNING','ERROR'},message='Context needs to be object mode for UV projector setup to work.')
return {"CANCELLED"}
uvProj = addUVProjModifier(context)
projectorContainer = None
for ob in context.selected_objects:
if ob != context.active_object:
projectorContainer = ob
break
for col in bpy.data.collections:
print(col)
print(bpy.data.collections)
uvProjectorsCollection = createOrFindUvProjectorsCollection()
if projectorContainer is None:
if defaultUVProjectorCollectionName not in uvProjectorsCollection.children:
uvProjector = createProjectorSystem()
uvDefaultProjectionCollection = uvProjector[0]
projectorContainer = uvProjector[1]
bpy.context.scene.collection.children.unlink(uvDefaultProjectionCollection)
uvProjectorsCollection.children.link(uvDefaultProjectionCollection)
else:
uvDefaultProjectionCollection = bpy.data.collections[defaultUVProjectorCollectionName]
projectorContainer = uvDefaultProjectionCollection.objects[0]
print(projectorContainer)
if projectorContainer is not None:
setProjectorsForModifier(uvProjModifier=uvProj,projectorDad=projectorContainer)
return {'FINISHED'}
class OBJECT_OT_texture_project_add_new(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.texture_project_add_new"
bl_label = "Texture Project add new custom"
@classmethod
def poll(cls, context):
return True
canDo = True
if context.active_object is None:
canDo = False
elif context.active_object.type is not 'MESH':
canDo = False
elif context.mode is not 'OBJECT':
canDo = False
return canDo
def execute(self, context):
if context.mode != 'OBJECT':
self.report(type={'OPERATOR','WARNING','ERROR'},message='Context needs to be object mode for UV projector setup to work.')
return {"CANCELLED"}
#First add UVProject if not allready in modifiers
uvProj = addUVProjModifier(context)
#create collection or find collection
uvProjectorsCollection = createOrFindUvProjectorsCollection()
collectionName=context.active_object.name + "_UVProjector"
#place the custom projector above the active
boundLoc = context.active_object.bound_box.data.location
boundDims = context.active_object.bound_box.data.dimensions
offset=(0.0 + boundLoc.x ,0.0 + boundLoc.y,0.0 + boundLoc.z + boundDims.z+3)
projectorSystem = createProjectorSystem(collectionName=collectionName,offset=offset)
bpy.context.scene.collection.children.unlink(projectorSystem[0])
uvProjectorsCollection.children.link(projectorSystem[0])
projectorContainer = projectorSystem[1]
if projectorContainer is not None:
setProjectorsForModifier(uvProjModifier=uvProj,projectorDad=projectorContainer)
return {'FINISHED'}
class OBJECT_OT_texture_project_init_from_selection(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.texture_project_init_from_selection"
bl_label = "init UV Project with children of selection"
@classmethod
def poll(cls, context):
return True
canDo = True
if context.active_object is None:
canDo = False
elif context.active_object.type is not 'MESH':
canDo = False
elif context.mode is not 'OBJECT':
canDo = False
return canDo
def execute(self, context):
if context.mode != 'OBJECT':
self.report(type={'OPERATOR','WARNING','ERROR'},message='Context needs to be object mode for UV projector setup to work.')
return {"CANCELLED"}
#First add UVProject if not allready in modifiers
uvProj = addUVProjModifier(context)
projectorContainer = None
for ob in context.selected_objects:
if ob is not context.active_object:
projectorContainer = ob
break
if projectorContainer is None:
self.report(type={'OPERATOR','WARNING','ERROR'},message='Select projectorDad then thing to add projector UV to - then use this operator')
return {"CANCELLED"}
setProjectorsForModifier(uvProjModifier=uvProj,projectorDad=projectorContainer)
return {'FINISHED'}
class OBJECT_MT_texture_project_setup_menu(bpy.types.Menu):
bl_idname = 'object.texture_project_setup_menu'
bl_label = 'Texture Project setup'
def draw(self, context):
layout = self.layout
layout.operator(OBJECT_OT_texture_project_setup.bl_idname)
layout.operator(OBJECT_OT_texture_project_add_new.bl_idname)
layout.operator(OBJECT_OT_texture_project_init_from_selection.bl_idname)
def menu_func(self, context):
self.layout.menu(OBJECT_MT_texture_project_setup_menu.bl_idname, icon='MESH_CUBE')
def register():
bpy.utils.register_class(OBJECT_OT_texture_project_setup)
bpy.utils.register_class(OBJECT_OT_texture_project_add_new)
bpy.utils.register_class(OBJECT_OT_texture_project_init_from_selection)
bpy.utils.register_class(OBJECT_MT_texture_project_setup_menu)
bpy.types.VIEW3D_MT_object_context_menu.append(menu_func)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_texture_project_setup)
bpy.utils.unregister_class(OBJECT_OT_texture_project_add_new)
bpy.utils.unregister_class(OBJECT_OT_texture_project_init_from_selection)
bpy.utils.unregister_class(OBJECT_MT_texture_project_setup_menu)
bpy.types.VIEW3D_MT_object_context_menu.remove(menu_func)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.texture_project_setup_operator()
@havchr
Copy link
Author

havchr commented Mar 9, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment