いまさら 超時空要塞マクロス デストロイド トマホーク 2

引き続き、デストロイド トマホークに注目していきましょう。

・脚部の機構

履帯に代わって、上部構造物を支えて不整地を走破し、砲撃時の反動を吸収するための脚部は重要な部分です。特に、04系はモジュール化により他機種にも使用されています。

現在の2足歩行ロボットの股関節は、片側3自由度を有しています。

マクロスクロニクルの資料によれば、トマホークが属する04系の構造は図のようになっているそうです。片側の自由度は3のようです。

現在の技術で球面モーターというものがあります。下のリンクは、産総研で研究されているものの動画です。

球面モーター – YouTube

これが、OverTechnorogyで小型・軽量・高出力化されれば、デストロイドの駆動に使えるようになると思います。

最高速度180km/hに達し、背部下方のスラスターを用いればジャンプ移動も可能となっています。これなら、リガードと十分渡り合えることになります。

熱核反応炉など動力源は、上部構造物にあると思います。武装ごとに必要な出力が違うでしょうし、上部に置けばエネルギーの発生源と消費部位を腰部(ロータリージョイントなど)介さずに連結できます。回転部へのエネルギー・信号の伝達は現在でも技術上の課題です。

いまさら 超時空要塞マクロス デストロイド トマホーク

デストロイド「トマホーク」です。マクロスで人気のあるメカの一つです。

登場回数は結構多いのですが、盛大に撃ち合うシーンは少なく、ダイダロスアタックの時の斉射くらいでしょうか。長距離で敵艦と撃ち合うには火力が足りず、高速で移動する戦闘ポッドとの戦闘はディフェンダーやスパルタンの方が目立っていました。

諸元
MBR-04-Mk.VI トマホーク
分類 重装砲撃型デストロイド
所属 地球統合軍
設計 ビガース/クラウラー社
開発 ビガース/クラウラー社
製造 ビガース/クラウラー社
全高 11.27m(頭部まで)
全備重量 31.3t
エンジン (主機)クランス・マッファイ MT828 熱核反応炉
(副機)GE EM9G 燃料発電機
出力 (主機)2800SHP
(副機)450kW
武装 PGB-11 荷電粒子ビーム砲×2
M-89 空冷マシンガン×2
TZ-III ガンクラスター×2
(レーザー砲、機関砲、火炎放射器など)
12連発ロケット弾ランチャー×2
6連発対空自己誘導ミサイル×1
乗員人数 2名

「超時空要塞マクロス」に登場する重装砲撃型デストロイドです。型式名のMBRはMain Battle Robotの略です。現用の主力戦車、MBT(Main Battle Tank)に由来してつけられたものです。その開発コンセプトは、機体前面に各種武装を配し、集中的火力により中・近距離域の敵陸上兵力を殲滅する、というものでした。
Mk.IVは初めて制式採用されたデストロイドシリーズの主力機であり、MK.Iの両腕を荷電粒子ビーム砲に置き換え、火力向上を図っています。格闘能力の低下は、胸部ガンクラスターの機関銃やグレネードランチャー、火炎放射器などの近接兵器で補っています。肩腕部はウェポンベイとして随時換装可能で、04系の特徴である上半身の独立した燃料発電機により稼動します。Mk.VIは出力強化型で、右肩にエリコーン対空自己誘導ミサイル6連発オプションパックを装備している。主力戦車的な機体であり、ダイダロスアタック時には最前列に陣取り、水平方向への強力な攻撃力を見せつけました。
トマホークの属する04シリーズは主機熱核反応エンジンと歩行制御システムを内蔵する下半身を共通プラットフォームとすることで、量産性・運用性の向上やコストダウンを図っています。例えるならトマホークが主力戦車、ディフェンダーが自走対空機関砲、ファランクスが自走対空ミサイルで、共通シャーシを用いたAFVのファミリー化と同じ発想といえますね。

 

いまさら 超時空要塞マクロス デストロイド スパルタン

いまさらですが、デストロイドに注目してみたいと思います。まずは、スパルタンです。

