742 lines · 27.4 KB
Raw Download
1
# <pep8 compliant>
2
#  This program is free software; you can redistribute it and/or
3
#  modify it under the terms of the GNU General Public License
4
#  as published by the Free Software Foundation; either version 2
5
#  of the License, or (at your option) any later version.
6
#
7
#  This program is distributed in the hope that it will be useful,
8
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
9
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
#  GNU General Public License for more details.
11
#
12
#  You should have received a copy of the GNU General Public License
13
#  along with this program; if not, write to the Free Software Foundation,
14
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15
16
import os
17
import bpy
18
import bpy.utils.previews
19
import random
20
import math
21
import colorsys
22
23
from bpy.props import (IntProperty,
24
                       FloatProperty,
25
                       FloatVectorProperty,
26
                       EnumProperty,
27
                       PointerProperty,
28
                       )
29
from bpy.types import (Panel,
30
                       Operator,
31
                       PropertyGroup,
32
                       )
33
34
35
# ----------------------------------------------------------------------------
36
#    Functions
37
# ----------------------------------------------------------------------------
38
39
40
def recurLayerCollection(layerColl, collName):
41
    found = None
42
    if (layerColl.name == collName):
43
        return layerColl
44
    for layer in layerColl.children:
45
        found = recurLayerCollection(layer, collName)
46
        if found:
47
            return found
48
49
50
def save_selected():
51
    name = bpy.context.view_layer.objects.active.name
52
    return name
53
54
55
def recall_selected(name):
56
    if name != '' and name is not None:
57
        ob = bpy.context.scene.objects[name]
58
        bpy.ops.object.select_all(action='DESELECT')
59
        bpy.context.view_layer.objects.active = ob
60
        ob.select_set(True)
61
62
63
def get_offset():
64
    x = y = z = 0
65
    if bpy.context.active_object:
66
        x, y, z = bpy.context.active_object.location
67
        return x, y, z
68
69
70
def set_position(type="sphere", number=20, radius=5, height=0,
71
                 offsetx=0, offsety=0, offsetz=0):
72
    if type == "sphere" or type == "topsphere":
73
        latDeg = random.randint(0, 360)
74
        lonDeg = random.randint(0, 360)
75
        latRad, lonRad = math.radians(latDeg), math.radians(lonDeg)
76
        x = math.cos(latRad) * math.cos(lonRad) * radius
77
        y = math.cos(latRad) * math.sin(lonRad) * radius
78
        if type == "sphere":
79
            z = math.sin(latRad) * radius
80
        elif type == "topsphere":
81
            z = abs(math.sin(latRad) * radius)
82
        return x + offsetx, y + offsety, z + offsetz
83
    elif type == "grid":
84
        z = height
85
        di = 1
86
        dj = 0
87
        seglen = 1
88
        x = 0
89
        y = 0
90
        segpas = 0
91
        if number == 0:
92
            return(0 + offsetx, 0 + offsety, z + offsetz)
93
        for n in range(number):
94
            x += di
95
            y += dj
96
            segpas = segpas + 1
97
            if n == number - 1:
98
                return((radius * x) + offsetx,
99
                       (radius * y) + offsety,
100
                       z + offsetz)
101
            if (segpas == seglen):
102
                segpas = 0
103
                buffer = di
104
                di = -dj
105
                dj = buffer
106
                if dj == 0:
107
                    seglen = seglen + 1
108
109
110
def make_tracker(name, collection, parent, x, y, z):
111
    tracktarget = bpy.context.scene.objects.get(name)
112
    if tracktarget:
113
        pass
114
    else:
115
        make_collection(collection, parent, True)
116
        bpy.ops.object.empty_add(type='SPHERE', radius=3, location=(x, y, z))
117
        bpy.context.active_object.name = name
118
119
120
def track_to(name):
121
    bpy.ops.object.constraint_add(type='TRACK_TO')
122
    bpy.context.object.constraints["Track To"].target = bpy.data.objects[name]
123
    bpy.context.object.constraints["Track To"].track_axis = 'TRACK_NEGATIVE_Z'
124
    bpy.context.object.constraints["Track To"].up_axis = 'UP_Y'
