いまさら 超時空要塞マクロス リガード 3Dモデル

超時空要塞マクロスのゼントラーディ軍の汎用兵器であるリガードです。ダチョウを思わせるそのフォルムですが、その特徴はなんといっても”逆関節”の脚でしょう。あの歩行を再現してみたいと思っていましたが、工作の技術に自信がないのでシミュレーションでの再現に挑戦することにしました。Open Dynamics Engineを使っていろいろやっていこうと思います。

歩くリガードを表示するための第一歩として、STLデータをPythonで表示させてみました。見た目を気にする作者としては外観は気になるところです。最近は3Dプリンタの普及も相まっていろいろなものの3Dデータが公開されています。今回のリガードはhttp://www.thingiverse.com/thingでSTLデータを使わせてもらっています。

0.なぜPython?

Open Dynamics Engineを使ってシミュレーションを行うには、大きくわけてC言語でコードを作成するか、Pythonを利用するかの2つでしょう。日頃は残業続きで会社に拘束されている時間が長いので、会社と自宅の両方で使用できる環境が必要でした。C言語のコンパイラ類は管理者権限が必要で、会社では使用できません。ですから、管理者権限が不要なPythonで開発環境を構築することにしました。

1.Pythonの準備

Pythonについては、既に多くの方が説明されていますので、詳細はそちらを見ていただくとして、参考書が多い3系を選択しました。後で後悔するのですが・・・。

まずはPythonのインストールです。

python-3.4.4.msi

をインストールしました。次にhttp://www.lfd.uci.edu/~gohlke/pythonlibs/から

  • numpy-1.10.4-cp34-none-win32.whl
  • ode-0.13.1-cp34-none-win32.whl
  • pygame-1.9.2a0-cp34-none-win32.whl
  • cgkit-2.0.0-cp34-none-win32.whl

をpipを使ってインストールしました。

http://pyopengl.sourceforge.net/へ行って、

  • PyOpenGL
  • PyOpenGL_accelerate
  • PyOpenGL Demo
  • OpenGLContext

をインストールすれば準備完了です。

2.結果

STLデータはバイナリファイルなので、

http://sukhbinder.wordpress.com/2013/11/28/binary-stl-file-reader-in-python-powered-by-numpy/

で紹介されている”numpyを使う方法”を参考にしました。実は、3系を選択したのですがcgkitでエラーが大量に出るのです。初心者の私ではさばききれなかったので、numpyを使う方法にしました。

ここから

import os
import struct

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame
from pygame.locals import *

import numpy as np
from struct import unpack

NormalV =[]
NodePoint = []
Vertex1 = []
Vertex2 = []
Vertex3 = []

Eye_from_x = 60.0
Eye_from_y = 50.0
Eye_from_z = 100.0

Center_X =0.
Center_Y =0.
Center_Z =0.

T_Color = [0.0, 1.0, 0.0, 1.0] #Height

def BinarySTL(fname):
fp = open(fname, ‘rb’)
Header = fp.read(80)
nn = fp.read(4)
Numtri = unpack(‘i’, nn)[0]
#print nn
record_dtype = np.dtype([
(‘normals’, np.float32,(3,)),
(‘Vertex1’, np.float32,(3,)),
(‘Vertex2’, np.float32,(3,)),
(‘Vertex3’, np.float32,(3,)) ,
(‘atttr’, ‘<i2’,(1,) )
])
data = np.fromfile(fp , dtype = record_dtype , count =Numtri)
fp.close()

Normals = data[‘normals’]
Vertex1= data[‘Vertex1’]
Vertex2= data[‘Vertex2’]
Vertex3= data[‘Vertex3’]

p = np.append(Vertex1,Vertex2,axis=0)
p = np.append(p,Vertex3,axis=0) #list(v1)
Points =np.array(list(set(tuple(p1) for p1 in p)))

return Header,Points,Normals,Vertex1,Vertex2,Vertex3

def draw_box():
global NormalV,Vertex1,Vertex2,Vertex3

glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, T_Color)
glPushMatrix()
glBegin(GL_TRIANGLES)
for i in range(len(NormalV)):
glNormal3f(NormalV[i][0],NormalV[i][1],NormalV[i][2])
glVertex3f(Vertex1[i][0],Vertex1[i][1],Vertex1[i][2])
glVertex3f(Vertex2[i][0],Vertex2[i][1],Vertex2[i][2])
glVertex3f(Vertex3[i][0],Vertex3[i][1],Vertex3[i][2])
glEnd()
glPopMatrix()

def prepare_GL():
global quadratic

quadratic = gluNewQuadric()
glViewport(0,0,640,480)

glClearColor(0.8,0.8,0.9,0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_LIGHTING)
glEnable(GL_LIGHTING)
glEnable(GL_NORMALIZE)
glShadeModel(GL_FLAT)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective (90,1.3333,0.1,1000.)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

