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

    Shadow map application main sample

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

    Module  : Shadow map application main sample
    File    : smpapp.cpp
    Create  : 2008/06/13 (Ver.0.10.01)
    Modify  : 2008/06/24 (Ver.0.10.02)
	            nnGetMaterialControlShadowMapDefaultStateDXG20 追加
			    nnSetMaterialControlShadowMapDXG20 変更
			  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 "SHADOWMAP"

#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 }, "NN_sample_Torus_Knot01.zno", NULL, NULL },
	{ NNE_OFF, { 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, "NN_sample_Plane01.zno", NULL, NULL },
};
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 SHADOW_MAP_SIZE	1024

static LPDIRECT3DSURFACE9 g_pShadowMapDepth = NULL;	// 深度バッファ

typedef
	struct{
		Sint32			MapSize;					// マップサイズ

		NNS_MATRIX		MapMatrix;					// マトリクス
		NNS_MATRIX44	LightProjMatrix;			// ライトのプロジェクションマトリクス

		LPDIRECT3DTEXTURE9 pShadowMap;				// シャドウマップ
		LPDIRECT3DSURFACE9 pShadowMapSuf;			// シャドウマップサーフェス
	}SHADOW;

#define NB_SHADOW	2	// 最大２
#if (NB_SHADOW >= 2)
#undef NB_SHADOW
#define NB_SHADOW 2
#endif
static SHADOW Shadow[NB_SHADOW];	// NNE_SHADOWMAP_MAX まで

static NNF_DRAWOBJ gShadowDrawFlag;	// シャドウの描画フラグ

