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

    Glare application main sample

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

    Module  : Glare application main sample
    File    : smpapp.cpp
    Create  : 2008/08/27 (Ver.0.80.05)
    Modify  : 2008/09/10 (Ver.0.80.06)
			    NNS_OBJECTのメモリ解放忘れの修正。
				NNS_OBJECTのメモリを取得して処理する方法に変更。
			  2008/09/12 (Ver.0.80.07)
                nnResetVertexDeclarationDXG20 を追加。
			  2008/11/04 (Ver.0.80.08)
                nnExitVertexBufferDXG20 の記述忘れ。
				nnCalcVertexBufferObjectSizeDXG20 に変更。
			  2008/11/14 (Ver.0.80.09)
			    プラットフォーム別処理関数を smpmain.cpp へ移動した。
				nnGetDirect3DDeviceDXG20, nnGetD3DPresentParametersDXG20 に変更。
				D3DUSAGE_DYNAMIC を D3DUSAGE_WRITEONLY に変更。
			  2008/11/17 (Ver.0.80.10)
			    MODEL 構造体のメンバにあるマトリクスをポインタに変更(マトリクスは align(16) されているのでポインタにした)。
              2008/11/19 (Ver.0.80.11)
			    nnGetPixelShaderShaderVertexVersionDXG20 関数名を nnGetPixelShaderVersionDXG20 に変更。
              2008/12/12 (Ver.0.80.12)
				D3DUSAGE_WRITEONLY を D3DUSAGE_DYNAMIC に変更(2008/11/14の変更をもとに戻した)。
			  2009/01/19 (Ver.0.80.13)
	            smpSetIncludePathDXG20 関数を追加。
			  2009/01/21 (Ver.0.80.14)
			    nnSetCompileErrorCallbackStdShaderDXG20 関数を追加。
			  2009/06/11 (Ver.0.80.15)
			    プラットフォーム別処理関数(IDirect3DDevice9_CreateVertexBuffer, IDirect3DVertexBuffer9_Release)を smpmain.cpp へ移動した。
    Version : 0.80.15
    Note    : 

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

#define __SMPAPP_CPP__

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

#include "glare.h"

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

////////////////////////////////////////////////////////////////////////////////
#define SAMPLENAME "GLARE"

#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_ON,  { 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, "post_sample_post_sample.zno", NULL, "post_sample_post_sample.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;		// プロジェクションマトリクス

static Float			g_aspect;		// アスペクト

////////////////////////////////////////////////////////////////////////////////
// 輝度テクスチャ
typedef
	struct{
		Sint32				m_size;
		LPDIRECT3DTEXTURE9	m_pTexture;
	}INTENSITYTEX;

#define INTENSITY_TEX_SIZE 256

static INTENSITYTEX IntensityTex;

////////////////////////////////////////////////////////////////////////////////
// グレア
static CGlare *g_pGlare;

////////////////////////////////////////////////////////////////////////////////
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;

	// 初期位置
#if 0
	cam->Position.x = 0.0f;
	cam->Position.y = 0.0f;
	cam->Position.z = 40.0f;
#else
	cam->Position.x = 0.0f;
	cam->Position.y = 300.0f;
	cam->Position.z = 300.0f;
#endif
	{
		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 InitIntensityTex(void)
{
	INTENSITYTEX *pIt;
	pIt = &IntensityTex;
	pIt->m_size = INTENSITY_TEX_SIZE;
	D3DXCreateTexture(g_pD3DDev, pIt->m_size, pIt->m_size, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R8G8B8, D3DPOOL_DEFAULT, &pIt->m_pTexture);
}

//--------------------------
// 輝度テクスチャのリリース
//--------------------------
static void ExitIntensityTex(void)
{
	INTENSITYTEX *pIt;
	pIt = &IntensityTex;
	_smpRelease(pIt->m_pTexture);
}

//----------------------
// 標準シェーダーの作成
//----------------------
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 );
			}
			{// 輝度描画用
				nnRegistObjectStdShaderProfiles( mdl->pObject, gDrawFlag | NND_DRAWOBJ_SHADER_USER_PROFILE1 );
				// CPUでエンベロープ計算して描画する場合は必要
				// プライアブルオブジェクト使用シェーダプロファイル登録
				nnRegistPliableObjectStdShaderProfiles( mdl->pPliableObject, gDrawFlag | NND_DRAWOBJ_SHADER_USER_PROFILE1 );
			}
		} else{
			{// 通常描画用
				nnRegistObjectStdShaderProfiles( mdl->pObject, gDrawFlag );
				// CPUでエンベロープ計算して描画する場合は必要
				// プライアブルオブジェクト使用シェーダプロファイル登録
				nnRegistPliableObjectStdShaderProfiles( mdl->pPliableObject, gDrawFlag );
			}
		}
	}

	// シェーダのインクルードのパス設定
	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
		g_aspect = (Float)pD3DPP->BackBufferWidth/(Float)pD3DPP->BackBufferHeight;
		nnMakePerspectiveMatrix( &gProjmtx, NNM_DEGtoA32( cam->FieldOfView ), g_aspect, cam->NearPlane, cam->FarPlane );
		nnSetProjection( &gProjmtx, NNE_PROJECTION_TYPE_PERSPECTIVE );

	}

	//
	BuildStdShader();
	
	// 輝度テクスチャの初期化
	InitIntensityTex();
	{	// グレア処理の初期化
		g_pGlare = CGlare::allocate();
		g_pGlare->init(g_pD3DDev);
#if 0
		static char *psVer[] = {
			"ps_2_0",
			"ps_2_a",
			"ps_2_b",
			"ps_3_0",
		};
		g_pGlare->buildShader(g_pD3DDev, psVer[ nnGetPixelShaderVersionDXG20() ]);
#else
		g_pGlare->buildShader(g_pD3DDev, nnGetPixelShaderVersionStringDXG20());
#endif
	}
}

