glitch3d 0.5.0.0 → 0.5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/README.md +21 -9
  4. data/bin/glitch3d +1 -1
  5. data/fixtures/base.blend +0 -0
  6. data/fixtures/{osl-shaders → osl_shaders}/glass.osl +0 -0
  7. data/fixtures/{osl-shaders → osl_shaders}/gold.osl +0 -0
  8. data/fixtures/{osl-shaders → osl_shaders}/green_marble.osl +0 -0
  9. data/fixtures/{osl-shaders → osl_shaders}/turbulent.osl +0 -0
  10. data/fixtures/{osl-shaders → osl_shaders}/veined_marble.osl +0 -0
  11. data/fixtures/{text → texts}/strings.txt +0 -0
  12. data/lib/glitch3d.rb +75 -22
  13. data/lib/glitch3d/bpy/canvas/aether.py +22 -58
  14. data/lib/glitch3d/bpy/canvas/dreamatorium.py +3 -9
  15. data/lib/glitch3d/bpy/canvas/empty.py +8 -0
  16. data/lib/glitch3d/bpy/canvas/fernandez.py +36 -10
  17. data/lib/glitch3d/bpy/canvas/lyfe.py +1 -3
  18. data/lib/glitch3d/bpy/canvas/metaballs.py +11 -7
  19. data/lib/glitch3d/bpy/canvas/sphere.py +23 -38
  20. data/lib/glitch3d/bpy/canvas/waves.py +15 -41
  21. data/lib/glitch3d/bpy/helpers.py +70 -40
  22. data/lib/glitch3d/bpy/main.py +219 -193
  23. data/lib/glitch3d/bpy/post-processing/average.py +8 -10
  24. data/lib/glitch3d/bpy/post-processing/mosaic.py +1 -1
  25. data/lib/glitch3d/bpy/post-processing/optimize.py +3 -6
  26. data/lib/glitch3d/bpy/post-processing/palette.py +3 -4
  27. data/lib/glitch3d/bpy/render_settings.py +35 -23
  28. data/lib/glitch3d/objects/vertex.rb +1 -1
  29. data/lib/glitch3d/strategies/default.rb +10 -8
  30. data/lib/glitch3d/strategies/duplication.rb +14 -12
  31. data/lib/glitch3d/strategies/find_and_replace.rb +19 -16
  32. data/lib/glitch3d/strategies/localized.rb +28 -18
  33. data/lib/glitch3d/strategies/none.rb +7 -5
  34. data/lib/glitch3d/version.rb +1 -1
  35. metadata +9 -9
  36. data/lib/glitch3d/bpy/canvas/particles.py +0 -33
@@ -5,7 +5,7 @@ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
5
5
  import helpers
6
6
 
7
7
  class Lyfe(canvas.Canvas):
8
- SIZE = 5
8
+ SIZE = 4
9
9
 
10
10
  def render(self):
11
11
  self.cubes = helpers.build_composite_object('Cube', self.SIZE-1, 0.5)
@@ -13,7 +13,6 @@ class Lyfe(canvas.Canvas):
13
13
  for b in a:
14
14
  for c in b:
15
15
  c.location += mathutils.Vector((self.SIZE/2, self.SIZE/2, self.SIZE/2))
16
- # code.interact(local=dict(globals(), **locals()))
17
16
  self.cells = [[[ 0 for i in range(self.SIZE)] for k in range(self.SIZE)] for j in range(self.SIZE)]
18
17
  self.next_generation = [[[ 0 for i in range(self.SIZE)] for k in range(self.SIZE)] for j in range(self.SIZE)]
19
18
 
@@ -52,7 +51,6 @@ class Lyfe(canvas.Canvas):
52
51
  x_index = (x + i + self.SIZE) % self.SIZE
53
52
  y_index = (y + j + self.SIZE) % self.SIZE
54
53
  z_index = (z + k + self.SIZE) % self.SIZE
55
- # code.interact(local=dict(globals(), **locals()))
56
54
  if not( x_index == x and y_index == y and z_index == z):
57
55
  neighbors_alive_count += self.cells[x_index][y_index][z_index]
58
56
  if self.cells[x][y][z] == 1 and neighbors_alive_count > 6:
@@ -8,10 +8,13 @@ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
8
8
  import helpers
9
9
 
10
10
  class Metaballs(canvas.Canvas):
11
+
12
+ TYPES = ['BALL', 'CAPSULE', 'PLANE', 'ELLIPSOID', 'CUBE']
13
+
11
14
  def render(self):
12
- diameter = 8.0
15
+ diameter = 4.0
13
16
  sz = 2.125 / diameter
14
- latitude = 16
17
+ latitude = 10
15
18
  longitude = latitude * 2
16
19
  invlatitude = 1.0 / (latitude - 1)
17
20
  current_frame = 0
@@ -23,12 +26,13 @@ class Metaballs(canvas.Canvas):
23
26
  center = Vector((0.0, 0.0, 0.0))
24
27
  baseaxis = Vector((0.0, 1.0, 0.0))
25
28
  axis = Vector((0.0, 0.0, 0.0))
26
- elemtypes = ['BALL', 'CAPSULE', 'PLANE', 'ELLIPSOID', 'CUBE']
27
- mbdata = bpy.data.metaballs.new('SphereData')
29
+ metaball_type = random.choice(self.TYPES)
30
+ mbdata = bpy.data.metaballs.new('meta_balls')
28
31
  mbdata.render_resolution = 0.075
29
- mbdata.resolution = 0.2
30
- mbobj = bpy.data.objects.new("Sphere", mbdata)
32
+ mbdata.resolution = 0.1
33
+ mbobj = bpy.data.objects.new("meta_bollocks", mbdata)
31
34
  bpy.context.scene.objects.link(mbobj)
35
+ helpers.assign_material(mbobj, helpers.random_material(self.MATERIALS_NAMES))
32
36
  for i in range(0, latitude, 1):
33
37
  phi = pi * (i + 1) * invlatitude
34
38
  pt.z = cos(phi) * diameter
@@ -36,7 +40,7 @@ class Metaballs(canvas.Canvas):
36
40
  theta = TWOPI * j / longitude
37
41
  pt.y = center.y + sin(phi) * sin(theta) * diameter
38
42
  pt.x = center.x + sin(phi) * cos(theta) * diameter
