glitch3d 0.1.0

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +55 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +10 -0
  11. data/bin/glitch3d +6 -0
  12. data/bin/setup +8 -0
  13. data/fixtures/cube.obj +18 -0
  14. data/fixtures/demo.png +0 -0
  15. data/fixtures/m4a1.obj +128741 -0
  16. data/fixtures/male_head.obj +22543 -0
  17. data/fixtures/skull.obj +34962 -0
  18. data/fixtures/textures/00.jpg +0 -0
  19. data/fixtures/textures/01.jpg +0 -0
  20. data/fixtures/textures/02.jpg +0 -0
  21. data/fixtures/textures/03.jpg +0 -0
  22. data/fixtures/textures/04.jpg +0 -0
  23. data/fixtures/textures/05.jpg +0 -0
  24. data/fixtures/textures/06.jpg +0 -0
  25. data/fixtures/textures/07.jpg +0 -0
  26. data/fixtures/textures/08.jpg +0 -0
  27. data/fixtures/textures/09.jpg +0 -0
  28. data/fixtures/textures/10.jpg +0 -0
  29. data/fixtures/textures/11.jpg +0 -0
  30. data/fixtures/textures/12.jpg +0 -0
  31. data/fixtures/textures/13.jpg +0 -0
  32. data/fixtures/textures/14.jpg +0 -0
  33. data/fixtures/textures/15.jpg +0 -0
  34. data/fixtures/textures/16.jpg +0 -0
  35. data/fixtures/textures/17.jpg +0 -0
  36. data/fixtures/textures/18.jpg +0 -0
  37. data/fixtures/textures/19.jpg +0 -0
  38. data/fixtures/textures/20.jpg +0 -0
  39. data/fixtures/textures/21.jpg +0 -0
  40. data/fixtures/textures/22.jpg +0 -0
  41. data/fixtures/textures/23.jpg +0 -0
  42. data/fixtures/textures/24.jpg +0 -0
  43. data/fixtures/textures/25.jpg +0 -0
  44. data/fixtures/textures/27.jpg +0 -0
  45. data/fixtures/textures/28.jpg +0 -0
  46. data/fixtures/textures/29.jpg +0 -0
  47. data/fixtures/textures/30.jpg +0 -0
  48. data/glitch3d.gemspec +34 -0
  49. data/lib/glitch3d/bpy/helpers.py +97 -0
  50. data/lib/glitch3d/bpy/rendering.py +159 -0
  51. data/lib/glitch3d/objects/face.rb +22 -0
  52. data/lib/glitch3d/objects/vertex.rb +55 -0
  53. data/lib/glitch3d/strategies/default.rb +17 -0
  54. data/lib/glitch3d/strategies/localized.rb +25 -0
  55. data/lib/glitch3d/version.rb +3 -0
  56. data/lib/glitch3d.rb +137 -0
  57. metadata +157 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/glitch3d.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'glitch3d/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "glitch3d"
