/*----------------------------------------------------------------------

    Cube map application main sample

    Copyright (C) 2008 SEGA Corporation Creative Center Graphics Sect.
    All Rights Reserved.

    Module  : Cube map application main sample
    File    : smpapp.cpp
    Create  : 2008/06/24 (Ver.0.10.00)
    Modify  : 2008/07/18 (Ver.0.30.01)
	            nnRegistPliableObjectStdShaderProfiles を追加
			  2008/09/10 (Ver.0.30.02)
			    NNS_OBJECTのメモリ解放忘れの修正。
				NNS_OBJECTのメモリを取得して処理する方法に変更。
			  2008/09/12 (Ver.0.30.03)
                nnResetVertexDeclarationDXG20 を追加。
			  2008/11/04 (Ver.0.30.04)
			    nnExitVertexBufferDXG20 の記述忘れ。
				nnCalcVertexBufferObjectSizeDXG20 に変更。
			  2008/11/14 (Ver.0.30.05)
			    プラットフォーム別処理関数を smpmain.cpp へ移動した。
				nnGetDirect3DDeviceDXG20, nnGetD3DPresentParametersDXG20 に変更。
				D3DUSAGE_DYNAMIC を D3DUSAGE_WRITEONLY に変更。
			  2008/11/17 (Ver.0.30.06)
			    MODEL 構造体のメンバにあるマトリクスをポインタに変更(マトリクスは align(16) されているのでポインタにした)。
			  2008/12/12 (Ver.0.30.07)
				D3DUSAGE_WRITEONLY を D3DUSAGE_DYNAMIC に変更(2008/11/14の変更をもとに戻した)。
			  2009/01/19 (Ver.0.30.08)
	            smpSetIncludePathDXG20 関数を追加。
			  2009/01/21 (Ver.0.30.09)
			    nnSetCompileErrorCallbackStdShaderDXG20 関数を追加。
			  2009/06/11 (Ver.0.30.10)
			    プラットフォーム別処理関数(IDirect3DDevice9_CreateVertexBuffer, IDirect3DVertexBuffer9_Release)を smpmain.cpp へ移動した。
    Version : 0.30.10
    Note    : 

----------------------------------------------------------------------*/

#define __SMPAPP_CPP__

//--------------------
// 組み込みファイル名
//--------------------
#include <stdio.h>
#include "smpapp.h"
#include "smputil.h"
#include "scene.h"
#include "cameractrl.h"

#define _smpRelease(p)      { if(p) { (p)->Release(); (p)=NULL; } }

////////////////////////////////////////////////////////////////////////////////
#define SAMPLENAME "CUBEMAP"

#define DEBUG_CHAR_MAX	(1200)

////////////////////////////////////////////////////////////////////////////////
NNS_MATRIXSTACK			gMtxStack;				// マトリクススタック

static void				*pPrintBuf;				// デバッグ文字用バッファ(メモリの解放が必要)

static void				*pMtxStackBuf;			// マトリクススタックバッファ先頭(メモリの解放が必要)

// 標準シェーダ用
static void				*pShaderMgrBuf;			// シェーダーマネージャバッファ領域(メモリの解放が必要)
static Uint32			VsCodeSize;				// 頂点シェーダコードサイズ
static Uint32			PsCodeSize;				// ピクセルシェーダコードサイズ
static void				*pVsCodeBuf;			// 頂点シェーダコードバッファ領域(メモリの解放が必要)
static void				*pPsCodeBuf;			// ピクセルシェーダコードバッファ領域(メモリの解放が必要)

static SMPPAD_STATUS	*pPad1, *pPad2;			// コントローラ情報

////////////////////////////////////////////////////////////////////////////////
// NN data table
typedef
	struct{
		NNE_BOOL		RenderToTexFlg;	// NNE_ON のときテクスチャに描画される

		NNS_VECTOR		Scaling;		// モデルのスケール
		NNS_VECTOR		Translation;	// モデルの座標

		char			*pObjFileName;
		char			*pObjMotFileName;
		char			*pMatMotFileName;
	}MDLDATA;