125
126
127
def find_collection(name, deselectall=True):
128
    """not used"""
129
    if deselectall:
130
        bpy.ops.object.select_all(action='DESELECT')
131
        bpy.context.view_layer.objects.active = None
132
    col = bpy.data.collections.get(name)
133
    if col:
134
        for obj in col.objects:
135
            obj.select_set(True)
136
137
138
def get_active_collection():
139
    active_coll = bpy.context.view_layer.active_layer_collection
140
    # probably want to use the .name of the collection like:
141
    # active_collection = get_active_collection()
142
    # set_collection(active_collection.name)
143
    return active_coll
144
145
146
def select_object(name, deselectall=True):
147
    """not used"""
148
    if deselectall:
149
        bpy.ops.object.select_all(action='DESELECT')
150
        bpy.context.view_layer.objects.active = None
151
    obj = bpy.context.scene.objects.get(name)
152
    if obj:
153
        for n in name:
154
            bpy.context.view_layer.objects.active = obj
155
            obj.select_set(True)
156
157
158
def del_all_in_collection(name):
159
    if bpy.context.blend_data.collections.find(name) > -1:
160
        delete = bpy.data.collections.get(name)
161
        for obj in delete.objects:
162
            bpy.data.objects.remove(obj, do_unlink=True)
163
164
165
def make_collection(name, parent='', active=True):
166
    # not scene / layer specific, issue, TODO
167
    if bpy.context.blend_data.collections.find(name) == -1:
168
        newcoll = bpy.data.collections.new(name)
169
        if parent:
170
            parentcoll = bpy.context.scene.collection.children.get(parent)
171
            parentcoll.children.link(newcoll)
172
        else:
173
            bpy.context.scene.collection.children.link(newcoll)
174
    if active:
175
        set_collection(name)
176
177
178
def set_collection(name):
179
    # is scene specific, gets active view layer (scene)
180
    layer_collection = bpy.context.view_layer.layer_collection
181
    # print("layer_collection ",layer_collection)
182
    # print("name ", name)
183
    layerColl = recurLayerCollection(layer_collection, name)
184
    # print("layerColl", layerColl)
185
    bpy.context.view_layer.active_layer_collection = layerColl
186
    # print("active", bpy.context.view_layer.active_layer_collection)
187
188
189
def make_light(type, color, energy, x, y, z, sizemin='', sizemax='',
190
               powermin='', powermax=''):
191
    if type == 'area':
192
        bpy.ops.object.light_add(type='AREA', radius=1, align='WORLD',
193
                                 location=(x, y, z))
194
        bpy.context.object.data.size = random.uniform(sizemin, sizemax)
195
        bpy.context.object.data.energy = energy * \
196
            random.uniform(powermin * 10, powermax * 10)
197
    elif type == 'sun':
198
        bpy.ops.object.light_add(type='SUN', radius=1, align='WORLD',
199
                                 location=(x, y, z))
200
        bpy.context.object.data.angle = \
201
            random.uniform(sizemin / 2000, sizemax / 2000)
202
        bpy.context.object.data.energy = energy * \
203
            random.uniform(powermin / 200, powermax / 200)
204
    elif type == 'spot':
205
        bpy.ops.object.light_add(type='SPOT', align='WORLD',
206
                                 location=(x, y, z))
207
        bpy.context.object.data.spot_size = \
208
            random.uniform(sizemin / 10, sizemax / 10)
209
        bpy.context.object.data.energy = energy * \
210
            random.uniform(powermin * 400, powermax * 400)
211
        bpy.context.object.data.spot_blend = 1
212
    elif type == 'point':
213
        bpy.ops.object.light_add(type='POINT', radius=1, align='WORLD',
214
                                 location=(x, y, z))
215
        bpy.context.object.data.shadow_soft_size = \
216
            random.uniform(sizemin, sizemax)
217
        bpy.context.object.data.energy = energy * \
218
            random.uniform(powermin * 70, powermax * 70)
219
    if type != 'plane':
220
        bpy.context.object.data.color = color
221
    if type == 'plane':
222
        bpy.ops.mesh.primitive_plane_add(size=random.uniform(sizemin, sizemax),
223
                                         enter_editmode=False, align='WORLD',
224
                                         location=(x, y, z))