//------
// 終了
//------
void UserExit(void)
{
	// グレア処理のメモリを解放
	CGlare::dispose(g_pGlare);
	// 輝度テクスチャのリリース
	ExitIntensityTex();
	{
		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 RenderToIntensityTex(NNS_MATRIX *viewmtx, INTENSITYTEX *pIt)
{
	LPDIRECT3DSURFACE9 pRenTgtOld, pSuf;

	// 現在のレンダターゲット
	g_pD3DDev->GetRenderTarget(0, &pRenTgtOld);

	// サーフェースを得る
	pIt->m_pTexture->GetSurfaceLevel(0, &pSuf);
	g_pD3DDev->SetRenderTarget(0, pSuf);
	_smpRelease(pSuf);
	{
		//
		g_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0x0, 0x0, 0x0), 1.0f, 0);

		MODEL *mdl;
		Sint32    i;
		for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
			if(mdl->RenderToTexFlg  == NNE_ON){
				DrawModel(viewmtx, mdl, gDrawFlag | NND_DRAWOBJ_SHADER_USER_PROFILE1 );
			}
		}
	}
	// もとの設定に戻す
	g_pD3DDev->SetRenderTarget(0, pRenTgtOld);
	_smpRelease(pRenTgtOld);
}

//--------------------------------
// モデルとグレアテクスチャの描画
//--------------------------------
static void DrawModelAndGlare( NNS_MATRIX *viewmtx)
{
	MODEL *mdl;
	Sint32 i;

	g_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0x80, 0x80, 0x80), 1.0f, 0);

	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);
		}
	}
	// グレアテクスチャの描画
	g_pGlare->drawGlareTexture(g_pD3DDev);
}

//--------------------
// セットアップライト
//--------------------
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();
	{
		// (1)輝度テクスチャに輝度のみレンダリング
		RenderToIntensityTex(&viewmtx, &IntensityTex);
		// (2)グレアテクスチャのレンダリング
		g_pGlare->setAspect(g_aspect);
		g_pGlare->renderGlareTexture(g_pD3DDev, IntensityTex.m_pTexture);
		// (3)モデルとグレアテクスチャの描画
		DrawModelAndGlare( &viewmtx );
	}
	// モーション再生中
	if(SampleScn->CurrentFrame < SampleScn->EndFrame){
		SampleScn->CurrentFrame += SampleScn->Step;
	} else{
		SampleScn->CurrentFrame = SampleScn->StartFrame;
	}
}

//----------------
// テクスチャ表示
//----------------
static void dispTexture(Sint32 num, LPDIRECT3DTEXTURE9 pTexture)
{
	// カリング無効
	g_pD3DDev->SetRenderState( D3DRS_CULLMODE,         D3DCULL_NONE);
	// ブレンド無効
	g_pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
	g_pD3DDev->SetRenderState( D3DRS_SEPARATEALPHABLENDENABLE , FALSE );
	// ライティング無効
	g_pD3DDev->SetRenderState( D3DRS_LIGHTING,         FALSE);
	//
	g_pD3DDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	// 深度バッファリングを無効
	g_pD3DDev->SetRenderState(D3DRS_ZENABLE, FALSE);
	// サンプラステート設定
	g_pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_BORDERCOLOR, D3DCOLOR_RGBA(255, 255, 255, 255));
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, 0);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MAXMIPLEVEL, 0);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_MAXANISOTROPY,1);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_SRGBTEXTURE, 0);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_ELEMENTINDEX, 0);
	g_pD3DDev->SetSamplerState(0, D3DSAMP_DMAPOFFSET, 0);
	//
	g_pD3DDev->SetTextureStageState(0,D3DTSS_COLOROP,	D3DTOP_SELECTARG1);
	g_pD3DDev->SetTextureStageState(0,D3DTSS_COLORARG1,	D3DTA_TEXTURE);
	g_pD3DDev->SetTextureStageState(1,D3DTSS_COLOROP,   D3DTOP_DISABLE);
	FLOAT size = 256.0f;
	struct TVERTEX{
		    FLOAT       p[4];
			FLOAT       tu, tv;
	} Vertex[4] = {
		// x      y  z rhw tu tv
		{   0, size, 0, 1, 0, 1,},
		{size, size, 0, 1, 1, 1,},
		{size,    0, 0, 1, 1, 0,},
		{   0,    0, 0, 1, 0, 0,},
	};
	D3DVIEWPORT9 vp;
	g_pD3DDev->GetViewport(&vp);
	for(int i=0;i<4; i++){
		Vertex[i].p[0] += (Float)num * size + (Float)(1 * num);
		Vertex[i].p[1] += (Float)vp.Height - size;
	}
	g_pD3DDev->SetTexture( 0, pTexture );
	g_pD3DDev->SetFVF( D3DFVF_XYZRHW | D3DFVF_TEX1 );
	g_pD3DDev->SetVertexShader(NULL);
	g_pD3DDev->SetPixelShader(NULL);
	g_pD3DDev->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, Vertex, sizeof( TVERTEX ) );
	//
	nnResetVertexDeclarationDXG20();
}

//----------------
// 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();

		dispTexture(0, IntensityTex.m_pTexture);
		dispTexture(1, g_pGlare->getTexture());
	}
	// フレーム終了時刻取得
	endtick = smpTmrGet();

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