// MDLDATA
static MDLDATA NNDataTbl[] = {
	// 必ず１番目にキューブマップが貼られるオブジェクトを設定しNNE_OFF する
	{ NNE_OFF, { 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, "cube_sample_GeoSphere01.zno", NULL, NULL },
	{ NNE_ON,  { 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, "cube_sample_Box01.zno", NULL, NULL },
	{ NNE_ON,  { 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, "cube_sample_Teapot01.zno", "cube_sample_Teapot01.znm", "cube_sample_Teapot01.znv"}
};
static Sint32	NbDataTbl = sizeof(NNDataTbl)/sizeof(NNDataTbl[0]);

////////////////////////////////////////////////////////////////////////////////
// SCENE
static SCENE *SampleScn;				// シーン管理オブジェクト
static CAMERA gCameraBack;

#define NB_LIGHT	2
#if ( NB_LIGHT >= 2 )
#undef NB_LIGHT
#define NB_LIGHT 2
static const NNF_DRAWOBJ gDrawFlag = NND_DRAWOBJ_FRAGPARALIGHT2;			// フラグメントライティング（パラレルライト×2)
#else
static const NNF_DRAWOBJ gDrawFlag = NND_DRAWOBJ_FRAGPARALIGHT1;			// フラグメントライティング（パラレルライト×1)
#endif

static NNS_MATRIX		*pMtxPalMax;	// 最大マトリクスパレット

static NNS_MATRIX44		gProjmtx;		// プロジェクションマトリクス

////////////////////////////////////////////////////////////////////////////////
// キューブマップ関係
#define CUBE_MAP_SIZE	64

typedef
	struct{
		D3DCUBEMAP_FACES	Target;		// DirectXのテクスチャターゲットの指定
		NNS_VECTOR			Rotation;	// 方向
    }CUBEMAPINFO;

typedef
	struct{
		Sint32					MapSize;

		NNS_MATRIX44			ProjMatrix;

		LPDIRECT3DCUBETEXTURE9	pCubeMap;

		Sint32					NbInfo;
		CUBEMAPINFO				*pInfo;
    }CUBEMAPTEX;

static CUBEMAPTEX CubeMapTex;

#define NB_CUBEMAPINFO	6
static CUBEMAPINFO CubeMapInfo[NB_CUBEMAPINFO] = {
	// 回転はカメラの向き
	{ D3DCUBEMAP_FACE_NEGATIVE_X,	{  0.0f,  90.0f,   0.0f} },	// 左
	{ D3DCUBEMAP_FACE_NEGATIVE_Z,	{  0.0f, 180.0f,   0.0f} },	// 後
	{ D3DCUBEMAP_FACE_POSITIVE_X,	{  0.0f, -90.0f,   0.0f} },	// 右
	{ D3DCUBEMAP_FACE_POSITIVE_Z,	{ 0.0f,   0.0f,    0.0f} },	// 前
	{ D3DCUBEMAP_FACE_POSITIVE_Y,	{ 90.0f,   0.0f,   0.0f} },	// 上
	{ D3DCUBEMAP_FACE_NEGATIVE_Y,	{-90.0f,   0.0f,   0.0f} },	// 下
};

////////////////////////////////////////////////////////////////////////////////
LPDIRECT3DDEVICE9 g_pD3DDev = NULL;

////////////////////////////////////////////////////////////////////////////////
//------------------------
// カメラコントロール関係
//------------------------
#define FRAMEIN_CAMERA

////////////////////////////////////////////////////////////////////////////////
//-------------
// NN の初期化
//-------------
static void InitNN(void)
{
	g_pD3DDev = (LPDIRECT3DDEVICE9) nnGetDirect3DDeviceDXG20();

	// デバッグプリント初期化
	Uint32 size;
	size      = nnGetPrintBufferSize( DEBUG_CHAR_MAX );
	pPrintBuf = smpMemAlloc( size );
	nnInitPrint( pPrintBuf, DEBUG_CHAR_MAX, NULL );

	// マトリクススタック領域の確保、設定
	pMtxStackBuf = smpMemAlloc( MATRIX_STACK_SIZE * sizeof( NNS_MATRIX ) );
	nnSetUpMatrixStack( &gMtxStack, pMtxStackBuf, MATRIX_STACK_SIZE );

	// 標準シェーダーの初期化
	{
		NNS_STDSHADER_CONFIG shaderconfig;
		Uint32				 size;

		// 標準シェーダマネージャの初期化
        // 
		nnSetUpStdShaderConfigBasic( &shaderconfig );
		shaderconfig.bNormalizeVertexNormal	= NNE_TRUE;					// 頂点法線正規化
		//shaderconfig.bRescaleVertexNormal	= NNE_TRUE;					// 頂点法線再スケール
		shaderconfig.nMaxParallelLight		= NB_LIGHT;					// パラレルライト最大数
		shaderconfig.nMaxPointLight			= 0;						// ポイントライト最大数
		shaderconfig.nMaxSpotLight			= 0;						// スポットライト最大数
		shaderconfig.bLightAmbient			= NNE_FALSE;				// ライトアンビエント
		shaderconfig.PointLightDistAtten	= NNE_ATTEN_CONSTANT;		// ポイントライト距離減衰モデル
		//shaderconfig.PointLightDistAtten	= NNE_ATTEN_INVLINEAR;
		//shaderconfig.PointLightDistAtten	= NNE_ATTEN_INVQUADRATIC;
		shaderconfig.SpotLightDistAtten		= NNE_ATTEN_CONSTANT;		// スポットライト距離減衰モデル
		//shaderconfig.SpotLightDistAtten	= NNE_ATTEN_INVLINEAR;
		//shaderconfig.SpotLightDistAtten	= NNE_ATTEN_INVQUADRATIC;
		shaderconfig.FogModel				= NNE_FOG_NONE;				// フォグモデル
		//shaderconfig.FogModel				= NNE_FOG_LINEAR;
		//shaderconfig.FogModel				= NNE_FOG_EXP;
		//shaderconfig.FogModel				= NNE_FOG_EXP2;
		shaderconfig.bDistanceFog			= NNE_FALSE;				// 距離フォグ or Z値フォグ
		shaderconfig.bFragmentFog			= NNE_FALSE;				// フラグメントフォグ
		// 
		size = nnCalcStdShaderManageBufferSize( STDSHADER_NUM );
		pShaderMgrBuf = smpMemAlloc( size );
		nnConfigureStdShader( &shaderconfig,  pShaderMgrBuf, STDSHADER_NUM );

        // 標準シェーダコードのロード
        // 頂点シェーダ
		VsCodeSize = smpGetFileSize( STDVSPATH );
		pVsCodeBuf = smpMemAlloc( VsCodeSize );
		smpReadFile( STDVSPATH, pVsCodeBuf );
        // ピクセルシェーダー
		PsCodeSize = smpGetFileSize( STDPSPATH );
		pPsCodeBuf = smpMemAlloc( PsCodeSize );
		smpReadFile( STDPSPATH, pPsCodeBuf );
	}

	// ライトの初期設定
	//   全てのライトの初期化
	nnInitLight();
	//   環境光 RGB
	nnSetAmbientColor( 0.2f, 0.2f, 0.2f );

    // 
	smpBinSetFilePath( BINPATH );
	smpTexSetFilePath( TEXPATH );

	// サンプル用パッドライブラリ初期化
	smpPadInit();
	pPad1 = smpPadGet( SMPPAD_PORT1 );	// １番ポート
	pPad2 = smpPadGet( SMPPAD_PORT2 );	// ２番ポート

	// サンプル用タイマライブラリ初期化
	smpTmrInit();
}

//----------------------------
// ベクトルから角度を求めます
//     -z方向が基準
//----------------------------
static void Vector2AngleXY(const NNS_VECTOR *v, NNS_ROTATE_A32 *a)
{
	a->x = nnArcTan2( v->y, nnSqrt((v->x * v->x) + (v->z * v->z)) );
	a->y = -nnArcTan2( v->x, -v->z );
	a->z = 0;
}
static void Vector2AngleXYDeg(const NNS_VECTOR *v, NNS_VECTOR *a)
{
	NNS_ROTATE_A32 ang;
	Vector2AngleXY(v, &ang);
	a->x = NNM_A32toDEG( ang.x );
	a->y = NNM_A32toDEG( ang.y );
	a->z = 0.0f;
}

//----------------
// カメラの初期化
//----------------
static void InitCamera(SCENE *scn)
{
	CAMERA *cam;
	Sint32  nb;
	nb = 1;

	cam = CameraAllocate(1);
	scn->NbCamera = nb;
	scn->pCamera = cam;

	// 初期位置
	cam->Position.x = 0.0f;
	cam->Position.y = 0.0f;
	cam->Position.z = 40.0f;
	{
		NNS_VECTOR v, r;
		v.x = cam->Interest.x - cam->Position.x;
		v.y = cam->Interest.y - cam->Position.y;
		v.z = cam->Interest.z - cam->Position.z;
		Vector2AngleXYDeg(&v, &r);
		cam->Rotation.x = r.x;
		cam->Rotation.y = r.y;
		cam->Rotation.z = r.z;
	}
}

//----------------
// ライトの初期化
//----------------
static void InitLight(SCENE *scn)
{
	LIGHT  *lit;
	Sint32  nb,
		    i;
	nb = NB_LIGHT;
	lit = LightAllocate(nb);
	scn->NbLight = nb;
	scn->pLight = lit;
	for(i=0; i<nb; i++, lit++){
		// パラレルライト
		lit->Type = NND_LIGHTTYPE_PARALLEL;
		lit->pLight = smpMemAlloc(sizeof(NNS_LIGHT_PARALLEL));

		lit->Color.r	= 0.6f;
		lit->Color.g	= 0.6f;
		lit->Color.b	= 0.6f;
		lit->Color.a	= 1.0f;
		lit->Position.x = 0.0;
#if 0
		lit->Position.y = 60.0f;
#else
		lit->Position.y = 100.0f;
#endif
		lit->Position.z = 0.0;
	}
}

//----------------------------
// プレイコントロールの初期化
//----------------------------
static void PlayControlSetFrame(SCENE *scn, NNS_MOTION *mot)
{
	static Sint32 fg=FALSE;
	if(fg == TRUE){
		if(scn->StartFrame > mot->StartFrame){
			scn->StartFrame = mot->StartFrame;
			scn->CurrentFrame = mot->StartFrame;
		}
		if(scn->EndFrame < (mot->EndFrame-1)){
			scn->EndFrame = mot->EndFrame-1;
		}
	} else{
		// 初期化
		scn->StartFrame = mot->StartFrame;
		scn->CurrentFrame = mot->StartFrame;
		scn->EndFrame = mot->EndFrame-1;
		fg = TRUE;
	}
}
static void InitPlayControl(SCENE *scn)
{
	MODEL  *mdl;
	Sint32 i;
	for(i=0, mdl=scn->pModel; i<scn->NbModel; i++, mdl++){
		if(mdl->pMotion != NULL) PlayControlSetFrame(scn, mdl->pMotion);
		if(mdl->pMotionMat != NULL) PlayControlSetFrame(scn, mdl->pMotionMat);
	}
}

//----------------
// シーンの初期化
//----------------
static void InitScene(void)
{
	NNS_OBJECT		*pLoadObject;
	NNS_TEXFILELIST	*pTexFileList;
	void		    *pBuf0;
	MODEL      *mdl;
	MDLDATA    *mdldata;
	Sint32      num, i, nbmtxpalmax;
	Uint32      size, fg;

	// SCENE のメモリの確保
	SampleScn = SceneAllocate();
	// カメラの初期化
 	InitCamera(SampleScn);
	// ライトの初期化
	InitLight(SampleScn);

	// データリストのメモリの確保
	SampleScn->NbModel = NbDataTbl;
	SampleScn->pModel = ModelAllocate(SampleScn->NbModel);
	nbmtxpalmax = 0;
	fg = FALSE;
	// モデル
	for( i=0, mdl=SampleScn->pModel, mdldata=NNDataTbl; i<NbDataTbl; i++, mdl++, mdldata++){
		{	// データのコピー
			mdl->RenderToTexFlg		= mdldata->RenderToTexFlg;
			mdl->Scaling			= mdldata->Scaling;
			mdl->Translation		= mdldata->Translation;
		}

		// オブジェクトデータロード
		size = smpBinGetFileSize( mdldata->pObjFileName );
		pBuf0 = smpMemAlloc( size );
		smpBinReadFile( mdldata->pObjFileName, pBuf0 );

		// オブジェクトデータのセットアップ
		pTexFileList = NULL;
		pLoadObject = NULL;
		smpBinSetUpObject( &pLoadObject, &pTexFileList, pBuf0 );
		if(pLoadObject == NULL){
			fprintf(stderr, "Error : Cannot read object file. [%s]\n", mdldata->pObjFileName);
			// 読み込みオブジェクトデータ領域の解放
			smpMemFree( pBuf0 );
			continue;
		}

		// ※※※ オブジェクトにD3Dリソースを割り当てる ※※※
#if 1 /* 2008.09.10〜2008.11.04 */
		size = nnCalcVertexBufferObjectSizeDXG20(pLoadObject, 0);
		mdl->pObjectBuf = (NNS_OBJECT*)smpMemAlloc( size );
		mdl->pObject = mdl->pObjectBuf;
		nnVertexBufferObjectDXG20( mdl->pObject, pLoadObject, 0);
#else
#if 1 /* 2008.09.10(バッファをセットして終了時にメモリを解放する) */
		mdl->pObjectDataBuf	= pBuf0;
#endif
		mdl->pObject = pLoadObject;
		mdl->pObjectBase = mdl->pObject;
		nnVertexBufferObjectDXG20( mdl->pObject, mdl->pObject, 0);
#endif

		// マテリアルのモーション
		if(mdldata->pMatMotFileName != NULL){
			size = smpBinGetFileSize( mdldata->pMatMotFileName );
			mdl->pMotionMatBuf = smpMemAlloc( size );
			smpBinReadFile( mdldata->pMatMotFileName , mdl->pMotionMatBuf );
			smpBinSetUpMaterialMotion( &mdl->pMotionMat, mdl->pMotionMatBuf );
			size = nnCalcMaterialMotionObjectBufferSize( mdl->pObject,  mdl->pMotionMat );
			mdl->pMatMotObj = (NNS_MATMOTOBJ*)smpMemAlloc( size );
			// マテリアルモーションオブジェクト初期化
			nnInitMaterialMotionObject( mdl->pMatMotObj, mdl->pObject, mdl->pMotionMat );
			{	// マテリアルオブジェクトに切り替える
				mdl->pObject = mdl->pMatMotObj;
			}
		}

		// エンベロープオブジェクト作成
		size = nnCalcPliableObjectBufferSize( mdl->pObject, 0 );
		mdl->pPliableObject = (NNS_PLIABLEOBJ*)smpMemAlloc( size );
		nnInitPliableObject( mdl->pPliableObject, mdl->pObject, 0 );

		// モーション
		if( mdldata->pObjMotFileName != NULL){
			size = smpBinGetFileSize( mdldata->pObjMotFileName );
			mdl->pMotionBuf = smpMemAlloc( size );
			smpBinReadFile( mdldata->pObjMotFileName, mdl->pMotionBuf );
			smpBinSetUpMotion( &mdl->pMotion, mdl->pMotionBuf );
		}

		// テクスチャリスト領域の確保
		if(pTexFileList != NULL){
			num = pTexFileList->nTex;	// テクスチャ数
			mdl->pTexListBuf = smpMemAlloc( nnEstimateTexlistSize(num) );
			// テクスチャリストの初期化
			nnSetUpTexlist( &mdl->pTexList, num, mdl->pTexListBuf );
			// テクスチャのロード、テクスチャ情報の設定
			if ( smpTexLoadFile( mdl->pTexList, pTexFileList ) < 0 ){
				fprintf(stderr, "Error : Cannot read texture file.\n");
				while(1){}	// Texture file read error
			}
		}

#if 1 /* 2008.09.10 */
		// 読み込みオブジェクトデータ領域の解放
		smpMemFree( pBuf0 );
#endif

#ifdef FRAMEIN_CAMERA
		// マトリクスリスト領域の確保
		num = mdl->pObject->nNode;	// ノード数
		mdl->pMtxList = (NNS_MATRIX*)smpMemAlloc( num * sizeof( NNS_MATRIX ) );
#endif
		//マトリクスパレット領域の確保
		num = mdl->pObject->nMtxPal;	// マトリクスパレット数
		mdl->pMtxPal = (NNS_MATRIX*)smpMemAlloc( num * sizeof( NNS_MATRIX ) );
		if(nbmtxpalmax < num ) nbmtxpalmax = num;
		// ノードステータスリスト領域の確保
		num = mdl->pObject->nNode;	//ノード数
		mdl->pNodestatList = (NNF_NODESTATUS*)smpMemAlloc( num * sizeof( NNF_NODESTATUS ) );
	}
	pMtxPalMax = (NNS_MATRIX*)smpMemAlloc( nbmtxpalmax * sizeof( NNS_MATRIX ) );

	InitPlayControl(SampleScn);
}

//------------------------
// キューブマップの初期化
//------------------------
static void InitCubeMap(void)
{
	CUBEMAPTEX *cb;
	cb = &CubeMapTex;
	// キューブマップの生成
	D3DXCreateCubeTexture(g_pD3DDev, CUBE_MAP_SIZE, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &cb->pCubeMap);
	//
	nnMakePerspectiveMatrix( &cb->ProjMatrix, NNM_DEGtoA32( 90.0f ), 1.0f, 1.0f, 10000.0f);
	//
	CubeMapTex.MapSize	= CUBE_MAP_SIZE;
	CubeMapTex.NbInfo	= NB_CUBEMAPINFO;
	CubeMapTex.pInfo	= CubeMapInfo;
}

//-
// 
//-
static void ExitCubeMap(void)
{
	CUBEMAPTEX *cb;
	cb = &CubeMapTex;
	// キューブマップをリリース
	_smpRelease(cb->pCubeMap);
}

//----------------------
// 標準シェーダーの作成
//----------------------
static void BuildStdShader(void)
{
	MODEL *mdl;
	Sint32 i;
	for( i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
		// オブジェクトが使用するシェーダプロファイルを登録する

		if(mdl->RenderToTexFlg == NNE_ON){
			// 通常描画用、キューブマップ生成用
			nnRegistObjectStdShaderProfiles( mdl->pObject, gDrawFlag );

			// CPUでエンベロープ計算して描画する場合は必要
			// プライアブルオブジェクト使用シェーダプロファイル登録
			nnRegistPliableObjectStdShaderProfiles( mdl->pPliableObject, gDrawFlag );

		} else{
			// キューブマップを貼って描画
			nnRegistObjectStdShaderProfiles( mdl->pObject, gDrawFlag | NND_DRAWOBJ_MATCTRL_USERSAMPLERCUBE1 );

			// CPUでエンベロープ計算して描画する場合は必要
			// プライアブルオブジェクト使用シェーダプロファイル登録
			nnRegistPliableObjectStdShaderProfiles( mdl->pPliableObject, gDrawFlag | NND_DRAWOBJ_MATCTRL_USERSAMPLERCUBE1 );
		}
	}

	// シェーダのインクルードのパス設定
	smpSetIncludePathDXG20(STDINCPATH);
#if OUTPUT_COMPILE_SHADER_ERROR
	// シェーダのコンパイルエラーコールバック関数の設定
	nnSetCompileErrorCallbackStdShaderDXG20(smpOutputCompileShaderErrorDXG20);
#endif
	// シェーダ作成
	nnBuildStdShader( (const char*)pVsCodeBuf, VsCodeSize,
					  (const char*)pPsCodeBuf, PsCodeSize );
}

//--------
// 初期化
//--------
void UserInit( void )
{
	pPrintBuf			= NULL;

	pMtxStackBuf		= NULL;

	{	// 標準シェーダ
		pShaderMgrBuf	= NULL;
		VsCodeSize		= 0;
		PsCodeSize		= 0;
		pVsCodeBuf		= NULL;
		pPsCodeBuf		= NULL;
	}
	{	// コントローラ
		pPad1			= NULL;
		pPad2			= NULL;
	}
	//
	InitNN();
	// シーンの初期化
	InitScene();
	{
		D3DPRESENT_PARAMETERS *pD3DPP;
		CAMERA                *cam;
		pD3DPP = (D3DPRESENT_PARAMETERS*) nnGetD3DPresentParametersDXG20();
		cam = SampleScn->pCamera;
		// カメラコントロールの初期化
		#ifndef FRAMEIN_CAMERA
			CameraCtrlSetCamera(SampleScn->pCamera, pD3DPP->BackBufferWidth, pD3DPP->BackBufferHeight);
		#else
			CameraCtrlSetCamera(SampleScn->pCamera, pD3DPP->BackBufferWidth, pD3DPP->BackBufferHeight);
			CameraCtrlSetModel(SampleScn->NbModel, SampleScn->pModel);
		#endif
		// プロジェクション設定
		// 視野角45度 アスペクト比640:480 Near 1.f Far 10000.f
		nnMakePerspectiveMatrix( &gProjmtx, NNM_DEGtoA32( cam->FieldOfView ),
								 (Float)pD3DPP->BackBufferWidth/(Float)pD3DPP->BackBufferHeight, cam->NearPlane, cam->FarPlane );
		nnSetProjection( &gProjmtx, NNE_PROJECTION_TYPE_PERSPECTIVE );

	}
	// キューブマップの初期化
	InitCubeMap();
	//
	BuildStdShader();
}

//------
// 終了
//------
void UserExit(void)
{
	ExitCubeMap();

	{
		smpMemFree(pMtxPalMax);

		// SCENE のメモリの解放
		SceneDispose(&SampleScn);
	}
	{	// シェーダの開放
		nnReleaseStdShader();			// ビルド済みシェーダ解放
		nnClearStdShaderProfiles();		// 登録済みシェーダの開放

		// シェーダーコード領域の開放
		smpMemFree( pPsCodeBuf );
		smpMemFree( pVsCodeBuf );

		// シェーダーマネージャー領域の開放
		smpMemFree( pShaderMgrBuf );
	}

	// マトリクススタック領域の解放
	smpMemFree( pMtxStackBuf );

	// デバッグプリントの終了
	nnExitPrint();
	smpMemFree( pPrintBuf );
}

//------------------------
// デバイスの消失時の処理
//------------------------
void UserDeviceLost(void)
{
	if(SampleScn != NULL){
		gCameraBack = *SampleScn->pCamera;
	}
	UserExit();
}

//--------------------------
// デバイスのリストアの処理
//--------------------------
void UserDeviceReset(void)
{
	UserInit();
	if(SampleScn != NULL){
		*SampleScn->pCamera = gCameraBack;
		{
			D3DPRESENT_PARAMETERS *pD3DPP;
			pD3DPP = (D3DPRESENT_PARAMETERS*) nnGetD3DPresentParametersDXG20();
			CameraCtrlSetCamera(SampleScn->pCamera, pD3DPP->BackBufferWidth, pD3DPP->BackBufferHeight);
		}
	}
}

//--------------------------------------------------
// モデルのモーションを計算します
//   マトリクスとマテリアルのモーションの計算も含む
//--------------------------------------------------
static void ModelCalcMotion(void)
{
	Sint32     i;
	MODEL      *mdl;
	NNS_MOTION *mot;
	Float       frame;
	frame = SampleScn->CurrentFrame;
	for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
#if 0
		// ワールドのパレットマトリクスを計算します
		nnCalcMatrixPalette( mdl->pMtxPal, NULL, mdl->pObject, &mdl->Matrix, &gMtxStack, 0 );
#else
		if( (mot=mdl->pMotion) == NULL ){
			// ワールドのパレットマトリクスを計算します
			nnCalcMatrixPalette( mdl->pMtxPal, NULL, mdl->pObject, mdl->pMatrix, &gMtxStack, 0 );
#ifdef FRAMEIN_CAMERA
			nnCalcMatrixList( mdl->pMtxList, mdl->pObject, mdl->pMatrix );
#endif
		} else{
			// ワールドのパレットマトリクスを計算します
			nnCalcMatrixPaletteMotion( mdl->pMtxPal, NULL, mdl->pObject, mdl->pMotion, frame, mdl->pMatrix, &gMtxStack, 0 );
#ifdef FRAMEIN_CAMERA
			nnCalcMatrixListMotion( mdl->pMtxList, mdl->pObject, mdl->pMotion, frame, mdl->pMatrix );
#endif
		}
#endif
		if(mot=mdl->pMotionMat){
			// マテリアルモーション計算
#if 1 /* 2008.09.10 */
			nnCalcMaterialMotion( mdl->pObject, mdl->pObjectBuf, mot, frame );
#else
			nnCalcMaterialMotion( mdl->pObject, mdl->pObjectBase, mot, frame );
#endif
		}

		// エンベロープオブジェクト計算
		nnCalcPliableObject( mdl->pPliableObject, mdl->pMtxPal, 0 );
	}
}

//--------------
// モデルの描画
//--------------
static void DrawModel(NNS_MATRIX *viewmtx, MODEL *mdl, NNF_DRAWOBJ drawflag)
{
	NNS_MATRIX	vomtx;		// 描画用マトリクス
	NNS_OBJECT	*obj;
	NNS_TEXLIST	*texLst;

	obj = mdl->pObject;
	texLst = mdl->pTexList;

	// ライト用にビューマトリクスを設定
	nnSetLightMatrix( viewmtx );

	// ライト用にビューマトリクスを設定
	nnPutLightSettings();

	// 描画用マトリクス生成
	nnMultiplyMatrix( &vomtx, viewmtx, mdl->pMatrix);

	// 視点座標系でのマトリクスパレットを計算します
	nnCalcMatrixPaletteMultiplyMatrix( pMtxPalMax, viewmtx, mdl->pMtxPal, mdl->pObject->nMtxPal );

	// テクスチャセット
	nnSetTextureList( texLst );

	// NNオブジェクト描画（不透明）
	nnDrawObject( obj, pMtxPalMax, NULL,
		NND_SUBOBJTYPE_OPAQUE | NND_SUBOBJTYPE_PUNCHTHROUGH |
		NND_SUBOBJTYPE_RIGID, drawflag );

	// NNオブジェクト描画（不透明、プライアブル）
	nnDrawPliableObject( mdl->pPliableObject, viewmtx, NULL,
		NND_SUBOBJTYPE_OPAQUE | NND_SUBOBJTYPE_PUNCHTHROUGH, drawflag );

	// NNオブジェクト描画（半透明）
	nnDrawObject( obj, pMtxPalMax, NULL,
		NND_SUBOBJTYPE_TRANSPARENT |
		NND_SUBOBJTYPE_RIGID, drawflag );

	// NNオブジェクト描画（半透明、プライアブル）
	nnDrawPliableObject( mdl->pPliableObject, viewmtx, NULL,
		NND_SUBOBJTYPE_TRANSPARENT, drawflag );
}

//------------------------------------------------
// キューブマップを生成するためオブジェクトを描画
//------------------------------------------------
static void DrawCubeMapModel( NNS_MATRIX *viewmtx, NNF_DRAWOBJ drawflag )
{
	MODEL *mdl;
	Sint32    i;
	for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
		if(mdl->RenderToTexFlg  == NNE_ON){
			DrawModel(viewmtx, mdl, drawflag);
		}
	}
}

//----------------------------
// キューブマップを生成します
//----------------------------
static void RenderToCubeMap(CUBEMAPTEX *cb, NNS_VECTOR *q)
{
	NNS_MATRIX  vm;
	NNS_VECTOR *r, *p;
	CAMERA     *cam;
	//
	cam = SampleScn->pCamera;
	r   = &cam->Rotation;
	p   = &cam->Position;
	// カメラの(逆)マトリクス
	nnMakeRotateXMatrix( &vm, NNM_DEGtoA32(-r->x) );
	nnRotateYMatrix( &vm, &vm, NNM_DEGtoA32(-r->y) );
	nnRotateZMatrix( &vm, &vm, NNM_DEGtoA32(-r->z) );
	// キューブマップを貼るオブジェクトの座標
	nnTranslateMatrix( &vm, &vm, -q->x, -q->y, -q->z);

	{
		LPDIRECT3DSURFACE9 pRenTgtOld, pFace;
		CUBEMAPINFO       *cbIf;
		NNS_MATRIX         mtx;
		Sint32             i;

		// 現在のレンダターゲット
		g_pD3DDev->GetRenderTarget(0, &pRenTgtOld);
		// キューブ用プロジェクション
		nnSetProjection( &cb->ProjMatrix, NNE_PROJECTION_TYPE_PERSPECTIVE );

		cbIf = cb->pInfo;
		for(i=0; i<cb->NbInfo; i++, cbIf++){

			// キューブマップを生成するための(逆)マトリクス(６方向)
			nnMakeRotateXMatrix( &mtx, NNM_DEGtoA32(-cbIf->Rotation.x) );
			nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32(-cbIf->Rotation.y) );
			nnRotateZMatrix( &mtx, &mtx, NNM_DEGtoA32(-cbIf->Rotation.z) );
			//
			nnMultiplyMatrix( &mtx, &mtx, &vm);

			// サーフェースを得る
			cb->pCubeMap->GetCubeMapSurface(cbIf->Target, 0, &pFace);
			//
			g_pD3DDev->SetRenderTarget(0, pFace);
			_smpRelease(pFace);

			// キューブマップのクリア
			g_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0x80, 0x80, 0x80), 1.0f, 0);

			//
			DrawCubeMapModel( &mtx, gDrawFlag );
		}
		// カメラのプロジェクションマトリクスに戻す
		nnSetProjection( &gProjmtx, NNE_PROJECTION_TYPE_PERSPECTIVE );

		// もとの設定に戻す
		g_pD3DDev->SetRenderTarget(0, pRenTgtOld);
		_smpRelease(pRenTgtOld);
	}
}

