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
27 "author": "Jacob Morris",
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"
38 from bpy
.types
import (
44 from random
import uniform
45 from math
import radians
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":
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
):
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
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
105 # only skip vertices if made of blocks
106 if style
== "blocks":
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
117 if not adv_obj
.cubester_use_image_color
and adv_obj
.cubester_color_image
in bpy
.data
.images
:
119 image
= bpy
.data
.images
[adv_obj
.cubester_color_image
]
124 image
= bpy
.data
.images
[adv_obj
.cubester_image
]
128 if scene
.render
.engine
== "CYCLES":
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")
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]
157 mat
.node_tree
.links
.new(
158 nodes
["Attribute"].outputs
[0],
159 nodes
["Diffuse BSDF"].inputs
[0]
162 if adv_obj
.cubester_materials
== "image" or scene
.render
.engine
!= "BLENDER_RENDER":
163 tex
= bpy
.data
.textures
.new("CubeSter_" + name
, "IMAGE")
166 slot
= mat
.texture_slots
.add()
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
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
)
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
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
)
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()
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
]
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
)
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"])
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))]
266 area
= bpy
.context
.area
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
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
):
291 for r
in range(width
):
292 if freq_frame
== "frame":
293 scene
.frame_current
= frame_off
295 h
= (c
+ 1) * freq_step
296 frame_off
+= frame_step
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
305 for i
in range(index
, index
+ 4):
306 curve
= i
* 3 + 2 # fcurve location
307 fcurves
[curve
].select
= True
309 bpy
.ops
.graph
.sound_bake(filepath
=bpy
.path
.abspath(audio_filepath
), low
=l
, high
=h
)
314 for i
in range(index
, index
+ 4):
315 curve
= i
* 3 + 2 # fcurve location
316 fcurves
[curve
].select
= False
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])
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
370 weights
= [uniform(0.0, 1.0) for i
in range(4)] # random weights
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):
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
)
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)]
389 vert_colors
+= [(r
, g
, b
) for i
in range(4)]
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
414 if adv_obj
.cubester_mesh_style
== "blocks":
415 create_uv_map(context
, rows
, int(len(faces
) / 6 / rows
))
417 create_uv_map(context
, rows
- 1, int(len(faces
) / (rows
- 1)))
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
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
])
436 create_material(scene
, ob
, image_name
)
439 bpy
.ops
.mesh
.vertex_color_add()
441 for c
in ob
.data
.vertex_colors
[0].data
:
442 c
.color
= vert_colors
[i
]
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
)
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
)
470 frame_heights
.append(h
)
471 if adv_obj
.cubester_mesh_style
== "blocks":
472 frame_colors
+= [(r
, g
, b
) for i
in range(24)]
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
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
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")
513 uv_layer
= bm
.loops
.layers
.uv
[0]
514 bm
.faces
.ensure_lookup_table()
516 x_scale
= 1 / columns
521 count
= columns
- 1 # hold current count to compare to if need to go to next row
524 if adv_obj
.cubester_mesh_style
== "blocks":
525 for fa
in range(int(len(bm
.faces
) / 6)):
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
)
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
)
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
]
564 image
= bpy
.data
.images
.load(load_path
)
567 self
.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(load_path
))
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
578 if not adv_obj
.cubester_advanced
:
579 composed
= 0.25 * r
+ 0.25 * g
+ 0.25 * b
+ 0.25 * a
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
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
599 h
= composed
* adv_obj
.cubester_height_scale
* normalize
606 # find all images that would belong to sequence
607 def find_sequence_images(self
, context
):
608 scene
= context
.scene
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
618 for i
in range(length
- 1, -1, -1):
619 if main
[i
].isdigit() and keep_going
:
622 keep_going
= not keep_going
623 name
= main
[0:length
]
625 dir_name
= path
.dirname(bpy
.path
.abspath(image
.filepath
))
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
))
639 def get_image_node(mat
):
640 nodes
= mat
.node_tree
.nodes
641 att
= nodes
["Image Texture"]
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]
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
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
]
683 for c
in ob
.data
.vertex_colors
[0].data
:
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
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
714 layout
.prop(adv_obj
, "cubester_audio_image")
716 if adv_obj
.cubester_audio_image
== "image":
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":
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
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")
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")
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")
752 layout
.prop(adv_obj
, "cubester_audio_path")
755 col
= box
.column(align
=True)
756 col
.prop(adv_obj
, "cubester_audio_min_freq")
757 col
.prop(adv_obj
, "cubester_audio_max_freq")
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")
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")
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
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")
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))
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))
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":
825 block_infl
, frame_infl
, intercept2
= 0.0025934, 0.38507, -0.5840189
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
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
)))
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
851 if time
> 60: # convert to minutes if needed
854 time
= round(time
, 3)
856 box
.label("Expected Time: " + str(time
) + " " + time_mod
)
859 if adv_obj
.cubester_audio_image
== "image":
860 icon_1
= "TRIA_DOWN" if adv_obj
.cubester_advanced
else "TRIA_RIGHT"
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")
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])
899 self
.report({'WARNING'},
900 "Please add an Image for Object generation. Operation Cancelled")
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
909 self
.report({'WARNING'},
910 "Please add an Sound File for Object generation. Operation 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)),
920 str(round(stop
- start
, 4)))
923 self
.report({"INFO"},
924 "CubeSter: {} points and {} frame(s) "
925 "in {}s" .format(str(len(verts
)),
927 str(round(stop
- start
, 4)))
934 bpy
.utils
.register_module(__name__
)
935 bpy
.app
.handlers
.frame_change_pre
.append(material_frame_handler
)
939 bpy
.utils
.unregister_module(__name__
)
940 bpy
.app
.handlers
.frame_change_pre
.remove(material_frame_handler
)
943 if __name__
== "__main__":