225
        # make new material, append to object
226
        new_mat = bpy.data.materials.new(name="Emit")
227
        so = bpy.context.active_object
228
        so.data.materials.append(new_mat)
229
        new_mat.use_nodes = True
230
        nodes = new_mat.node_tree.nodes
231
        material_output = nodes.get("Material Output")
232
        # set shader to emission
233
        node_emission = nodes.new(type='ShaderNodeEmission')
234
        # set emission color
235
        node_emission.inputs[0].default_value = (color[0],
236
                                                 color[1],
237
                                                 color[2], 1)
238
        # set emission brightness / strength
239
        node_emission.inputs[1].default_value = energy * \
240
            random.randint(powermin / 20, powermax / 20)
241
        # connect the nodes for emission shader
242
        links = new_mat.node_tree.links
243
        new_links = links.new(node_emission.outputs[0],
244
                              material_output.inputs[0])
245
246
247
def set_color(mode, basecolor='', huemin='', huemax='', satmin='',
248
              satmax='', huespread='', index=''):
249
    if mode != 'range':
250
        # convert hue from rgb (blender) to hsv so we can just get the hue
251
        hsv = colorsys.rgb_to_hsv(basecolor[0], basecolor[1], basecolor[2])
252
        hue = hsv[0]
253
        sat = hsv[1]
254
        # val = hsv[2]
255
    if mode == 'range':
256
        # create random hue, saturation for lights, and brightness
257
        lighthue = random.uniform(huemin, huemax)
258
        lightsat = random.uniform(satmin, satmax)
259
        lightval = 1
260
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
261
    elif mode == 'monochromatic':
262
        lighthue = hue + (random.uniform(huespread / 100,
263
                                         - huespread / 100)) + 1
264
        lightsat = random.uniform(sat * satmin, sat * satmax)
265
        lightval = random.uniform(0.5, 1.0)
266
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
267
    elif mode == 'complementary':
268
        if index % 2 == 0:
269
            lighthue = hue + (random.uniform(huespread / 100,
270
                                             -huespread / 100)) + 1
271
        if ((index + 1) % 2) == 0:
272
            lighthue = hue + (random.uniform(huespread / 100,
273
                                             -huespread / 100)) + 1.5
274
        lightsat = random.uniform(sat * satmin, sat * satmax)
275
        lightval = random.uniform(0.7, 1.0)
276
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
277
    elif mode == 'triad':
278
        if index % 3 == 0:
279
            lighthue = hue + (random.uniform(huespread / 100,
280
                                             -huespread / 100)) + 1
281
        if ((index + 2) % 3) == 0:
282
            lighthue = hue + (random.uniform(huespread / 100,
283
                                             -huespread / 100)) + 1.33
284
        if ((index + 1) % 3) == 0:
285
            lighthue = hue + (random.uniform(huespread / 100,
286
                                             -huespread / 100)) + 0.67
287
        lightsat = random.uniform(sat * satmin, sat * satmax)
288
        lightval = random.uniform(0.7, 1.0)
289
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
290
    elif mode == 'split complementary':
291
        if index % 3 == 0:
292
            lighthue = hue + (random.uniform(huespread / 100,
293
                                             -huespread / 100)) + 1
294
        if ((index + 2) % 3) == 0:
295
            lighthue = hue + (random.uniform(huespread / 100,
296
                                             -huespread / 100)) + 1.44
297
        if ((index + 1) % 3) == 0:
298
            lighthue = hue + (random.uniform(huespread / 100,
299
                                             -huespread / 100)) + 0.56
300
        lightsat = random.uniform(sat * satmin, sat * satmax)
301
        lightval = random.uniform(0.7, 1.0)
302
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
303
    elif mode == 'analogous':
304
        if index % 5 == 0:
305
            lighthue = hue + 1
306
        if ((index + 4) % 5) == 0:
307
            lighthue = hue + (random.uniform(huespread / 10,
308
                                             -huespread / 10)) + 1
309
        if ((index + 3) % 5) == 0:
310
            lighthue = hue - (random.uniform(huespread / 10,
311
                                             -huespread / 10)) + 1
