卵の観察日記
[内定が 決まって 浮かれている 卵 だ。]

テクスチャを管理しました

atori
記事: 43
登録日時: 13年前

テクスチャを管理しました

投稿記事 by atori » 12年前

今までアドバイスをたくさんもらったので、それを参考に作ってみました。
TextureManagerクラスと、Texture2Dクラスがあります。

クラス設計の授業で「friend指定はまだ早い」と言われていましたが、使いました。
自分的には問題ないと思うのですが、とりあえずはソースを。


[Texture2D.h]

CODE:

#pragma once

#include 

class CTextureManager;

class CTexture2D
{
private:
	friend CTextureManager;

	LPDIRECT3DTEXTURE9	m_pTexture2D;		// テクスチャデータ格納変数
	bool	m_isUse;						// 使用中フラグ(フラグが立っていると、描画される)

private:

	CTexture2D(void);
	virtual				~CTexture2D(void);

	void				Load(LPDIRECT3DDEVICE9 pd3dDevice, LPSTR fileName);	// テクスチャロード関数
	bool				CheckIsUse(void)const;
	LPDIRECT3DTEXTURE9	GetTexture(void)const;								// テクスチャ取得関数
	bool				Delete(void);

public:
	void				Use(void);
	void				UnUse(void);
};

[Texture2D.cpp]

CODE:

#include "Texture2D.h"


CTexture2D::CTexture2D(void)
{
	m_pTexture2D = NULL;
	m_isUse = false;
}

CTexture2D::~CTexture2D(void)
{
	// テクスチャ開放
	if(m_pTexture2D != NULL)
	{
		m_pTexture2D->Release();
		m_pTexture2D = NULL;
	}
	m_isUse = false;
}

// 2Dテクスチャをロード
void CTexture2D::Load(LPDIRECT3DDEVICE9 pd3dDevice, LPSTR fileName)
{
	if(FAILED( D3DXCreateTextureFromFile( pd3dDevice, fileName, &m_pTexture2D)))
	{	// テクスチャが読み込めなかったらエラー
		MessageBox(NULL, "Failed Texture Load...", "Warning!!", MB_ICONWARNING);
		exit(1);
	}
}

// テクスチャの取得
LPDIRECT3DTEXTURE9 CTexture2D::GetTexture(void)const
{
	return m_pTexture2D;
}

// テクスチャ使用開始
void	CTexture2D::Use(void)
{
	if(m_pTexture2D != NULL)
		m_isUse = true;
}

// テクスチャの使用終わり(削除はしない)
void	CTexture2D::UnUse(void)
{
	m_isUse = false;
}

bool	CTexture2D::CheckIsUse(void)const
{
	return m_isUse;
}

// テクスチャの削除
bool	CTexture2D::Delete(void)
{
	if(m_isUse)
		return false;

	m_pTexture2D->Release();
	m_pTexture2D = NULL;
	return true;
}	
[TextureManager.h]

CODE:

#pragma once
#include 
#include "Game.h"
#include "Texture2D.h"

class CTextureManager
{
private:
	LPDIRECT3DDEVICE9	m_pd3dDev;
	CTexture2D			m_texAry[GAME_RESOURCE_NUM];
	CTexture2D*			m_pFrontTex;

	int				m_lastResIndex;		// 配列の中で、リソースが入っている最後の要素
	int				m_accessTexID;		// ID_NONEのときは、アクセスしていないことを意味する

public:
	CTextureManager(LPDIRECT3DDEVICE9 pd3dDev);
	~CTextureManager(void);

	int		Load(LPTSTR	fileName);
	void	Delete(int id);

	CTexture2D&	Access(int id);	// 指定したIDのテクスチャにアクセスする

};
[TextureManager.cpp]

CODE:

#include "TextureManager.h"
#include

