骨格 立方体作成リスト
パネル追加で 矢印を作る 矢印長さは 円錐先端から 円柱後端までの 全長
この矢印が 立方体 空間内を通り 球体表面の1点を通過し
球体中心点に 届く
立方体と 球体の最新値を更新ボタンで取得し
矢印長さの中間点が 立方体表面 に 位置する
この立方体表面を 移動する矢印中間点を
UV座標で 操作したい

# Code Created : 2024_0526_1600
bl_info = {
"name": "Skeleton Cube Generator (Pro)",
"author": "Your Name",
"version": (2, 6, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar",
"description": "Generates a skeletal cube, sphere, and an interactive UV Arrow with display toggles.",
"category": "3D View",
}
import bpy
import bmesh
import math
import time
from mathutils import Vector, Matrix, Euler
# =========================================================
# 共通定義
# =========================================================
ADDON_TITLE = "Skeleton Cube"
PREFIX_VAL = "SKELETON_CUBE"
PREFIX = PREFIX_VAL.lower()
ADDON_CATEGORY = "Skeleton Cube"
# =========================================================
# ユーザー設定パラメータ (初期値)
# =========================================================
TARGET_COLLECTION_NAME = "SkeletonCube_Collection"
DEFAULT_CENTER_X, DEFAULT_CENTER_Y, DEFAULT_CENTER_Z = 0.0, 0.0, 0.0
DEFAULT_ROTATION_X, DEFAULT_ROTATION_Y, DEFAULT_ROTATION_Z = 0.0, 0.0, 0.0
DEFAULT_EDGE_LENGTH = 2.0
DEFAULT_CYLINDER_RADIUS = 0.05
DEFAULT_COLOR_R, DEFAULT_COLOR_G, DEFAULT_COLOR_B = 0.2, 0.6, 1.0
DEFAULT_ALPHA = 1.0
DEFAULT_VERT_SPHERES_SHOW = True
DEFAULT_VERT_SPHERE_RADIUS = 0.08
DEFAULT_VERT_SPH_COLOR_R, DEFAULT_VERT_SPH_COLOR_G, DEFAULT_VERT_SPH_COLOR_B = 1.0, 0.3, 0.3
DEFAULT_VERT_SPH_ALPHA = 1.0
DEFAULT_SINGLE_SPHERE_SHOW = True
DEFAULT_SINGLE_SPHERE_LOC_X, DEFAULT_SINGLE_SPHERE_LOC_Y, DEFAULT_SINGLE_SPHERE_LOC_Z = 0.0, 0.0, 0.0
DEFAULT_SINGLE_SPHERE_RADIUS = 0.5
DEFAULT_SINGLE_SPH_COLOR_R, DEFAULT_SINGLE_SPH_COLOR_G, DEFAULT_SINGLE_SPH_COLOR_B = 0.2, 1.0, 0.2
DEFAULT_SINGLE_SPH_ALPHA = 1.0
DEFAULT_ARROW_SHOW = True
DEFAULT_ARROW_U = 0.5
DEFAULT_ARROW_V = 0.5
DEFAULT_ARROW_AUTO_LEN = True
DEFAULT_ARROW_LENGTH = 3.0
DEFAULT_ARROW_RADIUS = 0.02
DEFAULT_ARROW_CONE_LEN = 0.2
DEFAULT_ARROW_COLOR_R, DEFAULT_ARROW_COLOR_G, DEFAULT_ARROW_COLOR_B = 1.0, 0.8, 0.1
DEFAULT_ARROW_ALPHA = 1.0
# =========================================================
# リンク・補助関数
# =========================================================
def link_to_collection(context, obj, coll_name):
coll = bpy.data.collections.get(coll_name)
if not coll:
coll = bpy.data.collections.new(coll_name)
context.scene.collection.children.link(coll)
if obj.name not in coll.objects:
coll.objects.link(obj)
for c in obj.users_collection:
if c != coll:
c.objects.unlink(obj)
def setup_material(mat_name, color, alpha):
mat = bpy.data.materials.get(mat_name)
if not mat:
mat = bpy.data.materials.new(mat_name)
mat.use_nodes = True
mat.use_backface_culling = False
mat.diffuse_color = (color[0], color[1], color[2], alpha)
if hasattr(mat, "blend_method"): mat.blend_method = 'BLEND'
if hasattr(mat, "shadow_method"): mat.shadow_method = 'NONE'
nodes = mat.node_tree.nodes
bsdf = nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Base Color'].default_value = (color[0], color[1], color[2], alpha)
if 'Alpha' in bsdf.inputs:
bsdf.inputs['Alpha'].default_value = alpha
return mat
# =========================================================
# メイン生成・更新ロジック
# =========================================================
def update_skeleton_cube(context):
props = context.scene.skeleton_cube_props
obj_name = PREFIX_VAL
mesh_name = f"{PREFIX_VAL}_MESH"
mesh = bpy.data.meshes.get(mesh_name)
if not mesh: mesh = bpy.data.meshes.new(mesh_name)
else: mesh.clear_geometry()
obj = bpy.data.objects.get(obj_name)
if not obj: obj = bpy.data.objects.new(obj_name, mesh)
link_to_collection(context, obj, props.target_collection_name)
bm = bmesh.new()
L = props.edge_length
h = L / 2.0
r_cyl = props.cylinder_radius
verts_pos = [
Vector((-h, -h, -h)), Vector((+h, -h, -h)), Vector((+h, +h, -h)), Vector((-h, +h, -h)),
Vector((-h, -h, +h)), Vector((+h, -h, +h)), Vector((+h, +h, +h)), Vector((-h, +h, +h))
]
edges = [(0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7)]
for i, j in edges:
start, end = verts_pos[i], verts_pos[j]
center = (start + end) / 2.0
direction = (end - start).normalized()
length = (start - end).length
rot_quat = direction.to_track_quat('Z', 'Y')
matrix = Matrix.Translation(center) @ rot_quat.to_matrix().to_4x4()
bmesh.ops.create_cone(bm, cap_ends=True, cap_tris=False, segments=16, radius1=r_cyl, radius2=r_cyl, depth=length, matrix=matrix)
if props.show_vert_spheres:
r_sph = props.vert_sphere_radius
for v in verts_pos:
start_face_count = len(bm.faces)
bmesh.ops.create_uvsphere(bm, u_segments=16, v_segments=16, radius=r_sph, matrix=Matrix.Translation(v))
bm.faces.ensure_lookup_table()
for face_idx in range(start_face_count, len(bm.faces)):
bm.faces[face_idx].material_index = 1
bm.to_mesh(mesh)
bm.free()
mesh.update()
obj.location = props.loc
obj.rotation_euler = props.rot
mat_cyl = setup_material(f"{PREFIX_VAL}_CYL_MAT", props.color, props.alpha)
mat_vert_sph = setup_material(f"{PREFIX_VAL}_VERT_SPH_MAT", props.vert_sphere_color, props.vert_sphere_alpha)
obj.data.materials.clear()
obj.data.materials.append(mat_cyl)
obj.data.materials.append(mat_vert_sph)
def update_single_sphere(context):
props = context.scene.skeleton_cube_props
obj_name = f"{PREFIX_VAL}_SINGLE_SPH"
mesh_name = f"{obj_name}_MESH"
mesh = bpy.data.meshes.get(mesh_name)
if not mesh: mesh = bpy.data.meshes.new(mesh_name)
else: mesh.clear_geometry()
obj = bpy.data.objects.get(obj_name)
if not obj: obj = bpy.data.objects.new(obj_name, mesh)
link_to_collection(context, obj, props.target_collection_name)
bm = bmesh.new()
bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=32, radius=props.single_sphere_radius)
bm.to_mesh(mesh)
bm.free()
mesh.update()
cube_obj = bpy.data.objects.get(PREFIX_VAL)
if cube_obj:
obj.parent = cube_obj
obj.location = Vector(props.single_sphere_loc)
obj.rotation_euler = (0, 0, 0)
else:
obj.parent = None
obj.location = Vector(props.single_sphere_loc)
obj.rotation_euler = (0, 0, 0)
mat = setup_material(f"{obj_name}_MAT", props.single_sphere_color, props.single_sphere_alpha)
obj.data.materials.clear()
obj.data.materials.append(mat)
def update_arrow(context):
props = context.scene.skeleton_cube_props
obj_name = f"{PREFIX_VAL}_ARROW"
mesh_name = f"{obj_name}_MESH"
mesh = bpy.data.meshes.get(mesh_name)
if not mesh: mesh = bpy.data.meshes.new(mesh_name)
else: mesh.clear_geometry()
obj = bpy.data.objects.get(obj_name)
if not obj: obj = bpy.data.objects.new(obj_name, mesh)
link_to_collection(context, obj, props.target_collection_name)
if not props.show_arrow:
mesh.update()
return
# 1. UV座標から立方体表面の中間点(M)を計算
theta = props.arrow_u * 2.0 * math.pi
phi = (props.arrow_v - 0.5) * math.pi
vx = math.cos(phi) * math.cos(theta)
vy = math.cos(phi) * math.sin(theta)
vz = math.sin(phi)
half_L = props.edge_length / 2.0
tx = abs(half_L / vx) if abs(vx) > 1e-6 else float('inf')
ty = abs(half_L / vy) if abs(vy) > 1e-6 else float('inf')
tz = abs(half_L / vz) if abs(vz) > 1e-6 else float('inf')
t = min(tx, ty, tz)
M_loc = Vector((vx * t, vy * t, vz * t))
# 2. ワールド座標への変換
rot_mat = Euler(props.rot, 'XYZ').to_matrix()
M_world = Vector(props.loc) + rot_mat @ M_loc
C_sph = Vector(props.loc) + rot_mat @ Vector(props.single_sphere_loc)
# 3. 方向と距離の計算
direction = C_sph - M_world
dist = direction.length
if dist < 1e-4:
mesh.update()
return
direction.normalize()
# 4. 全長の決定
if props.arrow_auto_length:
L_arrow = dist * 2.0 # 球体中心に届く長さ
else:
L_arrow = props.arrow_length
L_cone = props.arrow_cone_len
if L_cone > L_arrow: L_cone = L_arrow
L_cyl = L_arrow - L_cone
# 先端と後端の計算(中間点 M_world を中心にする)
P_start = M_world - direction * (L_arrow / 2.0)
P_cone_base = M_world + direction * (L_arrow / 2.0) - direction * L_cone
C_cyl = (P_start + P_cone_base) / 2.0
C_cone = P_cone_base + direction * (L_cone / 2.0)
rot_quat = direction.to_track_quat('Z', 'Y')
mat_rot = rot_quat.to_matrix().to_4x4()
bm = bmesh.new()
if L_cyl > 0:
matrix_cyl = Matrix.Translation(C_cyl) @ mat_rot
bmesh.ops.create_cone(bm, cap_ends=True, cap_tris=False, segments=16,
radius1=props.arrow_radius, radius2=props.arrow_radius,
depth=L_cyl, matrix=matrix_cyl)
if L_cone > 0:
matrix_cone = Matrix.Translation(C_cone) @ mat_rot
bmesh.ops.create_cone(bm, cap_ends=True, cap_tris=False, segments=16,
radius1=props.arrow_radius * 2.0, radius2=0,
depth=L_cone, matrix=matrix_cone)
bm.to_mesh(mesh)
bm.free()
mesh.update()
mat = setup_material(f"{obj_name}_MAT", props.arrow_color, props.arrow_alpha)
obj.data.materials.clear()
obj.data.materials.append(mat)
# =========================================================
# プロパティ
# =========================================================
def on_property_update(self, context):
if self.cube_created: update_skeleton_cube(context)
if self.single_created: update_single_sphere(context)
if self.arrow_created: update_arrow(context)
for area in context.screen.areas:
if area.type == 'VIEW_3D': area.tag_redraw()
def on_view_update(self, context):
obj_cube = bpy.data.objects.get(PREFIX_VAL)
if obj_cube:
obj_cube.hide_viewport = not self.view_show_cube
obj_cube.hide_render = not self.view_show_cube
obj_single = bpy.data.objects.get(f"{PREFIX_VAL}_SINGLE_SPH")
if obj_single:
obj_single.hide_viewport = not self.view_show_single
obj_single.hide_render = not self.view_show_single
obj_arrow = bpy.data.objects.get(f"{PREFIX_VAL}_ARROW")
if obj_arrow:
obj_arrow.hide_viewport = not self.view_show_arrow
obj_arrow.hide_render = not self.view_show_arrow
class SkeletonCubeProperties(bpy.types.PropertyGroup):
target_collection_name: bpy.props.StringProperty(name="保存コレクション名", default=TARGET_COLLECTION_NAME)
cube_created: bpy.props.BoolProperty(default=False)
single_created: bpy.props.BoolProperty(default=False)
arrow_created: bpy.props.BoolProperty(default=False)
# 表示・非表示トグル用
view_show_cube: bpy.props.BoolProperty(name="表示", default=True, update=on_view_update)
view_show_single: bpy.props.BoolProperty(name="表示", default=True, update=on_view_update)
view_show_arrow: bpy.props.BoolProperty(name="表示", default=True, update=on_view_update)
loc: bpy.props.FloatVectorProperty(name="位置", size=3, default=(DEFAULT_CENTER_X, DEFAULT_CENTER_Y, DEFAULT_CENTER_Z), update=on_property_update)
rot: bpy.props.FloatVectorProperty(name="回転", size=3, subtype='EULER', default=(math.radians(DEFAULT_ROTATION_X), math.radians(DEFAULT_ROTATION_Y), math.radians(DEFAULT_ROTATION_Z)), update=on_property_update)
edge_length: bpy.props.FloatProperty(name="立方体1辺", default=DEFAULT_EDGE_LENGTH, min=0.001, update=on_property_update)
cylinder_radius: bpy.props.FloatProperty(name="円柱半径", default=DEFAULT_CYLINDER_RADIUS, min=0.001, update=on_property_update)
color: bpy.props.FloatVectorProperty(name="色", subtype='COLOR', size=3, min=0.0, max=1.0, default=(DEFAULT_COLOR_R, DEFAULT_COLOR_G, DEFAULT_COLOR_B), update=on_property_update)
alpha: bpy.props.FloatProperty(name="透明度", default=DEFAULT_ALPHA, min=0.0, max=1.0, update=on_property_update)
show_vert_spheres: bpy.props.BoolProperty(name="頂点に球体を表示", default=DEFAULT_VERT_SPHERES_SHOW, update=on_property_update)
vert_sphere_radius: bpy.props.FloatProperty(name="球体半径", default=DEFAULT_VERT_SPHERE_RADIUS, min=0.001, update=on_property_update)
vert_sphere_color: bpy.props.FloatVectorProperty(name="球体色", subtype='COLOR', size=3, min=0.0, max=1.0, default=(DEFAULT_VERT_SPH_COLOR_R, DEFAULT_VERT_SPH_COLOR_G, DEFAULT_VERT_SPH_COLOR_B), update=on_property_update)
vert_sphere_alpha: bpy.props.FloatProperty(name="球体透明度", default=DEFAULT_VERT_SPH_ALPHA, min=0.0, max=1.0, update=on_property_update)
show_single_sphere: bpy.props.BoolProperty(name="単独球体を生成する", default=DEFAULT_SINGLE_SPHERE_SHOW, update=on_property_update)
single_sphere_loc: bpy.props.FloatVectorProperty(name="相対位置", size=3, default=(DEFAULT_SINGLE_SPHERE_LOC_X, DEFAULT_SINGLE_SPHERE_LOC_Y, DEFAULT_SINGLE_SPHERE_LOC_Z), update=on_property_update)
single_sphere_radius: bpy.props.FloatProperty(name="球体の半径", default=DEFAULT_SINGLE_SPHERE_RADIUS, min=0.001, update=on_property_update)
single_sphere_color: bpy.props.FloatVectorProperty(name="球体の色", subtype='COLOR', size=3, min=0.0, max=1.0, default=(DEFAULT_SINGLE_SPH_COLOR_R, DEFAULT_SINGLE_SPH_COLOR_G, DEFAULT_SINGLE_SPH_COLOR_B), update=on_property_update)
single_sphere_alpha: bpy.props.FloatProperty(name="球体の透明度", default=DEFAULT_SINGLE_SPH_ALPHA, min=0.0, max=1.0, update=on_property_update)
show_arrow: bpy.props.BoolProperty(name="矢印を表示する", default=DEFAULT_ARROW_SHOW, update=on_property_update)
arrow_u: bpy.props.FloatProperty(name="U (横周り)", min=0.0, max=1.0, default=DEFAULT_ARROW_U, update=on_property_update)
arrow_v: bpy.props.FloatProperty(name="V (上下)", min=0.0, max=1.0, default=DEFAULT_ARROW_V, update=on_property_update)
arrow_auto_length: bpy.props.BoolProperty(name="球体に届く長さに自動調整", default=DEFAULT_ARROW_AUTO_LEN, update=on_property_update)
arrow_length: bpy.props.FloatProperty(name="全体の長さ", min=0.001, default=DEFAULT_ARROW_LENGTH, update=on_property_update)
arrow_radius: bpy.props.FloatProperty(name="矢印の太さ", min=0.001, default=DEFAULT_ARROW_RADIUS, update=on_property_update)
arrow_cone_len: bpy.props.FloatProperty(name="先端の長さ", min=0.001, default=DEFAULT_ARROW_CONE_LEN, update=on_property_update)
arrow_color: bpy.props.FloatVectorProperty(name="矢印の色", subtype='COLOR', size=3, min=0.0, max=1.0, default=(DEFAULT_ARROW_COLOR_R, DEFAULT_ARROW_COLOR_G, DEFAULT_ARROW_COLOR_B), update=on_property_update)
arrow_alpha: bpy.props.FloatProperty(name="矢印の透明度", default=DEFAULT_ARROW_ALPHA, min=0.0, max=1.0, update=on_property_update)
# =========================================================
# Operators
# =========================================================
class SKELETON_CUBE_OT_CreateCube(bpy.types.Operator):
bl_idname = f"{PREFIX}.create_cube"
bl_label = "骨格立方体を作成"
def execute(self, context):
context.scene.skeleton_cube_props.cube_created = True
update_skeleton_cube(context)
return {'FINISHED'}
class SKELETON_CUBE_OT_DetachCube(bpy.types.Operator):
bl_idname = f"{PREFIX}.detach_cube"
bl_label = "骨格立方体を独立させる"
def execute(self, context):
obj = bpy.data.objects.get(PREFIX_VAL)
if obj:
suffix = str(int(time.time() % 100000))
obj.name = f"{PREFIX_VAL}_Fixed_{suffix}"
if obj.data: obj.data.name = f"{PREFIX_VAL}_MESH_{suffix}"
for slot in obj.material_slots:
if slot.material:
new_mat = slot.material.copy()
new_mat.name = f"{slot.material.name}_{suffix}"
slot.material = new_mat
context.scene.skeleton_cube_props.cube_created = False
return {'FINISHED'}
class SKELETON_CUBE_OT_CreateSingle(bpy.types.Operator):
bl_idname = f"{PREFIX}.create_single"
bl_label = "単独球体を作成"
def execute(self, context):
context.scene.skeleton_cube_props.single_created = True
update_single_sphere(context)
return {'FINISHED'}
class SKELETON_CUBE_OT_DetachSingle(bpy.types.Operator):
bl_idname = f"{PREFIX}.detach_single"
bl_label = "単独球体を独立させる"
def execute(self, context):
obj = bpy.data.objects.get(f"{PREFIX_VAL}_SINGLE_SPH")
if obj:
suffix = str(int(time.time() % 100000))
obj.name = f"{PREFIX_VAL}_SINGLE_SPH_Fixed_{suffix}"
if obj.data: obj.data.name = f"{obj.name}_MESH"
for slot in obj.material_slots:
if slot.material:
new_mat = slot.material.copy()
new_mat.name = f"{slot.material.name}_{suffix}"
slot.material = new_mat
obj.matrix_world = obj.matrix_world.copy()
obj.parent = None
context.scene.skeleton_cube_props.single_created = False
return {'FINISHED'}
class SKELETON_CUBE_OT_CreateArrow(bpy.types.Operator):
bl_idname = f"{PREFIX}.create_arrow"
bl_label = "矢印を更新・作成"
def execute(self, context):
context.scene.skeleton_cube_props.arrow_created = True
update_arrow(context)
return {'FINISHED'}
class SKELETON_CUBE_OT_DetachArrow(bpy.types.Operator):
bl_idname = f"{PREFIX}.detach_arrow"
bl_label = "矢印を独立させる"
def execute(self, context):
obj = bpy.data.objects.get(f"{PREFIX_VAL}_ARROW")
if obj:
suffix = str(int(time.time() % 100000))
obj.name = f"{PREFIX_VAL}_ARROW_Fixed_{suffix}"
if obj.data: obj.data.name = f"{obj.name}_MESH"
for slot in obj.material_slots:
if slot.material:
new_mat = slot.material.copy()
new_mat.name = f"{slot.material.name}_{suffix}"
slot.material = new_mat
obj.matrix_world = obj.matrix_world.copy()
obj.parent = None
context.scene.skeleton_cube_props.arrow_created = False
return {'FINISHED'}
class SKELETON_CUBE_OT_JoinSelected(bpy.types.Operator):
bl_idname = f"{PREFIX}.join_selected"
bl_label = "選択したObjectを一体化"
bl_description = "2つ以上のオブジェクトを選択して1つにまとめます"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context): return len(context.selected_objects) >= 2 and context.active_object is not None
def execute(self, context):
bpy.ops.object.join()
return {'FINISHED'}
class SKELETON_CUBE_OT_RemoveAddon(bpy.types.Operator):
bl_idname = f"{PREFIX}.remove_addon"
bl_label = "アドオン機能を消去する"
def execute(self, context):
bpy.app.timers.register(lambda: unregister() or None, first_interval=0.01)
return {'FINISHED'}
# =========================================================
# Panels
# =========================================================
class SKELETON_CUBE_PT_Main(bpy.types.Panel):
bl_label = ADDON_TITLE
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = ADDON_CATEGORY
def draw_header(self, context):
props = context.scene.skeleton_cube_props
self.layout.prop(props, "view_show_cube", text="", icon='HIDE_OFF' if props.view_show_cube else 'HIDE_ON')
def draw(self, context):
layout = self.layout
props = context.scene.skeleton_cube_props
layout.prop(props, "target_collection_name", text="保存コレクション")
layout.separator()
col = layout.column(align=True)
col.prop(props, "loc", text="全体位置")
col.prop(props, "rot", text="全体回転")
col.separator()
col.label(text="【骨格 (円柱)】")
col.prop(props, "edge_length", text="立方体1辺")
col.prop(props, "cylinder_radius", text="円柱半径")
col.prop(props, "color", text="色")
col.prop(props, "alpha", text="透明度")
col.separator()
col.label(text="【頂点の球体】")
col.prop(props, "show_vert_spheres", text="頂点球体を生成")
sub = col.column(align=True)
sub.enabled = props.show_vert_spheres
sub.prop(props, "vert_sphere_radius", text="半径")
sub.prop(props, "vert_sphere_color", text="色")
sub.prop(props, "vert_sphere_alpha", text="透明度")
col.separator()
layout.operator(SKELETON_CUBE_OT_CreateCube.bl_idname, icon='CUBE')
layout.operator(SKELETON_CUBE_OT_DetachCube.bl_idname, icon='UNLINKED')
class SKELETON_CUBE_PT_SingleSphere(bpy.types.Panel):
bl_label = "単独球体の設定"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = ADDON_CATEGORY
def draw_header(self, context):
props = context.scene.skeleton_cube_props
self.layout.prop(props, "view_show_single", text="", icon='HIDE_OFF' if props.view_show_single else 'HIDE_ON')
def draw(self, context):
layout = self.layout
props = context.scene.skeleton_cube_props
layout.prop(props, "show_single_sphere")
sub = layout.column(align=True)
sub.enabled = props.show_single_sphere
sub.prop(props, "single_sphere_loc", text="相対位置")
sub.prop(props, "single_sphere_radius", text="半径")
sub.prop(props, "single_sphere_color", text="色")
sub.prop(props, "single_sphere_alpha", text="透明度")
layout.separator()
layout.operator(SKELETON_CUBE_OT_CreateSingle.bl_idname, icon='SPHERE')
layout.operator(SKELETON_CUBE_OT_DetachSingle.bl_idname, icon='UNLINKED')
class SKELETON_CUBE_PT_Arrow(bpy.types.Panel):
bl_label = "矢印の設定 (UV操作)"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = ADDON_CATEGORY
def draw_header(self, context):
props = context.scene.skeleton_cube_props
self.layout.prop(props, "view_show_arrow", text="", icon='HIDE_OFF' if props.view_show_arrow else 'HIDE_ON')
def draw(self, context):
layout = self.layout
props = context.scene.skeleton_cube_props
layout.prop(props, "show_arrow")
sub = layout.column(align=True)
sub.enabled = props.show_arrow
sub.prop(props, "arrow_u", text="U座標 (経度)")
sub.prop(props, "arrow_v", text="V座標 (緯度)")
sub.separator()
sub.prop(props, "arrow_auto_length")
len_col = sub.column(align=True)
len_col.enabled = not props.arrow_auto_length
len_col.prop(props, "arrow_length")
sub.separator()
sub.prop(props, "arrow_radius", text="矢印の太さ")
sub.prop(props, "arrow_cone_len", text="先端の長さ")
sub.prop(props, "arrow_color", text="色")
sub.prop(props, "arrow_alpha", text="透明度")
layout.separator()
layout.operator(SKELETON_CUBE_OT_CreateArrow.bl_idname, icon='PLAY')
layout.operator(SKELETON_CUBE_OT_DetachArrow.bl_idname, icon='UNLINKED')
class SKELETON_CUBE_PT_Join(bpy.types.Panel):
bl_label = "オブジェクトの一体化"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = ADDON_CATEGORY
def draw(self, context):
layout = self.layout
layout.label(text="※2つ以上のObjectを選択して実行", icon='INFO')
layout.operator(SKELETON_CUBE_OT_JoinSelected.bl_idname, icon='LINKED')
class SKELETON_CUBE_PT_System(bpy.types.Panel):
bl_label = "システム設定"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = ADDON_CATEGORY
def draw(self, context):
layout = self.layout
layout.label(text="アドオンの完全消去を行います", icon='ERROR')
layout.operator(SKELETON_CUBE_OT_RemoveAddon.bl_idname, icon='CANCEL')
# =========================================================
# Register / Unregister / Viewport Setup
# =========================================================
classes = (
SkeletonCubeProperties,
SKELETON_CUBE_OT_CreateCube, SKELETON_CUBE_OT_DetachCube,
SKELETON_CUBE_OT_CreateSingle, SKELETON_CUBE_OT_DetachSingle,
SKELETON_CUBE_OT_CreateArrow, SKELETON_CUBE_OT_DetachArrow,
SKELETON_CUBE_OT_JoinSelected, SKELETON_CUBE_OT_RemoveAddon,
SKELETON_CUBE_PT_Main, SKELETON_CUBE_PT_SingleSphere,
SKELETON_CUBE_PT_Arrow, SKELETON_CUBE_PT_Join, SKELETON_CUBE_PT_System,
)
def setup_viewport_on_load():
context = bpy.context
if context and context.window_manager:
for window in context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_region_ui = True
space.shading.type = 'MATERIAL'
return None
def register():
for cls in classes: bpy.utils.register_class(cls)
bpy.types.Scene.skeleton_cube_props = bpy.props.PointerProperty(type=SkeletonCubeProperties)
# 起動時のUIパネル展開&マテリアルプレビュー化
bpy.app.timers.register(setup_viewport_on_load, first_interval=0.1)
def unregister():
if hasattr(bpy.types.Scene, "skeleton_cube_props"):
del bpy.types.Scene.skeleton_cube_props
for cls in reversed(classes):
try: bpy.utils.unregister_class(cls)
except RuntimeError: pass
if __name__ == "__main__":
register()