すでにご存じかと思いますが、「デストロイド」は「超時空要塞マクロス」に登場する架空のロボット兵器です。デストロイドは陸上兵器がロボットに発展したと想定した場合のものです。設定資料によりますと、デストロイドの系譜はビガース社・クラウラー社が共同開発した04シリーズと、センチネンタル社・クランスマン社が共同開発した07シリーズがあることになっています。スパルタンはMBR-07-MK2ですので後者の系統です。画面では動きが速くてわからなかったのですが、資料集をよく見ると、股関節にあたる部分や足の形が04シリーズと異なることがわかります。

左はMk.Iで右がMk.IIですね。スパルタンは、近接格闘型デストロイドということで、異星人との肉弾戦を想定した歩兵的機体です。Mk.Iに対空火器を付加したのがMk.IIで、のちにMk.Iも全機この仕様に改装されました。
07系の駆動系は、高出力の反応動力炉1基で駆動します。副動力を除いて軽量化し、その分を装甲と運動性能の両立に用いています。背面にはロケットノズルを備え、機動性の向上に一役かっています。腰部の複雑な動力伝達系の故障と主機DT2000シリーズの開発遅延から、07系は本機のみですが、理想主義的な高性能はパイロットから評価されていました。これら07系の設計思想はセンチネンタル社が係わるバトロイドの開発にもフィードバックされました。

スパルタン最大の特徴は、格闘戦を主目的としていますので両腕の先端の手の部分が五指のマニピュレーターになっていることです。また、格闘戦用ということで、パイロットのシートベルトもハーネスタイプのがっちりしたものとなっています。(下図)

 

実際の格闘戦(16話)でも、ひとまわり大きい敵戦闘ポッドおよびバトルスーツ相手に大型のマニピュレータでリガードの脚部を破壊するなど健闘しています。戦後の統治下では、ゼントラーディ人不満分子の鎮圧活動における警察警備的な活躍での利用が目立ちます(第34話では主人公一条輝が搭乗する)。この際には専用の暴徒鎮圧用棍棒を使用しています。

劇中では戦闘ポッド相手に健闘している場面と、反対にこてんぱんにやられている場面(ヌージャデル・ガーに投げ飛ばされるシーン)とがあり、やはり戦果は操縦者の技量に依存するのでしょうね。

 

諸元
MBR-07-Mk.II スパルタン
分類 近接格闘型デストロイド
所属 地球統合軍
設計 センチネンタル社/クランスマン社
開発 センチネンタル社/クランスマン社
製造 センチネンタル社/クランスマン社
全高 11.31m(肩まで)
全備重量 29.4t
エンジン グゲンハイマー DT2004 熱核反応炉[8]
出力 3200SHP
武装 TZ-IV ガンクラスター×1
(レーザー砲、機関砲、火炎放射器など)
RQV-10 対空レーザー機銃×2
ビフォーズ12連発近接自己誘導ロケット弾ランチャー×2
CH2 typeD 格闘用クローハンド×2
乗員人数 1名

いまさら 超時空要塞マクロス リガード ODE 自由落下

読み込んだSTL形式のデータを表示するところまでは達成しました。今回は、ODEでリガードを自由落下させてみましょう。

リガードのモデルで「質量」に関わるものは球形としています。これは扱いを簡単にするためです。そして、接触の判定に用いられる「形状」についてSTLデータからの三角形を使用しています。動画内の焦げ茶色の平面は地面を表しています。リガードのモデルと地面が接触する面の反発力は0、摩擦係数も0としています。重心を表す球は接触判定の対象外としています。(地面を貫通します。)

リガードのモデルの座標系(局所座標系)の原点を確認してみましょう。赤い球で局所座標系の原点を表示すると、下のように足元のあたりになります。リガードモデルを自由落下させると、着地のショックで少し揺れますが、すぐに静止します。右側に動画をリンクさせていますので見てください。(初めて画面の録画に挑戦したもので、上手くいかなかったのですが、雰囲気はわかって頂けると思います。)