39
- mbelm = mbdata.elements.new(type=elemtypes[0])
43
+ mbelm = mbdata.elements.new(type=metaball_type)
40
44
  mbelm.co = (latitude, longitude, 6)
41
45
  mbelm.radius = 0.15 + sz * abs(sin(phi)) * 1.85
42
46
  mbelm.stiffness = 1.0
@@ -9,8 +9,9 @@ import helpers
9
9
 
10
10
  class Sphere(canvas.Canvas):
11
11
  def render(self):
12
- diameter = 8.0
12
+ diameter = 4.0
13
13
  sz = 2.125 / diameter
14
+ base_object = helpers.infer_primitive(random.choice(self.PRIMITIVES), location = (100, 100, 100), radius=sz)
14
15
  latitude = 16
15
16
  longitude = latitude * 2
16
17
  invlatitude = 1.0 / (latitude - 1)
@@ -19,11 +20,7 @@ class Sphere(canvas.Canvas):
19
20
  jprc = 0.0
20
21
  phi = 0.0
21
22
  theta = 0.0
22
- currframe = 0
23
- fcount = 10
24
- invfcount = 1.0 / (fcount - 1)
25
- frange = bpy.context.scene.frame_end - bpy.context.scene.frame_start
26
- fincr = ceil(frange * invfcount)
23
+ invfcount = 1.0 / (self.NUMBER_OF_FRAMES - 1)
27
24
  # Animate center of the sphere.
28
25
  center = Vector((0.0, 0.0, 0.0))
29
26
  startcenter = Vector((0.0, -4.0, 0.0))
@@ -41,52 +38,40 @@ class Sphere(canvas.Canvas):
41
38
  for i in range(0, latitude, 1):
42
39
  iprc = i * invlatitude
43
40
  phi = pi * (i + 1) * invlatitude
44
- sinphi = sin(phi)
45
- cosphi = cos(phi)
46
- rad = 0.01 + sz * abs(sinphi) * 0.99
47
- pt.z = cosphi * diameter
41
+ rad = 0.01 + sz * abs(sin(phi)) * 0.99
42
+ pt.z = cos(phi) * diameter
48
43
  for j in range(0, longitude, 1):
49
44
  jprc = j * invlongitude
50
45
  theta = TWOPI * j / longitude
51
- sintheta = sin(theta)
52
- costheta = cos(theta)
53
- pt.y = center.y + sinphi * sintheta * diameter
54
- pt.x = center.x + sinphi * costheta * diameter
55
- bpy.ops.mesh.primitive_cube_add(location=pt, radius=rad)
56
- current = bpy.context.object
57
- current.name = 'Cube ({0:0>2d}, {1:0>2d})'.format(i, j)
46
+ pt.y = center.y + sin(phi) * sin(theta) * diameter
47
+ pt.x = center.x + sin(phi) * cos(theta) * diameter
48
+ current = helpers.duplicate_object(base_object)
49
+ current.location = pt
50
+ current.name = 'Object ({0:0>2d}, {1:0>2d})'.format(i, j)
58
51
  current.data.name = 'Mesh ({0:0>2d}, {1:0>2d})'.format(i, j)
59
52
  current.rotation_euler = (0.0, phi, theta)
60
- # Append a material.
61
- mat = bpy.data.materials.new(name='Material ({0:0>2d}, {1:0>2d})'.format(i, j))
62
- mat.diffuse_color = colorsys.hsv_to_rgb(jprc, 1.0 - iprc, 1.0)
63
- current.data.materials.append(mat)
64
- # Append a bevel modifier to each cube for polish.
65
- bpy.ops.object.modifier_add(type='BEVEL')
66
- bpy.context.object.modifiers['Bevel'].segments = 2
67
- bpy.context.object.modifiers['Bevel'].width = 0.03
68
- self.vecrotatex(theta, baseaxis, axis)
69
- currframe = bpy.context.scene.frame_start
53
+ helpers.assign_material(current, helpers.random_material(self.MATERIALS_NAMES))
54
+ axis = self.vecrotatex(theta, baseaxis)
70
55
  currot = startrot
71
56
  center = startcenter
72
- for f in range(0, fcount, 1):
73
- fprc = f / (fcount - 1)
57
+ for f in range(0, self.NUMBER_OF_FRAMES, 1):
58
+ fprc = f / (self.NUMBER_OF_FRAMES - 1)
74
59
  osc = abs(sin(TWOPI * fprc))
75
- bpy.context.scene.frame_set(currframe)
60
+ bpy.context.scene.frame_set(f)
76
61
  center = startcenter.lerp(stopcenter, osc)
77
62
  current.location = helpers.rotate_vector(TWOPI * fprc, axis, pt)
78
63
  current.keyframe_insert(data_path='location')
79
64
  currot = startrot.slerp(stoprot, jprc * fprc)
80
65
  current.rotation_euler = currot.to_euler()
81
66
  current.keyframe_insert(data_path='rotation_euler')
82
- mat.diffuse_color = colorsys.hsv_to_rgb(jprc, osc, 1.0)
83
- mat.keyframe_insert(data_path='diffuse_color')
84
- currframe += fincr
85
67
 
86
- def vecrotatex(self, angle, vin, vout):
87
- cosa = cos(angle)
88
- sina = sin(angle)
68
+ # Rotate vector by a given angle from a starting vector and return a new vector
69
+ # Trigonometry:
70
+ # x' = x cos θ − y sin θ
71
+ # y' = x sin θ + y cos θ
72
+ def vecrotatex(self, angle, vin):
73
+ vout = Vector((0.0, 0.0, 0.0))
89
74
  vout.x = vin.x
90
- vout.y = cosa * vin.y - sina * vin.z
91
- vout.z = cosa * vin.z + sina * vin.y
75
+ vout.y = cos(angle) * vin.y - sin(angle) * vin.z
76
+ vout.z = cos(angle) * vin.z + sin(angle) * vin.y
92
77
  return vout
@@ -7,49 +7,34 @@ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
7
7
  import helpers
8
8
 
9
9
  class Waves(canvas.Canvas):
10
+
11
+ COUNT=5
12
+
10
13
  def render(self):
11
- # Number of cubes.
12
- count = 16
14
+ count = self.COUNT
13
15
  # Size of grid.
14
16
  extents = 8.0
15
- # Spacing between cubes.
16
- padding = 0.002
17
- # Size of each cube.
18
17
  # The height of each cube will be animated, so we'll specify the minimum and maximum scale.