CTextureManager::CTextureManager(LPDIRECT3DDEVICE9 pd3dDev)
{
	m_pd3dDev = pd3dDev;
	memset(m_texAry, 0, sizeof(m_texAry));
	m_pFrontTex = m_texAry;
	m_accessTexID = ID_NONE;
}


CTextureManager::~CTextureManager(void)
{
}

// テクスチャをロード
int	CTextureManager::Load(LPSTR fileName)
{
	
	m_lastResIndex++;
	// これ以上配列に入らなかったら
	if(m_lastResIndex > GAME_RESOURCE_NUM)
	{
		m_lastResIndex--;
		return ID_NONE;
	}

	// ロード
	m_texAry[m_lastResIndex].Load(m_pd3dDev, fileName);
	return m_lastResIndex;
}

// テクスチャ削除
void	CTextureManager::Delete(int id)
{
	assert(id >= 0 || id = 0 || id < GAME_RESOURCE_NUM);	// 無効なIDチェック
	assert(m_texAry[id].GetTexture() != NULL);	// テクスチャが存在するかチェック

	return m_texAry[id];
}

TextureManager.hでインクルードしているGame.hでは、GAME_RESOURCE_NUMとID_NONEを定義いるだけです。
GAME_RESOURCE_NUMは、ゲーム内で同時に読み込めるテクスチャの最大数です。
ID_NONEはdefineで-1を定義しています。

CTexture2Dは、ユーザがインスタンス化できないようにしておきます。
また、DeleteやLoadも行えないようにします。
あくまで、テクスチャをいじれるのはTextureManagerからのみです。
使い方としては、TextureManagerクラスをインスタンス化して、Loadメソッドを呼び出します。
IDが返ってくるので、それをどこかに保存しておき、そのIDをAccessメソッドの引数にすることで、テクスチャにアクセスできるようになっています。

CODE:


void Hoge(void)
{
    CTextureManager(g_pd3dDev) texMng;
    CTexture2D   tex;   // エラー。


    int id = texMng.Load("resource/foo.png");
    texMng.Access(id).Use();
    texMng.Access(id).Delete();   // エラー。
    
}

後はTextureManagerクラスに描画を追加する予定で、そのためには頂点情報をどこで処理するのがいいかなと考えています。
この設計はいかがでしょうか・・・?


追記:
私も馬鹿なもんで、テクスチャとTexture2Dクラスは一対一である必要はないと考えていませんでした。
テクスチャ削除したらクラスごと削除する、ってやってました。

アバター
せんちゃ
記事: 50
登録日時: 15年前

Re: テクスチャを管理しました

投稿記事 by せんちゃ » 12年前

おお、かなりいい感じになってきましたね。
friendの使い方も見せるところだけに見せているので間違った使い方でもないと思います
マネージャー層でバリデーションもちゃんとやってますし。

今後の課題はAccess()やUse、UnUseという関数の存在を利用者が意識しなくてもいいようにするという点ですね。
Loadをよんだら番号を貰って、あとは利用者はDrawとかDeleteという関数に番号を投げるだけで指定のテクスチャが描画されるような仕組みにすると
(つまり番号からマネージャー側がどこのリソースが割り当てられていて、何を描画するのか判別できるようにする)

あとは細かい点ですが、リソースは未割当てでも呼ばなきゃいけないケースが結構あるので
IDが不正でもassertせずに何も表示しない、何も値を返さない
みたいな作りにするとアプリ側はただメソッドを呼ぶだけで良い作りになるのでタイトになりますよ。

atori
記事: 43
登録日時: 13年前

RE: テクスチャを管理しました

投稿記事 by atori » 12年前

Access(),Use(),UnUse()もやめたほうがいいですか。

自分は、TextureManagerでどのシーンも関係なく1つでまとめて管理するつもりでいて、
Use()が呼ばれているテクスチャは削除させない。と言った風に管理したかったのですが・・・。

テクスチャを管理するのと、プールは分けたほうがいいということなのでしょうか。

アバター
せんちゃ
記事: 50
登録日時: 15年前

RE: テクスチャを管理しました