Animation

これを、下の位置に移動させます。球を大きく表示しただけですが、赤いリガードもカッコイイかも。でも結果はあまり変わり映えしませんね。3Dプリンタで製作するためのモデルですから、安定性はそれなりに考えられているのですね。

Animation2

もう少し重心を後ろに移動させてみましょう。今度は見事にスリップダウンしました。

Animation-u

Animation-l

では、今回のCodeを添付します。後方に転倒した時のデータとなっています。CodeについてはこれからPythonを勉強して改善していこうと思います。そしてODEを用いて鳥の脚(逆関節)の2足歩行について検討していく予定です。

Sample_STL_read4

 

いまさら 超時空要塞マクロス 第36話(最終回) やさしさサヨナラ

第36話最終回となる「やさしさサヨナラ」です。反応炉エンジンの部品の強奪に成功し、カムジンたちは逃げ去っていきました。一条君とミンメイちゃんは、本編で3回目となる二人きりの夜を迎えます。早瀬さんといいミンメイちゃんといい、やはり主人公の一条君には強い女性運があるようですね。

クリスマスを終えたマクロスシティーは暗い冬の空です。そんな中、マクロスの展望台から一人空を見上げる早瀬さんは、軍を辞めることを考え思い悩んでいました。もちろん、原因は一条君とミンメイちゃんです。

その一条君も、ミンメイちゃんから軍を辞めるように迫られていました。「あなたが軍人を辞めてくれるなら、私、歌手を辞めてもいいわ。」とまで言ってもらっていました。そして、一条君とであった頃に戻りましょうと。この時に使われていたのが、「結婚式でもやりますか」のシーンです。つまるところこれは、ミンメイちゃんからの逆プロポーズなのでしょうか。

一方、エンジンの部品を手に入れたカムジンたちは、戦艦を一隻稼働状態に仕上げていました。そして昔のように戦場で暴れまくるために出発します。まずはマクロスを目標に進路を取ります。まあ、カムジンはマクロスにこだわりますね。あんなボロ船なんかほっといて行けばいいのにと思うのですが。

一人黄昏ている早瀬さんのところに、クローディアさんがグローバル総司令が呼んでいることを告げます。そして、早瀬さんの胸中を察して励まします。そんなクローディアさんに、早瀬さんは自身のコンプレックスを話すのでした。出頭した早瀬さんにグローバル総司令は新しい任務を依頼します。それは、新しい星間移民船の艦長でした。除隊を申請しようとする早瀬さんに、グローバル総司令は、「一条大尉が原因かね?」と突いてきます。「女性の士官が突然軍を辞めたいと言い出すときは、決まってそんなものさ」と年長者の貫禄を見せます。「文化を絶やさないためにも宇宙への移民を始めようと思う」という総司令の説得に早瀬さんの心は動かされるのでした。

一条君の方は相変わらず、決断できずに迷っています。そこに、宇宙移民艦の艦長を引き受けることを決意した早瀬さんが、お別れの挨拶にやってくるのでした。そして、一条君に自分の想いを自分の言葉で伝えるのでした。「一条君、あなたが好きです」と。そして、ミンメイちゃんにも「あなたも歌を大切に」とミンメイちゃんにも言葉を送るのでした。去りゆく早瀬さんを追いかけようとした一条君をミンメイちゃんが制止します。そして、まさにそのとき、カムジンたちの戦艦からのミサイル攻撃が始まるのでした。

市街がつぎつぎと破壊されいてくのを見て、早瀬さんと一条君は迎撃に向かおうとします。それをミンメイちゃんは、一条君の出撃を止めようとします。「どうして、そんなにまでして闘いにいくの?!」と。それに対し、早瀬さんは「ミンメイさん。あなたは一体誰のために歌を歌うの。」と問いかけます。そして「あなたの歌を愛してくれている人たちのためじゃなくて」「私たちが戦いに行くのもあなたが歌を歌うことと同じ理由なの」と続けます。それでもミンメイちゃんは「一人ぼっちにしないで」とすがります。それを振り切って一条君も出撃するのでした。

