Cleanup: quiet strict name warnings for addons a..h.
[blender-addons.git] / add_advanced_objects_menu / cubester.py
blobf1a8f5817804a39e9023464c2df1e3ecb55ab337
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # Original Author = Jacob Morris
20 # URL = blendingjacob.blogspot.com
22 # Note: scene properties are moved into __init__ together with the 3 update functions
23 # for properties search for the name patterns adv_obj and advanced_objects
25 bl_info = {
26 "name": "CubeSter",
27 "author": "Jacob Morris",
28 "version": (0, 7, 1),
29 "blender": (2, 78, 0),
30 "location": "View 3D > Toolbar > CubeSter",
31 "description": "Takes image, image sequence, or audio file and converts it "
32 "into a height map based on pixel color and alpha values",
33 "category": "Add Mesh"
36 import bpy
37 import bmesh
38 from bpy.types import (
39 Operator,
40 Panel,
43 import timeit
44 from random import uniform
45 from math import radians
46 from os import (
47 path,
48 listdir,
52 # create block at center position x, y with block width 2 * hx and 2 * hy and height of h
53 def create_block(x, y, hw, h, verts: list, faces: list):
54 if bpy.context.scene.advanced_objects.cubester_block_style == "size":
55 z = 0.0
56 else:
57 z = h
58 h = 2 * hw
60 p = len(verts)
61 verts += [(x - hw, y - hw, z), (x + hw, y - hw, z), (x + hw, y + hw, z), (x - hw, y + hw, z)]
62 verts += [(x - hw, y - hw, z + h), (x + hw, y - hw, z + h),
63 (x + hw, y + hw, z + h), (x - hw, y + hw, z + h)]
65 faces += [(p, p + 1, p + 5, p + 4), (p + 1, p + 2, p + 6, p + 5),
66 (p + 2, p + 3, p + 7, p + 6), (p, p + 4, p + 7, p + 3),
67 (p + 4, p + 5, p + 6, p + 7), (p, p + 3, p + 2, p + 1)]
70 # go through all frames in len(frames), adjusting values at frames[x][y]
71 def create_f_curves(mesh, frames, frame_step_size, style):
72 # use data to animate mesh
73 action = bpy.data.actions.new("CubeSterAnimation")
75 mesh.animation_data_create()
76 mesh.animation_data.action = action
78 data_path = "vertices[%d].co"
80 vert_index = 4 if style == "blocks" else 0 # index of first vertex
82 # loop for every face height value
83 for frame_start_vert in range(len(frames[0])):
84 # only go once if plane, otherwise do all four vertices that are in top plane if blocks
85 end_point = frame_start_vert + 4 if style == "blocks" else frame_start_vert + 1
87 # loop through to get the four vertices that compose the face
88 for frame_vert in range(frame_start_vert, end_point):
89 # fcurves for x, y, z
90 fcurves = [action.fcurves.new(data_path % vert_index, i) for i in range(3)]
91 frame_counter = 0 # go through each frame and add position
92 temp_v = mesh.vertices[vert_index].co
94 # loop through frames
95 for frame in frames:
96 # new x, y, z positions
97 vals = [temp_v[0], temp_v[1], frame[frame_start_vert]]
98 for i in range(3): # for each x, y, z set each corresponding fcurve
99 fcurves[i].keyframe_points.insert(frame_counter, vals[i], {'FAST'})
101 frame_counter += frame_step_size # skip frames for smoother animation
103 vert_index += 1
105 # only skip vertices if made of blocks
106 if style == "blocks":
107 vert_index += 4
110 # create material with given name, apply to object
111 def create_material(scene, ob, name):
112 mat = bpy.data.materials.new("CubeSter_" + name)
113 adv_obj = scene.advanced_objects
114 image = None
116 # image
117 if not adv_obj.cubester_use_image_color and adv_obj.cubester_color_image in bpy.data.images:
118 try:
119 image = bpy.data.images[adv_obj.cubester_color_image]
120 except:
121 pass
122 else:
123 try:
124 image = bpy.data.images[adv_obj.cubester_image]
125 except:
126 pass
128 if scene.render.engine == "CYCLES":
129 mat.use_nodes = True
130 nodes = mat.node_tree.nodes
132 att = nodes.new("ShaderNodeAttribute")
133 att.attribute_name = "Col"
134 att.location = (-200, 300)
136 att = nodes.new("ShaderNodeTexImage")
137 if image:
138 att.image = image
140 if adv_obj.cubester_load_type == "multiple":
141 att.image.source = "SEQUENCE"
142 att.location = (-200, 700)
144 att = nodes.new("ShaderNodeTexCoord")
145 att.location = (-450, 600)
147 if adv_obj.cubester_materials == "image":
148 mat.node_tree.links.new(
149 nodes["Image Texture"].outputs[0],
150 nodes["Diffuse BSDF"].inputs[0]
152 mat.node_tree.links.new(
153 nodes["Texture Coordinate"].outputs[2],
154 nodes["Image Texture"].inputs[0]
156 else:
157 mat.node_tree.links.new(
158 nodes["Attribute"].outputs[0],
159 nodes["Diffuse BSDF"].inputs[0]
161 else:
162 if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER":
163 tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE")
164 if image:
165 tex.image = image
166 slot = mat.texture_slots.add()
167 slot.texture = tex
168 else:
169 mat.use_vertex_color_paint = True
171 ob.data.materials.append(mat)
174 # generate mesh from audio
175 def create_mesh_from_audio(self, scene, verts, faces):
176 adv_obj = scene.advanced_objects
177 audio_filepath = adv_obj.cubester_audio_path
178 width = adv_obj.cubester_audio_width_blocks
179 length = adv_obj.cubester_audio_length_blocks
180 size_per_hundred = adv_obj.cubester_size_per_hundred_pixels
182 size = size_per_hundred / 100
184 # create all blocks
185 y = -(width / 2) * size + (size / 2)
186 for r in range(width):
187 x = -(length / 2) * size + (size / 2)
188 for c in range(length):
189 create_block(x, y, size / 2, 1, verts, faces)
191 x += size
192 y += size
194 # create object
195 mesh = bpy.data.meshes.new("cubed")
196 mesh.from_pydata(verts, [], faces)
197 ob = bpy.data.objects.new("cubed", mesh)
198 bpy.context.scene.objects.link(ob)
199 bpy.context.scene.objects.active = ob
200 ob.select = True
202 # inital vertex colors
203 if adv_obj.cubester_materials == "image" and adv_obj.cubester_color_image != "":
204 picture = bpy.data.images[adv_obj.cubester_color_image]
205 pixels = list(picture.pixels)
206 vert_colors = []
208 skip_y = int(picture.size[1] / width)
209 skip_x = int(picture.size[0] / length)
211 for row in range(0, picture.size[1], skip_y + 1):
212 # go through each column, step by appropriate amount
213 for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
214 r, g, b, a = get_pixel_values(picture, pixels, row, column)
215 vert_colors += [(r, g, b) for i in range(24)]
217 bpy.ops.mesh.vertex_color_add()
219 i = 0
220 vert_colors_size = len(vert_colors)
221 for c in ob.data.vertex_colors[0].data:
222 if i < vert_colors_size:
223 c.color = vert_colors[i]
224 i += 1
226 # image sequence handling
227 if adv_obj.cubester_load_type == "multiple":
228 images = find_sequence_images(self, bpy.context)
230 frames_vert_colors = []
232 max_images = adv_obj.cubester_max_images + 1 if \
233 len(images[0]) > adv_obj.cubester_max_images else len(images[0])
235 # goes through and for each image for each block finds new height
236 for image_index in range(0, max_images, adv_obj.cubester_skip_images):
237 filepath = images[0][image_index]
238 name = images[1][image_index]
239 picture = fetch_image(self, name, filepath)
240 pixels = list(picture.pixels)
242 frame_colors = []
244 for row in range(0, picture.size[1], skip_y + 1):
245 for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
246 r, g, b, a = get_pixel_values(picture, pixels, row, column)
247 frame_colors += [(r, g, b) for i in range(24)]
249 frames_vert_colors.append(frame_colors)
251 adv_obj.cubester_vertex_colors[ob.name] = \
252 {"type": "vertex", "frames": frames_vert_colors,
253 "frame_skip": adv_obj.cubester_frame_step,
254 "total_images": max_images}
256 # either add material or create
257 if ("CubeSter_" + "Vertex") in bpy.data.materials:
258 ob.data.materials.append(bpy.data.materials["CubeSter_" + "Vertex"])
259 else:
260 create_material(scene, ob, "Vertex")
262 # set keyframe for each object as initial point
263 frame = [1 for i in range(int(len(verts) / 8))]
264 frames = [frame]
266 area = bpy.context.area
267 old_type = area.type
268 area.type = "GRAPH_EDITOR"
270 scene.frame_current = 0
272 create_f_curves(mesh, frames, 1, "blocks")
274 # deselect all fcurves
275 fcurves = ob.data.animation_data.action.fcurves.data.fcurves
276 for i in fcurves:
277 i.select = False
279 max_images = adv_obj.cubester_audio_max_freq
280 min_freq = adv_obj.cubester_audio_min_freq
281 freq_frame = adv_obj.cubester_audio_offset_type
283 freq_step = (max_images - min_freq) / length
284 freq_sub_step = freq_step / width
286 frame_step = adv_obj.cubester_audio_frame_offset
288 # animate each block with a portion of the frequency
289 for c in range(length):
290 frame_off = 0
291 for r in range(width):
292 if freq_frame == "frame":
293 scene.frame_current = frame_off
294 l = c * freq_step
295 h = (c + 1) * freq_step
296 frame_off += frame_step
297 else:
298 l = c * freq_step + (r * freq_sub_step)
299 h = c * freq_step + ((r + 1) * freq_sub_step)
301 pos = c + (r * length) # block number
302 index = pos * 4 # first index for vertex
304 # select curves
305 for i in range(index, index + 4):
306 curve = i * 3 + 2 # fcurve location
307 fcurves[curve].select = True
308 try:
309 bpy.ops.graph.sound_bake(filepath=bpy.path.abspath(audio_filepath), low=l, high=h)
310 except:
311 pass
313 # deselect curves
314 for i in range(index, index + 4):
315 curve = i * 3 + 2 # fcurve location
316 fcurves[curve].select = False
318 area.type = old_type
320 # UV unwrap
321 create_uv_map(bpy.context, width, length)
323 # if radial apply needed modifiers
324 if adv_obj.cubester_audio_block_layout == "radial":
325 # add bezier curve of correct width
326 bpy.ops.curve.primitive_bezier_circle_add()
327 curve = bpy.context.object
328 # slope determined off of collected data
329 curve_size = (0.319 * (width * (size * 100)) - 0.0169) / 100
330 curve.dimensions = (curve_size, curve_size, 0.0)
331 # correct for z height
332 curve.scale = (curve.scale[0], curve.scale[0], curve.scale[0])
334 ob.select = True
335 curve.select = False
336 scene.objects.active = ob
338 # data was collected and then multi-variable regression was done in Excel
339 # influence of width and length
340 width_infl, length_infl, intercept = -0.159125, 0.49996, 0.007637
341 x_offset = ((width * (size * 100) * width_infl) +
342 (length * (size * 100) * length_infl) + intercept) / 100
343 ob.location = (ob.location[0] + x_offset, ob.location[1], ob.location[2])
345 ob.rotation_euler = (radians(-90), 0.0, 0.0)
346 bpy.ops.object.modifier_add(type="CURVE")
347 ob.modifiers["Curve"].object = curve
348 ob.modifiers["Curve"].deform_axis = "POS_Z"
351 # generate mesh from image(s)
352 def create_mesh_from_image(self, scene, verts, faces):
353 context = bpy.context
354 adv_obj = scene.advanced_objects
355 picture = bpy.data.images[adv_obj.cubester_image]
356 pixels = list(picture.pixels)
358 x_pixels = picture.size[0] / (adv_obj.cubester_skip_pixels + 1)
359 y_pixels = picture.size[1] / (adv_obj.cubester_skip_pixels + 1)
361 width = x_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels
362 height = y_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels
364 step = width / x_pixels
365 half_width = step / 2
367 y = -height / 2 + half_width
369 vert_colors = []
370 weights = [uniform(0.0, 1.0) for i in range(4)] # random weights
371 rows = 0
373 # go through each row of pixels stepping by adv_obj.cubester_skip_pixels + 1
374 for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1):
375 rows += 1
376 x = -width / 2 + half_width # reset to left edge of mesh
377 # go through each column, step by appropriate amount
378 for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
379 r, g, b, a = get_pixel_values(picture, pixels, row, column)
380 h = find_point_height(r, g, b, a, scene)
382 # if not transparent
383 if h != -1:
384 if adv_obj.cubester_mesh_style == "blocks":
385 create_block(x, y, half_width, h, verts, faces)
386 vert_colors += [(r, g, b) for i in range(24)]
387 else:
388 verts += [(x, y, h)]
389 vert_colors += [(r, g, b) for i in range(4)]
391 x += step
392 y += step
394 # if plane not blocks, then remove last 4 items from vertex_colors
395 # as the faces have already wrapped around
396 if adv_obj.cubester_mesh_style == "plane":
397 del vert_colors[len(vert_colors) - 4:len(vert_colors)]
399 # create faces if plane based and not block based
400 if adv_obj.cubester_mesh_style == "plane":
401 off = int(len(verts) / rows)
402 for r in range(rows - 1):
403 for c in range(off - 1):
404 faces += [(r * off + c, r * off + c + 1, (r + 1) * off + c + 1, (r + 1) * off + c)]
406 mesh = bpy.data.meshes.new("cubed")
407 mesh.from_pydata(verts, [], faces)
408 ob = bpy.data.objects.new("cubed", mesh)
409 context.scene.objects.link(ob)
410 context.scene.objects.active = ob
411 ob.select = True
413 # uv unwrap
414 if adv_obj.cubester_mesh_style == "blocks":
415 create_uv_map(context, rows, int(len(faces) / 6 / rows))
416 else:
417 create_uv_map(context, rows - 1, int(len(faces) / (rows - 1)))
419 # material
420 # determine name and if already created
421 if adv_obj.cubester_materials == "vertex": # vertex color
422 image_name = "Vertex"
423 elif not adv_obj.cubester_use_image_color and \
424 adv_obj.cubester_color_image in bpy.data.images and \
425 adv_obj.cubester_materials == "image": # replaced image
426 image_name = adv_obj.cubester_color_image
427 else: # normal image
428 image_name = adv_obj.cubester_image
430 # either add material or create
431 if ("CubeSter_" + image_name) in bpy.data.materials:
432 ob.data.materials.append(bpy.data.materials["CubeSter_" + image_name])
434 # create material
435 else:
436 create_material(scene, ob, image_name)
438 # vertex colors
439 bpy.ops.mesh.vertex_color_add()
440 i = 0
441 for c in ob.data.vertex_colors[0].data:
442 c.color = vert_colors[i]
443 i += 1
445 frames = []
446 # image sequence handling
447 if adv_obj.cubester_load_type == "multiple":
448 images = find_sequence_images(self, context)
449 frames_vert_colors = []
451 max_images = adv_obj.cubester_max_images + 1 if \
452 len(images[0]) > adv_obj.cubester_max_images else len(images[0])
454 # goes through and for each image for each block finds new height
455 for image_index in range(0, max_images, adv_obj.cubester_skip_images):
456 filepath = images[0][image_index]
457 name = images[1][image_index]
458 picture = fetch_image(self, name, filepath)
459 pixels = list(picture.pixels)
461 frame_heights = []
462 frame_colors = []
464 for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1):
465 for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
466 r, g, b, a = get_pixel_values(picture, pixels, row, column)
467 h = find_point_height(r, g, b, a, scene)
469 if h != -1:
470 frame_heights.append(h)
471 if adv_obj.cubester_mesh_style == "blocks":
472 frame_colors += [(r, g, b) for i in range(24)]
473 else:
474 frame_colors += [(r, g, b) for i in range(4)]
476 if adv_obj.cubester_mesh_style == "plane":
477 del vert_colors[len(vert_colors) - 4:len(vert_colors)]
479 frames.append(frame_heights)
480 frames_vert_colors.append(frame_colors)
482 # determine what data to use
483 if adv_obj.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE":
484 adv_obj.cubester_vertex_colors[ob.name] = {
485 "type": "vertex", "frames": frames_vert_colors,
486 "frame_skip": adv_obj.cubester_frame_step,
487 "total_images": max_images
489 else:
490 adv_obj.cubester_vertex_colors[ob.name] = {
491 "type": "image", "frame_skip": scene.cubester_frame_step,
492 "total_images": max_images
494 att = get_image_node(ob.data.materials[0])
495 att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step
497 # animate mesh
498 create_f_curves(
499 mesh, frames,
500 adv_obj.cubester_frame_step,
501 adv_obj.cubester_mesh_style
505 # generate uv map for object
506 def create_uv_map(context, rows, columns):
507 adv_obj = context.scene.advanced_objects
508 mesh = context.object.data
509 mesh.uv_textures.new("cubester")
510 bm = bmesh.new()
511 bm.from_mesh(mesh)
513 uv_layer = bm.loops.layers.uv[0]
514 bm.faces.ensure_lookup_table()
516 x_scale = 1 / columns
517 y_scale = 1 / rows
519 y_pos = 0.0
520 x_pos = 0.0
521 count = columns - 1 # hold current count to compare to if need to go to next row
523 # if blocks
524 if adv_obj.cubester_mesh_style == "blocks":
525 for fa in range(int(len(bm.faces) / 6)):
526 for i in range(6):
527 pos = (fa * 6) + i
528 bm.faces[pos].loops[0][uv_layer].uv = (x_pos, y_pos)
529 bm.faces[pos].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos)
530 bm.faces[pos].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale)
531 bm.faces[pos].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale)
533 x_pos += x_scale
535 if fa >= count:
536 y_pos += y_scale
537 x_pos = 0.0
538 count += columns
540 # if planes
541 else:
542 for fa in range(len(bm.faces)):
543 bm.faces[fa].loops[0][uv_layer].uv = (x_pos, y_pos)
544 bm.faces[fa].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos)
545 bm.faces[fa].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale)
546 bm.faces[fa].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale)
548 x_pos += x_scale
550 if fa >= count:
551 y_pos += y_scale
552 x_pos = 0.0
553 count += columns
555 bm.to_mesh(mesh)
558 # if already loaded return image, else load and return
559 def fetch_image(self, name, load_path):
560 if name in bpy.data.images:
561 return bpy.data.images[name]
562 else:
563 try:
564 image = bpy.data.images.load(load_path)
565 return image
566 except RuntimeError:
567 self.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(load_path))
568 return None
571 # find height for point
572 def find_point_height(r, g, b, a, scene):
573 adv_obj = scene.advanced_objects
574 if a: # if not completely transparent
575 normalize = 1
577 # channel weighting
578 if not adv_obj.cubester_advanced:
579 composed = 0.25 * r + 0.25 * g + 0.25 * b + 0.25 * a
580 else:
581 # user defined weighting
582 if not adv_obj.cubester_random_weights:
583 composed = adv_obj.cubester_weight_r * r + adv_obj.cubester_weight_g * g + \
584 adv_obj.cubester_weight_b * b + adv_obj.cubester_weight_a * a
585 total = adv_obj.cubester_weight_r + adv_obj.cubester_weight_g + adv_obj.cubester_weight_b + \
586 adv_obj.cubester_weight_a
588 normalize = 1 / total
589 # random weighting
590 else:
591 weights = [uniform(0.0, 1.0) for i in range(4)]
592 composed = weights[0] * r + weights[1] * g + weights[2] * b + weights[3] * a
593 total = weights[0] + weights[1] + weights[2] + weights[3]
594 normalize = 1 / total
596 if adv_obj.cubester_invert:
597 h = (1 - composed) * adv_obj.cubester_height_scale * normalize
598 else:
599 h = composed * adv_obj.cubester_height_scale * normalize
601 return h
602 else:
603 return -1
606 # find all images that would belong to sequence
607 def find_sequence_images(self, context):
608 scene = context.scene
609 images = [[], []]
611 if scene.advanced_objects.cubester_image in bpy.data.images:
612 image = bpy.data.images[scene.advanced_objects.cubester_image]
613 main = image.name.split(".")[0]
615 # first part of name to check against other files
616 length = len(main)
617 keep_going = True
618 for i in range(length - 1, -1, -1):
619 if main[i].isdigit() and keep_going:
620 length -= 1
621 else:
622 keep_going = not keep_going
623 name = main[0:length]
625 dir_name = path.dirname(bpy.path.abspath(image.filepath))
627 try:
628 for file in listdir(dir_name):
629 if path.isfile(path.join(dir_name, file)) and file.startswith(name):
630 images[0].append(path.join(dir_name, file))
631 images[1].append(file)
632 except FileNotFoundError:
633 self.report({"ERROR"}, "CubeSter: '{}' directory not found".format(dir_name))
635 return images
638 # get image node
639 def get_image_node(mat):
640 nodes = mat.node_tree.nodes
641 att = nodes["Image Texture"]
643 return att
646 # get the RGBA values from pixel
647 def get_pixel_values(picture, pixels, row, column):
648 # determine i position to start at based on row and column position
649 i = (row * picture.size[0] * 4) + column
650 pixs = pixels[i: i + 4]
651 r = pixs[0]
652 g = pixs[1]
653 b = pixs[2]
654 a = pixs[3]
656 return r, g, b, a
659 # frame change handler for materials
660 def material_frame_handler(scene):
661 frame = scene.frame_current
662 adv_obj = scene.advanced_objects
664 keys = list(adv_obj.cubester_vertex_colors.keys())
666 # get keys and see if object is still in scene
667 for i in keys:
668 # if object is in scene then update information
669 if i in bpy.data.objects:
670 ob = bpy.data.objects[i]
671 data = adv_obj.advanced_objects.cubester_vertex_colors[ob.name]
672 skip_frames = data["frame_skip"]
674 # update materials using vertex colors
675 if data['type'] == "vertex":
676 colors = data["frames"]
678 if frame % skip_frames == 0 and 0 <= frame < (data['total_images'] - 1) * skip_frames:
679 use_frame = int(frame / skip_frames)
680 color = colors[use_frame]
682 i = 0
683 for c in ob.data.vertex_colors[0].data:
684 c.color = color[i]
685 i += 1
687 else:
688 att = get_image_node(ob.data.materials[0])
689 offset = frame - int(frame / skip_frames)
690 att.image_user.frame_offset = -offset
692 # if the object is no longer in the scene then delete then entry
693 else:
694 del adv_obj.advanced_objects.cubester_vertex_colors[i]
697 class CubeSterPanel(Panel):
698 bl_idname = "OBJECT_PT_cubester"
699 bl_label = "CubeSter"
700 bl_space_type = "VIEW_3D"
701 bl_region_type = "TOOLS"
702 bl_category = "Create"
703 bl_options = {"DEFAULT_CLOSED"}
704 bl_context = "objectmode"
706 def draw(self, context):
707 layout = self.layout.box()
708 scene = bpy.context.scene
709 adv_obj = scene.advanced_objects
710 images_found = 0
711 rows = 0
712 columns = 0
714 layout.prop(adv_obj, "cubester_audio_image")
716 if adv_obj.cubester_audio_image == "image":
717 box = layout.box()
718 box.prop(adv_obj, "cubester_load_type")
719 box.label("Image To Convert:")
720 box.prop_search(adv_obj, "cubester_image", bpy.data, "images")
721 box.prop(adv_obj, "cubester_load_image")
723 # find number of approriate images if sequence
724 if adv_obj.cubester_load_type == "multiple":
725 box = layout.box()
726 # display number of images found there
727 images = find_sequence_images(self, context)
728 images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \
729 else adv_obj.cubester_max_images
731 if len(images[0]):
732 box.label(str(len(images[0])) + " Images Found", icon="PACKAGE")
734 box.prop(adv_obj, "cubester_max_images")
735 box.prop(adv_obj, "cubester_skip_images")
736 box.prop(adv_obj, "cubester_frame_step")
738 box = layout.box()
739 col = box.column(align=True)
740 col.prop(adv_obj, "cubester_skip_pixels")
741 col.prop(adv_obj, "cubester_size_per_hundred_pixels")
742 col.prop(adv_obj, "cubester_height_scale")
743 box.prop(adv_obj, "cubester_invert", icon="FILE_REFRESH")
745 box = layout.box()
746 box.prop(adv_obj, "cubester_mesh_style", icon="MESH_GRID")
748 if adv_obj.cubester_mesh_style == "blocks":
749 box.prop(adv_obj, "cubester_block_style")
750 else:
751 # audio file
752 layout.prop(adv_obj, "cubester_audio_path")
754 box = layout.box()
755 col = box.column(align=True)
756 col.prop(adv_obj, "cubester_audio_min_freq")
757 col.prop(adv_obj, "cubester_audio_max_freq")
759 box.separator()
760 box.prop(adv_obj, "cubester_audio_offset_type")
762 if adv_obj.cubester_audio_offset_type == "frame":
763 box.prop(adv_obj, "cubester_audio_frame_offset")
764 box.prop(adv_obj, "cubester_audio_block_layout")
765 box.separator()
767 col = box.column(align=True)
768 col.prop(adv_obj, "cubester_audio_width_blocks")
769 col.prop(adv_obj, "cubester_audio_length_blocks")
771 rows = adv_obj.cubester_audio_width_blocks
772 columns = adv_obj.cubester_audio_length_blocks
774 col.prop(adv_obj, "cubester_size_per_hundred_pixels")
776 # materials
777 box = layout.box()
778 box.prop(adv_obj, "cubester_materials", icon="MATERIAL")
780 if adv_obj.cubester_materials == "image":
781 box.prop(adv_obj, "cubester_load_type")
783 # find number of approriate images if sequence
784 if adv_obj.cubester_load_type == "multiple":
785 # display number of images found there
786 images = find_sequence_images(self, context)
787 images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \
788 else adv_obj.cubester_max_images
790 if len(images[0]):
791 box.label(str(len(images[0])) + " Images Found", icon="PACKAGE")
792 box.prop(adv_obj, "cubester_max_images")
793 box.prop(adv_obj, "cubester_skip_images")
794 box.prop(adv_obj, "cubester_frame_step")
796 box.separator()
798 if adv_obj.cubester_audio_image == "image":
799 box.prop(adv_obj, "cubester_use_image_color", icon="COLOR")
801 if not adv_obj.cubester_use_image_color or adv_obj.cubester_audio_image == "audio":
802 box.label("Image To Use For Colors:")
803 box.prop_search(adv_obj, "cubester_color_image", bpy.data, "images")
804 box.prop(adv_obj, "cubester_load_color_image")
806 if adv_obj.cubester_image in bpy.data.images:
807 rows = int(bpy.data.images[adv_obj.cubester_image].size[1] /
808 (adv_obj.cubester_skip_pixels + 1))
809 columns = int(bpy.data.images[adv_obj.cubester_image].size[0] /
810 (adv_obj.cubester_skip_pixels + 1))
812 box = layout.box()
814 if adv_obj.cubester_mesh_style == "blocks":
815 box.label("Approximate Cube Count: " + str(rows * columns))
816 box.label("Expected Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6))
817 else:
818 box.label("Approximate Point Count: " + str(rows * columns))
819 box.label("Expected Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1)))
821 # blocks and plane generation time values
822 if adv_obj.cubester_mesh_style == "blocks":
823 slope = 0.0000876958
824 intercept = 0.02501
825 block_infl, frame_infl, intercept2 = 0.0025934, 0.38507, -0.5840189
826 else:
827 slope = 0.000017753
828 intercept = 0.04201
829 block_infl, frame_infl, intercept2 = 0.000619, 0.344636, -0.272759
831 # if creating image based mesh
832 points = rows * columns
833 if adv_obj.cubester_audio_image == "image":
834 if adv_obj.cubester_load_type == "single":
835 time = rows * columns * slope + intercept # approximate time count for mesh
836 else:
837 time = (points * slope) + intercept + (points * block_infl) + \
838 (images_found / adv_obj.cubester_skip_images * frame_infl) + intercept2
840 box.label("Images To Be Used: " + str(int(images_found / adv_obj.cubester_skip_images)))
841 else:
842 # audio based mesh
843 box.label("Audio Track Length: " + str(adv_obj.cubester_audio_file_length) + " frames")
845 block_infl, frame_infl, intercept = 0.0948, 0.0687566, -25.85985
846 time = (points * block_infl) + (adv_obj.cubester_audio_file_length * frame_infl) + intercept
847 if time < 0.0: # usually no audio loaded
848 time = 0.0
850 time_mod = "s"
851 if time > 60: # convert to minutes if needed
852 time /= 60
853 time_mod = "min"
854 time = round(time, 3)
856 box.label("Expected Time: " + str(time) + " " + time_mod)
858 # advanced
859 if adv_obj.cubester_audio_image == "image":
860 icon_1 = "TRIA_DOWN" if adv_obj.cubester_advanced else "TRIA_RIGHT"
861 # layout.separator()
862 box = layout.box()
863 box.prop(adv_obj, "cubester_advanced", icon=icon_1)
865 if adv_obj.cubester_advanced:
866 box.prop(adv_obj, "cubester_random_weights", icon="RNDCURVE")
868 if not adv_obj.cubester_random_weights:
869 box.label("RGBA Channel Weights", icon="COLOR")
870 col = box.column(align=True)
871 col.prop(adv_obj, "cubester_weight_r")
872 col.prop(adv_obj, "cubester_weight_g")
873 col.prop(adv_obj, "cubester_weight_b")
874 col.prop(adv_obj, "cubester_weight_a")
876 # generate mesh
877 layout.operator("mesh.cubester", icon="OBJECT_DATA")
880 class CubeSter(Operator):
881 bl_idname = "mesh.cubester"
882 bl_label = "Generate Mesh"
883 bl_description = "Generate a mesh from an Image or Sound File"
884 bl_options = {"REGISTER", "UNDO"}
886 def execute(self, context):
887 verts, faces = [], []
889 start = timeit.default_timer()
890 scene = bpy.context.scene
891 adv_obj = scene.advanced_objects
893 if adv_obj.cubester_audio_image == "image":
894 if adv_obj.cubester_image != "":
895 create_mesh_from_image(self, scene, verts, faces)
896 frames = find_sequence_images(self, context)
897 created = len(frames[0])
898 else:
899 self.report({'WARNING'},
900 "Please add an Image for Object generation. Operation Cancelled")
901 return {"CANCELLED"}
902 else:
903 if (adv_obj.cubester_audio_path != "" and
904 path.isfile(adv_obj.cubester_audio_path) and adv_obj.cubester_check_audio is True):
906 create_mesh_from_audio(self, scene, verts, faces)
907 created = adv_obj.cubester_audio_file_length
908 else:
909 self.report({'WARNING'},
910 "Please add an Sound File for Object generation. Operation Cancelled")
911 return {"CANCELLED"}
913 stop = timeit.default_timer()
915 if adv_obj.cubester_mesh_style == "blocks" or adv_obj.cubester_audio_image == "audio":
916 self.report({"INFO"},
917 "CubeSter: {} blocks and {} frame(s) "
918 "in {}s".format(str(int(len(verts) / 8)),
919 str(created),
920 str(round(stop - start, 4)))
922 else:
923 self.report({"INFO"},
924 "CubeSter: {} points and {} frame(s) "
925 "in {}s" .format(str(len(verts)),
926 str(created),
927 str(round(stop - start, 4)))
930 return {"FINISHED"}
933 def register():
934 bpy.utils.register_module(__name__)
935 bpy.app.handlers.frame_change_pre.append(material_frame_handler)
938 def unregister():
939 bpy.utils.unregister_module(__name__)
940 bpy.app.handlers.frame_change_pre.remove(material_frame_handler)
943 if __name__ == "__main__":
944 register()