//------------------------
// キューブマップあり描画
//------------------------
static void DrawModelAndCubeMap( NNS_MATRIX *viewmtx, CUBEMAPTEX *cb)
{
	MODEL *mdl;
	Uint32 state[NNE_SAMPLERSTATETYPE_MAX];
	NNS_MATRIX44 mtx;
	Sint32 i;
	//
	g_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0x80, 0x80, 0x80), 1.0f, 0);
	//
	nnMakeUnitMatrix(&mtx);
	// ユーザーサンプラのデフォルトのサンプラステートを返す
	nnGetMaterialControlUserSamplerDefaultStateDXG20(state);
	state[NNE_SAMPLERSTATETYPE_MAGFILTER] = D3DTEXF_LINEAR;
	state[NNE_SAMPLERSTATETYPE_MINFILTER] = D3DTEXF_LINEAR;
	// ユーザーサンプラの設定
	nnSetMaterialControlUserSamplerDXG20( NNE_USER_SAMPLER_CUBE_1, cb->pCubeMap, &mtx, state);

	for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
		if(mdl->RenderToTexFlg == NNE_ON){
			// 通常描画
			DrawModel(viewmtx, mdl, gDrawFlag);
		} else{
			// キューブマップを貼って描画
			DrawModel(viewmtx, mdl, gDrawFlag | NND_DRAWOBJ_MATCTRL_USERSAMPLERCUBE1);
		}
	}
}