カムジンたちの戦艦は、マクロスを射程にとらえます。そして主砲の一撃をマクロスに放ちます。

それは、マクロス右舷中央部に命中。現司令センター区画を吹き飛ばします。市民の見ている中、ゆっくり後方へ倒れていくマクロス。早瀬さんの安否を気遣いながらも、一条君のバルキリー隊は空戦ポッドとの乱戦へ突入していきます。早瀬さんの機転で司令センターのメンバーはブリッジへ急ぎます。そこにはクローディアさんとグローバル艦長が待っていました。

浮上したマクロスから放たれた一撃は見事カムジンたちの戦艦を捉えます。

最期を悟ったカムジンは、体当たり攻撃を仕掛けます。マクロス側も残ったデストロイドでありったけの火力を向けるのでした。このとき、またも謎のデストロイドが映っています。某誌で取り上げられていましたが改めて見ると確かにありました。

カムジンたちの命を懸けた攻撃で、マクロスは右腕となるダイダロスを引きちぎられ大破させられましたが、轟沈は免れました。そして、これによりゼントラーディ人による大規模な反乱も終結したのでした。

廃墟となった街で、早瀬さんと一条君の互いの無事を確かめあう姿を見たミンメイちゃんは自分の決意を告げます。「知らない街へ行ってみる。そこでもう一度歌ってみる。もう歌を辞めるなんて言わない」と。そしていつの日か本当の自分の歌が歌えるようになったら、早瀬さんの艦に乗せてくれるよう頼むのでした。

二人のもとを去っていくミンメイちゃんの姿は降る雪で隠されていきます。降りゆく雪は、肩を抱き合う早瀬さんと一条君も包んでいくのでした。

 

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

前回からの続きです。Open Dynamics Engineでは、任意の三角形は、節点の座標データと、節点の番号データの配列で扱われます。STLデータでは、各三角形の頂点座標と法線ベクトルで構成されているので、少々変換が必要です。下はPyOdeのTutorialから、TriMeshDataの説明です。

前回のコードの読み込む処理を変更しました。

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 = []
NVertex = [[0,1,2],[0,1,2]]

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.5, 0.7, 0.5, 1.0] #Object Color
 
def BinarySTL(fname):
 global NVertex
 #read start of file to determine if its a binay stl file or a ascii stl file
 fp=open(fname,'rb')
 h=fp.read(80)
 type=h[0:5]
 fp.close()

if type=='solid':
 print ( "reading text file"+str(fname))
 return
 else:
 print ("reading binary stl file "+str(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)))

NVertex = np.resize(NVertex,(len(Vertex1),3))
 NVertex[0][0] = Points.tolist().index([Vertex1[0][0],Vertex1[0][1],Vertex1[0][2]])
 NVertex[0][1] = Points.tolist().index([Vertex2[0][0],Vertex2[0][1],Vertex2[0][2]])
 NVertex[0][2] = Points.tolist().index([Vertex3[0][0],Vertex3[0][1],Vertex3[0][2]])
 for i in range(1,len(Vertex1)):
# print(i)
 NVertex[i][0] = Points.tolist().index([Vertex1[i][0],Vertex1[i][1],Vertex1[i][2]])
 NVertex[i][1] = Points.tolist().index([Vertex2[i][0],Vertex2[i][1],Vertex2[i][2]])
 NVertex[i][2] = Points.tolist().index([Vertex3[i][0],Vertex3[i][1],Vertex3[i][2]])
 
 return Header,Points,Normals
 
 return
 
