Blender 2.9, Python 奇数偶数ビル  

blender 2.9, python.  movie 1sec.  400*300px resolution.  奇数偶数で大小の直方体重ねて２色塗り分け。カメラ円周沿いで移動。色分けした直方体を乱雑に並べたら建築に見えるかなと思いましたが直方体ではあまりに無表情なので、マンションのベランダ形状を真似て奇数で小さい、偶数で大きい直方体を重ねました。少ないポリゴンでそれなりの建築風味の外観。で建物の階の全体数と色分けもやってみました。
動画はtwitterで[blender 2.9, python.  ...奇数偶数で大小の直方体重ねて ](https://twitter.com/naohiko7/status/1313479815756947461) をどうぞ。
（なお、最近の記事はblender 2.9で作業したものですが、blender 2.8タグ付けています。2.9タグがないもので。自分で触った範囲では pythonスクリプトは2.8から2.9で変化ほぼ無いかの様子です ）
![bpy_nh27PNG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690216/0150e07b-3919-85c8-0938-fa80fb3e591e.png)


```python3
import bpy

# ========= DELETE ALL mesh, light, camera, みな削除する2行 =========
for item in bpy.data.objects:
    bpy.data.objects.remove(item)

xy_bump = 1.05
#FThick = 0.4
def hex_to_rgb( hex_value ): #=== hex color code to RGB
    b = (hex_value & 0xFF) / 255.0
    g = ((hex_value >> 8) & 0xFF) / 255.0
    r = ((hex_value >> 16) & 0xFF) / 255.0
    return r, g, b

def add_material(obj, material_name, h):  #=== material to cube 
    material = bpy.data.materials.get(material_name)
    if material is None:
        material = bpy.data.materials.new(material_name)
    material.use_nodes = True
    principled_bsdf = material.node_tree.nodes['Principled BSDF']
    if principled_bsdf is not None:
        principled_bsdf.inputs[0].default_value = (*hex_to_rgb(h), 1)  
    obj.active_material = material
# =================== definition , create apartment
def create_apartment(x_origin, y_origin, z_rot, NumOfStories, xScale, yScale, FThick, odd_color, even_color):
    for i in range (0, 2*NumOfStories, 2):
        bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(x_origin, y_origin, i * FThick), rotation=(0.0, 0.0, z_rot), scale=(xScale, yScale, FThick))
        obj = bpy.context.object
#        add_material( obj, "color01", odd_color )  
        add_material( obj, "", odd_color )  
        bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(x_origin, y_origin, FThick + i * FThick), rotation=(0.0, 0.0, z_rot), scale=(xScale*xy_bump, yScale*xy_bump, FThick))
        obj = bpy.context.object
#        add_material( obj, "color02", even_color ) 
        add_material( obj, "", even_color )    
  
# ======== create now z_rot:rotation,  NumOfStories:floors, 
# create_apartment(x_origin, y_origin, z_rot, NumOfStories, xScale, yScale, FThick, odd_color, even_color):
create_apartment(-1, -1, 1/180*3.14, 8, 1, 2, 0.2, 0x2F2F2F, 0xffe4b5) #DARKSLATEGRAY#2F4F4F
create_apartment(-2, 2, 60/180*3.14, 15, 2, 1.8, 0.2, 0x2F2F2F, 0xffe4b5)  #DARKSLATEGRAY#2F4F4F
create_apartment(-1.5, 4, 60/180*3.14, 6, 1.5, 1.8, 0.2, 0x8b4513, 0xffe4b5)
create_apartment(-1, 7, 0/180*3.14, 3, 1.5, 3, 0.2, 0x800000, 0xA9A9A9)  #MAROON#800000 ,   #DARKGRAY#A9A9A9

# Add a plane for ground  ==================
bpy.ops.mesh.primitive_plane_add(size=200.0, align='WORLD', location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0) )
matp = bpy.data.materials.new('Plane')
matp.diffuse_color = (0.08, 0.08, 0.03, 0) 
obj = bpy.context.view_layer.objects.active
obj.data.materials.append(matp)

# ================== world - surface - background (背景) 
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value = (0.01, 0.15, 0.25, 1)
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[1].default_value = 0.7      

# ============== "light"
# create light datablock, set attributes
#light_data = bpy.data.lights.new(name="light_spot1", type='SPOT')
light_data = bpy.data.lights.new(name="light_spot1", type='SUN')
light_data.energy = 5
# create new object with our light datablock
light_object1 = bpy.data.objects.new(name="light_spot1", object_data=light_data)
# link light object
bpy.context.collection.objects.link(light_object1)
# make it active 
bpy.context.view_layer.objects.active = light_object1
#change location
light_object1.location = (20, -10, 50)
light_object1.delta_rotation_euler = (1.3, 0, 0.3) #ゼロゼロゼロで真下を向く。
# update scene, if needed
dg = bpy.context.evaluated_depsgraph_get() 
dg.update()
# ================

#  ================== = camera movement
bpy.ops.curve.primitive_bezier_circle_add(enter_editmode=False, align='WORLD', location=(6, 6, 15))
bpy.context.object.scale[0] = 15
bpy.context.object.scale[1] = 15
bpy.ops.object.empty_add(type='CUBE', align='WORLD', location=(0,0,0))
bpy.ops.object.camera_add(enter_editmode=False, align='VIEW', location=(0,0,0), rotation=(0, 0, 0))

bpy.data.objects['Empty'].select_set(True)
bpy.data.objects['Camera'].select_set(True)

bpy.context.view_layer.objects.active = bpy.data.objects['Empty']
bpy.ops.object.parent_set(type='OBJECT')

bpy.data.objects['Camera'].select_set(False)
bpy.data.objects['Empty'].select_set(True)

bpy.ops.object.constraint_add(type='FOLLOW_PATH')
bpy.context.object.constraints["Follow Path"].target = bpy.data.objects["BezierCircle"]

bpy.context.object.constraints["Follow Path"].use_curve_follow = True
bpy.context.object.constraints["Follow Path"].use_fixed_location = True

bpy.data.objects['Empty'].select_set(False)
bpy.data.objects['Camera'].select_set(True)

bpy.ops.object.constraint_add(type='TRACK_TO')

#bpy.context.object.constraints["Track To"].target = bpy.data.objects["Cube.016"]
bpy.context.object.constraints["Track To"].target = bpy.data.objects["Cube.020"]
bpy.context.object.constraints["Track To"].up_axis = 'UP_Y'
bpy.context.object.constraints["Track To"].track_axis = 'TRACK_NEGATIVE_Z'  #5m00sec
#Camera Keyframe #(Insert keyframe to object's Offset Factor Python API - stack exchange)
bpy.data.objects['Camera'].select_set(False)
bpy.data.objects['Empty'].select_set(True)
bpy.context.scene.frame_current = 1
bpy.context.object.constraints["Follow Path"].offset_factor = 0
ob = bpy.context.object
# ob.constraints['Follow Path']
# bpy.data.objects['Empty'].constraints["Follow Path"]
# [bpy.data.objects['Empty'].constraints["Follow Path"]]
con = ob.constraints.get("Follow Path")
con.offset_factor = 0.0
con.keyframe_insert("offset_factor", frame=1)
con.offset_factor = 0.25
con.keyframe_insert("offset_factor", frame=8)
con.offset_factor = 0.50
con.keyframe_insert("offset_factor", frame=16)
con.offset_factor = 0.75
con.keyframe_insert("offset_factor", frame=23)
con.offset_factor = 0.99
con.keyframe_insert("offset_factor", frame=30)
# ==== END of camera movement

```