Newer
Older
image_user = texture.image_user
image_user.use_auto_refresh = self.use_auto_refresh
image_user.frame_start = img_spec.frame_start
image_user.frame_offset = img_spec.frame_offset
image_user.frame_duration = img_spec.frame_duration
# Image sequences need auto refresh to display reliably
if img_spec.image.source == 'SEQUENCE':
image_user.use_auto_refresh = True
def apply_material_options(self, material, slot):
shader = self.shader
if self.use_transparency:
material.alpha = 0.0
material.specular_alpha = 0.0
slot.use_map_alpha = True
else:
material.alpha = 1.0
material.specular_alpha = 1.0
slot.use_map_alpha = False
material.specular_intensity = 0
material.diffuse_intensity = 1.0
material.use_transparency = self.use_transparency
material.transparency_method = 'Z_TRANSPARENCY'
material.use_shadeless = (shader == 'SHADELESS')
material.use_transparent_shadows = (shader == 'DIFFUSE')
material.emit = self.emit_strength if shader == 'EMISSION' else 0.0
# -------------------------------------------------------------------------
def create_cycles_texnode(self, context, node_tree, img_spec):
tex_image = node_tree.nodes.new('ShaderNodeTexImage')
tex_image.image = img_spec.image
tex_image.interpolation = self.interpolation
tex_image.extension = self.extension
self.apply_texture_options(tex_image, img_spec)
def create_cycles_material(self, context, img_spec):
image = img_spec.image
name_compat = bpy.path.display_name_from_filepath(image.filepath)
material = None
if self.overwrite_material:
for mat in bpy.data.materials:
if mat.name == name_compat:
material = mat
if not material:
material = bpy.data.materials.new(name=name_compat)
material.use_nodes = True
material.blend_method = self.blend_method
material.shadow_method = self.shadow_method
material.use_backface_culling = self.use_backface_culling
material.show_transparent_back = self.show_transparent_back
node_tree = material.node_tree
out_node = clean_node_tree(node_tree)
tex_image = self.create_cycles_texnode(context, node_tree, img_spec)
if self.shader == 'PRINCIPLED':
core_shader = node_tree.nodes.new('ShaderNodeBsdfPrincipled')
elif self.shader == 'SHADELESS':
core_shader = get_shadeless_node(node_tree)
elif self.shader == 'EMISSION':
core_shader = node_tree.nodes.new('ShaderNodeBsdfPrincipled')
core_shader.inputs['Emission Strength'].default_value = self.emit_strength
core_shader.inputs['Base Color'].default_value = (0.0, 0.0, 0.0, 1.0)
core_shader.inputs['Specular'].default_value = 0.0
# Connect color from texture
if self.shader in {'PRINCIPLED', 'SHADELESS'}:
node_tree.links.new(core_shader.inputs[0], tex_image.outputs['Color'])
elif self.shader == 'EMISSION':
node_tree.links.new(core_shader.inputs['Emission'], tex_image.outputs['Color'])
if self.shader in {'PRINCIPLED', 'EMISSION'}:
node_tree.links.new(core_shader.inputs['Alpha'], tex_image.outputs['Alpha'])
Bastien Montagne
committed
else:
bsdf_transparent = node_tree.nodes.new('ShaderNodeBsdfTransparent')
mix_shader = node_tree.nodes.new('ShaderNodeMixShader')
node_tree.links.new(mix_shader.inputs['Fac'], tex_image.outputs['Alpha'])
node_tree.links.new(mix_shader.inputs[1], bsdf_transparent.outputs['BSDF'])
Bastien Montagne
committed
node_tree.links.new(mix_shader.inputs[2], core_shader.outputs[0])
core_shader = mix_shader
node_tree.links.new(out_node.inputs['Surface'], core_shader.outputs[0])
auto_align_nodes(node_tree)
return material
# -------------------------------------------------------------------------
# Geometry Creation
def create_image_plane(self, context, name, img_spec):
width, height = self.compute_plane_size(context, img_spec)
# Create new mesh
bpy.ops.mesh.primitive_plane_add('INVOKE_REGION_WIN')
# Why does mesh.primitive_plane_add leave the object in edit mode???
bpy.ops.object.mode_set(mode='OBJECT')
plane.dimensions = width, height, 0.0
plane.data.name = plane.name = name
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
# If sizing for camera, also insert into the camera's field of view
if self.size_mode == 'CAMERA':
offset_axis = self.axis_id_to_vector[self.offset_axis]
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
translate_axis = [0 if offset_axis[i] else 1 for i in (0, 1)]
center_in_camera(context.scene, context.scene.camera, plane, translate_axis)
self.align_plane(context, plane)
return plane
def compute_plane_size(self, context, img_spec):
"""Given the image size in pixels and location, determine size of plane"""
px, py = img_spec.size
# can't load data
if px == 0 or py == 0:
px = py = 1
if self.size_mode == 'ABSOLUTE':
y = self.height
x = px / py * y
elif self.size_mode == 'CAMERA':
x, y = compute_camera_size(
context, context.scene.cursor.location,
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
self.fill_mode, px / py
)
elif self.size_mode == 'DPI':
fact = 1 / self.factor / context.scene.unit_settings.scale_length * 0.0254
x = px * fact
y = py * fact
else: # elif self.size_mode == 'DPBU'
fact = 1 / self.factor
x = px * fact
y = py * fact
return x, y
def align_plane(self, context, plane):
"""Pick an axis and align the plane to it"""
if 'CAM' in self.align_axis:
# Camera-aligned
camera = context.scene.camera
if (camera):
# Find the axis that best corresponds to the camera's view direction
Vector((0, 0, 1)) - camera.matrix_world.col[3].xyz
# pick the axis with the greatest magnitude
mag = max(map(abs, axis))
# And use that axis & direction
axis = Vector([
n / mag if abs(n) == mag else 0.0
for n in axis
])
else:
# No camera? Just face Z axis
axis = Vector((0, 0, 1))
self.align_axis = 'Z+'
axis = self.axis_id_to_vector[self.align_axis]
# rotate accordingly for x/y axiis
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
if not axis.z:
plane.rotation_euler.x = pi / 2
if axis.y > 0:
plane.rotation_euler.z = pi
elif axis.y < 0:
plane.rotation_euler.z = 0
elif axis.x > 0:
plane.rotation_euler.z = pi / 2
elif axis.x < 0:
plane.rotation_euler.z = -pi / 2
# or flip 180 degrees for negative z
elif axis.z < 0:
plane.rotation_euler.y = pi
if self.align_axis == 'CAM':
constraint = plane.constraints.new('COPY_ROTATION')
constraint.target = camera
constraint.use_x = constraint.use_y = constraint.use_z = True
if not self.align_track:
bpy.ops.object.visual_transform_apply()
plane.constraints.clear()
if self.align_axis == 'CAM_AX' and self.align_track:
constraint = plane.constraints.new('LOCKED_TRACK')
constraint.target = camera
constraint.track_axis = 'TRACK_Z'
constraint.lock_axis = 'LOCK_Y'
# -----------------------------------------------------------------------------
# Register
Bastien Montagne
committed
Florian Meyer
committed
def import_images_button(self, context):
Bastien Montagne
committed
self.layout.operator(IMPORT_IMAGE_OT_to_plane.bl_idname, text="Images as Planes", icon='TEXTURE')
Campbell Barton
committed
classes = (
IMPORT_IMAGE_OT_to_plane,
)
for cls in classes:
bpy.utils.register_class(cls)
Brecht Van Lommel
committed
bpy.types.TOPBAR_MT_file_import.append(import_images_button)
bpy.types.VIEW3D_MT_image_add.append(import_images_button)
bpy.app.handlers.load_post.append(register_driver)
register_driver()
Campbell Barton
committed
Brecht Van Lommel
committed
bpy.types.TOPBAR_MT_file_import.remove(import_images_button)
bpy.types.VIEW3D_MT_image_add.remove(import_images_button)
# This will only exist if drivers are active
if check_drivers in bpy.app.handlers.depsgraph_update_post:
bpy.app.handlers.depsgraph_update_post.remove(check_drivers)
Bastien Montagne
committed
bpy.app.handlers.load_post.remove(register_driver)
del bpy.app.driver_namespace['import_image__find_plane_corner']
for cls in classes:
bpy.utils.unregister_class(cls)
Campbell Barton
committed
# Run simple doc tests
import doctest
doctest.testmod()
unregister()