//--------------------
// セットアップライト
//--------------------
static NNE_BOOL SetUpLight(NNE_LIGHT no, LIGHT *lit)
{
	NNE_BOOL fg = NNE_TRUE;
	switch(lit->Type){
		case NND_LIGHTTYPE_PARALLEL:{
			NNS_VECTOR dir;
			nnNormalizeVector(&dir, &lit->Position);
			dir.x = -dir.x;
			dir.y = -dir.y;
			dir.z = -dir.z;
			nnSetUpParallelLight( (NNS_LIGHT_PARALLEL*)lit->pLight, &lit->Color, lit->Intensity, &dir );
			nnSetLight( no, lit->pLight, lit->Type );
			nnSetLightSwitch( no, NNE_ON );
			}break;
		default:
			fg = NNE_FALSE;
			break;
	}
	return(fg);
}

//----------------
// ライトのメイン
//----------------
static void LightMain(void)
{
	NNS_MATRIX mtx;
	LIGHT     *lit;
	Float      ofs;
	Sint32     no, num, i;
	static     Angle32 rot =0;

	// マトリクスの計算
	nnMakeRotateYMatrix( &mtx, rot);
	// 回転を進める
	rot += NNM_DEGtoA32( 0.5f );
	ofs = 0;
	if(SampleScn->NbLight > 0){
		ofs = 360.0f/(Float) SampleScn->NbLight;
	}
	num = 0;
	for(i=0, lit=SampleScn->pLight; i<SampleScn->NbLight; i++, lit++){
		lit->Position.x = mtx[0][0] * 70.0f;
		lit->Position.z = mtx[0][2] * 70.0f;

		nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32(ofs));

		no  = NNE_LIGHT_0 + num;
		if(SetUpLight((NNE_LIGHT)no, lit) == NNE_TRUE){
			num++;
		}
	}
}

