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

    Primitive3D application main sample

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

    Module  : Primitive3D application main sample
    File    : smpapp.cpp
    Create  : 2008/07/22
    Modify  : 2008/11/04 (Ver.0.30.02)
                nnExitVertexBufferDXG20 の記述忘れ。
			  2008/11/14 (Ver.0.30.03)
			    プラットフォーム別処理関数を smpmain.cpp へ移動した。
				nnGetDirect3DDeviceDXG20, nnGetD3DPresentParametersDXG20 に変更。
				D3DUSAGE_DYNAMIC を D3DUSAGE_WRITEONLY に変更。
			  2008/12/12 (Ver.0.30.04)
				D3DUSAGE_WRITEONLY を D3DUSAGE_DYNAMIC に変更(2008/11/14の変更をもとに戻した)。
			  2009/01/19 (Ver.0.30.05)
	            smpSetIncludePathDXG20 関数を追加。
			  2009/01/21 (Ver.0.30.06)
			    nnSetCompileErrorCallbackStdShaderDXG20 関数を追加。
			  2009/06/11 (Ver.0.30.07)
			    プラットフォーム別処理関数(IDirect3DDevice9_CreateVertexBuffer, IDirect3DVertexBuffer9_Release)を smpmain.cpp へ移動した。
    Version : 0.30.07
    Note    : 

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

#define __SMPAPP_CPP__

//--------------------
// 組み込みファイル名
//--------------------
#include "smpapp.h"
#include "smputil.h"

#include "smpdata.h"

////////////////////////////////////////////////////////////////////////////////
#define SAMPLENAME "PRIMITIVE3D"

#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;			// コントローラ情報

//------------------------------------------------------------------------------
// データ用

static void				*pTexListBuf0;			// TEXFILELIST0用テクスチャ情報領域用バッファ(メモリの解放が必要)
static NNS_TEXLIST		*pTexList0;				// TEXLIST0(メモリの解放が必要)

////////////////////////////////////////////////////////////////////////////////
//-------------
// NN の初期化
//-------------
static void InitNN(void)
{
	// デバッグプリント初期化
	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		= 1;						// パラレルライト最大数
		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 );
	}

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

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

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

//------------------------
// サンプルデータのロード
//------------------------
static void LoadImage(void)
{
	Sint32 num;

	// テクスチャリスト領域の確保 (TEXFILELIST0用)
	num = TEXFILELIST0->nTex;	// テクスチャ数
	pTexListBuf0 = smpMemAlloc( (Uint32)nnEstimateTexlistSize(num) );

	// テクスチャリストの初期化
	nnSetUpTexlist( &pTexList0, num, pTexListBuf0 );

	// テクスチャのロード、テクスチャ情報の設定 (TEXFILELIST0用)
	if( smpTexLoadFile( pTexList0, TEXFILELIST0 ) < 0 ){
		while(1){}	// Texture file read error
	}
}

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

//--------------------------
// メインループ前の初期設定
//--------------------------
static void SetInitEnv(void)
{
	D3DPRESENT_PARAMETERS *pD3DPP;
	pD3DPP = (D3DPRESENT_PARAMETERS*) nnGetD3DPresentParametersDXG20();

	NNS_MATRIX44        projmtx;
	NNS_VECTOR          LightDir = { 0.0f, 0.0f, -1.0f };
	NNS_RGBA            LightCol = { 1.0f, 1.0f, 1.0f, 1.0f };
	Float               LightSpec = 1.f;
	NNS_LIGHT_PARALLEL	LightParam;

	// プロジェクションの設定
	nnMakePerspectiveMatrix( &projmtx, NNM_DEGtoA32( 45.f ), (Float)pD3DPP->BackBufferWidth / (Float)pD3DPP->BackBufferHeight, 1.f, 10000.f );
	nnSetProjection( &projmtx, NNE_PROJECTION_TYPE_PERSPECTIVE );

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

	//   ライト０初期設定
	nnSetUpParallelLight( &LightParam, &LightCol, LightSpec, &LightDir );
	nnSetLight( NNE_LIGHT_0, &LightParam, NND_LIGHTTYPE_PARALLEL );
	nnSetLightSwitch( NNE_LIGHT_0, NNE_ON );
}

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

	pMtxStackBuf		= NULL;

	{	// 標準シェーダ
		pShaderMgrBuf	= NULL;
		VsCodeSize		= 0;
		PsCodeSize		= 0;
		pVsCodeBuf		= NULL;
		pPsCodeBuf		= NULL;
	}
	{	// コントローラ
		pPad1			= NULL;
		pPad2			= NULL;
	}
	//
	InitNN();
	//
	LoadImage();
	//
	BuildStdShader();
	//
	SetInitEnv();
}

