まーにゃ@エンタメ系火事場エンジニアの日々

数々の「火だるまプロジェクト」を安請け合いし何度でも復活する 「自称・不死身のエンジニア」の物欲まみれの日々をつづる

【思い出編】「★開発秘話@描画エンジン:2Dグラフィック」と「CAD」と私

【思い出編】「★開発秘話@エンジン開発@2Dグラフィック表示」と「CAD」と私
と題しまして小話を1つ。
一時期、CAD用のグラフィックエンジン(ライブラリ)開発の
仕事をしたことがありました。

<グラフィックエンジンの提供機能(ざっくり)>
(1)基本図形の描画
(点、一本の線、複数の接点を持つ連続した線
 四角形、多角形、円、楕円、文字など)描画
※基本図形の属性として色、
 閉じる要素(四角形、多角形、円、楕円)は塗りつぶし属性
 などが追加となります。
(2)基本図形をディスプレイツリーにグループ化
※この時、各図形のID番号、呼び出し時の座標情報
 各図形の占める領域の左上端、右下端の矩形
 座標を登録。
 また、グループ全体として占める矩形を記憶
(3)指定図形グループの移動、回転、拡大、縮小、削除
(4)画面上に描画された図形をユーザが
   マウスでタッチした場合、どのグループが
   タッチされたかアプリケーションにIDを返す。
(5)再描画(REDRAW)指令を受けたら
   現在の画面領域に含まれる描画要素を
   ディスプレイツリーから読みだして画面に描画する。
3Dゲームのエンジンとしては、
Direct3D
・VALCAN
OpenGL
・UNITY
UNREAL
FARCRY
など、百花繚乱ですが、基本的な発想は
2次元のCADエンジンと似たようなものです。
(X、YにZ軸が加わるのと、
 3D表示するためのポリゴン分割処理、シェーディング処理とか
 一次元の追加で座標変換、大幅に処理は重くなるでしょうが。。)
ゲームが遅いとイライラするのと同様に
CADも画面の描画が遅い、ローディングが遅く
待たされるとイライラします。
そして、エンジンを使用するアプリケーション
に負担をかけないように
・メモリーを食わない。
・速度は限界までチューニング
といった制約条件の中での開発となります。
WindowsLINUXなどのOSにはOSの基本機能として
(ファイル入出力、メモリ管理、など)
がシステムCALLと呼ばれる関数が提供されています。
メモリ食わない、速度限界までチューニング
アプリケーションに迷惑かけないという
制約条件に対しては、
応答性に影響を与える
OSのシステムコールの使用は禁止となりました。
つまり、メモリ管理系のシステムCALL
LINUXで言うとmalloc,freeは使用禁止ということです。
それじゃどうしたらいいの?というと
static(静的割り当て)で大きな配列を定義し
その配列へ描画要素のデータを格納するという
普通に考えて??ってことになりました。
巨大な固定配列に対してmalloc,freeをエミュレートする
関数をこしらえます。
ディスプレイツリーに登録(グループ化)指定された
図形があるとそのメモリに登録します。
例えば疑似コードで書くと
OpenGroup(グループ番号)
|-線
|-円
|-四角
CloseGroup()
イメージがピンとこないかもしれませんが、
例えば、プリント基板CADには設計の部品ライブラリ
(IC、コンデンサキャパシタなど)があり
部品をプリント基板に配置します。
配置する一つ一つの部品形状は色々な形状で
構成されますがそれが上記の図形グループの
一つとなります。
ゲームエンジンだと、例えば主人公の持つアイテム、
RPGの中の建造物など3次元の要素が加わりますが
基本的に似たようなもんかと思います。
<★データのロードの場合>
ディスクに格納されたデータを読み込み
アプリケーションから指定された単位で
ディスプレイグループを生成して、画面に表示
<★画面描画の場合>
画面を拡大、縮小、移動させた場合
定義された図形グループを画面に表示させるかさせないか
はグループの占める領域の矩形が画面の領域に
含まれるか否か?で決めます。
指定範囲外の座標に含まれる図形グループは
画面に表示しません。
RPGで、主人公がいろんな場所を歩き回ったりする場合
主人公からみて画面から見えなくなる要素は
表示処理の対象外になります。
<★ユーザが画面で部品をタッチした場合>
タッチされた座標上の存在する図形グループのIDが
アプリケーションに渡されます。
アプリケーションはそのIDで図形グループを指定し
移動・回転・削除などの処理をおこないます。
例えば、
シューティングゲームでは、打った弾丸の
 着弾地点の座標に敵キャラの図形が含まれた場合
⇒アプリケーションには、敵キャラの図形のIDが
⇒返されます。ここでその図形が敵の体などの場合は
⇒敵のダメージ処理(血が出る、体が粉砕など)をします。
・プリント基板CADでは、プリント基板上の
 IC部品が選択されたものとしてそのIC部品の
 グループ番号がアプリケーションに返され
 アプリケーション側でその部品を移動・回転・消去
 などの処理をします。
描画の高速化のために
・メモリ管理は自前でやりOSの関数は使わない。
他に、描画速度を少しでも稼ぐため
・やらなくてもいい処理は
 後でまとめてやる(遅延評価処理)
⇒これをやらないとデータのローディングにえらい時間がとられる。
・最後の描画処理は、OSの描画関数をCALLせざおえないが
 できるだけ、まとめて描画命令を出す(バッファリング処理)
・一度だけ計算すればよい処理は、処理済フラグを立てて
 二度と計算させない。(無駄に同じ処理を繰り返させない)
⇒画面の描画速度に影響
・CPUの癖、コンパイラの癖を把握して
 幾何演算の方法、順番を工夫する。
コンパイラが出力したアセンブラのコードを見て
 高速化のため、計算の順番を変える。
・数値演算関数は安易にCALLしない。
⇒例えば、図形の回転の際は、SIN,COS関数を使いますが
 数値演算関数は、安易に呼び出すと
 とんでもなくCPUパワーを食います。
 アプリケーションが0、45、90、135、180程度しか
 部品を回転させなら、角度に応じたSIN,COSの
 計算結果をあらかじめテーブルで保持しておき
 座標変換時にテーブル参照で計算すれば
 CPUパワーは大幅に抑えられます。
 めったにしかやらないレアケース以外は、
 数値演算関数はCALLせず極限まで無駄な演算を削る。
⇒精度が要求されない部分は倍精度でなく単精度にする。
⇒自前のメモリ管理で登録した描画グループの
 検索には、INDEX検索、バイナリーツリー検索を用いる。
RDB(リレーショナルデータベース)基本ですね。
色々、勉強させていただいたお仕事でした。
では・・また・・:_;)/