//----------
// サンプル
//----------
static void Sample(void)
{
	NNS_MATRIX viewmtx;	// ビューマトリクス

	{	// ビューマトリクスの計算
		NNS_VECTOR *r, *p;
		CAMERA     *cam;
		cam = SampleScn->pCamera;
		p = &cam->Position;
#if 0
		printf("P = %f %f %f\n", cam->Position.x, cam->Position.y, cam->Position.z);
		printf("I = %f %f %f\n", cam->Interest.x, cam->Interest.y, cam->Interest.z);
		printf("R = %f %f %f\n", cam->Rotation.x, cam->Rotation.y, cam->Rotation.z);
#endif
		r = &cam->Rotation;
		nnMakeRotateXMatrix( &viewmtx, NNM_DEGtoA32(-r->x) );
		nnRotateYMatrix( &viewmtx, &viewmtx, NNM_DEGtoA32(-r->y) );
		nnRotateZMatrix( &viewmtx, &viewmtx, NNM_DEGtoA32(-r->z) );
		nnTranslateMatrix( &viewmtx, &viewmtx, -p->x, -p->y, -p->z);
	}
	{	// モデルのマトリクスの計算
		MODEL  *mdl;
		Sint32  i;
		for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
			nnMakeTranslateMatrix(mdl->pMatrix, mdl->Translation.x, mdl->Translation.y, mdl->Translation.z);
			nnScaleMatrix(mdl->pMatrix, mdl->pMatrix, mdl->Scaling.x, mdl->Scaling.y, mdl->Scaling.z);
		}
	}

	// ライトのメイン
	LightMain();

	// 最初にモーションとマトリクスの計算をしておく
	ModelCalcMotion();

	{
		NNS_VECTOR q;
		{
			NNS_MATRIX  m, im;
			MODEL      *mdl;
			NNS_NODE   *node;
			// キューブマップを貼るオブジェクト
			mdl = &SampleScn->pModel[0];	// ０番目
			// 親のノード
			node = &mdl->pObject->pNodeList[0];
			// 基本姿勢マトリクスを求める
			nnInvertMatrix( &im, &node->InvInitMtx );
			// 基本逆行列をパレットマトリクスから取り去った階層マトリクスを計算
			nnMultiplyMatrix(&m, &mdl->pMtxPal[0], &im);
			// そのオブジェクトの座標
			q.x = NNM_MTX(m,0,3);
			q.y = NNM_MTX(m,1,3);
			q.z = NNM_MTX(m,2,3);
			//printf("%f %f %f\n", q.x, q.y, q.z);
		}

		// キューブマップを生成します
		RenderToCubeMap(&CubeMapTex, &q);
	}
	// キューブマップあり描画
	DrawModelAndCubeMap(&viewmtx, &CubeMapTex);

	// モーション再生中
	if(SampleScn->CurrentFrame < SampleScn->EndFrame){
		SampleScn->CurrentFrame += SampleScn->Step;
	} else{
		SampleScn->CurrentFrame = SampleScn->StartFrame;
	}
}