//------
// 終了
//------
void UserExit(void)
{
	// テクスチャの解放 (TEXLIST0用)
	if(pTexList0 != NULL){
		if ( smpTexRelease( pTexList0 ) < 0 ){
			while(1){}	// Texture release error
		}
	}
	// テクスチャ情報領域の解放 (TEXLIST0用)
	smpMemFree( pTexListBuf0 );

	{	// シェーダの開放
		nnReleaseStdShader();			// ビルド済みシェーダ解放
		nnClearStdShaderProfiles();		// 登録済みシェーダの開放

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

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

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

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

//------------------------
// デバイスの消失時の処理
//------------------------
void UserDeviceLost(void)
{
	UserExit();
}

//--------------------------
// デバイスのリストアの処理
//--------------------------
void UserDeviceReset(void)
{
	UserInit();
}

//----------
// サンプル
//----------
static void Sample(void)
{
	Sint32	i;
	NNS_MATRIX	mtx, ViewMtx;
	static	Angle32	WorldRX = 0,
					PolyRZ = 0;
	static	Float	CameraRY = 0.0f,
					CameraTZ = 0.0f;
	NNS_CAMERA_TARGET_ROLL Camera = {
		0,
		NNM_DEGtoA16( 70.0f ),
		0.f,
		0.f,
		0.f,
		{ 0.0f, 0.0f, 14.0f },
		{ 0.0f, 0.0f, 0.0f },
		NNM_DEGtoA32( 0.0f ),
	};
	NNS_RGBA	Diffuse = { 1.f, 1.f, 1.f, 0.7f };
	NNS_RGB		Ambient = { 0.2f, 0.2f, 0.2f };
	Float		Specular = 0.5f;
	NNS_RGBA	LineColor = { 1.f, 1.f, 1.f, 1.f };
	NNS_RGBA	PointColor = { 1.f, 1.f, 1.f, 1.f };

	/* パッド情報更新 */
	smpPadCheck();

	/* ビューマトリクスの計算 */
	if( pPad1->On & SMPPAD_BTN_R )	CameraRY += 1.f;
	if( pPad1->On & SMPPAD_BTN_L )	CameraRY -= 1.f;
	if( pPad1->On & SMPPAD_BTN_D )	CameraTZ += 1.f;
	if( pPad1->On & SMPPAD_BTN_U )	CameraTZ -= 1.f;

	if(CameraTZ == 0.f) CameraTZ = 0.01f;//ハング予防

	nnMakeRotateYMatrix( &mtx, NNM_DEGtoA32( CameraRY ) );

	nnTranslateMatrix( &mtx, &mtx, 0.0f, 0.0f, CameraTZ );
	nnTransformVector( &Camera.Position, &mtx, &Camera.Position );
	nnMakeTargetRollCameraViewMatrix( &ViewMtx, &Camera );

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

	/* ライト設定の転送 */
	nnPutLightSettings();

	/********************************/
	/* ３Ｄラインプリミティブの描画 */
	/********************************/

	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* プリミティブの描画 */
	nnBeginDrawPrimitiveLine3D( &LineColor, NNE_PRIM_ALPHABLEND_ON );
	nnDrawPrimitiveLine3D( NNE_PRIM_LINE_STRIP,
							primLineData.pPrim[0].pVtx,
							primLineData.pPrim[0].vnum );
	nnEndDrawPrimitiveLine3D();

	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32( 60 ) );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* プリミティブの描画*/
	nnBeginDrawPrimitiveLine3D( &LineColor, NNE_PRIM_ALPHABLEND_ON );
	nnDrawPrimitiveLine3D( NNE_PRIM_LINE_STRIP,
							primLineData.pPrim[0].pVtx,
							primLineData.pPrim[0].vnum );
	nnEndDrawPrimitiveLine3D();


	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32( 120 ) );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* プリミティブの描画*/
	nnBeginDrawPrimitiveLine3D( &LineColor, NNE_PRIM_ALPHABLEND_ON );
	nnDrawPrimitiveLine3D( NNE_PRIM_LINE_STRIP,
							primLineData.pPrim[0].pVtx,
							primLineData.pPrim[0].vnum );
	nnEndDrawPrimitiveLine3D();

	/****************************************/
	/* ３Ｄプリミティブの描画（１）         */
	/* ライティング有り、テクスチャ環境張り */
	/****************************************/

	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnRotateZMatrix( &mtx, &mtx, PolyRZ );
	nnTranslateMatrix( &mtx, &mtx, 5.0f, 0.0f, 0.0f );
	nnScaleMatrix( &mtx, &mtx, 0.5f, 0.5f, 0.5f );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* ブレンディング、マテリアル、テクスチャの設定 */
	nnSetPrimitiveBlend( NNE_PRIM_BLEND_BLEND );
	nnSetPrimitive3DMaterial( &Diffuse, &Ambient, Specular );
	nnSetPrimitiveTexNum( pTexList0, 0 );
	nnSetPrimitiveTexState( NNE_PRIM_TEXBLEND_MODULATE, NNE_PRIM_TEXCOORD_ENVIRONMENT, NNE_PRIM_TEXWRAP_REPEAT, NNE_PRIM_TEXWRAP_REPEAT );

	/* プリミティブの描画*/
	nnBeginDrawPrimitive3D( NNE_PRIM3D_FMT_PN, NNE_PRIM_ALPHABLEND_ON, NNE_PRIM_LIGHT_ENABLE, NNE_PRIM_CULL_R );
	for( i = 0; i < primSphereDataL.pnum; i++ )
	{
		nnDrawPrimitive3D( NNE_PRIM_TRIANGLE_STRIP,
							primSphereDataL.pPrim[i].pVtx,
							primSphereDataL.pPrim[i].vnum );
	}
	nnEndDrawPrimitive3D();

	nnBeginDrawPrimitive3D( NNE_PRIM3D_FMT_PN, NNE_PRIM_ALPHABLEND_ON, NNE_PRIM_LIGHT_ENABLE, NNE_PRIM_CULL_L );
	for( i = 0; i < primSphereDataR.pnum; i++ )
	{
		nnDrawPrimitive3D( NNE_PRIM_TRIANGLE_STRIP,
							primSphereDataR.pPrim[i].pVtx,
							primSphereDataR.pPrim[i].vnum );
	}
	nnEndDrawPrimitive3D();

	/********************************/
	/* ３Ｄプリミティブの描画（２） */
	/* 頂点カラー、テクスチャUV張り */
	/********************************/

	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32( 60 ) );
	nnRotateZMatrix( &mtx, &mtx, PolyRZ );
	nnTranslateMatrix( &mtx, &mtx, 5.0f, 0.0f, 0.0f );
	nnScaleMatrix( &mtx, &mtx, 0.8f, 0.8f, 0.8f );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* ブレンディング、マテリアル、テクスチャの設定 */
	nnSetPrimitiveBlend( NNE_PRIM_BLEND_BLEND );
	nnSetPrimitive3DMaterial( &Diffuse, &Ambient, Specular );
	nnSetPrimitiveTexNum( pTexList0, 0 );
	nnSetPrimitiveTexState( NNE_PRIM_TEXBLEND_MODULATE, NNE_PRIM_TEXCOORD_UV, NNE_PRIM_TEXWRAP_REPEAT, NNE_PRIM_TEXWRAP_REPEAT );

	/* プリミティブの描画*/
	nnBeginDrawPrimitive3D( NNE_PRIM3D_FMT_PCT, NNE_PRIM_ALPHABLEND_ON, NNE_PRIM_LIGHT_DISABLE, NNE_PRIM_CULL_R );
	for( i = 0; i < primCubeData.pnum; i++ )
	{
		nnDrawPrimitive3D( NNE_PRIM_TRIANGLE_STRIP,
							primCubeData.pPrim[i].pVtx,
							primCubeData.pPrim[i].vnum );
	}
	nnEndDrawPrimitive3D();

	/*******************************/
	/* ３Ｄプリミティブの描画（３）*/
	/* 頂点カラー                  */
	/*******************************/

	/* 描画用マトリクスの生成と設定 */
	nnMakeRotateXMatrix( &mtx, WorldRX );
	nnRotateYMatrix( &mtx, &mtx, NNM_DEGtoA32( 120 ) );
	nnRotateZMatrix( &mtx, &mtx, PolyRZ );
	nnTranslateMatrix( &mtx, &mtx, 5.0f, 0.0f, 0.0f );
	nnScaleMatrix( &mtx, &mtx, 0.6f, 0.6f, 0.6f );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* ブレンディング、マテリアル、テクスチャの設定 */
	nnSetPrimitiveBlend( NNE_PRIM_BLEND_BLEND );

	/* プリミティブの描画*/
	nnBeginDrawPrimitive3D( NNE_PRIM3D_FMT_PC, NNE_PRIM_ALPHABLEND_OFF, NNE_PRIM_LIGHT_DISABLE, NNE_PRIM_CULL_NONE );

	for( i = 0; i < primTetraData.pnum; i++ )
	{
		nnDrawPrimitive3D( NNE_PRIM_TRIANGLE_STRIP,
							primTetraData.pPrim[i].pVtx,
							primTetraData.pPrim[i].vnum );
	}
	nnEndDrawPrimitive3D();

	PolyRZ  += 30;
	WorldRX += 30;

	/**********************************/
	/* ３Ｄポイントプリミティブの描画 */
	/**********************************/

	/* 描画用マトリクスの生成と設定 */
	nnMakeUnitMatrix( &mtx );
	nnMultiplyMatrix( &mtx, &ViewMtx, &mtx );
	nnSetPrimitive3DMatrix( &mtx );

	/* プリミティブの描画*/
	nnBeginDrawPrimitivePoint3D( NNE_PRIM3D_POINT_FMT_PC, &PointColor, NNE_PRIM_ALPHABLEND_OFF );
	
	{
		NNS_PRIM3D_PC *pPC = (NNS_PRIM3D_PC*)primPointData.pPrim[0].pVtx;

		for( i = 0; i < primPointData.pPrim[0].vnum; i++ ){
			pPC->Col = 0xFF0000FF;
			pPC++;
		}
	}
	nnDrawPrimitivePoint3D( primPointData.pPrim[0].pVtx,
							primPointData.pPrim[0].vnum );
	nnEndDrawPrimitivePoint3D();
}

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

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

	// 情報表示
	nnPrint( 3, 3, "NN "SAMPLENAME" SAMPLE" );
	nnPrint( 3, 4, "CPU Time%6d[us]", smpTmrDiff( endtick, starttick ) );
	nnPrint( 3, 25, "L, R BUTTON -> Rotate camera" );
	nnPrint( 3, 26, "U, D BUTTON -> Move camera" );

	// 登録文字の描画
	nnFlushPrint();
}