glLightfv(GL_LIGHT0,GL_POSITION,[0,0,1,0])
glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
glLightfv(GL_LIGHT0,GL_SPECULAR,[1,1,1,1])
glEnable(GL_LIGHT0)

glDisable(GL_CULL_FACE)

gluLookAt (Eye_from_x, Eye_from_y, Eye_from_z, Center_X, Center_Y, Center_Z, 0.0, 0.0, 1.0)

def init_GL():

glutInit ([])

glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)

x = 0
y = 0
width = 640
height = 480
glutInitWindowPosition (x, y);
glutInitWindowSize (width, height);
glutCreateWindow (“test”.encode(“cp932”))
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);

def _keyfunc (c, x, y):
global Eye_from_x ,Eye_from_y, Eye_from_z,Center_Z

if c == “p”.encode(“cp932”):
Center_Z = Center_Z + 5.0
elif c == “l”.encode(“cp932”):
Center_Z = Center_Z – 5.0
if c == “j”.encode(“cp932”):
Eye_from_x = Eye_from_x + 5.0
elif c == “f”.encode(“cp932”):
Eye_from_x = Eye_from_x – 5.0
elif c == “b”.encode(“cp932”):
Eye_from_z = Eye_from_z + 5.0
elif c == “c”.encode(“cp932”):
Eye_from_z = Eye_from_z – 5.0
elif c == “u”.encode(“cp932”):
Eye_from_y = Eye_from_y + 5.0
elif c == “r”.encode(“cp932”):
Eye_from_y = Eye_from_y – 5.0
elif c == “q”.encode(“cp932”):
sys.exit (0)
else:
print (“key missed”)

glutPostRedisplay()

def _drawfunc ():

green = [0.0, 1.0, 0.0, 1.0]
blue = [0.0, 0.0, 1.0, 1.0]
red = [1.0, 0.0, 0.0, 1.0]
prepare_GL()

draw_box()
glutSwapBuffers ()

def _idlefunc ():

glutPostRedisplay ()

#main program loop
def main():
global NormalV,Vertex1,Vertex2,Vertex3

fname = “Zentradi_Battlepod_-_Low_Poly.stl” # “porsche.stl”

head,NodePoint,NormalV,Vertex1,Vertex2,Vertex3 = BinarySTL(fname)
print (len(NormalV))

max_x = -99999999999.
min_x = 99999999999.
for i in range(len(NormalV)):
if (max_x < Vertex1[i][0]): max_x = Vertex1[i][0]
if (min_x > Vertex1[i][0]): min_x = Vertex1[i][0]
if (max_x < Vertex2[i][0]): max_x = Vertex2[i][0]
if (min_x > Vertex2[i][0]): min_x = Vertex2[i][0]
if (max_x < Vertex3[i][0]): max_x = Vertex3[i][0]
if (min_x > Vertex3[i][0]): min_x = Vertex3[i][0]
print (max_x,min_x)
max_y = -99999999999.
min_y = 99999999999.
for i in range(len(NormalV)):
if (max_y < Vertex1[i][1]): max_y = Vertex1[i][1]
if (min_y > Vertex1[i][1]): min_y = Vertex1[i][1]
if (max_y < Vertex2[i][1]): max_y = Vertex2[i][1]
if (min_y > Vertex2[i][1]): min_y = Vertex2[i][1]
if (max_y < Vertex3[i][1]): max_y = Vertex3[i][1]
if (min_y > Vertex3[i][1]): min_y = Vertex3[i][1]
print (max_y,min_y)
max_z = -99999999999.
min_z = 99999999999.
for i in range(len(NormalV)):
if (max_z < Vertex1[i][2]): max_z = Vertex1[i][2]
if (min_z > Vertex1[i][2]): min_z = Vertex1[i][2]
if (max_z < Vertex2[i][2]): max_z = Vertex2[i][2]
if (min_z > Vertex2[i][2]): min_z = Vertex2[i][2]
if (max_z < Vertex3[i][2]): max_z = Vertex3[i][2]
if (min_z > Vertex3[i][2]): min_z = Vertex3[i][2]
print (max_z,min_z)
Center_X = (max_x+min_x)/2.
Center_Y = (max_y+min_y)/2.
Center_Z = (max_z+min_z)/2.

init_GL()
glutKeyboardFunc (_keyfunc)
glutDisplayFunc (_drawfunc)
glutIdleFunc (_idlefunc)

glutMainLoop ()

 

if __name__ == ‘__main__’:
main()

ここまで。

結果はこんな感じです。前方から見たものです。

おなじく側方から。

最後は後方からです。

と、表示までは達成することができました。この後は、ODEで扱える形式に変換する作業と、歩行シミュレーション用のモデルの作成です。