312
        if ((index + 2) % 5) == 0:
313
            lighthue = hue + ((random.uniform(huespread / 10,
314
                                              -huespread / 10)) * 2) + 1
315
        if ((index + 1) % 5) == 0:
316
            lighthue = hue - ((random.uniform(huespread / 10,
317
                                              -huespread / 10)) * 2) + 1
318
        lightsat = random.uniform(sat * satmin, sat * satmax)
319
        lightval = random.uniform(0.7, 1.0)
320
        lightcolor = colorsys.hsv_to_rgb(lighthue, lightsat, lightval)
321
    return lightcolor
322
323
324
def purge_unused():
325
    if (2, 83, 0) <= bpy.app.version <= (2, 91, 99):
326
        bpy.ops.outliner.orphans_purge()
327
    elif (2, 92, 0) <= bpy.app.version:
328
        bpy.ops.outliner.orphans_purge(do_local_ids=True, do_recursive=True)
329
330
331
# ----------------------------------------------------------------------------
332
#    Scene Properties
333
# ----------------------------------------------------------------------------
334
335
# custom icons for dropdown
336
preview_collections = {}
337
# Note that preview collections returned by bpy.utils.previews
338
# are regular py objects - you can use them to store custom data.
339
GENESISicons = bpy.utils.previews.new()
340
# path to the folder where the icon is
341
# the path is calculated relative to this py file inside the addon folder
342
# '..' goes up a level, seems to be needed?
343
# my_icons_dir = os.path.join(os.path.dirname(__file__), '..', "genesis-icons")
344
my_icons_dir = os.path.join(os.path.dirname(__file__), "genesis-icons")
345
# load a preview thumbnail of a file and store in the previews collection
346
GENESISicons.load("icon_mono",
347
                  os.path.join(my_icons_dir, "monochromatic.png"), 'IMAGE')
348
GENESISicons.load("icon_comp",
349
                  os.path.join(my_icons_dir, "complementary.png"), 'IMAGE')
350
GENESISicons.load("icon_spco",
351
                  os.path.join(my_icons_dir, "splitcomplementary.png"),
352
                  'IMAGE')
353
GENESISicons.load("icon_trid",
354
                  os.path.join(my_icons_dir, "triad.png"), 'IMAGE')
355
GENESISicons.load("icon_anlg",
356
                  os.path.join(my_icons_dir, "analogous.png"), 'IMAGE')
357
GENESISicons.load("icon_rang",
358
                  os.path.join(my_icons_dir, "range.png"), 'IMAGE')
359
preview_collections["main"] = GENESISicons
360
# create aliases for icon names
361
customicons = preview_collections["main"]
362
ic1 = customicons["icon_rang"]
363
ic2 = customicons["icon_comp"]
364
ic3 = customicons["icon_trid"]
365
ic4 = customicons["icon_mono"]
366
ic5 = customicons["icon_spco"]
367
ic6 = customicons["icon_anlg"]
368
# end custom icons for dropdown
369
370
371
class GENESISProperties(PropertyGroup):
372
    lightmode: EnumProperty(
373
        name="Light Type:",
374
        description="Type of light(s) to create",
375
        items=[('point', "Point Lights", "Emit light in every direction",
376
                'LIGHT_POINT', 1),
377
               ('sun', "Sun Lights", "Emit light from an infinite distance",
378
                'LIGHT_SUN', 2),
379
               ('spot', "Spot Lights", "Emit light in a cone shape",
380
                'LIGHT_SPOT', 3),
381
               ('area', "Area Lights", "Emit light from a plane",
382
                'LIGHT_AREA', 4),
383
               ('plane', "Plane Lights", "Emission shader from a plane",
384
                'MESH_GRID', 5)
385
               ],
386
        default='area'
387
    )