19
- sz = (extents / count) - padding
18
+ sz = (extents / count)
19
+ base_object = helpers.infer_primitive(random.choice(self.PRIMITIVES), location = (100, 100, 100), radius=sz)
20
+ helpers.assign_material(base_object, helpers.random_material(self.MATERIALS_NAMES))
20
21
  minsz = sz * 0.25
21
22
  maxsz = sz * extents
22
- diffsz = maxsz - minsz
23
23
  # To convert abstract grid position within loop to real-world coordinate.
24
24
  jprc = 0.0
25
25
  kprc = 0.0
26
26
  countf = 1.0 / (count - 1)
27
27
  diffex = extents * 2
28
- # Position of each cube.
29
28
  y = 0.0
30
29
  x = 0.0
31
- # Center of grid.
32
30
  centerz = 0.0
33
31
  centery = 0.0
34
32
  centerx = 0.0
35
- # Distances of cube from center.
36
33
  # The maximum possible distance is used to normalize the distance.
37
34
  rise = 0.0
38
35
  run = 0.0
39
36
  normdist = 0.0
40
37
  maxdist = sqrt(2 * extents * extents)
41
- # For animation, track current frame, specify desired number of key frames.
42
- currframe = 0
43
- fcount = 10
44
- invfcount = 1.0 / (fcount - 1)
45
- # If the default frame range is 0, then default to 1 .. 150.
46
- frange = bpy.context.scene.frame_end - bpy.context.scene.frame_start
47
- if frange == 0:
48
- bpy.context.scene.frame_end = 150
49
- bpy.context.scene.frame_start = 0
50
- frange = 150
51
- # Number of keyframes per frame.
52
- fincr = ceil(frange * invfcount)
53
38
  # For generating the wave.
54
39
  offset = 0.0
55
40
  angle = 0.0
@@ -71,29 +56,18 @@ class Waves(canvas.Canvas):
71
56
  # Remap the normalized distance to a range -PI .. PI
72
57
  normdist = sqrt(rise + run) / maxdist
73
58
  offset = -TWOPI * normdist + pi
74
- # Add grid world position to cube local position.
75
- bpy.ops.mesh.primitive_cube_add(location=(centerx + x, centery + y, centerz), radius=sz)
76
- # Remember and rename the current object being edited.
77
- current = bpy.context.object
78
- current.name = 'Cube ({0:0>2d}, {1:0>2d})'.format(k, j)
59
+ current = helpers.duplicate_object(base_object)
60
+ current.location = (centerx + x, centery + y, centerz)
61
+ current.name = 'Object ({0:0>2d}, {1:0>2d})'.format(k, j)
79
62
  current.data.name = 'Mesh ({0:0>2d}, {1:0>2d})'.format(k, j)
80
- # Create a material and add it to the current object.
81
- mat = bpy.data.materials.new(name='Material ({0:0>2d}, {1:0>2d})'.format(k, j))
82
- mat.diffuse_color = colorsys.hsv_to_rgb(normdist, 0.875, 1.0)
83
- current.data.materials.append(mat)
84
- # Track the current key frame.
85
- currframe = bpy.context.scene.frame_start
86
- for f in range(0, fcount, 1):
63
+ for f in range(0, self.NUMBER_OF_FRAMES, 1):
87
64
  # Convert the keyframe into an angle.
88
- fprc = f * invfcount
65
+ fprc = f * 1.0 / (self.NUMBER_OF_FRAMES - 1)
89
66
  angle = TWOPI * fprc
90
67
  # Set the scene to the current frame.
91
- bpy.context.scene.frame_set(currframe)
68
+ bpy.context.scene.frame_set(f)
92
69
  # Change the scale.
93
70
  # sin returns a value in the range -1 .. 1. abs changes the range to 0 .. 1.
94
71
  # The values are remapped to the desired scale with min + percent * (max - min).
95
- current.scale[2] = minsz + abs(sin(offset + angle)) * diffsz
96
- # Insert the key frame for the scale property.
97
- current.keyframe_insert(data_path='scale', index=2)
98
- # Advance by the keyframe increment to the next keyframe.
99
- currframe += fincr
72
+ current.scale.z = minsz + abs(sin(offset + angle)) * (maxsz - minsz)
73
+ helpers.add_frame([current], ['scale'])
@@ -1,7 +1,6 @@
1
- import sys, code, random, os, math, bpy, numpy, uuid, mathutils
1
+ import sys, code, random, os, math, bpy, numpy, uuid, mathutils, subprocess
2
2
 
3
3
  def pry(globs=globals(), locs=locals()):
4
- import sys
5
4
  code.interact(local=dict(globs, **locs))
6
5
  sys.exit("Aborting execution")
7
6
 
@@ -38,6 +37,12 @@ def apply_displacement(obj, height_map_folder, strength = 0.2, subdivisions = 2)
38
37
  displace.texture = new_texture
39
38
  displace.strength = strength
40
39
 
40
+ def decimate(obj):
41
+ modifier = obj.modifiers.new(name='decimate', type='DECIMATE')
42
+ modifier.decimate_type = 'DISSOLVE'
43
+ bpy.context.scene.objects.active = obj
44
+ bpy.ops.object.modifier_apply(modifier="decimate")
45
+
41
46
  def look_at(obj):
42
47
  location_camera = CAMERA.matrix_world.to_translation()
43
48
  location_object = obj.matrix_world.to_translation()
@@ -61,7 +66,9 @@ def output_name(model_path, index = 0):
61
66
  def rand_color_value():
62
67
  return random.uniform(0, 255) / 255
63
68
 
64
- def rand_location(boundary):
69
+ def rand_location(boundary, positive = False):
70
+ if positive:
71
+ return (random.uniform(0, boundary), random.uniform(0, boundary), random.uniform(0, boundary))
65
72
  return (random.uniform(-boundary, boundary), random.uniform(-boundary, boundary), random.uniform(-boundary, boundary))
66
73
 
67
74
  def rand_rotation():
@@ -102,8 +109,9 @@ def assign_material(obj, material):
102
109
  obj.data.materials[0] = material
103
110
  return material
104
111
 
105
- def random_material(materials_list):
106
- return fetch_material(random.choice(materials_list))
112
+ def random_material(materials_list, blacklist = [ 'Smoke Domain Material' ]):
113
+ eligible_materials = list(set(materials_list) - set(blacklist))
114
+ return fetch_material(random.choice(eligible_materials))
107
115
 
