/
operators.py
661 lines (489 loc) · 27.3 KB
/
operators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
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
480
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
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
'''
Copyright (C) 2023 Blender Foundation and Adrien Rouquié
https://blender.org
https://www.linkedin.com/in/adrien-rouquie/
orangeturbine@cgcookie.com
Created by the Blender Foundation, modified by Adrien Rouquié.
This file is part of a Texture Diffusion add-on.
Texture Diffusion is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <https://www.gnu.org/licenses>.
'''
from math import radians
import bpy
from . import functions
subject_prop_name = "Subject mesh"
proj_collection_prop_name = "Proj collection"
img_dir_prop_name = "Image directory"
facing_path_prop_name = "Facing mask path"
facing_path_mirrored_prop_name = "Facing mask path mirrored"
camera_occlusion_prop_name = "Camera occlusion"
camera_occlusion_mirrored_prop_name = "Camera occlusion mirrored"
uv_layer_proj_prop_name = "UV layer projection"
uv_layer_proj_mirrored_prop_name = "UV layer projection mirrored"
shading_mesh_prop_name = "Shading mesh"
gen_node_group_prop_name = "SD Gen node group"
projection_scene_prop_name = "Projection scene"
breakdown_collection_prop_name = "Breakdown collection"
final_mesh_collection_prop_name = "Final mesh collection"
tweaking_collection_prop_name = "Tweaking collection"
custom_mask_prop_name = "Custom mask"
class TexDiff_OT_CreateNewProjScene(bpy.types.Operator):
bl_idname = "textures_diffusion.create_new_proj_scene"
bl_label = "Create new projection scene"
bl_description = "Create a projection scene to create data for Stable Diffusion and shading scene"
@classmethod
def poll(cls, context):
return context.active_object and subject_prop_name not in context.scene
def execute(self, context):
# get active object
active_obj = context.active_object
# create a new scene for the projection
proj_scene = bpy.data.scenes.new(name=f"{active_obj.name} SD projection scene")
proj_scene.render.resolution_x = 512
proj_scene.render.resolution_y = 512
# todo : create a parameter for the resolution in the addon preferences
# create a collection
mesh_collection = bpy.data.collections.new(name=f"{active_obj.name} projection meshes")
proj_scene.collection.children.link(mesh_collection)
# create the camera
camera_data = bpy.data.cameras.new(name="SD_Camera")
camera_obj = bpy.data.objects.new(name="SD_Camera", object_data=camera_data)
proj_scene.collection.objects.link(camera_obj)
proj_scene.camera = camera_obj
camera_data.type = 'ORTHO'
camera_obj.location = (0, -10, 0)
camera_obj.rotation_euler = (radians(90), 0, 0)
# set the world background dark grey
proj_world = bpy.data.worlds.new(f"{active_obj.name} projection world")
proj_world.use_nodes = True
proj_world.node_tree.nodes["Background"].inputs[0].default_value = (0.1, 0.1, 0.1, 1)
proj_scene.world = proj_world
# enable the ambient occlusion and set the distance to 2 and factor to 10
proj_scene.eevee.use_gtao = True
proj_scene.eevee.gtao_distance = 2
proj_scene.eevee.gtao_factor = 10
# set color management to standard
proj_scene.view_settings.view_transform = 'Standard'
# Store custom properties
proj_scene[subject_prop_name] = active_obj
proj_scene[proj_collection_prop_name] = mesh_collection
# place the projection meshes
active_obj_copy_1 = active_obj.copy()
active_obj_copy_1.data = active_obj.data.copy()
active_obj_copy_1.location = (-1.5, 0, 0)
active_obj_copy_1.rotation_euler = (0, 0, 0)
mesh_collection.objects.link(active_obj_copy_1)
active_obj_copy_2 = active_obj.copy()
active_obj_copy_2.data = active_obj.data.copy()
active_obj_copy_2.location = (1.5, 0, 0)
active_obj_copy_2.rotation_euler = (0, 0, radians(90))
mesh_collection.objects.link(active_obj_copy_2)
# Set the scene the active one
bpy.context.window.scene = proj_scene
return {'FINISHED'}
class TexDiff_OT_RenderRefImg(bpy.types.Operator):
bl_idname = "textures_diffusion.render_ref_img"
bl_label = "Render ref images"
bl_description = "Render image to use in Stable Diffusion"
@classmethod
def poll(cls, context):
camera_in_scene = context.scene.camera is not None
proj_collection_in_scene = proj_collection_prop_name in context.scene.keys()
subject_prop_in_scene = subject_prop_name in context.scene.keys()
return camera_in_scene and proj_collection_in_scene and subject_prop_in_scene
def execute(self, context):
# check if the .blend file is saved
if not bpy.data.is_saved:
self.report({'ERROR'}, "Please save the .blend file before baking")
return {'CANCELLED'}
# set the variables
proj_scene = context.scene
functions.create_img_dir(proj_scene)
subject_name = proj_scene[subject_prop_name].name
image_directory = proj_scene[img_dir_prop_name]
beauty_path = f"{image_directory}/{subject_name}_beauty.png"
normal_path = f"{image_directory}/{subject_name}_normal.png"
depth_path = f"{image_directory}/{subject_name}_depth.png"
enable_beauty = context.scene.textures_diffusion_props.enable_beauty_ref
enable_normal = context.scene.textures_diffusion_props.enable_normal_ref
enable_depth = context.scene.textures_diffusion_props.enable_depth_ref
# render the images
bpy.context.window.cursor_set("WAIT")
if enable_beauty:
functions.render_beauty(beauty_path)
if enable_normal:
functions.render_normal(normal_path)
if enable_depth:
functions.render_depth(depth_path, proj_scene[proj_collection_prop_name])
# change the mouse cursor back to the default
bpy.context.window.cursor_set("DEFAULT")
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
# tell the user the maps have been baked
self.report({'INFO'}, "Baking done ! Check the folder 'SD_maps' in the same folder as the .blend file")
return {'FINISHED'}
class TexDiff_OT_BakeProjMasks(bpy.types.Operator):
bl_idname = "textures_diffusion.bake_proj_masks"
bl_label = "Bake projection masks"
bl_description = "Bake camera occlusion and facing masks"
@classmethod
def poll(cls, context):
camera_in_scene = context.scene.camera is not None
proj_collection_in_scene = proj_collection_prop_name in context.scene.keys()
return camera_in_scene and proj_collection_in_scene
def execute(self, context):
# check if the .blend file is saved
if not bpy.data.is_saved:
self.report({'ERROR'}, "Please save the .blend file before baking")
return {'CANCELLED'}
# set the variables
proj_scene = context.scene
functions.create_img_dir(proj_scene)
collection = proj_scene[proj_collection_prop_name]
image_directory = proj_scene[img_dir_prop_name]
masks_resolution = proj_scene.textures_diffusion_props.masks_resolution
mask_samples = proj_scene.textures_diffusion_props.masks_samples
# bake the masks
bpy.context.window.cursor_set("WAIT")
for obj in collection.objects:
assert obj.type == "MESH", f"Object {obj.name} is not a mesh"
facing_path = f"{image_directory}/Masks/{obj.name}_facing_mask.exr"
facing_mirrored_path = f"{image_directory}/Masks/{obj.name}_facing_mask_mirrored.exr"
camera_occlusion_path = f"{image_directory}/Masks/{obj.name}_camera_occlusion_mask.exr"
camera_occlusion_mirrored_path = f"{image_directory}/Masks/{obj.name}_camera_occlusion_mask_mirrored.exr"
# bake masks
functions.render_facing(obj, facing_path, masks_resolution, mask_samples)
functions.render_camera_occlusion(obj, camera_occlusion_path, masks_resolution, mask_samples)
# add file output to the object custom properties
obj[facing_path_prop_name] = facing_path
obj[camera_occlusion_prop_name] = camera_occlusion_path
# mirror
if proj_scene.textures_diffusion_props.use_mirror_X:
functions.mirror_obj(obj, "X")
functions.render_facing(obj, facing_mirrored_path, masks_resolution, mask_samples)
functions.render_camera_occlusion(obj, camera_occlusion_mirrored_path, masks_resolution,
mask_samples)
functions.mirror_obj(obj, "X")
# add file output to the object custom properties
obj[facing_path_mirrored_prop_name] = facing_mirrored_path
obj[camera_occlusion_mirrored_prop_name] = camera_occlusion_mirrored_path
# change the mouse cursor back to the default
bpy.context.window.cursor_set("DEFAULT")
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
for image in bpy.data.images:
image.reload()
# tell the user the maps have been baked
self.report({'INFO'}, "Baking done !")
return {'FINISHED'}
class TexDiff_OT_CreateProjUVs(bpy.types.Operator):
bl_idname = "textures_diffusion.create_proj_uvs"
bl_label = "Create Projected UVs"
bl_description = "Project the UVs of the collection from the camera"
@classmethod
def poll(cls, context):
camera_in_scene = context.scene.camera is not None
proj_collection_in_scene = proj_collection_prop_name in context.scene.keys()
return camera_in_scene and proj_collection_in_scene
def execute(self, context):
proj_scene = context.scene
collection = proj_scene[proj_collection_prop_name]
camera = proj_scene.camera
for obj in collection.objects:
assert obj.type == "MESH", f"Object {obj.name} is not a mesh"
uv_layer_name = f"{obj.name}_cam_proj"
uv_layer_mirrored_name = f"{obj.name}_cam_proj_mirrored"
# project the UVs
functions.project_uvs_from_camera(obj, camera, uv_layer_name)
# add file output to the object custom properties
obj[uv_layer_proj_prop_name] = uv_layer_name
# mirror
if proj_scene.textures_diffusion_props.use_mirror_X:
functions.mirror_obj(obj, "X")
functions.project_uvs_from_camera(obj, camera, uv_layer_mirrored_name)
functions.mirror_obj(obj, "X")
# add file output to the object custom properties
obj[uv_layer_proj_mirrored_prop_name] = uv_layer_mirrored_name
# change the mouse cursor back to the default
bpy.context.window.cursor_set("DEFAULT")
# tell the user the maps have been baked
self.report({'INFO'}, "UVs projected !")
return {'FINISHED'}
class TexDiff_OT_CreateNewShadingScene(bpy.types.Operator):
bl_idname = "textures_diffusion.create_new_shading_scene"
bl_label = "Create new shading scene"
bl_description = "Create a new shading scene to apply the SD generated texture"
@classmethod
def poll(cls, context):
return proj_collection_prop_name in context.scene \
and img_dir_prop_name in context.scene \
and context.scene.textures_diffusion_props.img_generated_path != "//"
def execute(self, context):
# set the variables
proj_scene = context.scene
subject_mesh = proj_scene[subject_prop_name]
subject_name = subject_mesh.name
proj_mesh_collection = proj_scene[proj_collection_prop_name]
img_gen_path = context.scene.textures_diffusion_props.img_generated_path
shading_scene_name = f"{subject_name} SD shading"
# check if the proj mesh collection is ok
for obj in proj_mesh_collection.objects:
if not obj.type == "MESH":
self.report({'ERROR'}, f"Object {obj.name} is not a mesh")
if obj[uv_layer_proj_prop_name] not in obj.data.uv_layers.keys():
self.report({'ERROR'}, f"Projection uvs not found in object {obj.name}")
# create a new scene for the shading
shading_scene = bpy.data.scenes.new(name=shading_scene_name)
context.window.scene = shading_scene
shading_scene.render.resolution_x = proj_scene.render.resolution_x
shading_scene.render.resolution_y = proj_scene.render.resolution_y
shading_scene.camera = proj_scene.camera
# set the color management to standard
shading_scene.view_settings.view_transform = "Standard"
# create Stable Diffusion gen group node
sd_gen_node_group = functions.create_sd_gen_node_group(img_gen_path)
shading_scene[gen_node_group_prop_name] = sd_gen_node_group
# copy the subject into the shading scene
shading_mesh = subject_mesh.copy()
shading_mesh.data = subject_mesh.data.copy()
shading_mesh.name = f"{subject_name}_shading"
shading_mesh.data.name = f"{subject_name}_shading"
# add a color attribute to the shading mesh
shading_mesh.data.color_attributes.new(name="color_under", type='FLOAT_COLOR', domain="POINT")
final_shading_mesh_collection = bpy.data.collections.new(name=f"{shading_scene_name} final assembly")
shading_scene.collection.children.link(final_shading_mesh_collection)
final_shading_mesh_collection.objects.link(shading_mesh)
# create custom properties
shading_scene[shading_mesh_prop_name] = shading_mesh
shading_scene[projection_scene_prop_name] = proj_scene
shading_scene[final_mesh_collection_prop_name] = final_shading_mesh_collection
shading_scene[img_dir_prop_name] = proj_scene[img_dir_prop_name]
shading_scene.textures_diffusion_props.img_generated_path = img_gen_path
shading_scene.textures_diffusion_props.use_mirror_X = proj_scene.textures_diffusion_props.use_mirror_X
# create collection for tweaking
tweak_mesh_collection = functions.clone_collection(context, proj_mesh_collection,
name=f"{shading_scene_name} projection tweaks",
suffix="_tweaks")
shading_scene.view_layers[0].layer_collection.children[tweak_mesh_collection.name].hide_viewport = True
shading_scene[tweaking_collection_prop_name] = tweak_mesh_collection
aspect_x = proj_scene.render.resolution_x
aspect_y = proj_scene.render.resolution_y
camera = proj_scene.camera
for obj in tweak_mesh_collection.objects:
uv_layer = obj.data.uv_layers[0].name
functions.add_uv_project_modifier(obj, uv_layer, aspect_x, aspect_y, camera)
# transfer proj UVs into shading mesh
shading_scene.collection.children.link(proj_mesh_collection)
for obj in tweak_mesh_collection.objects:
uv_layer = obj[uv_layer_proj_prop_name]
functions.transfer_uvs(obj, shading_mesh, uv_layer)
if shading_scene.textures_diffusion_props.use_mirror_X:
uv_layer_mirrored = obj[uv_layer_proj_mirrored_prop_name]
functions.transfer_uvs(obj, shading_mesh, uv_layer_mirrored)
shading_scene.collection.children.unlink(proj_mesh_collection)
# create the tweak uvs material
tweak_uvs_mat = functions.create_tweak_uvs_material(sd_gen_node_group)
for obj in tweak_mesh_collection.objects:
obj.data.materials.clear()
obj.data.materials.append(tweak_uvs_mat)
# create a new collection "breakdown"
breakdown_collection = bpy.data.collections.new(name=f"{shading_scene_name} breakdown")
shading_scene.collection.children.link(breakdown_collection)
shading_scene[breakdown_collection_prop_name] = breakdown_collection
shading_scene.view_layers[0].layer_collection.children[breakdown_collection.name].hide_viewport = True
offset = 0
proj_node_groups = []
for obj in proj_mesh_collection.objects:
# duplicate the shading_mesh and move it sideways
new_shading_mesh = shading_mesh.copy()
new_shading_mesh.data = shading_mesh.data.copy()
new_shading_mesh.name = f"{obj.name}_projection_shading"
offset += shading_mesh.dimensions[0] + 0.5
new_shading_mesh.location.x = offset
breakdown_collection.objects.link(new_shading_mesh)
# create custom map image
custom_mask_image = bpy.data.images.new(name=f"{obj.name}_custom_mask", width=1024, height=1024, alpha=True)
custom_mask_image.generated_color = (0, 0, 0, 0)
custom_mask_image.filepath_raw = f"{proj_scene[img_dir_prop_name]}/Masks/{custom_mask_image.name}.exr"
custom_mask_image.file_format = 'OPEN_EXR'
custom_mask_image.save()
# create Subject proj material
proj_data = {
"proj_mesh_name": obj.name,
"use_mirror_X": proj_scene.textures_diffusion_props.use_mirror_X,
"proj_uv_layer": obj[uv_layer_proj_prop_name],
"sd_gen_node_group": sd_gen_node_group,
"custom_mask_image": custom_mask_image,
"cam_occlusion": obj[camera_occlusion_prop_name],
"facing_mask": obj[facing_path_prop_name],
}
if shading_scene.textures_diffusion_props.use_mirror_X:
proj_data["proj_uv_layer_mirrored"] = obj[uv_layer_proj_mirrored_prop_name]
proj_data["cam_occlusion_mirrored"] = obj[camera_occlusion_mirrored_prop_name]
proj_data["facing_mask_mirrored"] = obj[facing_path_mirrored_prop_name]
# create material
proj_node_group = functions.create_proj_node_group(proj_data)
proj_node_groups.append(proj_node_group)
proj_material = functions.create_proj_material(obj.name, proj_node_group, custom_mask_image)
# append material at object level
shading_mesh.data.materials.clear()
new_shading_mesh.data.materials.append(None)
new_shading_mesh.material_slots[0].link = "OBJECT"
new_shading_mesh.material_slots[0].material = proj_material
# add custom prop custom mask image
new_shading_mesh[custom_mask_prop_name] = custom_mask_image
# create Subject final material
final_assembly_material = functions.create_final_assembly_material(proj_node_groups,
sd_gen_node_group)
shading_mesh.data.materials.clear()
shading_mesh.data.materials.append(final_assembly_material)
# set the mesh same as the shading mesh
for obj in breakdown_collection.objects:
obj.data = shading_mesh.data
return {'FINISHED'}
class TexDiff_OT_ReloadSdImgPath(bpy.types.Operator):
bl_idname = "textures_diffusion.reload_sd_img_path"
bl_label = "Reload SD image path"
bl_description = "Reload the Sable Diffusion image Path into the Stable_diffusion_gen node group"
@classmethod
def poll(cls, context):
gen_node_group_exist = gen_node_group_prop_name in context.scene
img_generated_path_exist = "img_generated_path" in context.scene.textures_diffusion_props
return gen_node_group_exist and img_generated_path_exist
def execute(self, context):
new_img_generated_path = context.scene.textures_diffusion_props.img_generated_path
gen_node_group = context.scene[gen_node_group_prop_name]
functions.get_node('Stable_diffusion_gen', gen_node_group).image.filepath = new_img_generated_path
return {'FINISHED'}
class TexDiff_OT_TweakProjection(bpy.types.Operator):
bl_idname = "textures_diffusion.tweak_projection"
bl_label = "Tweak projection"
bl_description = "Enter edit mode and tweak the projection of the selected object"
@classmethod
def poll(cls, context):
UVProject_modifier_exists = "UVProject" in context.active_object.modifiers
return UVProject_modifier_exists
def execute(self, context):
if "Subdivision" in context.active_object.modifiers:
context.active_object.modifiers["Subdivision"].show_on_cage = True
bpy.ops.object.select_all(action='DESELECT')
# view through the camera
if context.region_data.view_perspective != 'CAMERA':
context.region_data.view_perspective = 'CAMERA'
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
class TexDiff_OT_TransferTweakedUvs(bpy.types.Operator):
bl_idname = "textures_diffusion.transfer_tweaked_uvs"
bl_label = "Transfer projection tweaks"
bl_description = "Transfer projection tweaks to the shading mesh and the breakdown collection meshes"
@classmethod
def poll(cls, context):
tweaking_collection_exists = tweaking_collection_prop_name in context.scene
shading_mesh_exists = shading_mesh_prop_name in context.scene
proj_scene_exists = projection_scene_prop_name in context.scene
return tweaking_collection_exists and shading_mesh_exists and proj_scene_exists
def execute(self, context):
# if edit mode exit
if context.mode == 'EDIT_MESH':
bpy.ops.object.mode_set(mode='OBJECT')
# get the variables
active_scene = context.scene
tweaking_collection = active_scene[tweaking_collection_prop_name]
final_mesh_collection = active_scene[final_mesh_collection_prop_name]
shading_mesh = active_scene[shading_mesh_prop_name]
proj_scene_camera = active_scene[projection_scene_prop_name].camera
active_scene.collection.objects.link(proj_scene_camera)
# transfer the tweaks
for obj in tweaking_collection.objects:
active_scene.view_layers[0].layer_collection.children[final_mesh_collection.name].hide_viewport = False
uv_layer = obj[uv_layer_proj_prop_name]
functions.project_uvs_from_camera(obj, proj_scene_camera, uv_layer)
functions.transfer_uvs(obj, shading_mesh, uv_layer)
if active_scene.textures_diffusion_props.use_mirror_X:
uv_layer_mirrored = obj[uv_layer_proj_mirrored_prop_name]
functions.mirror_obj(obj, "X")
functions.project_uvs_from_camera(obj, proj_scene_camera, uv_layer_mirrored)
functions.mirror_obj(obj, "X")
functions.transfer_uvs(obj, shading_mesh, uv_layer_mirrored)
# unlink the camera
active_scene.collection.objects.unlink(proj_scene_camera)
active_scene.view_layers[0].layer_collection.children[tweaking_collection.name].hide_viewport = True
# toggle camera view
bpy.ops.view3d.view_camera()
return {'FINISHED'}
class TexDiff_OT_PaintCustomMask(bpy.types.Operator):
bl_idname = "textures_diffusion.paint_custom_mask"
bl_label = "Paint custom mask"
bl_description = "Enter painting mode and paint the custom mask of the selected object"
@classmethod
def poll(cls, context):
custom_mask_image_exists = custom_mask_prop_name in context.active_object
return custom_mask_image_exists
def execute(self, context):
custom_mask_image = context.active_object[custom_mask_prop_name]
bpy.ops.view3d.view_selected(use_all_regions=False)
bpy.ops.paint.texture_paint_toggle()
bpy.context.scene.tool_settings.image_paint.mode = 'IMAGE'
bpy.context.scene.tool_settings.image_paint.canvas = custom_mask_image
# set uv_layer 0 active
context.active_object.data.uv_layers.active_index = 0
bpy.context.space_data.shading.type = 'MATERIAL'
return {'FINISHED'}
class TexDiff_OT_BakeProjection(bpy.types.Operator):
bl_idname = "textures_diffusion.bake_projection"
bl_label = "Bake final texture"
bl_description = "Bake the projected texture of the final mesh"
@classmethod
def poll(cls, context):
shading_mesh_active = False
shading_mesh_exists = shading_mesh_prop_name in context.scene
img_dir_prop_name_exists = img_dir_prop_name in context.scene
if shading_mesh_exists:
shading_mesh_active = context.scene[shading_mesh_prop_name] == context.active_object
return shading_mesh_exists and img_dir_prop_name_exists and shading_mesh_active
def execute(self, context):
# create a new collection
new_name = context.scene[shading_mesh_prop_name].name + "_bake"
bake_collection = bpy.data.collections.new(new_name)
context.scene.collection.children.link(bake_collection)
# copy the shading mesh into the new collection
shading_mesh_copy = context.scene[shading_mesh_prop_name].copy()
shading_mesh_copy.name = new_name
shading_mesh_copy.data = context.scene[shading_mesh_prop_name].data.copy()
shading_mesh_copy.data.name = new_name
bake_collection.objects.link(shading_mesh_copy)
# make it selected
functions.select_object_solo(shading_mesh_copy)
# set the first uv_layer as active
shading_mesh_copy.data.uv_layers.active_index = 0
# Bake the emission + alpha
bake_render_path = f"{context.scene[img_dir_prop_name]}/{shading_mesh_copy.name}.exr"
bake_resolution = context.scene.textures_diffusion_props.bake_resolution
functions.set_output_node_active(shading_mesh_copy, "Material Output Color")
baked_color_image = functions.bake_emission(context, "color_bake", shading_mesh_copy, bake_render_path,
bake_resolution)
functions.set_output_node_active(shading_mesh_copy, "Material Output Alpha")
baked_alpha_image = functions.bake_emission(context, "alpha_bake", shading_mesh_copy, "", bake_resolution)
functions.set_output_node_active(shading_mesh_copy, "Material Output Color")
# combine the color and the alpha
functions.set_alpha_channel(baked_color_image, baked_alpha_image)
baked_color_image.save()
# create the baked material and assign
baked_image_material = functions.create_baked_image_material(new_name, bake_render_path)
shading_mesh_copy.data.materials.clear()
shading_mesh_copy.data.materials.append(baked_image_material)
# clean the projection uvs
functions.clean_cam_proj_uvs(shading_mesh_copy)
# hide the final assembly collection and make the baked mesh active
layer_collection = context.scene.view_layers[0].layer_collection
layer_collection.children[context.scene[final_mesh_collection_prop_name].name].hide_viewport = True
context.view_layer.objects.active = shading_mesh_copy
# clean the scene
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
return {'FINISHED'}