388
389
    colormode: EnumProperty(
390
        name="Color Mode:",
391
        description="Type of color mode to use for light colors",
392
        items=[('range', "Range", "Infinite Colors between Min Hue and Max "
393
                "Hue range on color wheel", ic1.icon_id, 1),
394
               ('complementary', "Complementary",
395
                "2 Colors on opposite sides of color wheel", ic2.icon_id, 2),
396
               ('triad', "Triad", "3 Colors on opposite sides of color wheel",
397
                ic3.icon_id, 3),
398
               ('monochromatic', "Monochromatic", "1 Color, all the same hue",
399
                ic4.icon_id, 4),
400
               ('split complementary', "Split Complementary",
401
                "3 Colors on opposite sides of color wheel", ic5.icon_id, 5),
402
               ('analogous', "Analogous",
403
                "5 Colors on same side of color wheel", ic6.icon_id, 6)
404
               ],
405
        default='range'
406
    )
407
408
    layoutmode: EnumProperty(
409
        name="Layout Mode:",
410
        description="How lights should be located in the scene",
411
        items=[('sphere', "Sphere",
412
                "Sphere shape layout around the selected object",
413
                'SPHERE', 1),
414
               ('topsphere', "Top Sphere",
415
                "Sphere Shape, but only above the horizon",
416
                'MATSPHERE', 2),
417
               ('grid', "Grid",
418
                "Grid shape layout above the selected object",
419
                'OUTLINER_DATA_LATTICE', 3)
420
               ],
421
        default='sphere'
422
    )
423
424
    lightnumber: IntProperty(
425
        name="Number",
426
        description="Total number of lights to create",
427
        default=9,
428
        min=1,
429
        max=200
430
    )
431
432
    lightradius: FloatProperty(
433
        name="Radius",
434
        description="Distance between lights and the center point",
435
        default=40.0,
436
        min=0.0001,
437
        max=1000.0
438
    )
439
440
    gridheight: FloatProperty(
441
        name="Grid Height",
442
        description="Distance lights are above the horizon",
443
        default=40.0,
444
        min=0.0001,
445
        max=1000.0
446
    )
447
448
    opm: FloatProperty(
449
        name="Power Multiplier",
450
        description="Overall light strength",
451
        default=1.0,
452
        min=0.01,
453
        max=1000.0
454
    )
455
456
    basecolor: FloatVectorProperty(
457
        subtype='COLOR',
458
        name="Base Color",
459
        min=0.0,
460
        max=1.0,
461
        default=(1.0, 1.0, 0.0)
462
    )
463
464
    huemin: FloatProperty(
465
        name="Min Hue",
466
        description="Minimum Hue Value (0 red, 0.3 green, 0.6 blue)",
467
        default=0.0,
468
        min=0.0,
469
        max=1.0
470
    )
471
472
    huemax: FloatProperty(
473
        name="Max Hue",
474
        description="Maximum Hue Value (0 red, 0.3 green, 0.6 blue)",
475
        default=1.0,
476
        min=0.0,
477
        max=1.0
478
    )
479
480
    huespread: FloatProperty(
481
        name="Hue Spread",
482
        description="Hue Range to pick from",
483
        default=0.1,
484
        min=0.0,
485
        max=15.0
486
    )
487
488
    satmin: FloatProperty(
489
        name="Min Sat",
490
        description="Minimum Saturation Value (0 no saturation, "
491
                    "0.5 medium saturation, 1.0 full saturation)",
492
        default=0.7,
493
        min=0.0,
494
        max=1.0
495
    )
496
497
    satmax: FloatProperty(
498
        name="Max Sat",
499
        description="Maximum Saturation Value (0 no saturation, "
500
                    "0.5 medium saturation, 1.0 full saturation)",
501
        default=1.0,
502
        min=0.0,
503
        max=1.0
504
    )
505
506
    sizemin: FloatProperty(
507
        name="Min Size",
508
        description="Minimum light size",
509
        default=7.0,
510
        min=0.0001,
511
        max=1000.0
512
    )
513
514
    sizemax: FloatProperty(
515
        name="Max Size",
516
        description="Maximum light size",
517
        default=15.0,
518
        min=0.0001,
519
        max=1000.0
520
    )
521
522
    powermin: FloatProperty(
523
        name="Min Power",
524
        description="Minimum light strength",
525
        default=500.0,
526
        min=0.0,
527
        max=1000000.0
528
    )