108
116
  # Returns a new Cycles material with default DiffuseBsdf node linked to output
109
117
  def create_cycles_material(name = 'object_material_', clean=False):
@@ -368,7 +376,6 @@ def duplicate_object(obj):
368
376
  new_object.data = obj.data.copy()
369
377
  new_object.animation_data_clear()
370
378
  new_object.cycles_visibility.camera = True
371
- # assign_material(new_object, obj.data.materials[-1])
372
379
  bpy.context.scene.objects.link(new_object)
373
380
  return new_object
374
381
 
@@ -382,53 +389,58 @@ def random_text(file_path):
382
389
  return lines[random.randrange(len(lines))]
383
390
 
384
391
  def add_faces(obj):
385
- vertices = []
386
- for v in obj.data.vertices:
387
- vertices.append(v.co)
388
- new_obj = create_mesh(obj.name, vertices, random_faces(vertices), obj.location)
389
- bpy.data.objects.remove(obj, do_unlink=True)
390
- return new_obj
392
+ vertices = []
393
+ for v in obj.data.vertices:
394
+ vertices.append(v.co)
395
+ new_obj = create_mesh(obj.name, vertices, random_faces(vertices), obj.location)
396
+ bpy.data.objects.remove(obj, do_unlink=True)
397
+ return new_obj
391
398
 
392
399
  def random_faces(vertices):
393
- faces = []
394
- for i in range(int(len(vertices)/100)):
395
- target = vertices[random.choice((range(len(vertices))))]
396
- if (random.randint(0, 1) == 1):
397
- faces.append(((target + 2), int(target / 6), int(target - 1), target))
398
- else:
399
- faces.append((int(target / 6), int(target - 1), target))
400
- return faces
400
+ faces = []
401
+ for i in range(int(len(vertices)/100)):
402
+ target = vertices[random.choice((range(len(vertices))))]
403
+ if (random.randint(0, 1) == 1):
404
+ faces.append(((target + 2), int(target / 6), int(target - 1), target))
405
+ else:
406
+ faces.append((int(target / 6), int(target - 1), target))
407
+ return faces
401
408
 
402
409
  ############
403
410
  # <geometry>
404
411
  ############
405
412
 
406
413
  def center(obj):
407
- bpy.context.scene.objects.active = obj
408
- bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
409
- bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")
410
- local_bounding_box_center = 0.125 * sum((mathutils.Vector(b) for b in obj.bound_box), mathutils.Vector())
411
- obj.location -= local_bounding_box_center
412
- return obj
413
-
414
- def resize(obj, pace = 0.05, minimum = 3.0, maximum = 8.0):
415
- print("Resizing: " + obj.name)
416
- assert minimum < maximum
417
- obj.scale = (1,1,1)
418
- scale_multiplier = max(obj.dimensions) / (maximum - minimum)
419
- if max(obj.dimensions) > maximum:
420
- init_scale = obj.scale
421
- obj.scale = init_scale - init_scale * scale_multiplier # downscale
422
- if obj.scale.x < 0:
423
- obj.scale = init_scale * scale_multiplier
424
- else:
425
- obj.scale = obj.scale + obj.scale * scale_multiplier # upscale
414
+ bpy.context.scene.objects.active = obj
415
+ bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
416
+ bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")
417
+ local_bounding_box_center = 0.125 * sum((mathutils.Vector(b) for b in obj.bound_box), mathutils.Vector())
418
+ obj.location -= local_bounding_box_center
419
+ obj.location = (0, 0, 0)
420
+ return obj
421
+
422
+ def resize(obj, minimum = 4.0, maximum = 8.0):
423
+ print("Resizing: " + obj.name)
424
+ init_scale = obj.scale
425
+ assert minimum < maximum
426
+ scale_multiplier =init_scale.x / (max(obj.dimensions) / (maximum - minimum))
427
+ if max(obj.dimensions) > maximum:
428
+ print("Downscaling by: " + str(scale_multiplier))
429
+ while max(obj.dimensions) > maximum:
430
+ obj.scale = init_scale + mathutils.Vector((- scale_multiplier, - scale_multiplier, - scale_multiplier))
431
+ bpy.ops.wm.redraw_timer(type='DRAW', iterations=1)
432
+ elif max(obj.dimensions) < minimum:
433
+ print("Upscaling by: " + str(scale_multiplier))
434
+ while max(obj.dimensions) < minimum:
435
+ obj.scale = obj.scale + mathutils.Vector((scale_multiplier, scale_multiplier, scale_multiplier))
436
+ bpy.ops.wm.redraw_timer(type='DRAW', iterations=1)
426
437
 
427
438
  def extrude(obj, thickness=0.05):
428
439
  bpy.context.scene.objects.active = obj
429
440
  bpy.ops.object.mode_set(mode='EDIT')
430
441
  bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={"mirror":False}, TRANSFORM_OT_translate={"value":(thickness, 0, 0), "constraint_orientation":'GLOBAL', "mirror":True, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False, "use_accurate":False})
431
442
  bpy.ops.object.mode_set(mode='OBJECT')
443
+ bpy.ops.object.select_all(action='DESELECT')
432
444
 
433
445
  def create_line(name, point_list, color, thickness = 0.002, location = (0,0,0)):
434
446
  line_data = bpy.data.curves.new(name=name,type='CURVE')
@@ -468,7 +480,7 @@ def pitched_array(minimum, maximum, pitch):
468
480
 
469
481
  def create_mesh(name, verts, faces, location, edges=[]):
470
482
  mesh_data = bpy.data.meshes.new("mesh_data")
471
- faces = faces if len(faces) == 0 else random_faces(verts)
483
+ faces = faces if (len(faces) == 0 or len(faces) > 0) else random_faces(verts)
472
484
  mesh_data.from_pydata(verts, edges, faces)
473
485
  mesh_data.update()
474
486
  obj = bpy.data.objects.new(name, mesh_data)
@@ -520,4 +532,22 @@ def rotate_vector(angle, axis, vin):
520
532
 
521
533
  #############
522
534
  # </geometry>