def draw_box():
 global NormalV,NodePoint,NVertex

 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(NodePoint[NVertex[i][0]][0],NodePoint[NVertex[i][0]][1],NodePoint[NVertex[i][0]][2])
 glVertex3f(NodePoint[NVertex[i][1]][0],NodePoint[NVertex[i][1]][1],NodePoint[NVertex[i][1]][2])
 glVertex3f(NodePoint[NVertex[i][2]][0],NodePoint[NVertex[i][2]][1],NodePoint[NVertex[i][2]][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
 elif 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.2, 0.8, 0.3, 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,NVertex,NodePoint

fname = "Zentradi_Battlepod_-_Low_Poly.stl" # "porsche.stl"

head,NodePoint,NormalV = BinarySTL(fname)

print(NVertex)
 print(len(NVertex))

print(NodePoint)
 print(len(NodePoint))
 
 max_x = -99999999999.
 min_x = 99999999999.
 max_y = -99999999999.
 min_y = 99999999999.
 max_z = -99999999999.
 min_z = 99999999999.
 for i in range(len(NodePoint)):
 if (max_x < NodePoint[i][0]): max_x = NodePoint[i][0]
 if (min_x > NodePoint[i][0]): min_x = NodePoint[i][0]
 if (max_y < NodePoint[i][1]): max_y = NodePoint[i][1]
 if (min_y > NodePoint[i][1]): min_y = NodePoint[i][1]
 if (max_z < NodePoint[i][2]): max_z = NodePoint[i][2]
 if (min_z > NodePoint[i][2]): min_z = NodePoint[i][2]
 print (max_x,min_x)
 print (max_y,min_y)
 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における質量などの情報の付加です。

いまさら 超時空要塞マクロス 第35話 ロマネスク

第35話「ロマネスク」です。コンサート(仕事)を放棄して、マネージャーのカイフンとも別れ、行く当てもなく一人公園でブランコを漕ぐミンメイちゃんでした。クリーニング屋をクビになっておもちゃ屋に転職したワレラ、ロリー、コンダたちに声を掛けられましたがどこかに走り去ってしまいました。

ミンメイちゃんの”プレゼントトラップ”に引っかかって、早瀬さんとの仲が気まずい一条君は、仲直りのチャンスを探しますが、二人はすれ違ったままです。そうこうしているうちにクリスマスがやってきました。テレビではミンメイちゃん失踪のニュースが流れています。そして、玄関の呼び鈴に応じてドアを開けると、なんとそこにはミンメイちゃんがいるではありませんか。優しく部屋に招き入れる一条君に、「もう歌えない」と心の内をさらすミンメイちゃんでした。さらに、「ここのいちゃダメ?」と尋ねるのです。「僕はかまわないけど」と応える一条君です。

ここまでは、「昔の想い人との再会」なのですが、そこに一夜を一条君と過ごそうと早瀬さんが訪ねてきます。不用心にも玄関を開けっ放しだったので、二人の様子は、早瀬さんに筒抜けになっていました。早瀬さんは、悲しみと怒りでその場を走り去るのでした。そして、クローディアさんの行きつけで一人寂しくグラスを傾けるのでした。それも二日酔いになるまで。

人質を使った戦艦入手に失敗したカムジンたちは、ジャングルに墜落した戦艦の修理に励んでいました。この2年間で地球人の”修理”に関する知識と技術をものにしたのです。”闘い”のためには努力を惜しまない姿勢は賞賛ものです。戦艦復活に必要な部品がパワーコンデンサーであることを特定し、強奪のための部隊を率いてオノギ・シティーにむけて出動します。

防衛部隊の指揮をとる早瀬さんは、マックス夫妻のバルキリー隊と、一条君の所属する東部バルキリー隊を現場に向かわせます。しかし、一条君へは、事態を伝えることはしませんでした。ミンメイちゃんとのひと時を過ごしているであろう一条君を気付かったのでした。テレビの報道で、カムジンたちの襲撃を知った一条君は、ミンメイちゃんに「いつものことさ」といって出撃するのでした。

カムジンたちの目的はエキセドルに読まれていました。反応炉エンジンの部品を巡って戦闘が始まります。ラプ・ラミズの駆るクアドラン・ローとミリアの対決も起きます。その中で、カムジンたちは目的の部品を手に入れ、伏せておいたケルカリアで逃げ去りました。逃げ際に市街に仕掛けておいた爆弾で市街を炎上させることで、バルキリー隊の追撃を鈍らせたのです。

帰還後、スクランブルの指示がなかったことを早瀬さんに問い詰める一条君ですが、反対にあの夜のことをさりげなく指摘されるのでした。部屋に戻るとミンメイちゃんがクリスマスパーティーの用意をして待っていました。一条君は、心配から「軍人なんかやめて」と懇願するミンメイちゃんに黙っていることしかできませんでした。二人だけで、クリスマスの夜を過ごすのでした。

 

 

いまさら 超時空要塞マクロス 第34話 プライベート・タイム

第34話「プライベート・タイム」です。クローディアさんとフォッカーさんの馴れ初めを聴いて、早瀬さんは一条君と仲直りすることに成功しました。そして、舞台は、早瀬さんのお宅の中で、早瀬さんがウキウキ気分でお弁当のサンドイッチを準備していました。「ヤッホー!」なんていままでの早瀬さんからは想像できないセリフも飛び出します。それもそのはず。なんと休暇の日に一条君に森林地帯へのピクニック(デート)に誘われていたのでした。

しかし、一条君はの方はミンメイちゃんからの急な電話を受けて困っています。”マクロス・シティにいる人ならいつでも会えるじゃないの”という強引な言葉に押し切られ、早瀬さんとの約束を反故にしてしまいます。一条君も早瀬さんに予定急変によるデートのキャンセルの電話をかけますが、既に待ち合わせ場所にいる早瀬さんに連絡がつきません。

ハイランダー・シティのレストランで、早瀬さんのことを気にしながらも一条君はミンメイちゃんと2年ぶりにふたりだけの時間をすごす。ミンメイちゃんはイオネスコ市で救出してくれたお礼に、一条君に白いロング・マフラーをプレゼントしるのですが、そのマフラーには一条君とミンメイちゃんのイニシャルが縫いこんであったのでした(まさにこれがブービートラップとなるのです)。
よい雰囲気の二人ですが、ミンメイちゃんを探して店にあらわれたカイフンによって状況は一変します。仕事が入ったことを告げるカイフンに対し、「仕事に行きたくない」と反抗するミンメイちゃんです。テーブルのグラスを干して「もう酔っちゃった」と子供じみたことをしますが、業を煮やしたカイフンは、コップの水をかけてミンメイちゃんを強引に連れていってしましました。一人残された一条君はやけ酒を(少々)あおりますが、マクロスシティーに戻るためハイランダー・シティの空港へ向かいます。おりしも空港ではゼントラーディ人の暴動が起きており閉鎖されていました。軍にかけあった一条君は自らデストロイド・スパルタンを操縦して暴動鎮圧の指揮をとります。暴徒たちを撃退してファンライナーでマクロスシティーへ急ぎます。

マクロスシティーでは夕闇がせまる中、早瀬さんは一条君を待ち続けていました。健気です。(とっても)遅れて到着した一条君を早瀬さんは責めることなく、遅れたわけを訊くわけでもなく、やさしく許すのでした。腕を組んで歩き始める早瀬さんと一条君。その肩に、一条君はミンメイちゃんからもらったばかりのマフラーの片端をかけるのですが、そこには何ということでしょう、一条君とあろうことかミンメイちゃんのイニシャルが刺繍されていたのでした。二人のイニシャルを見つけた早瀬さんは「かけてあげる人をまちがえているわよ!」といって一条君を残して去っていってしまうのでした。

ミンメイちゃんはサウスコースト市のコンサートで、とうとうコンサート会場から逃げだしてステージを放棄するという職場放棄という不祥事を起こしてしまいました。会場ではファンが声援を送りつづけてくれたというのに・・・。ミンメイちゃんのあまりにも身勝手な行いにあきれたカイフンは、「きみの歌に人を思うやさしさがあればほんものの歌手になれる」と残していずこかへ姿を消してしまうのでした。

いまさら 超時空要塞マクロス リガード 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で扱える形式に変換する作業と、歩行シミュレーション用のモデルの作成です。