#if 0 /* Geforce6800GT(Geforce6世代) で不具合が発生したと報告を受けたので取りあえず 0 とする。(2008.07.07) */
#define _SHADOW_16F /* 16bitのfloat対応 */
#endif

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

	Sint32 nbLight = 0;
	if(NB_LIGHT <= NB_SHADOW){
		nbLight = NB_LIGHT;
	} else{
		nbLight = NB_SHADOW;
	}
	if(nbLight == 2){
		gShadowDrawFlag = NND_DRAWOBJ_MATCTRL_SHADOWMAP2;
	} else{
		gShadowDrawFlag = NND_DRAWOBJ_MATCTRL_SHADOWMAP1;
	}

	// 標準シェーダーの初期化
	{
		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 1
	cam->Position.x = 1.6f * 10.0f;
	cam->Position.y = 6.3f * 10.0f;
	cam->Position.z = 16.2f * 10.0f;
#else
	cam->Position.x = -0.239944f;
	cam->Position.y = 4.369975f;
	cam->Position.z = 0.834319f;
#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 */
		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 InitShadow(void)
{
	SHADOW *sd;
	Sint32  i;

	// 深度バッファの生成
	g_pD3DDev->CreateDepthStencilSurface(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE,
		//D3DFMT_D16,
		D3DFMT_D24X8,
		D3DMULTISAMPLE_NONE, 0, TRUE, &g_pShadowMapDepth, NULL);

	for(i=0, sd=Shadow; i<NB_SHADOW; i++, sd++){
		// 初期設定
		sd->MapSize = SHADOW_MAP_SIZE;
		nnMakeUnitMatrix( &sd->MapMatrix );

		// シャドウマップの生成
		D3DXCreateTexture(g_pD3DDev, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1, D3DUSAGE_RENDERTARGET,
#ifdef _SHADOW_16F
						  D3DFMT_R16F,
#else
						  D3DFMT_A8R8G8B8,
#endif
						  D3DPOOL_DEFAULT, &sd->pShadowMap);
		// シャドウマップサーフェスを得る
		sd->pShadowMap->GetSurfaceLevel(0, &sd->pShadowMapSuf);

		// ライトのプロジェクションマトリクス
#if 0
		nnMakePerspectiveMatrix(&sd->LightProjMatrix, NNM_DEGtoA32( 30.f ), 1.0, 5.0f, 200.0f );
#else
		nnMakePerspectiveMatrix(&sd->LightProjMatrix, NNM_DEGtoA32( 45.f ), 1.0, 5.0f, 200.0f );
#endif
	}
}

//-
// 
//-
static void ExitShadow(void)
{
	SHADOW *sd;
	Sint32  i;

	for(i=0, sd=Shadow; i<NB_SHADOW; i++, sd++){
		// シャドウマップサーフェスをリリース
		_smpRelease(sd->pShadowMapSuf);

		// シャドウマップをリリース
		_smpRelease(sd->pShadowMap);
	}
	// 深度バッファをリリース
	_smpRelease(g_pShadowMapDepth);
}

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

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

		{	// シャドウマップの生成時
			NNF_DRAWOBJ flag = NND_DRAWOBJ_SHADER_USER_PROFILE1 |
				               //NND_DRAWOBJ_DEPTH_ONLY |			// DirectX では必要ない。(ピクセルシェーダでカラーに出力しているので、有効にすると描画されないから。)
							   NND_DRAWOBJ_DISABLE_LIGHTING |
							   NND_DRAWOBJ_IGNORETEXTURE;
			nnRegistObjectStdShaderProfiles( mdl->pObject, flag);

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

		{	// シャドウマップを貼る時
			nnRegistObjectStdShaderProfiles( mdl->pObject, gDrawFlag | gShadowDrawFlag );

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

	// シェーダのインクルードのパス設定
	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 );

	}
	// シャドウマップの初期化
	InitShadow();
	//
	BuildStdShader();
}

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

	{
		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 CalcShadouMapMatrix(SHADOW *sd, const NNS_MATRIX *viewmtx, const NNS_MATRIX *litvmtx)
{
	/*---------------------------------------------
                         -1
       [T][S][Lp][Lv][Cv]

       Cv : カメラのビュー行列
       Lv : ライトのビュー行列
       Lp : ライトのプロジェクション行列

       頂点座標からテクスチャの座標に変換する際、
	   X と Y はそれぞれ -1 〜 1 の値を 0 〜 1 に
       変換します。
       Z は 深度の調整aです。

       調整a : 深度に対するオフセット
	   調整b : 深度に対するスケール(基本は 1.0 )

       [T][S] = [ 0.5, 0.0, 0.0,   0.5   ]
                [ 0.0,-0.5, 0.0,   0.5   ]
                [ 0.0, 0.0, 調整b, 調整a ]
                [ 0.0, 0.0, 0.0,   1.0   ]
	---------------------------------------------*/
	NNS_MATRIX invvmtx;
	// ビュの逆行列
	nnInvertMatrix( &invvmtx, viewmtx );
	nnMultiplyMatrix( &sd->MapMatrix, litvmtx, &invvmtx );
	nnMultiplyProjectionMatrix( &sd->MapMatrix, &sd->LightProjMatrix, &sd->MapMatrix);
#ifdef _SHADOW_16F
	nnMultiplyScalingMatrix44( &sd->MapMatrix, 0.5f, -0.5f, 1.0f-0.000f, &sd->MapMatrix );
#else
	nnMultiplyScalingMatrix44( &sd->MapMatrix, 0.5f, -0.5f, 1.0f, &sd->MapMatrix );
#endif
#ifdef _SHADOW_16F
	nnMultiplyTranslationMatrix44( &sd->MapMatrix, 0.5f, 0.5f, -0.0006f, &sd->MapMatrix );
#else
	nnMultiplyTranslationMatrix44( &sd->MapMatrix, 0.5f, 0.5f, -0.0025f, &sd->MapMatrix );
#endif
}

//--------------------------------------------------
// モデルのモーションを計算します
//   マトリクスとマテリアルのモーションの計算も含む
//--------------------------------------------------
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 DrawShadowModel( 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 RenderToShadowMap( NNS_MATRIX *viewmtx, SHADOW *sd)
{
	LPDIRECT3DSURFACE9 pRenTgtOld, pDpOld;

	// 現在のレンダターゲットと深度バッファ
	g_pD3DDev->GetRenderTarget(0, &pRenTgtOld);
	g_pD3DDev->GetDepthStencilSurface(&pDpOld);
	// シャドウマップサフェースとシャドウマップ用深度バッファをセット
	g_pD3DDev->SetRenderTarget(0, sd->pShadowMapSuf);
	g_pD3DDev->SetDepthStencilSurface(g_pShadowMapDepth);

	{
		// シャドウマップのクリア
		g_pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);

		// ライトのプロジェクション
		nnSetProjection( &sd->LightProjMatrix, NNE_PROJECTION_TYPE_PERSPECTIVE );

		{
			// NND_DRAWOBJ_DEPTH_ONLY は DirectX では必要ない。(ピクセルシェーダでカラーに出力しているので、有効にすると描画されないから。)
			//DrawShadowModel( viewmtx, NND_DRAWOBJ_SHADER_USER_PROFILE1 | NND_DRAWOBJ_DEPTH_ONLY | NND_DRAWOBJ_DISABLE_LIGHTING | NND_DRAWOBJ_IGNORETEXTURE );
			DrawShadowModel( viewmtx, NND_DRAWOBJ_SHADER_USER_PROFILE1 | NND_DRAWOBJ_DISABLE_LIGHTING | NND_DRAWOBJ_IGNORETEXTURE );
		}

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

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

//------------------
// シャドウあり描画
//------------------
static void DrawModelAndShadow( NNS_MATRIX *viewmtx, Sint32 nb, SHADOW *sd )
{
	MODEL *mdl;
	Sint32    i;
	Uint32 state[NNE_SAMPLERSTATETYPE_MAX];
	// シャドウマップのデフォルトのサンプラステートを返す
    nnGetMaterialControlShadowMapDefaultStateDXG20(state);
	for(i=0; i<nb; i++, sd++){
#if 0
		nnSetMaterialControlShadowMapDXG20( (NNE_SHADOWMAP)(NNE_SHADOWMAP_1 + i), sd->pShadowMap, &sd->MapMatrix, 0.6f, 0.6f, 0.6f, state);
#else
		nnSetMaterialControlShadowMapDXG20( (NNE_SHADOWMAP)(NNE_SHADOWMAP_1 + i), sd->pShadowMap, &sd->MapMatrix, 0.3f, 0.3f, 0.3f, state);
#endif
	}

	for(i=0, mdl=SampleScn->pModel; i<SampleScn->NbModel; i++, mdl++){
		DrawModel(viewmtx, mdl, gDrawFlag | gShadowDrawFlag);
	}
}

//--------------------
// セットアップライト
//--------------------
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;	// ビューマトリクス
	//RENDER    *rd;
//	Sint32     i;

	{	// ビューマトリクスの計算
		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_MATRIX	lvmtx;	// ライトビューマトリクス
		LIGHT      *lit;
		SHADOW     *sd;
		Sint32      nb,
			        i;
		lit=SampleScn->pLight;
		sd = Shadow;
		if(SampleScn->NbLight <= NB_SHADOW){
			nb = SampleScn->NbLight;
		} else{
			nb = NB_SHADOW;
		}
		for(i=0; i<nb; i++, lit++, sd++){
			// ライトビューマトリクスの計算
			{
				NNS_VECTOR     v;
				NNS_ROTATE_A32 ang;
				v.x = 0.0f - lit->Position.x;
				v.y = 0.0f - lit->Position.y;
				v.z = 0.0f - lit->Position.z;
				Vector2AngleXY(&v, &ang);

				nnMakeRotateXMatrix( &lvmtx, -ang.x );
				nnRotateYMatrix(&lvmtx, &lvmtx, -ang.y);
				nnTranslateMatrix(&lvmtx, &lvmtx, -lit->Position.x, -lit->Position.y, -lit->Position.z);
			}
			// シャドウマップを生成します
			RenderToShadowMap( &lvmtx, sd );
			// シャドウマップ用のマトリクスを求めます
			CalcShadouMapMatrix( sd, &viewmtx, &lvmtx );
		}

		// シャドウあり描画
		DrawModelAndShadow( &viewmtx, nb, Shadow );
	}

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

//----------------------
// シャドウマップを表示
//----------------------
static void dispShadowMap(Sint32 num, const SHADOW *sd)
{
	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, sd->pShadowMap );
	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();

		Sint32 i;
		for(i=0; i<NB_SHADOW; i++){
			dispShadowMap(i, &Shadow[i]);
		}
	}
	// フレーム終了時刻取得
	endtick = smpTmrGet();

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