529
530
    powermax: FloatProperty(
531
        name="Max Power",
532
        description="Maximum light strength",
533
        default=1000.0,
534
        min=0.0,
535
        max=1000000.0
536
    )
537
538
# ----------------------------------------------------------------------------
539
#    Operators
540
# ----------------------------------------------------------------------------
541
542
543
class WM_OT_GENESISGenerateLights(Operator):
544
545
    """Add Lights to the Scene"""
546
    bl_label = "Generate Lights"
547
    bl_idname = "wm.genesisgenerate"
548
    bl_options = {"REGISTER", "UNDO"}
549
550
    def execute(self, context):
551
        scene = context.scene
552
        lt = scene.genesis_tools
553
554
        so = save_selected()
555
        sox, soy, soz = get_offset()
556
        active_collection = get_active_collection()
557
        make_collection("GENESIS")
558
        del_all_in_collection("GENESISLights")
559
        purge_unused()
560
        make_collection("GENESISUtilities", "GENESIS")
561
        make_tracker("GENESISEmpty", "GENESISUtilities", "GENESIS",
562
                     sox, soy, soz)
563
        make_collection("GENESISLights", "GENESIS")
564
        for l in range(lt.lightnumber):
565
            x, y, z = set_position(lt.layoutmode, l, lt.lightradius,
566
                                   lt.gridheight, sox, soy, soz)
567
            color = set_color(lt.colormode, lt.basecolor,
568
                              lt.huemin, lt.huemax, lt.satmin, lt.satmax,
569
                              lt.huespread, l)
570
            make_light(lt.lightmode, color, lt.opm, x, y, z,
571
                       lt.sizemin, lt.sizemax, lt.powermin, lt.powermax)
572
            track_to("GENESISEmpty")
573
        bpy.ops.object.select_all(action='DESELECT')
574
        set_collection(active_collection.name)
575
        recall_selected(so)
576
577
        return {'FINISHED'}
578
579
580
class WM_OT_GENESISGenerateLightsWithKeyframes(Operator):
581
582
    """Add Lights to the Scene, Keyframed on and off. """ \
583
        """May take a while to complete if the timeline duration """ \
584
        """is long. Test with short (less than 10 frame) timelines first"""
585
    bl_label = "Lights with Keyframes"
586
    bl_idname = "wm.genesisgeneratekeys"
587
    bl_options = {"REGISTER", "UNDO"}
588
589
    def execute(self, context):
590
        scene = context.scene
591
        lt = scene.genesis_tools
592
        beg = scene.frame_start
593
        end = scene.frame_end
594
595
        so = save_selected()
596
        sox, soy, soz = get_offset()
597
        active_collection = get_active_collection()
598
        make_collection("GENESIS")
599
        del_all_in_collection("GENESISLights")
600
        purge_unused()
601
        make_collection("GENESISUtilities", "GENESIS")
602
        make_tracker("GENESISEmpty", "GENESISUtilities", "GENESIS",
603
                     sox, soy, soz)
604
        make_collection("GENESISLights", "GENESIS")
605
        for gindex in range(end):
606
            for l in range(lt.lightnumber):
607
                x, y, z = set_position(lt.layoutmode, l, lt.lightradius,
608
                                       lt.gridheight, sox, soy, soz)
609
                color = set_color(lt.colormode, lt.basecolor,
610
                                  lt.huemin, lt.huemax, lt.satmin, lt.satmax,
611
                                  lt.huespread, l)
612
                make_light(lt.lightmode, color, lt.opm, x, y, z,
613
                           lt.sizemin, lt.sizemax, lt.powermin, lt.powermax)
614
                track_to("GENESISEmpty")
615
                obj = bpy.context.object
616
                frame = gindex + 1
617
                previousframe = frame - 1
618
                nextframe = frame + 1
619
                obj.hide_render = True
620
                obj.hide_viewport = True
621
                obj.keyframe_insert(data_path="hide_render",
622
                                    frame=previousframe)
623
                obj.keyframe_insert(data_path="hide_viewport",
624
                                    frame=previousframe)
625
                obj.hide_render = False
626
                obj.hide_viewport = False
627
                obj.keyframe_insert(data_path="hide_render",
628
                                    frame=frame)