#define CUBE_BACK	0
#define CUBE_LEFT	1
#define CUBE_BOTTOM	2
#define CUBE_FRONT	3
#define CUBE_RIGHT	4
#define CUBE_TOP    5
static void DrawQuadCube(const CUBEMAPTEX *cb, Sint32 num, Float x, Float y, Float size)
{
	// ４角形を表示
	static NNS_VECTOR uvw[][4] = {
		// XY平面(-Z)
		{{-1.0, -1.0, -1.0f}, {1.0, -1.0, -1.0f}, {1.0, 1.0, -1.0f}, {-1.0, 1.0, -1.0f}}, // 後
		// ZY平面(-X)
		{{-1.0, -1.0, 1.0f}, {-1.0, -1.0, -1.0f}, {-1.0, 1.0, -1.0f}, {-1.0, 1.0, 1.0f}}, // 左
		// XZ平面(-Y)
		{{-1.0, -1.0, 1.0f}, {1.0, -1.0, 1.0f}, {1.0, -1.0, -1.0f}, {-1.0, -1.0, -1.0f}}, // 下
		// XY平面(Z)
		{{1.0, -1.0, 1.0f}, {-1.0, -1.0, 1.0f}, {-1.0, 1.0, 1.0f}, {1.0, 1.0, 1.0f}}, // 前
		// ZY平面(X)
		{{1.0, -1.0, -1.0f}, {1.0, -1.0, 1.0f}, {1.0, 1.0, 1.0f}, {1.0, 1.0, -1.0f}}, // 右
		// XZ平面(Y)
		{{-1.0, 1.0, -1.0f}, {1.0, 1.0, -1.0f}, {1.0, 1.0, 1.0f}, {-1.0, 1.0, 1.0f}}, // 上
	};
	static Float pos[][4] = {
		{  0.0f, size, 0.0f, 1.0f },
		{  size, size, 0.0f, 1.0f },
		{  size, 0.0f, 0.0f, 1.0f },
		{  0.0f, 0.0f, 0.0f, 1.0f }
	};
	struct TVERTEX{
		FLOAT	p[4];
		FLOAT	tu, tv, tw;
	} Vertex[4], *pV;
	D3DVIEWPORT9 vp;
	Sint32       i;
	g_pD3DDev->GetViewport(&vp);
	pV = Vertex;
	for(i=0; i<4; i++, pV++){
		pV->p[0] = pos[i][0] + x;
		pV->p[1] = pos[i][1] + y + (Float)vp.Height - size;
		pV->p[2] = pos[i][2];
		pV->p[3] = pos[i][3];
		pV->tu = uvw[num][i].x;
		pV->tv = uvw[num][i].y;
		pV->tw = uvw[num][i].z;
	}
	g_pD3DDev->SetTextureStageState(0,D3DTSS_COLOROP,	D3DTOP_SELECTARG1);
	g_pD3DDev->SetTextureStageState(0,D3DTSS_COLORARG1,	D3DTA_TEXTURE);
	g_pD3DDev->SetTextureStageState(1,D3DTSS_COLOROP,   D3DTOP_DISABLE);
	g_pD3DDev->SetTexture( 0, cb->pCubeMap );
	g_pD3DDev->SetFVF( D3DFVF_XYZRHW | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0) );
	g_pD3DDev->SetVertexShader(NULL);
	g_pD3DDev->SetPixelShader(NULL);
	g_pD3DDev->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, Vertex, sizeof( TVERTEX ) );
	//
	nnResetVertexDeclarationDXG20();
}