投稿記事 by せんちゃ » 12年前

atori さんが書きました: Access(),Use(),UnUse()もやめたほうがいいですか。
自分は、TextureManagerでどのシーンも関係なく1つでまとめて管理するつもりでいて、
Use()が呼ばれているテクスチャは削除させない。と言った風に管理したかったのですが・・・。
その方法を間違っている、と言っているわけではないですね。
その機能を利用者が気にしなければいけない、という状態があまりよくないです。
例えば現状ですとUse、UnUse関数を呼ばないとリソースが使えない形式になっていますが、
これがリソースを30も40も使うシーンになったとき、すべての利用状態を利用者が意識しなくてはいけない上に、
解放時に利用者が任意にUseを呼んだ数だけUnUseを呼ばなくてはいけません。

間違えなく正確にこれらを呼ぶのは大変です。
それも原因を突き止めにくいタイプのバグに繋がるので、こういった機能は下でやれるようにする必要がありますね。
逆に言えば利用者に見えないようにしていればアリだと思います

atori
記事: 43
登録日時: 13年前

RE: テクスチャを管理しました

投稿記事 by atori » 12年前

確かにそのとおりですね。
どう管理してるか知らないけれども、ユーザはIDだけ知っておけば正しく動いてくれるというのが理想ということですね。

わかりました。再度組み直してみます。ありがとうございました。

ISLe
記事: 2650
登録日時: 15年前

Re: テクスチャを管理しました

投稿記事 by ISLe » 12年前

こういうのは参照カウント方式にすると便利ですよ。

atori
記事: 43
登録日時: 13年前

RE: テクスチャを管理しました

投稿記事 by atori » 12年前

参照カウントというのは・・・テクスチャごとに参照される度にカウントアップしていき、
参照が終わる度にカウントダウンしていって、カウンタが0になったらテクスチャを削除するといった感じなのでしょうか?

ISLe
記事: 2650
登録日時: 15年前

Re: テクスチャを管理しました

投稿記事 by ISLe » 12年前

そういうのです。
例えばスマートポインタを使えば作成できるインスタンスの上限は無くなるしID管理も不要です。
スマートポインタで参照しているあいだは所有権があるということなので分かりやすいです。
最後に編集したユーザー ISLe on 2013年7月21日(日) 23:23 [ 編集 1 回目 ]

atori
記事: 43
登録日時: 13年前

RE: テクスチャを管理しました

投稿記事 by atori » 12年前

ISLe さんが書きました:例えばスマートポインタを使えば作成できるインスタンスの上限は無くなるしID管理も不要です。
スマートポインタで参照しているあいだは所有権があるということなので分かりやすいです。
返信が遅くなってしまって申し訳ないのですが、質問があります。
インスタンスの上限がなくなるというのはわかるのですが、ID管理が不要というのがよくわかりません。
IDがないとどのテクスチャを指すのかわからなくなるのではないでしょうか・・・?
それともスマートポインタ自身がIDの代わりになるということでしょうか?

ISLe
記事: 2650
登録日時: 15年前

RE: テクスチャを管理しました

投稿記事 by ISLe » 12年前

atori さんが書きました:それともスマートポインタ自身がIDの代わりになるということでしょうか?
そうです。スマートポインタ自身がIDの代わりになります。

スマートポインタを使わないとしても、typedefした型名を使っておいたほうが変更に強くなります。
そういう抽象化されたIDを『ハンドル』と呼んだりしますが。

atori
記事: 43
登録日時: 13年前

RE: テクスチャを管理しました

投稿記事 by atori » 12年前

なるほど。ハンドルというのですか・・・。

私のサンプルで言うならば、Load()でCTexture2Dをスマートポインタで動的に作って、テクスチャをロードする。
スマートポインタ(typedefしたやつ)を戻り値にして、そしてその戻り値を引数にDraw()などを呼べば良い感じですね。


また改良したものを上げます。ありがとうございました。