629
                obj.keyframe_insert(data_path="hide_viewport",
630
                                    frame=frame)
631
                obj.hide_render = True
632
                obj.hide_viewport = True
633
                obj.keyframe_insert(data_path="hide_render",
634
                                    frame=nextframe)
635
                obj.keyframe_insert(data_path="hide_viewport",
636
                                    frame=nextframe)
637
        bpy.ops.object.select_all(action='DESELECT')
638
        set_collection(active_collection.name)
639
        recall_selected(so)
640
641
        return {'FINISHED'}
642
643
644
# ----------------------------------------------------------------------------
645
#    Panel in Object Mode
646
# ----------------------------------------------------------------------------
647
648
649
class OBJECT_PT_CustomPanel(Panel):
650
651
    """Generates light creation tools in 3D viewport Sidebar [N]"""
652
    bl_label = "Genesis"
653
    bl_idname = "OBJECT_PT_custom_panel"
654
    bl_space_type = "VIEW_3D"
655
    bl_region_type = "UI"
656
    bl_category = "Tool"
657
    bl_context = "objectmode"
658
659
    @classmethod
660
    def poll(self, context):
661
        return context.object is not None
662
663
    def draw(self, context):
664
        global custom_icons
665
        layout = self.layout
666
        scene = context.scene
667
        genesistools = scene.genesis_tools
668
        # duration = scene.frame_end - scene.frame_start
669
670
        col = layout.column(align=True)
671
        # generate lights button
672
        col.operator("wm.genesisgenerate", icon='LIGHT')
673
        col.separator()
674
        # light/color type dropdown
675
        col.prop(genesistools, "lightmode", text="Type")
676
        col.prop(genesistools, "colormode", text="Colors")
677
        col.prop(genesistools, "layoutmode", text="Layout")
678
        col.prop(genesistools, "lightnumber")
679
        if genesistools.layoutmode == 'grid':
680
            col.prop(genesistools, "lightradius", text="Grid Spacing")
681
            col.prop(genesistools, "gridheight")
682
        else:
683
            col.prop(genesistools, "lightradius")
684
        col.prop(genesistools, "opm")
685
        if genesistools.colormode == 'range':
686
            col.label(text="Hue:")
687
            col.prop(genesistools, "huemin")
688
            col.prop(genesistools, "huemax")
689
        else:
690
            col.prop(genesistools, "basecolor")
691
            col.prop(genesistools, "huespread")
692
        col.label(text="Saturation:")
693
        col.prop(genesistools, "satmin")
694
        col.prop(genesistools, "satmax")
695
        if genesistools.lightmode == 'spot':
696
            col.label(text="Spot Size / Angle:")
697
        else:
698
            col.label(text="Size:")
699
        col.prop(genesistools, "sizemin")
700
        col.prop(genesistools, "sizemax")
701
        col.label(text="Power:")
702
        col.prop(genesistools, "powermin")
703
        col.prop(genesistools, "powermax")
704
        col.separator()
705
        col.operator("wm.genesisgeneratekeys", icon='RENDER_ANIMATION')
706
707
708
# ----------------------------------------------------------------------------
709
#    Registration
710
# ----------------------------------------------------------------------------
711
712
classes = (
713
    GENESISProperties,
714
    WM_OT_GENESISGenerateLights,
715
    WM_OT_GENESISGenerateLightsWithKeyframes,
716
    OBJECT_PT_CustomPanel
717
)
718
719
720
def register():
721
    from bpy.utils import register_class
722
    for cls in classes:
723
        register_class(cls)
724
    bpy.types.Scene.genesis_tools = PointerProperty(type=GENESISProperties)
725
726
727
def unregister():
728
    from bpy.utils import unregister_class
729
    for cls in reversed(classes):
730
        unregister_class(cls)
731
    del bpy.types.Scene.genesis_tools
732
    # remove custom icons
733
    for GENESISicons in preview_collections.values():
734
        bpy.utils.previews.remove(GENESISicons)
735
    preview_collections.clear()
736
737
738
if __name__ == "__main__":
739
    # The path of this text (if saved)
740
    __file__ = bpy.context.space_data.text.filepath
741
    register()
742