//----------------------------------------
// キューブマップのテクスチャを描画します
//   num :
//     0 : 左
//     1 : 後
//     2 : 右
//     3 : 前
//     4 : 上
//     5 : 下
//----------------------------------------
static void DrawQuadCubeTextureColor(const CUBEMAPTEX *cb, Sint32 num, Float x, Float y, Float size)
{
	static Sint32 cubNum[] = {
		CUBE_LEFT,		// 左
		CUBE_BACK,		// 後
		CUBE_RIGHT,		// 右
		CUBE_FRONT,		// 前
		CUBE_TOP,		// 上
		CUBE_BOTTOM,	// 下
	};
	DrawQuadCube(cb, cubNum[num], x, y, size);
}

//--------------------------------------
// キューブマップテクスチャを表示します
//--------------------------------------
static void DispCubeTexture(const CUBEMAPTEX *cb)
{
	Float  size, _size_2,
		   x, y;
	Sint32 i;
	size = 100.0f;
	_size_2 = size * 0.5f;
	for(i=0; i<CubeMapTex.NbInfo; i++){
		if(i < 4){
			x = (size) * (Float)(i);
			y =	-size;
		} else{
			if( i== 4 ){
				x = size;
				y = -size * 2;
			} if( i == 5){
				x = size;
				y = 0.0f;
			}
		}
		DrawQuadCubeTextureColor(cb, i, x, y, size);
	}
}