535
+ #############
536
+
537
+
538
+ #############
539
+ # <physics>
540
+ #############
541
+
542
+ def add_rigid_body(objs=[], type = 'ACTIVE', mass=2.0):
543
+ for obj in objs:
544
+ obj.select = True
545
+ bpy.ops.rigidbody.objects_add(type='ACTIVE')
546
+ for obj in objs:
547
+ if type == 'ACTIVE':
548
+ obj.rigid_body.mass = mass
549
+ obj.rigid_body.collision_shape = 'MESH'
550
+
551
+ #############
552
+ # </physics>
523
553
  #############
@@ -1,27 +1,30 @@
1
1
  # Rendering script
2
2
  # Run by calling the blender executable with -b -P <script_name>
3
3
  # Use `pry()` to pry into the script
4
- import argparse, random, math, os, code
4
+ import sys, argparse, random, math, os, code, requests
5
5
 
6
6
  def get_args():
7
- parser = argparse.ArgumentParser()
8
- _, all_arguments = parser.parse_known_args()
9
- double_dash_index = all_arguments.index('--')
10
- script_args = all_arguments[double_dash_index + 1: ]
11
- parser.add_argument('-f', '--file', help="obj file to render")
12
- parser.add_argument('-n', '--shots-number', help="number of shots desired")
13
- parser.add_argument('-m', '--mode', help="quality mode: low | high")
14
- parser.add_argument('-p', '--path', help="root path of assets")
15
- parser.add_argument('-a', '--animate', help="render animation") # bool
16
- parser.add_argument('-frames', '--frames', help="number of frames") # int
17
- parser.add_argument('-normals', '--normals', help="normal render") # bool
18
- parser.add_argument('-d', '--debug', help="render blank scene with subject for testing purposes") # bool
19
- parser.add_argument('-width', '--width', help="width of render") # int
20
- parser.add_argument('-eight', '--eight', help="height of render") # int
21
- parser.add_argument('-assets', '--assets', help="user assets path") # string
22
- parser.add_argument('-canvas', '--canvas', help="selection of canvas modules by name") # string
23
- parsed_script_args, _ = parser.parse_known_args(script_args)
24
- return parsed_script_args
7
+ parser = argparse.ArgumentParser()
8
+ _, all_arguments = parser.parse_known_args()
9
+ double_dash_index = all_arguments.index('--')
10
+ script_args = all_arguments[double_dash_index + 1: ]
11
+ parser.add_argument('-f', '--file', help="obj file to render")
12
+ parser.add_argument('-n', '--shots-number', help="number of shots desired")
13
+ parser.add_argument('-m', '--mode', help="quality mode: low | high")
14
+ parser.add_argument('-p', '--path', help="root path of gem assets")
15
+ parser.add_argument('-a', '--animate', help="render animation") # bool
16
+ parser.add_argument('-frames', '--frames', help="number of frames") # int
17
+ parser.add_argument('-normals', '--normals', help="normal render") # bool
18
+ parser.add_argument('-d', '--debug', help="render blank scene with subject for testing purposes") # bool
19
+ parser.add_argument('-width', '--width', help="width of render") # int
20
+ parser.add_argument('-eight', '--eight', help="height of render") # int
21
+ parser.add_argument('-assets', '--assets', help="user assets path") # string
22
+ parser.add_argument('-canvas', '--canvas', help="selection of canvas modules by name") # string
23
+ parser.add_argument('-post', '--post-process', help="post-processing") # bool
24
+ parser.add_argument('-webhook', '--webhook', help="webhook url") # string
25
+ parser.add_argument('-seed', '--seed', help="random seed") # int
26
+ parsed_script_args, _ = parser.parse_known_args(script_args)
27
+ return parsed_script_args
25
28
 
26
29
  args = get_args()
27
30
  file = args.file
@@ -32,183 +35,206 @@ animate = (args.animate == 'True')
32
35
  shots_number = int(args.shots_number)
33
36
  width = int(args.width)
34
37
  height = int(args.eight)
38
+ post_process = (args.post_process == 'True')
39
+ assets = args.assets
40
+ webhook = args.webhook
35
41
 
36
42
  # TODO: add proper args validation cycle
37
43
  #####################################
38
44
  #####################################
39
45
  #####################################
40
46
 
