|
昨日の記事を掲載したところ、途端に反響が来たので紹介させていただきます。
中でも、長大なソースコード付きのお手紙を下さったのは、3D野郎大会でPSのプログレッシブ・ランドスケープを披露し、しかもビンゴの景品、VisualStudio97までかっさらっていったgakuさんです。
ちゅーす、いささかハイテンションのガクです。覚えておいででショーか。shi3z
クンは元気にしてるかナー。ただいまプライベートメールアドレスが使用不可で
比較的怒号な状況なので、会社用メールアドレスにて失礼いたしまス。
4/16付のPrejudice、読ませていただきました。極めて研究熱心な様子が伺えて、
オレとしてはオチオチ仕事などしている場合ではないですなというような感じで
すが、仕事で悪戦苦闘中ですので、趣味にもオチオチしておられません。そんな
わけで、家に帰っては、わずかばかりの時間でglideと悪戦苦闘しておりますが、
やはり「如何にしてレンダリングステートの変更を押さえるか」というところで、
さらに悪戦苦闘の真っ最中です。その前に英語マニュアルで悪戦苦闘です。もう
悪戦苦闘なので、ダレカタすケテー。
それはさておき本題の「二重主従関係のジレンマ」ですが、フレームからはメッ
シュを直接参照せずに間接的に参照することで、メッシュが主、フレームが従、
とユー感じにしちゃえば二重主従関係でなくなるのではと思い、せっせとコード
を書き始めたのですが、あぁっぅ、なんか最後の方に
「もちろん、直接的にオブジェクトを扱おうとせず、オブジェクトを扱うためだ
けの偽のリストを使えば可能ですが、それは余計なオーバーヘッドを増やしてし
まうため、あまり使いたくありません(といっても一回ポインタを読み直す程度の
ものですが)。」
なんて書いてあるし。よーするにオレが考えたのはそーゆーことです。せっかく
なんで一応コードもつけてみました。解読しづらいかもしれませんが、ご勘弁を。
でも、これってそんな気にするほどのオーバーヘッドではないと思いますけど、
如何でしょう。オレ的には、こっちよりもテンプレートのほうが足をひっぱりそ
うな気がします。PSx用gnuコンパイラやCodeWarriorでは、テンプレートで記述さ
れたクラスのメンバ関数は、たとえinline指定をしていても絶対にインライン展
開されないようなので、キャッシュに乗りにくいかなぁというワケなのです。た
だし、この辺りはコンパイラの仕様でしょうから、また他のコンパイラとなると
状況は変わってくるかもしれませんが。っていうか、他のコンパイラではどんな
感じなんでしょーかね。
あと、「一つのオブジェクトが複数のリストに従属」するには、多重継承がわ
りと有効ではないかと。ただ、多重継承っていう技自体がスマートじゃないよう
な気もしますが、オレ的には許容範囲内なので、控えつつもそれなりに多重継承
を使用しています。
そろそろ下校時刻となってしまったので、家に帰ってしまいますので、そろそ
ろおイトマいたします。というわけで、今後とも愉快な記事を楽しみにしており
ますので、大々的にハッスルしてください。どうでもいいですが、Prejudiceに
てストレッチマンを取り上げていただけるとうれしいです。オレもストレッチ
マンの大ファンなんスよ。というわけで、失礼確実な一読者が長々とお送りし
てみました。ではでは。
Gaku Tanimoto
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\ mailto:gaku01@tyo.co.jp
TYO / digital frontier / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\ http://www.tyo.co.jp/
TEL:03-3794-2471 / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\ FAX:03-3794-2472
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|
とかなりハイテンションなお手紙を頂戴しました。
それにしてもストレッチマンと来た。ストレッチマン。NHKに受信料払うとストレッチマンのギャラになってるのかと思うとかなりシオシオですが、ストレッチマンとかペペロンチーノ王子とか、グルグルとか、子供が嫌がりそうなキャラクターを良くもNHK教育で出すものです(最初、「よい子っち」かとおもいました)。
しかしストレッチマンのファンということは、gakuさんは一体何時に出社なさってるんでしょう。ストレッチマンってわりと11時くらいだったと思うんですけど。それとも会社でご覧になってるんでしょうか。
それはさておき閑話休題、二重の従属関係のことなのですが、BBSのほうではREKIさんの発言にレスする形で一応の回答を示していたのですが、なんかgakuさんが凄い長いのをパパパっと送ってくださったので、とにかく紹介します(こんな短時間ですごい!)
//- 型定義 ---------//
//............. 双方向連結リスト ...........................................//
class GtDoubleLinkList
{
protected:
GtDoubleLinkList* mPrev; // 前の要素へのポインタ
GtDoubleLinkList* mNext; // 次の要素へのポインタ
protected:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtDoubleLinkList( void )
{
// コードは省略
}
//--------------------------------------------------------------------
// thisの次に要素を挿入:
//--------------------------------------------------------------------
void InsertNext(
GtDoubleLinkList* aComp // 挿入する要素
){
// コードは省略
}
//--------------------------------------------------------------------
// thisの前に要素を挿入:
//--------------------------------------------------------------------
void InsertPrev(
GtDoubleLinkList* aComp // 挿入する要素
){
// コードは省略
}
//--------------------------------------------------------------------
// thisをリストから切り離す:
//--------------------------------------------------------------------
void Detach( void )
{
// コードは省略
}
};
//............. 双方向連結ツリー ...........................................//
class GtTreeNode :
public GtDoubleLinkList
{
protected:
GtTreeNode* pParent; // 親ノードへのポインタ
GtTreeNode* pChild; // 子リスト先頭ノードへのポインタ
public:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtTreeNode( void ):
GtDoubleLinkList()
{
// コードは省略
}
//--------------------------------------------------------------------
// 親に接続:
//--------------------------------------------------------------------
void Attach(
GtTreeNode* pParent // 新しい親ノード
){
// コードは省略
}
};
//............. メッシュ参照体 .............................................//
class GtMeshRefer :
public GtDoubleLinkList
{
protected:
GtFrame* mFrame; // メッシュ
public:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtMeshRefer( void ):
GtDoubleLinkList()
{
mFrame = 0;
}
//--------------------------------------------------------------------
// デストラクタ:
//--------------------------------------------------------------------
~GtMeshRefer()
{
// メッシュ参照リストからthisを切り離す
Detach();
}
//--------------------------------------------------------------------
// メッシュに接続:
//--------------------------------------------------------------------
void Link(
GtMesh* pMesh, // 参照するメッシュ
GtFrame* pFrame // フレーム
){
mFrame = pFrame;
pMesh- > Link( this );
}
}
//............. フレーム基底クラス .........................................//
class GtFrame :
public GtTreeNode
{
protected:
GtMatrix mLocal2World; // ローカル座標系から絶対座標系への変換行列
protected:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtFrame( void ):
GtTreeNode()
{
pParent = 0;
}
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtFrame(
GtFrame* pParentFrame // 親となるフレーム
):
GtTreeNode()
{
// 親に接続する
Attach( pParentFrame );
}
};
//............. メッシュフレーム ...........................................//
class GtMeshFrame :
public GtFrame
{
protected:
GtMeshRefer mMeshRefer; // メッシュ参照体
public:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtMeshFrame(
GtFrame* pParentFrame, // 親となるフレーム
GtMesh* pMesh // 参照するメッシュ
):
GtFrame( pParentFrame )
{
mMeshRefer- > Link( this, pMesh )
}
};
//............. メッシュ ...................................................//
class GtMesh {
protected:
GtMeshFrame* mLastRefer // メッシュ参照リストの末端ノード
public:
//--------------------------------------------------------------------
// コンストラクタ:
//--------------------------------------------------------------------
GtMesh( void )
{
// リストを空にする
mLastRefer = 0;
}
//--------------------------------------------------------------------
// デストラクタ:
//--------------------------------------------------------------------
~GtMesh()
{
// 参照体リストが空ではないか判定
if( mLastRefer != 0 ){
// リストを分解する
// コードは省略
}
}
//--------------------------------------------------------------------
// メッシュ参照体を参照リストに接続:
//--------------------------------------------------------------------
void Link(
GtMeshRefer* pRefer
){
// リストが空でないか判定
if( mLastRefer != 0 ){
mLastRefer- > InsertNext( pRefer );
}
// 末端要素の更新
mLastRefer = pRefer;
}
//--------------------------------------------------------------------
// 描画:
//--------------------------------------------------------------------
void Draw( void )
{
GtMeshRefer *aMesh;
if( mLastRefer != 0 ){
aMesh = mLastRefer- > mNext;
do{
// フレームの座標系を読み出して描画
// コードは省略
// 次のメッシュへ
aMesh = aMesh- > mNext;
}while( aMesh != mLastRefer );
}
}
};
//- 変数 -//
GtMesh sMeshList[4]; // メッシュ配列
unsigned int sNumMesh = 4; // 上記配列の要素数
//-------- 関数 -------------------------------------------------------//
//-----------------------------------------------------------------------------
// かなりテキトーなサンプル:
//-----------------------------------------------------------------------------
void SampleCode( void )
{
GtFrame *aRootFrame; // ルート
GtMeshFrame *aMeshFrame[4]; // メッシュフレーム
// ルートの生成
aRootFrame = ::new GtMesh();
// フレームツリーの組み立て
aMeshFrame[0] = ::new GtMeshFrame( aRootFrame, sMeshList + 0 );
aMeshFrame[1] = ::new GtMeshFrame( aMeshFrame + 0, sMeshList + 1 );
aMeshFrame[2] = ::new GtMeshFrame( aMeshFrame + 0, sMeshList + 1 );
aMeshFrame[3] = ::new GtMeshFrame( aRootFrame, sMeshList + 2 );
// メインループ
while( 1 ){
// 必要に応じて各フレームの座標系を変更
// コードは省略
// 描画
aMesh = sMeshList + sNumMesh;
while( sMeshList < aMesh ){
aMesh--;
aMesh- > Draw();
}
}
}
|
レイによって良く読みたい人は隣のコードをカット・アンド・ペーストしてエディターかなんかに貼り付けてください。
ふむふむ。
ざざっと呼んだところ、gakuさんは継承によって多重の従属関係を解消しているようです。
たしかにひとつのテではあるのですが、これだと、このコードのように、双方向リンク(と単方向リンクをあわせて線形リンクと呼ぶことにします)とツリーリンクの組み合わせはできても、双方向リンクと双方向リンクの組み合わせはできないんじゃないかと思います。つまり用途が限られてしまう、と。
昨日は単なる一例であって、管理上の問題から、あとになって従属関係が増えたり減ったりすることも考えられます。
たとえば面倒なので書かなかったのですが、パケットは実行順の他に、Zオーダー順の並べ替えも必要になりますし、Zオーダー順に並べ替えつつどのパケットを実行して、どのパケットを実行しないかという別のキュー(線形リンク)も必要になります。
僕としては、このような線型リンクも階層型リンクも、あとでどんどん増やしてもビクともしないものが作りたかったのですよねー。
しかし、どうもお便りによると、PS用のGCC(GNU C Compiler)やCodeWarriorではテンプレートのインライン関数が展開されないと。うーん、それは問題ですな。やはり2メガしかメモリがないから極力コードを膨らまさないように働くのでしょうか。
とりあえず次の節で僕の考えたやりかたを示します。
|
|
|
|
template < class T >
class vsFamilyListMirage{
T *real;
vsFamilyListMirage *next,parent,child;
public:
vsFamilyListMirage(){
real = NULL;
}
vsFamilyListMirage(T *nodeReal){
real = nodeReal;
}
void setReal(T *nodeReal){
real = nodeReal;
}
T inline *getReal(){
return real;
}
vsFamilyListMirage < class T > inline &getNext;(){
return next;
}
vsFamilyListMirage < class T > inline &getChild;(){
return child;
}
void inline addChild(vsFamilyListMirage < class T > &listChild;){
listChild.next = child;
child = listChild;
child.parent = this;
}
void inline addNext(vsFamilyListMirage < class T > &listNext;){
listNext.next = next;
next = listNext;
}
};
template < class T >
class vsMonoListMirage{
T *real;
vsMonoListMirage < class T > *next;
public:
vsMonoListMirage(){
real = NULL;
}
vsMonoListMirage(T *nodeReal){
real = nodeReal;
}
void setReal(T *nodeReal){
real = nodeReal;
}
vsMonoListMirage < class T > inline &getNext;(){
return next;
}
void inline addNext(vsMonoListMirage < class T > &listNext;){
listNext.next = next;
next = listNext;
}
};
class vsFrame{ // フレームクラス
vsFamilyListMirage < vsFrame > familyList;
static vsFrame tempFrame;
public:
struct axis{
vsVector x,y,z,p; // 座標軸
}
vsFrame(){
frameNext = frameChild = NULL;
frameParent = NULL;
familyList.setReal(this);
}
void inline add(vsFrame &frame;){ // 子フレームを追加
familyList.addChild(frame);
}
};
|
右がちょっと書いてみたコードです。BBSで示したものをより発展させたものです。でもコンパイルのテストはしてないので動くかどうかわかりません(ガーン)。
これはテンプレートを採用しています。
まず、出てくるテンプレートとクラスについて説明します。
最初に宣言されているvsFamilyListMirageテンプレートクラスは、いわゆる階層リスト構造を示すためのクラスです。ポイントは最初に宣言されているrealというポインタメンバで、これが管理するオブジェクトへのポインタを持ちます。
ListMirageという名前は、「リスト(ノード)の幻」というような意味あいです。実体はrealで示されるわけです。
このテンプレートクラスの場合、実体(realメンバ)をコンストラクタのパラメータとして渡すか、setRealメソッドを呼び出すかします。
もうひとつ、似たやつでvsMonoListMirageという、単方向線型リストのテンプレートも定義します。
これを実際に使っているのがvsFrameで、ここではひとつの従属関係しか指定していませんが、vsFamilyListMirage < vsFrame > 型のfamilyListメンバがそれです。
このようにしてテンプレートを使うと、複数の従属関係をかなり簡潔に扱うことができるのではないかと自負しています。
ただ、リストをたどるとき、目的のノードに辿り着いたかどうか調べるためにいちいちrealメンバを経由して調べなくてはならないのが玉に傷ですね。このオーバーヘッドがどのくらいなのかわかりませんけど、直接記述するのには適わないことは確かです。
ご意見、御感想をおまちしいてます。
|
|
|