//----------------
// FPS を返します
//----------------
static Float getFPS(void)
{
	// FPS測定
	static Float  FpsLast = 0.f;
	static Sint32 FrameCount = 0;
	static Uint32 FpsInterval = 250001;
	static Uint32 FpsLastFrameCount = 0;
	static Uint32 FpsFrameCount;

	FrameCount++;
	FpsFrameCount = smpTmrGet();
	FpsInterval += smpTmrDiff( FpsFrameCount, FpsLastFrameCount );
	FpsLastFrameCount = FpsFrameCount;
	if ( FpsInterval > 250000 ){
		// 0.25秒経過
		FpsLast = ( (Float) FrameCount ) / ( (Float) FpsInterval ) * 1000000.f;
		FpsInterval = 0;
		FrameCount = 0;
	}
	return(FpsLast);
}

//--------
// メイン
//--------
void UserMain(void)
{
	Uint32 starttick, endtick;

	// フレーム開始時刻取得
	starttick = smpTmrGet();
	{
		//
		Sample();

		//
		DispCubeTexture(&CubeMapTex);
	}
	// フレーム終了時刻取得
	endtick = smpTmrGet();

	// 情報表示
	nnPrint( 3, 3, "NN "SAMPLENAME" SAMPLE" );
	nnPrint( 3, 4, "CPU Time%6d[us]", smpTmrDiff( endtick, starttick ) );
	nnPrint( 3, 5, "%.0fFPS", getFPS() );
	// 登録文字の描画
	nnFlushPrint();
}