41
- NUMBER_OF_FRAMES = int(args.frames)
42
- NORMALS_RENDERING = (args.normals == 'True')
43
-
44
- # Randomize module usage at runtime or pick selection from arguments
45
- canvas_path = os.path.dirname(__file__) + '/canvas'
46
- MODULES_AVAILABLE = args.canvas.split(",") if args.canvas else [ f[0:-3] for f in os.listdir(canvas_path) if os.path.isfile(os.path.join(canvas_path, f)) and f != 'canvas.py']
47
- MODULES_ENABLED = MODULES_AVAILABLE if debug or args.canvas else random.sample(MODULES_AVAILABLE, int(random.uniform(0, len(MODULES_AVAILABLE)) + 1))
48
- print("modules enabled: " + str(list(MODULES_ENABLED)))
49
-
50
- FIXTURES_FOLDER_PATH = args.assets if args.assets else path + '/../fixtures/'
51
- TEXTURE_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'textures/'
52
- MODELS_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'models/'
53
- HEIGHT_MAP_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'height_maps/'
54
- SCENE_NAME = "glitch3d"
55
- REFLECTOR_SCALE = random.uniform(9, 10)
56
- REFLECTOR_STRENGTH = random.uniform(12, 15)
57
- REFLECTOR_LOCATION_PADDING = random.uniform(10, 12)
58
- REPLACE_TARGET = str(random.uniform(0, 9))
59
- REPLACEMENT = str(random.uniform(0, 9))
60
- OSL_ENABLED = True
61
- ORIGIN = (0,0,2)
62
- CANVAS_BOUNDARY = 6
63
- SCATTER_INTENSITY = 0.015
64
- ABSORPTION_INTENSITY = 0.25
65
- DISPLAY_SCALE = (2, 2, 2)
66
- PRIMITIVES = ['Pyramid', 'Cube']
67
- YELLOW = (1, 0.7, 0.1, 1)
68
- GREY = (0.2, 0.2, 0.2 ,1)
69
- BLUE = (0.1, 0.1, 0.8, 0.4)
70
- PINK = (0.8, 0.2, 0.7, 1.0)
71
- RENDER_OUTPUT_PATHS = []
72
- FIXED_CAMERA = False
73
- FUNCTIONS = {
74
- (lambda x: math.sin(x) * math.cos(20*x)): 4,
75
- (lambda x: math.sin(x) * math.sin(20*x)): 3,
76
- (lambda x: INITIAL_CAMERA_LOCATION[2]): 2,
77
- (lambda x: x) : 2,
78
- math.sin : 1,
79
- math.cos : 1,
80
- (lambda x: 0.5 * math.sin(0.5*x) * math.cos(x)) : 1,
81
- (lambda x: random.uniform(1, 10) * math.cos(x) ** 3) : 1,
82
- (lambda x: random.uniform(1, 10)) : 1,
83
- (lambda x: random.uniform(1, 2) + random.uniform(0.75, 3) * math.sin(random.uniform(0.1, 1)*x) + math.cos(random.uniform(0.75, 5)*x)) : 1,
84
- (lambda x: math.sin(math.pi*x) + x + 3 * math.pi) : 1,
85
- (lambda x: x**3 + math.cos(x/2)) : 2,
86
- (lambda x: random.uniform(1, 10) * math.sin(x)): 3
87
- }
88
-
89
- import importlib.util, os, ntpath, bpy, datetime, math, random, mathutils, random, uuid, sys, logging, string, colorsys, code, numpy
90
- from subprocess import call
91
-
92
- def load_file(file_path):
93
- # load and define function and vars in global namespace, yolo
94
- exec(open(file_path).read(), globals())
95
-
96
- def load_module_path(file_path):
97
- print("loading module " + file_path)
98
- sys.path.append(os.path.dirname(os.path.expanduser(file_path)))
99
-
100
- # Create directory for renders
101
- directory = os.path.dirname('./renders')
102
- if not os.path.exists(directory):
103
- os.makedirs(directory)
104
-
105
- load_file(os.path.join(path + '/glitch3d/bpy/helpers.py'))
106
- load_file(os.path.join(path + '/glitch3d/bpy/render_settings.py'))
107
- load_file(os.path.join(path + '/glitch3d/bpy/lighting.py'))
108
-
109
- # Create groups
110
- for s in ['texts', 'lines', 'displays', 'reflectors', 'neons']:
111
- bpy.data.groups.new(s)
112
- for primitive in PRIMITIVES:
113
- bpy.data.groups.new(primitive.lower().title())
114
-
115
- FISHEYE = True
116
- COLORS = rand_color_palette(5)
117
- CAMERA_OFFSET = 5
118
- INITIAL_CAMERA_LOCATION = (CAMERA_OFFSET, CAMERA_OFFSET, random.uniform(0, 8))
119
- TEXT_FILE_PATH = FIXTURES_FOLDER_PATH + 'text/strings.txt'
120
-
121
- print("Loading materials...")
122
- MATERIALS_NAMES = []
123
- load_osl_materials(FIXTURES_FOLDER_PATH + 'osl-shaders/')
124
- for mat in bpy.data.materials: # merge base scene materials + osl shaders
125
- if mat.name != 'emission':
126
- MATERIALS_NAMES.append(mat.name)
127
- print("Detected " + str(len(MATERIALS_NAMES)) + " materials in base scene: " + str(MATERIALS_NAMES))
128
-
129
- # Scene
130
- new_scene = bpy.data.scenes[SCENE_NAME]
131
- bpy.context.screen.scene = new_scene
132
- SCENE = new_scene
133
- SCENE.render.engine = 'CYCLES'
134
-
135
- flush_objects()
136
-
137
- camera_data = bpy.data.cameras.new(name = 'CAMERA')
138
- bpy.data.objects.new('CAMERA', object_data=camera_data)
139
- CAMERA = bpy.data.objects['CAMERA']
140
- new_scene.objects.link(CAMERA)
141
- SCENE.camera = CAMERA
142
- CAMERA.location = INITIAL_CAMERA_LOCATION
143
- SCENE.frame_start = 0
144
- SCENE.frame_end = NUMBER_OF_FRAMES
145
-
146
- if FISHEYE:
147
- CAMERA.data.type = 'PANO'
148
- CAMERA.data.cycles.panorama_type = 'FISHEYE_EQUISOLID'
149
- CAMERA.data.cycles.fisheye_lens = 12
150
- CAMERA.data.cycles.fisheye_fov = 2.5
151
- CAMERA.data.sensor_width = 20
152
- CAMERA.data.sensor_height = 20
153
-
154
- # Load model
155
- model_path = os.path.join(file)
156
- bpy.ops.import_scene.obj(filepath = model_path, use_edges=True)
157
- SUBJECT = bpy.data.objects['0_glitch3d_' + ntpath.basename(file).replace("_glitched.obj", '')]
158
- SUBJECT.select = True
159
- center(SUBJECT)
160
- SUBJECT.location = ORIGIN
161
- let_there_be_light(SCENE)
162
- random.shuffle(list(MODULES_ENABLED))
163
-
164
- for module in MODULES_ENABLED:
165
- load_module_path(os.path.join(path + '/glitch3d/bpy/canvas', module + '.py'))
166
- mod = __import__(module)
167
- new_canvas = eval("mod." + module[:1].upper() + module[1:] + "(locals())")
168
- new_canvas.render()
169
-
170
- render_settings(animate, mode, NORMALS_RENDERING, width, height)
171
- print('Rendering images with resolution: ' + str(SCENE.render.resolution_x) + ' x ' + str(SCENE.render.resolution_y))
172
-
173
- CAMERA_PATH = camera_path(NUMBER_OF_FRAMES)
174
-
175
- for frame in range(0, NUMBER_OF_FRAMES):
176
- bpy.context.scene.frame_set(frame)
177
- bpy.context.scene.camera.location = CAMERA_PATH[frame]
178
- SUBJECT.rotation_euler.z += math.radians(1)
179
- look_at(SUBJECT)
180
- add_frame([bpy.context.scene.camera], ["location", "rotation_euler"])
181
-
182
- if animate:
183
- print('ANIMATION RENDERING BEGIN')
184
- output_path = output_name(model_path)
185
- print('AVI file -> ' + output_path)
186
- shoot(output_path)
187
- else:
188
- print('STILL RENDERING BEGIN')
189
- for index in range(0, shots_number):
190
- frame_cursor = int(index * (bpy.context.scene.frame_end / shots_number))
191
- print('>> FRAME #' + str(frame_cursor))
192
- bpy.context.scene.frame_set(int(bpy.context.scene.frame_end/(index+1)))
193
- SUBJECT.rotation_euler.z = math.radians(index * (360.0 / shots_number))
194
- output_path = output_name(model_path, index)
195
- print("-------------------------- " + str(index) + " --------------------------")
196
- print("PNG file -> " + output_path)
197
- shoot(output_path)
198
-
199
- # Save scene as .blend file
200
- bpy.ops.wm.save_as_mainfile(filepath=output_name(model_path) + '.blend')
201
-
202
- print("Files rendered with " + str(NUMBER_OF_FRAMES) + " frames in simulation:")
203
- for p in RENDER_OUTPUT_PATHS:
204
- print(p)
205
-
206
- if animate == False and debug == False:
207
- call(["python3", os.path.join(path + '/glitch3d/bpy/post-processing/optimize.py')] + [ str(bpy.context.scene.render.resolution_x), str(bpy.context.scene.render.resolution_y) ] + RENDER_OUTPUT_PATHS)
208
- call(["python3", os.path.join(path + '/glitch3d/bpy/post-processing/palette.py')] + list(map(str, list(map(tuple, COLORS)))) + [os.path.join(path + '/../fixtures/fonts/helvetica_neue.ttf')])
209
- if shots_number > 1:
210
- call(["python3", os.path.join(path + '/glitch3d/bpy/post-processing/average.py')] + RENDER_OUTPUT_PATHS)
211
- if shots_number > 10:
212
- call(["python3", os.path.join(path + '/glitch3d/bpy/post-processing/mosaic.py')])
213
- print('FINISHED ¯\_()_/¯')
214
- sys.exit()
47
+ try:
48
+ NUMBER_OF_FRAMES = int(args.frames)
49
+ NORMALS_RENDERING = (args.normals == 'True')
50
+ random.seed = args.seed
51
+
52
+ # Randomize module usage at runtime or pick selection from arguments
53
+ canvas_path = os.path.dirname(__file__) + '/canvas'
54
+ MODULES_AVAILABLE = args.canvas.split(",") if args.canvas else [ f[0:-3] for f in os.listdir(canvas_path) if os.path.isfile(os.path.join(canvas_path, f)) and f != 'canvas.py' and f != 'empty.py']
55
+ MODULES_ENABLED = MODULES_AVAILABLE if debug or args.canvas else random.sample(MODULES_AVAILABLE, int(random.uniform(0, len(MODULES_AVAILABLE)) + 1))
56
+ print("modules enabled: " + str(list(MODULES_ENABLED)))
57
+
58
+ FIXTURES_FOLDER_PATH = assets if assets else path + '/../fixtures/'
59
+ TEXTURE_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'textures/'
60
+ MODELS_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'models/'
61
+ HEIGHT_MAP_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'height_maps/'
62
+ FONT_FOLDER_PATH = FIXTURES_FOLDER_PATH + 'fonts/'
63
+ SCENE_NAME = "glitch3d"
64
+ REFLECTOR_SCALE = random.uniform(9, 10)
65
+ REFLECTOR_STRENGTH = random.uniform(12, 15)
66
+ REFLECTOR_LOCATION_PADDING = random.uniform(10, 12)
67
+ REPLACE_TARGET = str(random.uniform(0, 9))
68
+ REPLACEMENT = str(random.uniform(0, 9))
69
+ OSL_ENABLED = True
70
+ ORIGIN = (0,0,2)
71
+ CANVAS_BOUNDARY = 6
72
+ SCATTER_INTENSITY = 0.015
73
+ ABSORPTION_INTENSITY = 0.25
74
+ DISPLAY_SCALE = (2, 2, 2)
75
+ PRIMITIVES = ['Pyramid', 'Cube']
76
+ YELLOW = (1, 0.7, 0.1, 1)
77
+ GREY = (0.2, 0.2, 0.2 ,1)
78
+ BLUE = (0.1, 0.1, 0.8, 0.4)
79
+ PINK = (0.8, 0.2, 0.7, 1.0)
80
+ RENDER_OUTPUT_PATHS = []
81
+ FIXED_CAMERA = False
82
+ FUNCTIONS = {
83
+ (lambda x: math.sin(x) * math.cos(20*x)): 4,
84
+ (lambda x: math.sin(x) * math.sin(20*x)): 3,
85
+ (lambda x: INITIAL_CAMERA_LOCATION[2]): 2,
86
+ (lambda x: x) : 2,
87
+ math.sin : 1,
88
+ math.cos : 1,
89
+ (lambda x: 0.5 * math.sin(0.5*x) * math.cos(x)) : 1,
90
+ (lambda x: random.uniform(1, 10) * math.cos(x) ** 3) : 1,
91
+ (lambda x: random.uniform(1, 10)) : 1,
92
+ (lambda x: random.uniform(1, 2) + random.uniform(0.75, 3) * math.sin(random.uniform(0.1, 1)*x) + math.cos(random.uniform(0.75, 5)*x)) : 1,
93
+ (lambda x: math.sin(math.pi*x) + x + 3 * math.pi) : 1,
94
+ (lambda x: x**3 + math.cos(x/2)) : 2,
95
+ (lambda x: random.uniform(1, 10) * math.sin(x)): 3
96
+ }
97
+
98
+ import importlib.util, os, ntpath, bpy, datetime, math, random, mathutils, random, sys, logging, string, colorsys, code, numpy
99
+ from subprocess import call
100
+
101
+ def load_file(file_path):
102
+ # load and define function and vars in global namespace, yolo
103
+ exec(open(file_path).read(), globals())
104
+
105
+ def load_module_path(file_path):
106
+ print("loading module " + file_path)
107
+ sys.path.append(os.path.dirname(os.path.expanduser(file_path)))
108
+
109
+ sys.path.append(os.environ['PYTHON_MODULES_PATH'])
110
+
111
+ # Create directory for renders
112
+ directory = os.path.dirname('./renders')
113
+ if not os.path.exists(directory):
114
+ os.makedirs(directory)
115
+
116
+ load_file(os.path.join(path + '/glitch3d/bpy/helpers.py'))
117
+ load_file(os.path.join(path + '/glitch3d/bpy/render_settings.py'))
118
+ load_file(os.path.join(path + '/glitch3d/bpy/lighting.py'))
119
+
120
+ # Create groups
121
+ for s in ['texts', 'lines', 'displays', 'reflectors', 'neons']:
122
+ bpy.data.groups.new(s)
123
+ for primitive in PRIMITIVES:
124
+ bpy.data.groups.new(primitive.lower().title())
125
+
126
+ FISHEYE = random.sample([True, False], 1)
127
+ COLORS = rand_color_palette(5)
128
+ CAMERA_OFFSET = 5
129
+ INITIAL_CAMERA_LOCATION = (CAMERA_OFFSET, CAMERA_OFFSET, random.uniform(0, 8))
130
+ TEXT_FILE_PATH = FIXTURES_FOLDER_PATH + 'texts/strings.txt'
131
+
132
+ print("Loading materials...")
133
+ MATERIALS_NAMES = []
134
+ load_osl_materials(FIXTURES_FOLDER_PATH + 'osl_shaders/')
135
+ for mat in bpy.data.materials: # merge base scene materials + osl shaders
136
+ if mat.name != 'emission':
137
+ MATERIALS_NAMES.append(mat.name)
138
+ print("Detected " + str(len(MATERIALS_NAMES)) + " materials in base scene: " + str(MATERIALS_NAMES))
139
+
140
+ # Scene
141
+ new_scene = bpy.data.scenes[SCENE_NAME]
142
+ bpy.context.screen.scene = new_scene
143
+ SCENE = new_scene
144
+ SCENE.render.engine = 'CYCLES'
145
+
146
+ flush_objects()
147
+
148
+ camera_data = bpy.data.cameras.new(name = 'CAMERA')
149
+ bpy.data.objects.new('CAMERA', object_data=camera_data)
150
+ CAMERA = bpy.data.objects['CAMERA']
151
+ new_scene.objects.link(CAMERA)
152
+ SCENE.camera = CAMERA
153
+ CAMERA.location = INITIAL_CAMERA_LOCATION
154
+ SCENE.frame_start = 0
155
+ SCENE.frame_end = NUMBER_OF_FRAMES
156
+
157
+ if FISHEYE:
158
+ CAMERA.data.type = 'PANO'
159
+ CAMERA.data.cycles.panorama_type = 'FISHEYE_EQUISOLID'
160
+ CAMERA.data.cycles.fisheye_lens = 12
161
+ CAMERA.data.cycles.fisheye_fov = 2.5
162
+ CAMERA.data.sensor_width = 20
163
+ CAMERA.data.sensor_height = 20
164
+
165
+ #####################################
166
+ #####################################
167
+ #####################################
168
+
169
+ # Load model
170
+ model_path = os.path.join(file)
171
+ bpy.ops.import_scene.obj(filepath = model_path, use_edges=True)
172
+ SUBJECT_NAME = '0_glitch3d_' + ntpath.basename(file).replace("_glitched.obj", '')
173
+ SUBJECT = bpy.data.objects[SUBJECT_NAME]
174
+ SUBJECT.select = True
175
+ center(SUBJECT)
176
+ SUBJECT.location = ORIGIN
177
+ let_there_be_light(SCENE)
178
+ random.shuffle(list(MODULES_ENABLED))
179
+
180
+ for module in MODULES_ENABLED:
181
+ load_module_path(os.path.join(path + '/glitch3d/bpy/canvas', module + '.py'))
182
+ mod = __import__(module)
183
+ new_canvas = eval("mod." + module[:1].upper() + module[1:] + "(locals())")
184
+ new_canvas.render()
185
+
186
+ render_settings(animate, mode, NORMALS_RENDERING, width, height, debug)
187
+ print('Rendering images with resolution: ' + str(SCENE.render.resolution_x) + ' x ' + str(SCENE.render.resolution_y))
188
+
189
+ if bpy.context.scene.rigidbody_world:
190
+ bpy.ops.rigidbody.bake_to_keyframes(frame_start=0, frame_end=NUMBER_OF_FRAMES)
191
+
192
+ CAMERA_PATH = camera_path(NUMBER_OF_FRAMES)
193
+
194
+ for frame in range(0, NUMBER_OF_FRAMES):
195
+ bpy.context.scene.frame_set(frame)
196
+ bpy.context.scene.camera.location = CAMERA_PATH[frame]
197
+ SUBJECT.rotation_euler.z += math.radians(1)
198
+ look_at(SUBJECT)
199
+ add_frame([bpy.context.scene.camera], ["location", "rotation_euler"])
200
+
201
+ if animate:
202
+ print('ANIMATION RENDERING BEGIN')
203
+ output_path = output_name(model_path)
204
+ print('AVI file -> ' + output_path)
205
+ shoot(output_path)
206
+ else:
207
+ print('STILL RENDERING BEGIN')
208
+ for index in range(0, shots_number):
209
+ frame_cursor = int(index * (bpy.context.scene.frame_end / shots_number))
210
+ print('>> FRAME #' + str(frame_cursor))
211
+ bpy.context.scene.frame_set(int(bpy.context.scene.frame_end/(index+1)))
212
+ SUBJECT.rotation_euler.z = math.radians(index * (360.0 / shots_number))
213
+ output_path = output_name(model_path, index)
214
+ print("-------------------------- " + str(index) + " --------------------------")
215
+ print("PNG file -> " + output_path)
216
+ shoot(output_path)
217
+
218
+ # Save scene as .blend file
219
+ bpy.ops.wm.save_as_mainfile(filepath=output_name(model_path) + '.blend')
220
+
221
+ print("Files rendered with " + str(NUMBER_OF_FRAMES) + " frames in simulation:")
222
+ for p in RENDER_OUTPUT_PATHS:
223
+ print(p)
224
+
225
+ if post_process:
226
+ load_file(os.path.join(path + '/glitch3d/bpy/post-processing/optimize.py'))
227
+ if shots_number > 1:
228
+ load_file(os.path.join(path + '/glitch3d/bpy/post-processing/average.py'))
229
+ if shots_number > 10:
230
+ load_file(os.path.join(path + '/glitch3d/bpy/post-processing/mosaic.py'))
231
+ load_file(os.path.join(path + '/glitch3d/bpy/post-processing/palette.py'))
232
+ print('FINISHED ¯\_(ツ)_/¯ with seed: ' + random.seed)
233
+ sys.exit(0)
234
+
235
+ except Exception as e:
236
+ if webhook:
237
+ requests.post(webhook, data={'error': str(e)})
238
+ if debug:
239
+ raise e # See error
240
+ sys.exit(1) # Just return 1 error code in production