Last active
July 10, 2016 06:13
-
-
Save TakosukeGH/03a08e231ced1e387adb270105016936 to your computer and use it in GitHub Desktop.
Rubix Cube Generator #rubix_cube_script #blender #script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import logging | |
import collections | |
import math | |
import random | |
import numpy as np | |
logger = logging.getLogger("rubix_cube_generator") | |
if not logger.handlers: | |
hdlr = logging.StreamHandler() | |
formatter = logging.Formatter("%(levelname)-7s %(asctime)s %(message)s (%(funcName)s)", datefmt="%H:%M:%S") | |
hdlr.setFormatter(formatter) | |
logger.addHandler(hdlr) | |
# TODO: set level | |
logger.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, CRITICAL | |
logger.debug("init logger") # debug, info, warning, error, critical | |
class RubixCubeGenerator(): | |
name = "RubixCube" | |
bone_name = "Bone" | |
MoveNotation = collections.namedtuple("MoveNotation", "F B R L U D S M E") | |
mn = MoveNotation(1,2,3,4,5,6,7,8,9) | |
RotationDirection = collections.namedtuple("RotationDirection", "n r") | |
rd = RotationDirection(0,1) | |
materials = [] | |
indexes = [(x, y, z) for x in [-1, 0, 1] for y in [-1, 0, 1] for z in [-1, 0, 1] if x or y or z] | |
bone_names = [[[None for x in range(3)] for y in range(3)] for z in range(3)] | |
axis_map = {mn.F:(0.0,-1.0,0.0),mn.B:(0.0,1.0,0.0),mn.R:(1.0,0.0,0.0),mn.L:(-1.0,0.0,0.0),mn.U:(0.0,0.0,1.0),mn.D:(0.0,0.0,-1.0),mn.S:(0.0,1.0,0.0),mn.M:(1.0,0.0,0.0),mn.E:(0.0,0.0,1.0)} | |
rotate_map = {rd.n: math.radians(-90.0),rd.r: math.radians(90.0)} | |
prop_map = {mn.F:"y",mn.B:"y",mn.L:"x",mn.R:"x",mn.U:"z",mn.D:"z",mn.S:"y",mn.M:"x",mn.E:"z",} | |
# ----------------------------------------------------------------------------- | |
# Level.1 properties : 直感的に設定できるプロパティ | |
scale = 1.0 # ルービックキューブ全体のスケール | |
cube_scale = scale / 2.0 # 一つ一つのキューブの大きさ | |
frame_start = 1 # 回転を開始するフレーム番号 | |
frame_rotate = 5 # 1回の回転に必要なフレーム数 | |
frame_interval = 5 # 回転終了後、次の回転まで静止するフレーム数 | |
move_data = [ | |
# ルービックキューブの回転を、回転記号と回転方向で指定する。 | |
# mnはMove Notationの略。F,Bなどの記号はルービックキューブのwikiを参照 | |
# rdはRotation Directionの略。nは時計回り、rはその逆。 | |
(mn.F, rd.n), | |
(mn.B, rd.n), | |
(mn.R, rd.n), | |
(mn.L, rd.n), | |
(mn.U, rd.n), | |
(mn.D, rd.n), | |
(mn.S, rd.n), | |
(mn.M, rd.n), | |
(mn.E, rd.n), | |
] | |
# ----------------------------------------------------------------------------- | |
# Level.2 properties : Blender機能に関連したプロパティ | |
handle_scale = 3.0 # ルートボーンに設定するカスタムシェイプオブジェクトのスケール | |
bevel_segments = 2 # キューブに設定するベベルモディファイアのセグメント数 | |
bevel_width = scale * 0.05 # キューブに設定するベベルモディファイアの幅 | |
# ----------------------------------------------------------------------------- | |
# Level.3 properties : スクリプト特有のプロパティ | |
border = scale / 2.0 # ボーンを選択する際の座標閾値 | |
colors = collections.OrderedDict((("green",(0.0,1.0,0.0)),("orange",(1.0,0.2140411,0.0)),("blue",(0.0,0.0,1.0)),("red",(1.0,0.0,0.0)),("yellow",(1.0,1.0,0.0)),("white",(1.0,1.0,1.0)),("black",(0.0,0.0,0.0)))) # 面の色。順番は-x,y,x,-y,-z,zの順。 | |
bevel_material_index = 6 # ベベル面に設定するマテリアルのインデックス | |
# ----------------------------------------------------------------------------- | |
def __init__(self): | |
logger.info("start") | |
self.obj_rig = None | |
self.amt = None | |
# 回転をランダムに設定するためのコード | |
# self.move_data = [] | |
# for i in range(30): | |
# self.move_data.append((random.randint(1,9), random.randint(0,1))) | |
logger.info("end") | |
def execute(self): | |
logger.info("start") | |
utnd = bpy.context.user_preferences.system.use_translate_new_dataname | |
bpy.context.user_preferences.system.use_translate_new_dataname = False | |
self.create_material() | |
self.create_rig() | |
self.create_cube() | |
self.create_action() | |
bpy.context.user_preferences.system.use_translate_new_dataname = utnd | |
logger.info("end") | |
def create_material(self): | |
logger.info("start") | |
for color_name, color_value in self.colors.items(): | |
mat = bpy.data.materials.new(name="{}.{}".format(self.name, color_name)) | |
mat.diffuse_color = color_value | |
self.materials.append(mat) | |
logger.info("end") | |
def create_rig(self): | |
logger.info("start") | |
bpy.ops.object.empty_add(type='SPHERE',radius=self.handle_scale,location=(0.0, 0.0, 0.0)) | |
boen_shape = bpy.context.object | |
boen_shape.layers = self.create_layers(19) | |
bpy.ops.object.add(type='ARMATURE',location=(0.0,0.0,0.0)) | |
self.obj_rig = bpy.context.object | |
self.obj_rig.show_x_ray = True | |
self.obj_rig.name = self.name | |
self.amt = self.obj_rig.data | |
self.amt.name = self.name + "Amt" | |
self.amt.draw_type = 'STICK' | |
bpy.ops.object.mode_set(mode='EDIT') | |
bone_root = self.amt.edit_bones.new(self.bone_name) | |
bone_root.head = 0,0,0 | |
bone_root.tail = 0,0,self.scale | |
for x, y, z in self.indexes: | |
bone = self.amt.edit_bones.new(self.bone_name) | |
bone.use_connect = False | |
bone.parent = bone_root | |
bone.head = 0,0,0 | |
bone.tail = x*self.scale,y*self.scale,z*self.scale | |
self.bone_names[x][y][z] = bone.name | |
bpy.ops.object.mode_set(mode='OBJECT') | |
self.obj_rig.pose.bones[self.bone_name].custom_shape = boen_shape | |
logger.info("end") | |
def create_cube(self): | |
for x, y, z in self.indexes: | |
bpy.ops.mesh.primitive_cube_add(radius=self.cube_scale,location=(x*self.scale, y*self.scale, z*self.scale)) | |
cube = bpy.context.object | |
cube.parent = self.obj_rig | |
vg = cube.vertex_groups.new(self.bone_names[x][y][z]) | |
for v in cube.data.vertices: | |
vg.add([v.index], 1.0, 'REPLACE') | |
for mat in self.materials: | |
cube.data.materials.append(mat) | |
for i in range(0, 6): | |
poly = cube.data.polygons[i] | |
poly.material_index = i | |
bpy.ops.object.shade_smooth() | |
bpy.ops.object.modifier_add(type='BEVEL') | |
mod_bevel = cube.modifiers["Bevel"] | |
mod_bevel.segments = self.bevel_segments | |
mod_bevel.width = self.bevel_width | |
mod_bevel.material = self.bevel_material_index | |
bpy.ops.object.modifier_add(type='ARMATURE') | |
mod_amt = cube.modifiers["Armature"] | |
mod_amt.object = self.obj_rig | |
def create_action(self): | |
logger.info("start") | |
scn = bpy.context.scene | |
scn.frame_set(self.frame_start) | |
self.obj_rig.select = True | |
scn.objects.active = self.obj_rig | |
bpy.ops.object.mode_set(mode='POSE') | |
pose_bones = [bone for bone in self.obj_rig.pose.bones if bone.name != self.bone_name] | |
for mn, rd in self.move_data: # move notation, rotation direction | |
for bone in pose_bones: bone.bone.select = True | |
bpy.ops.anim.keyframe_insert_menu(type='Rotation') | |
for bone in pose_bones: bone.bone.select = False | |
scn.frame_set(scn.frame_current + self.frame_rotate) | |
for bone in pose_bones: | |
self.select_bone(bone, mn) | |
bpy.ops.transform.rotate(value=self.rotate_map[rd], axis=self.axis_map[mn]) | |
bpy.ops.anim.keyframe_insert_menu(type='Rotation') | |
scn.frame_set(scn.frame_current + self.frame_interval) | |
for bone in pose_bones: bone.bone.select = False | |
scn.frame_end = scn.frame_current if scn.frame_current > self.frame_start else self.frame_start | |
bpy.ops.object.mode_set(mode='OBJECT') | |
logger.info("end") | |
def select_bone(self, bone, mn): | |
prop = getattr(bone.tail, self.prop_map[mn]) | |
if mn in [self.mn.B, self.mn.R, self.mn.U]: | |
is_select_bone = prop > self.border | |
elif mn in [self.mn.F, self.mn.L, self.mn.D]: | |
is_select_bone = prop < -self.border | |
else: | |
is_select_bone = -self.border < prop < self.border | |
if is_select_bone: | |
bone.bone.select = True | |
else: | |
bone.bone.select = False | |
def create_layers(self, layer_number): | |
layers = [False] * 20 | |
layers[layer_number] = True | |
return layers | |
if __name__ == "__main__": | |
RubixCubeGenerator().execute() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment