Skip to content

Instantly share code, notes, and snippets.

@TakosukeGH
Last active July 10, 2016 06:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TakosukeGH/03a08e231ced1e387adb270105016936 to your computer and use it in GitHub Desktop.
Save TakosukeGH/03a08e231ced1e387adb270105016936 to your computer and use it in GitHub Desktop.
Rubix Cube Generator #rubix_cube_script #blender #script
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