8
+ spec.version = Glitch3d::VERSION
9
+ spec.authors = ["pskl"]
10
+ spec.email = ["hello@pascal.cc"]
11
+
12
+ spec.summary = %q{Alter 3D models and renders pictures.}
13
+ spec.description = %q{Glitch3D is a library designed to transform a 3D model randomly and render screenshots.}
14
+ spec.homepage = "http://pascal.cc"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "bin"
27
+ spec.executables = ["glitch3d"]
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.12"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency "byebug"
34
+ end
@@ -0,0 +1,97 @@
1
+ # Helper methods
2
+ def look_at(camera_object, point):
3
+ location_camera = camera_object.matrix_world.to_translation()
4
+ location_point = point.matrix_world.to_translation()
5
+ direction = location_point - location_camera
6
+ rot_quat = direction.to_track_quat('-Z', 'Y')
7
+ camera_object.rotation_euler = rot_quat.to_euler()
8
+
9
+ def empty_materials():
10
+ mats = bpy.data.materials
11
+ for mat in mats.keys():
12
+ mats.remove(mats[mat])
13
+
14
+ def shoot(camera, model_object, filepath):
15
+ look_at(camera, model_object)
16
+ print('Camera now at location: ' + camera_location_string(camera) + ' / rotation: ' + camera_rotation_string(camera))
17
+ bpy.context.scene.render.filepath = filepath
18
+ bpy.ops.render.render(write_still=True)
19
+
20
+ def output_name(index, model_path):
21
+ return 'renders/' + os.path.splitext(model_path)[0].split('/')[1] + '_' + str(index) + '_' + str(datetime.date.today()) + '.png'
22
+
23
+ def rotate(model_object, index):
24
+ model_object.rotation_euler[2] = math.radians(index * (360.0 / shots_number))
25
+
26
+ def rand_color_value():
27
+ return round(random.uniform(0.1, 1.0), 10)
28
+
29
+ def rand_location():
30
+ return (rand_location_value(), rand_location_value(), rand_location_value())
31
+
32
+ def rand_rotation():
33
+ return (rand_rotation_value(), rand_rotation_value(), rand_rotation_value())
34
+
35
+ def rand_rotation_value():
36
+ return round(random.uniform(0, 1), 10)
37
+
38
+ def rand_location_value():
39
+ return round(random.uniform(-8, 8), 10)
40
+
41
+ def rand_color_vector():
42
+ return (rand_color_value(), rand_color_value(), rand_color_value(), 1)
43
+
44
+ def rand_scale():
45
+ return round(random.uniform(0, 0.3), 10)
46
+
47
+ def get_args():
48
+ parser = argparse.ArgumentParser()
49
+
50
+ # get all script args
51
+ _, all_arguments = parser.parse_known_args()
52
+ double_dash_index = all_arguments.index('--')
53
+ script_args = all_arguments[double_dash_index + 1: ]
54
+
55
+ # add parser rules
56
+ parser.add_argument('-f', '--file', help="obj file to render")
57
+ parser.add_argument('-n', '--shots-number', help="number of shots desired")
58
+ parser.add_argument('-m', '--mode', help="quality mode: low | high")
59
+
60
+ parsed_script_args, _ = parser.parse_known_args(script_args)
61
+ return parsed_script_args
62
+
63
+ def camera_rotation_string(camera):
64
+ return str(int(camera.rotation_euler.x)) + ' ' + str(int(camera.rotation_euler.y)) + ' ' + str(int(camera.rotation_euler.z))
65
+
66
+ def camera_location_string(camera):
67
+ return str(int(camera.location.x)) + ' ' + str(int(camera.location.y)) + ' ' + str(int(camera.location.z))
68
+
69
+ def assign_material(model_object, material):
70
+ model_object.data.materials.append(material)
71
+
72
+ def assign_node_to_output(material, new_node):
73
+ assert material.use_nodes == True
74
+ output_node = material.node_tree.nodes['Material Output']
75
+ material.node_tree.links.new(new_node.outputs[0], output_node.inputs['Surface'])
76
+
77
+ def create_cycles_material():
78
+ material = bpy.data.materials.new('Object Material - ' + str(uuid.uuid1()))
79
+ material.use_nodes = True
80
+
81
+ nodes = material.node_tree.nodes
82
+ new_node = nodes.new(random.choice(SHADERS))
83
+
84
+ assign_node_to_output(material, new_node)
85
+ return material
86
+
87
+ def assign_random_texture_to_material(material):
88
+ assert material.use_nodes == True
89
+ bsdf_node = material.node_tree.nodes['Diffuse BSDF']
90
+ assign_node_to_output(material, bsdf_node)
91
+ texture_node = material.node_tree.nodes.new('ShaderNodeTexture')
92
+ material.node_tree.links.new(texture_node.outputs[0], bsdf_node.inputs[0])
93
+ # code.interact(local=dict(globals(), **locals()))
94
+ texture_path = os.path.expanduser('fixtures/textures/25.jpg')
95
+ new_texture = bpy.data.textures.new('ColorTex', type = 'IMAGE')
96
+ new_texture.image = bpy.data.images.load(texture_path)
97
+ texture_node.texture = new_texture
@@ -0,0 +1,159 @@
1
+ # Rendering script
2
+ # Run by calling the blender executable with -b -P <script_name>
3
+ # Disclaimer: I never did Python before so this is mostly hacks
4
+ #
5
+ # process:
6
+ # 1) Load model given as a parameter
7
+ # 2) Create emitting surfaces to act as lamps
8
+ # 3) Create camera
9
+ # 4) Rotate model and shoot image at each step
10
+ #
11
+ # Use `code.interact(local=dict(globals(), **locals()))` to pry into the script
12
+
13
+ import bpy
14
+ import os
15
+ import argparse
16
+ import datetime
17
+ import bmesh
18
+ import random
19
+ import code
20
+ import math
21
+ import mathutils
22
+ import random
23
+ import uuid
24
+
25
+ exec(open("lib/glitch3d/bpy/helpers.py").read())
26
+
27
+ # Arguments parsing
28
+ args = get_args()
29
+ file = args.file
30
+ mode = args.mode
31
+ shots_number = int(args.shots_number)
32
+
33
+ context = bpy.context
34
+
35
+ REFLECTOR_SCALE = 5
36
+ REFLECTOR_STRENGTH = 12
37
+ REFLECTOR_LOCATION_PADDING = 10
38
+ PROPS_NUMBER = 100
39
+ SHADERS = ['ShaderNodeBsdfGlossy', 'ShaderNodeBsdfDiffuse', 'ShaderNodeBsdfVelvet']
40
+
41
+ # Scene
42
+ new_scene = bpy.data.scenes.new("Automated Render Scene")
43
+ bpy.ops.scene.delete() # Delete old scene
44
+ context.screen.scene = new_scene # selects the new scene as the current one
45
+
46
+ # Render settings
47
+ context.scene.render.resolution_x = 1920
48
+ context.scene.render.resolution_y = 1080
49
+ context.scene.render.engine = 'CYCLES'
50
+ context.scene.render.resolution_percentage = 25
51
+ # bpy.context.scene.cycles.device = 'GPU'
52
+ context.scene.render.image_settings.compression = 0
53
+ context.scene.cycles.samples = 25
54
+ context.scene.render.image_settings.color_mode ='RGBA'
55
+ context.scene.render.image_settings.file_format='PNG'
56
+
57
+ if mode == 'high':
58
+ context.scene.render.image_settings.compression = 90
59
+ context.scene.cycles.samples = 500
60
+ context.scene.render.resolution_percentage = 100
61
+
62
+ # Load model
63
+ model_path = os.path.join(file)
64
+ bpy.ops.import_scene.obj(filepath = model_path, use_edges=True)
65
+ model_object = bpy.data.objects['Glitch3D']
66
+
67
+ # Use center of mass to center object
68
+ model_object.select = True
69
+ bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")
70
+ model_object.location = (0, 0, 0)
71
+
72
+ # --------------
73
+ # Create camera
74
+ # --------------
75
+ camera_data = bpy.data.cameras.new('Render Camera')
76
+ bpy.data.objects.new('Render Camera', object_data=camera_data)
77
+ camera_object = bpy.data.objects['Render Camera']
78
+ new_scene.objects.link(camera_object)
79
+ camera_object.location = (8, 8, 0)
80
+
81
+ # Add reflectors
82
+ bpy.ops.mesh.primitive_plane_add(location=(0,8 + REFLECTOR_LOCATION_PADDING, 0))
83
+ bpy.ops.mesh.primitive_plane_add(location=(8 + REFLECTOR_LOCATION_PADDING,0,0))
84
+
85
+ plane1 = bpy.data.objects['Plane']
86
+ plane2 = bpy.data.objects['Plane.001']
87
+
88
+ # Adjust camera
89
+ context.scene.camera = camera_object
90
+ look_at(camera_object, model_object)
91
+ assign_material(model_object, create_cycles_material())
92
+
93
+ for index, plane in enumerate([plane1, plane2]):
94
+ plane.scale = (REFLECTOR_SCALE, REFLECTOR_SCALE, REFLECTOR_SCALE)
95
+ plane.rotation_euler.x += math.radians(90)
96
+ emissive_material = bpy.data.materials.new('Emissive Material #' + str(index))
97
+ emissive_material.use_nodes = True
98
+ emission_node = emissive_material.node_tree.nodes.new('ShaderNodeEmission')
99
+ # Set color
100
+ emission_node.inputs[0].default_value = rand_color_vector()
101
+ # Set strength
102
+ emission_node.inputs[1].default_value = REFLECTOR_STRENGTH
103
+ assign_node_to_output(emissive_material, emission_node)
104
+ assign_material(plane, emissive_material)
105
+
106
+ # Tilt one of the reflectors
107
+ plane2.rotation_euler.z += math.radians(90)
108
+
109
+ # Add other elements
110
+ for index in range(0, int(PROPS_NUMBER)):
111
+ bpy.ops.mesh.primitive_cube_add(location=rand_location(),radius=rand_scale(), rotation=rand_rotation())
112
+ if index == 0:
113
+ object_name = 'Cube'
114
+ elif index > 9:
115
+ object_name = 'Cube.0' + str(index)
116
+ elif index > 99:
117
+ object_name = 'Cube.' + str(index)
118
+ else:
119
+ object_name = 'Cube.00' + str(index)
120
+ object = bpy.data.objects[object_name]
121
+ new_material = create_cycles_material()
122
+ assign_random_texture_to_material(new_material)
123
+ assign_material(object, new_material)
124
+
125
+ for index in range(0, int(PROPS_NUMBER)):
126
+ bpy.ops.mesh.primitive_circle_add(location=rand_location(),radius=rand_scale(), rotation=rand_rotation())
127
+ if index == 0:
128
+ object_name = 'Circle'
129
+ elif index > 9:
130
+ object_name = 'Circle.0' + str(index)
131
+ elif index > 99:
132
+ object_name = 'Circle.' + str(index)
133
+ else:
134
+ object_name = 'Circle.00' + str(index)
135
+ object = bpy.data.objects[object_name]
136
+ new_material = create_cycles_material()
137
+ assign_random_texture_to_material(new_material)
138
+ assign_material(object, new_material)
139
+
140
+ # Add background to world
141
+ world = bpy.data.worlds[0]
142
+ world.use_nodes = True
143
+ world_node_tree = world.node_tree
144
+ # code.interact(local=dict(globals(), **locals()))
145
+ gradient_node = world_node_tree.nodes.new(type="ShaderNodeTexGradient")
146
+ background_node = world_node_tree.nodes['Background']
147
+ world_node_tree.links.new(gradient_node.outputs['Color'], background_node.inputs['Color'])
148
+ gradient_node.gradient_type = 'EASING'
149
+
150
+ # ------
151
+ # Shoot
152
+ # ------
153
+ print('Rendering images with resolution: ' + str(context.scene.render.resolution_x) + ' x ' + str(context.scene.render.resolution_y))
154
+ for index in range(0, int(shots_number)):
155
+ print("-------------------------- " + str(index) + " --------------------------")
156
+ rotate(model_object, index)
157
+ shoot(camera_object, model_object, output_name(index, model_path))
158
+
159
+ print('FINISHED ¯\_(ツ)_/¯')
@@ -0,0 +1,22 @@
1
+ class Face
2
+ attr_accessor :v1, :v2, :v3
3
+
4
+ def initialize(v1, v2, v3)
5
+ @v1 = v1
6
+ @v2 = v2
7
+ @v3 = v3
8
+ end
9
+
10
+ def to_s
11
+ return nil unless !v1.nil? && !v2.nil? && !v3.nil?
12
+ "f #{v1.index} #{v2.index} #{v3.index}"
13
+ end
14
+
15
+ def rand_attr
16
+ [:v1, :v2, :v3].sample
17
+ end
18
+
19
+ def fuck(new_vertex)
20
+ send("#{rand_attr}=", new_vertex)
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ class Vertex
2
+ attr_accessor :x, :y, :z, :index
3
+
4
+ def initialize(x, y, z, index)
5
+ @x = x
6
+ @y = y
7
+ @z = z
8
+ @index = index
9
+ end
10
+
11
+ def to_s
12
+ "v #{x} #{y} #{z}"
13
+ end
14
+
15
+ def rand_attr
16
+ [:x, :y, :z].sample
17
+ end
18
+
19
+ def rescale(offset)
20
+ [:x, :y, :z].each do |attr|
21
+ value = send(attr)
22
+ send("#{attr}=", value * 0.8)
23
+ end
24
+ end
25
+
26
+ def fuck
27
+ attr = rand_attr
28
+ send("#{attr}=", send(attr) + Glitch3d.rand_vertex_glitch_offset)
29
+ end
30
+
31
+ def max
32
+ [@x.abs, @y.abs].max.round
33
+ end
34
+
35
+ def self.boundaries(vertices_list)
36
+ [
37
+ [vertices_list.max_by(&:x).x.ceil, vertices_list.min_by(&:x).x.round],
38
+ [vertices_list.max_by(&:y).y.ceil, vertices_list.min_by(&:y).y.round],
39
+ [vertices_list.max_by(&:z).z.ceil, vertices_list.min_by(&:z).z.round]
40
+ ]
41
+ end
42
+
43
+ def self.rescale(vertices, offset)
44
+ vertices.each do |v|
45
+ v.rescale(offset)
46
+ end
47
+ end
48
+
49
+ # Pass functions like :negative? or :positive?
50
+ def self.subset(x:, y:, z:, vertex_list:)
51
+ vertex_list.select do |vertex|
52
+ vertex.x.send(x) && vertex.y.send(y) && vertex.y.send(z)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,17 @@
1
+ module Glitch3d
2
+ module Default
3
+ def alter_vertices(vertices_objects_array)
4
+ (VERTEX_GLITCH_ITERATION_RATIO * vertices_objects_array.size).to_i.times do |_|
5
+ random_element(vertices_objects_array).fuck
6
+ end
7
+ vertices_objects_array
8
+ end
9
+
10
+ def alter_faces(faces_objects_array, vertex_objects_array)
11
+ (FACE_GLITCH_ITERATION_RATIO * faces_objects_array.count).to_i.times do |_|
12
+ random_element(faces_objects_array).fuck(random_element(vertex_objects_array))
13
+ end
14
+ faces_objects_array
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Glitch3d
2
+ module Localized
3
+ def alter_vertices(vertices_objects_array)
4
+ (VERTEX_GLITCH_ITERATION_RATIO * vertices_objects_array.size).to_i.times do |_|
5
+ random_element(target(vertices_objects_array)).fuck
6
+ end
7
+ vertices_objects_array
8
+ end
9
+
10
+ def alter_faces(faces_objects_array, vertices_objects_array)
11
+ (FACE_GLITCH_ITERATION_RATIO * faces_objects_array.count).to_i.times do |_|
12
+ random_element(faces_objects_array).fuck(random_element(target(vertices_objects_array)))
13
+ end
14
+ faces_objects_array
15
+ end
16
+
17
+ def selected_area(vertices_objects_array)
18
+ Vertex.subset(x: :negative?, y: :positive?, z: :zero?, vertex_list: vertices_objects_array)
19
+ end
20
+
21
+ def target(vertices_objects_array)
22
+ @target ||= selected_area(vertices_objects_array)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Glitch3d
2
+ VERSION = "0.1.0"
3
+ end
data/lib/glitch3d.rb ADDED
@@ -0,0 +1,137 @@
1
+ require "glitch3d/version"
2
+ require "glitch3d/objects/vertex"
3
+ require "glitch3d/objects/face"
4
+ require "glitch3d/strategies/default"
5
+ require "glitch3d/strategies/localized"
6
+ require "pry"
7
+
8
+ module Glitch3d
9
+ VERTEX_GLITCH_ITERATION_RATIO = 0.001
10
+ VERTEX_GLITCH_OFFSET = 1
11
+
12
+ FACE_GLITCH_ITERATION_RATIO = 0.0
13
+ FACE_GLITCH_OFFSET = 0.5
14
+ BOUNDARY_LIMIT = 4 # Contain model within 2x2x2 cube
15
+
16
+ BLENDER_EXECUTABLE_PATH = ENV['BLENDER_EXECUTABLE_PATH'].freeze
17
+ RENDERING_SCRIPT_PATH = "lib/glitch3d/bpy/rendering.py".freeze
18
+
19
+ def process_model(source_file)
20
+ raise 'Set Blender executable path in your env variables before using glitch3d' unless BLENDER_EXECUTABLE_PATH.present?
21
+ args = Hash[ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/)]
22
+ self.class.include infer_strategy(args["mode"] || 'default')
23
+ @quality = args["quality"] || 'low'
24
+ source_file = source_file
25
+ base_file_name = source_file.gsub(/.obj/, '')
26
+ target_file = base_file_name + '_glitched.obj'
27
+ boundaries = create_glitched_file(glitch(read_source(source_file)), target_file)
28
+ render(target_file, boundaries, args["shots-number"] || 6) unless args["no-render"]
29
+ end
30
+
31
+ def infer_strategy(mode)
32
+ return Glitch3d::Default if mode.nil?
33
+ {
34
+ default: Glitch3d::Default,
35
+ localized: Glitch3d::Localized
36
+ }[mode.to_sym]
37
+ end
38
+
39
+ class << self
40
+ def rand_vertex_glitch_offset
41
+ rand(-VERTEX_GLITCH_OFFSET..VERTEX_GLITCH_OFFSET)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def read_source(path)
48
+ File.open(path, 'r') do |f|
49
+ source_file_content = f.readlines
50
+ vertices_list = build_vertices(source_file_content.select { |s| s[0..1] == 'v ' })
51
+ faces_list = build_faces(source_file_content.select { |s| s[0..1] == 'f ' }, vertices_list)
52
+ {
53
+ vertices: vertices_list,
54
+ faces: faces_list
55
+ }
56
+ end
57
+ end
58
+
59
+ def build_vertices(vertices_string_array)
60
+ vertices_list = []
61
+ vertices_string_array.each_with_index.map do |sv, i|
62
+ v = sv.split(' ')
63
+ vertices_list << Vertex.new(v[1].to_f, v[2].to_f, v[3].to_f, i)
64
+ end
65
+ vertices_list
66
+ end
67
+
68
+ def build_faces(faces_string_array, vertices_list)
69
+ faces_list = []
70
+ faces_string_array.map do |sf|
71
+ f = sf.split(' ')
72
+ next if f.length <= 3
73
+ faces_list << Face.new(
74
+ vertices_list[f[1].to_i],
75
+ vertices_list[f[2].to_i],
76
+ vertices_list[f[3].to_i]
77
+ )
78
+ end
79
+ faces_list
80
+ end
81
+
82
+ def glitch(file_hash_content)
83
+ {
84
+ vertices: alter_vertices(file_hash_content[:vertices]),
85
+ faces: alter_faces(file_hash_content[:faces], file_hash_content[:vertices])
86
+ }
87
+ end
88
+
89
+ def random_element(array)
90
+ array[rand(0..array.size - 1)]
91
+ end
92
+
93
+ def create_glitched_file(content_hash, target_file)
94
+ boundaries = Vertex.boundaries(content_hash[:vertices])
95
+ puts boundaries.to_s
96
+ while rescale_needed?(boundaries)
97
+ content_hash[:vertices] = Vertex.rescale(content_hash[:vertices], (boundaries.flatten.map(&:abs).max.abs - BOUNDARY_LIMIT).abs)
98
+ boundaries = Vertex.boundaries(content_hash[:vertices])
99
+ end
100
+ boundaries = Vertex.boundaries(content_hash[:vertices])
101
+ puts boundaries.to_s
102
+ File.open(target_file, 'w') do |f|
103
+ f.puts '# Data corrupted with glitch3D script'
104
+ f.puts '# Boundaries: ' + boundaries.to_s
105
+ f.puts ''
106
+ f.puts 'g Glitch3D'
107
+ f.puts ''
108
+ f.puts content_hash[:vertices].map(&:to_s)
109
+ f.puts ''
110
+ f.puts content_hash[:faces].map(&:to_s).compact
111
+ end
112
+ boundaries
113
+ end
114
+
115
+ def rescale_needed?(boundaries)
116
+ boundaries.flatten.map(&:abs).max.abs > BOUNDARY_LIMIT
117
+ end
118
+
119
+ def render(file_path, boundaries, shots_number)
120
+ args = [
121
+ BLENDER_EXECUTABLE_PATH,
122
+ '-b',
123
+ '-P',
124
+ RENDERING_SCRIPT_PATH,
125
+ '--',
126
+ '-f',
127
+ file_path,
128
+ '-n',
129
+ shots_number.to_s,
130
+ '-m',
131
+ @quality
132
+ ]
133
+ unless system(*args)
134
+ fail 'Make sure Blender is correctly installed'
135
+ end
136
+ end
137
+ end