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

    XSI IK sample

    Copyright (c) 2009 SEGA Corporation CS R&D Support Department
    All Rights Reserved.

    Module  : XSI IK sample
    File    : smpapp.cpp
    Create  : 2009/04/20
    Modify  : 2009/05/14
			  2009/06/11 (Ver.1.01.06)
			    プラットフォーム別処理関数(IDirect3DDevice9_CreateVertexBuffer, IDirect3DVertexBuffer9_Release)を smpmain.cpp へ移動した。
    Version : 1.01.06
    Note    : 

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

#define __SMPAPP_CPP__

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

////////////////////////////////////////////////////////////////////////////////
#define SAMPLENAME "XSI IK"

#define DEBUG_CHAR_MAX	(1200)

#define	XSIIK_FUNC_TEST_MODE	(0)
#define NO_TEX					(0)
#define NO_MOT					(0)

////////////////////////////////////////////////////////////////////////////////
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データ用
static NNS_MATRIX		*pMtxPalMax;			// 最大マトリクスパレット

static void				*pBuf0;					// OBJECTFILE0用バイナリロード用バッファ(メモリの解放が必要)
static NNS_OBJECT		*pObject0;				// OBJECT0(メモリの解放が必要)
static NNS_TRS			*pTRSListObject0;		// OBJECT0用TRSリスト先頭(メモリの解放が必要)
static NNS_MATRIX		*pMtxListObject0;		// OBJECT0用マトリクスリスト先頭(メモリの解放が必要)
static NNS_MATRIX		*pMtxPalObject0;		// OBJECT0用マトリクスパレット先頭(メモリの解放が必要)
static NNF_NODESTATUS	*pNodestatListObject0;	// OBJECT0用ノードステータスリスト先頭(メモリの解放が必要)

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

static NNS_PLIABLEOBJ	*pPliableObject0;		// エンベロープオブジェクト

static void				*pBuf1;					// MOTIONFILE0用バイナリロード用バッファ(メモリの解放が必要)
static void				*pBuf2;					// NODENAMELISTFILE0用バイナリロード用バッファ(メモリの解放が必要)
static NNS_NODENAMELIST	*pNodeNameListObject0;	// ノードネームリスト先頭
static NNS_MOTION		*pMotion0;				// モーション先頭

//------------------------------------------------------------------------------
// 描画フラグ
static const NNF_DRAWOBJ drawflag = NND_DRAWOBJ_FRAGPARALIGHT1;			// フラグメントライティング（パラレルライト×1)

////////////////////////////////////////////////////////////////////////////////
// モーション
static enum {
	MOTION_OFF,
	MOTION_ON,
	MOTION_STOP
#if NO_MOT
} MotionMode = MOTION_OFF;
#else
} MotionMode = MOTION_ON;
#endif

static Float						Frame = 0.0f;		// フレーム番号
static Float						GroundTime = 0.0f;	// 時間（地面用）

// ボーン描画モード
static enum {
	DRAW_BONE_NONE,
	DRAW_BONE_WIRE,
	DRAW_BONE_POLYGON,
} DrawBoneMode = DRAW_BONE_NONE;

// オブジェクトルートの上下移動
NNE_BOOL	MoveObjectRoot = NNE_TRUE;

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

// カメラ
static NNS_CAMERA_TARGET_ROLL		gCamera;		// ターゲットロールカメラ

// ライト
static NNS_LIGHT_PARALLEL			gLight;			// Parallel light
static NNS_VECTOR					gLightDir = { 0.f, 0.f, -1.f };		// 方向
static NNS_RGBA						gLightCol = { 1.f, 1.f, 1.f, 1.f };	// 色
static Float						gLightInten = 1.f;					// 強さ

/* 地面高さ */
Float GroundHeight( Float x, Float z, Angle32 ax, Angle32 az, Float alpha ){

	Float height;
	nnRandomSeed( alpha * x * 10.f + z * 5.f );
	height = 1.4f * nnSin( NNM_DEGtoA32( ( x + alpha * 0.06f ) * 20.f ) )
					 + 0.6f * nnSin( NNM_DEGtoA32( ( z + alpha * 0.1f ) * 15.f ) );
	height += nnTan( az ) * x + nnTan( ax ) * z;
	return( height );
}

/* 地面描画 */
void DrawGround( NNS_MATRIX *mtx, Angle32 ax, Angle32 az, Float time )
{
	Sint32 size = 30;
	Float unit = 1.f;
	Float *ht;
	static NNS_RGBA col0 = { 0.5f, 1.f, 1.f, 0.5f };
	static NNS_RGBA col1 = { 1.f, 1.f, 0.5f, 0.5f };
	NNS_PRIM3D_P *vtx;
	Sint32 x, z;

	/* メモリ確保 */
	ht = (Float *)smpMemAlloc( size * size * sizeof( Float ) );
	vtx = (NNS_PRIM3D_P *)smpMemAlloc( size * sizeof( NNS_PRIM3D_P ) );

	/* 地面の各点の高さを計算 */
	for ( z = 0; z < size; z++ ){
		for ( x = 0; x < size; x++ ){
			ht[ z * size + x ] = 
				GroundHeight(
				(Float)( ( x - size / 2 ) ) * unit,
				(Float)( ( z - size / 2 ) ) * unit,
				ax, az, time );
		}
	}
	/* ライン描画 */
	for ( x = 0; x < size; x++ ){
		for ( z = 0; z < size; z++ ){
			vtx[ z ].Pos.x = (Float) ( x - size / 2) * unit;
			vtx[ z ].Pos.y = ht[ z * size + x ];
			vtx[ z ].Pos.z = (Float) ( z - size / 2) * unit;
		}
		nnSetPrimitiveBlend( NNE_PRIM_BLEND_BLEND );
		nnSetPrimitive3DMatrix( mtx );
		nnBeginDrawPrimitiveLine3D( &col0, NNE_PRIM_ALPHABLEND_ON );
		nnDrawPrimitiveLine3D( NNE_PRIM_LINE_STRIP, vtx, size );
		nnEndDrawPrimitiveLine3D();
	}
	for ( z = 0; z < size; z++ ){
		for ( x = 0; x < size; x++ ){
			vtx[ x ].Pos.x = (Float) ( x - size / 2) * unit;
			vtx[ x ].Pos.y = ht[ z * size + x ];
			vtx[ x ].Pos.z = (Float) ( z - size / 2) * unit;
		}
		nnSetPrimitiveBlend( NNE_PRIM_BLEND_BLEND );
		nnSetPrimitive3DMatrix( mtx );
		nnBeginDrawPrimitiveLine3D( &col1, NNE_PRIM_ALPHABLEND_ON );
		nnDrawPrimitiveLine3D( NNE_PRIM_LINE_STRIP, vtx, size );
		nnEndDrawPrimitiveLine3D();
	}
	smpMemFree( vtx );
	smpMemFree( ht );
}

////////////////////////////////////////////////////////////////////////////////
//-------------
// 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;
	Uint32 size;
	NNS_OBJECT		*pLoadObject0;
	NNS_TEXFILELIST	*pTexFileList0;

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

	// オブジェクトデータのセットアップ
	smpBinSetUpObject( &pLoadObject0, &pTexFileList0, pBuf0 );

	// ※※※ オブジェクトにD3Dリソースを割り当てる ※※※
	size = nnCalcVertexBufferObjectSizeDXG20(pLoadObject0, 0);
	pObject0 = (NNS_OBJECT*)smpMemAlloc( size );
	nnVertexBufferObjectDXG20( pObject0, pLoadObject0, 0);

	if(pTexFileList0 != NULL){
		// テクスチャ情報領域の確保 (TEXFILELIST0用)
		num = pTexFileList0->nTex;	// テクスチャ数
		pTexListBuf0 = smpMemAlloc( (Uint32)nnEstimateTexlistSize(num) );

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

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

	// 読み込みオブジェクトデータ領域の解放
	smpMemFree( pBuf0 );

	// エンベロープオブジェクト作成
	size = nnCalcPliableObjectBufferSize( pObject0, 0 );
	pPliableObject0 = (NNS_PLIABLEOBJ*)smpMemAlloc( size );
	nnInitPliableObject( pPliableObject0, pObject0, 0 );

	// モーションデータロード キャラ
#if !NO_MOT
	size = smpBinGetFileSize( MOTIONFILE0 );
	pBuf1 = smpMemAlloc( size );
	smpBinReadFile( MOTIONFILE0, pBuf1 );
	smpBinSetUpMotion( &pMotion0, pBuf1 );
#endif

	// ノードネームリストデータロード
	size = smpBinGetFileSize( NODENAMELISTFILE0 );
	pBuf2 = smpMemAlloc( size );
	smpBinReadFile( NODENAMELISTFILE0, pBuf2 );
	// ノードネームリストデータのセットアップ
	smpBinSetUpNodeNameList( &pNodeNameListObject0, pBuf2 );

	// マトリクスパレット領域の確保 (OBJECT0用)
	num = pObject0->nMtxPal;	// マトリクスパレット数
	pMtxPalObject0 = (NNS_MATRIX*)smpMemAlloc( num * sizeof( NNS_MATRIX ) );

	// マトリクスリスト領域の確保 (OBJECT0用)
	num = pObject0->nNode;	// ノード数
	pMtxListObject0 = (NNS_MATRIX*)smpMemAlloc( num * sizeof( NNS_MATRIX ) );

	// TRSリスト領域の確保 (OBJECT0用)
	pTRSListObject0 = (NNS_TRS*)smpMemAlloc( num * sizeof( NNS_TRS ) );

	// ノードステータスリスト領域の確保 (OBJECT0用)
	num = pObject0->nNode;	// ノード数
	pNodestatListObject0 = (NNF_NODESTATUS*)smpMemAlloc( num * sizeof( NNF_NODESTATUS ) );
}

//----------------------
// 標準シェーダーの作成
//----------------------
static void BuildStdShader(void)
{
	// シェーダー登録
	nnRegistObjectStdShaderProfiles( pObject0, drawflag );

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

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

	// カメラ初期設定
	gCamera.Position    = gCamera.Target = pObject0->Center;
	gCamera.Position.x += pObject0->Radius * 2.f;
	gCamera.Position.z += pObject0->Radius * 4.f;
	gCamera.Roll        = NNM_DEGtoA32( 0.f );	// カメラロール
	gCamera.Fovy        = NNM_DEGtoA32( 45.f );	// カメラ視野角 
	gCamera.Aspect      = (Float)pD3DPP->BackBufferWidth / (Float)pD3DPP->BackBufferHeight;	// アスペクト
	gCamera.ZNear       = 1.f; 					// カメラ近接面
	gCamera.ZFar        = 10000.f;				// カメラ遠方面

	// プロジェクション設定
	nnMakePerspectiveMatrix( &gProjmtx, gCamera.Fovy, gCamera.Aspect,
										gCamera.ZNear, gCamera.ZFar );
	nnSetProjection( &gProjmtx, NNE_PROJECTION_TYPE_PERSPECTIVE );

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

	//   ライト０初期設定
	nnSetUpParallelLight( &gLight, &gLightCol, gLightInten, &gLightDir );
	nnSetLight( NNE_LIGHT_0, &gLight, 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;
	}
	{	// データ
		pBuf0			= NULL;
		pObject0		= NULL;
		pTRSListObject0	= NULL;
		pMtxListObject0	= NULL;
		pMtxPalObject0	= NULL;
		pNodestatListObject0	= NULL;

		pTexListBuf0	= NULL;
		pTexList0		= NULL;

		pPliableObject0	= NULL;

		pBuf1			= NULL;
#if !NO_MOT
		pMotion0		= NULL;
#endif
	}
	//
	InitNN();
	//
	LoadImage();
	//
	BuildStdShader();
	//
	SetInitEnv();
}

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

	// エンベロープオブジェクト解放
	nnExitPliableObject( pPliableObject0 );
	smpMemFree( pPliableObject0 );

	// ノードステータスリスト領域の解放 (OBJECT0用)
	smpMemFree( pNodestatListObject0 );

	// マトリクスパレット領域の解放 (OBJECT0用)
	smpMemFree( pMtxPalObject0 );

	// マトリクスリスト領域の解放 (OBJECT0用)
	smpMemFree( pMtxListObject0 );

	// TRSリスト領域の解放 (OBJECT0用)
	smpMemFree( pTRSListObject0 );

	// ※※※ オブジェクトに割り当てたD3Dリソース解放 ※※※
	nnDeleteVertexObjectDXG20(pObject0);

	// オブジェクトデータ領域の解放
	smpMemFree( pObject0 );

	// モーションデータ領域の解放
	smpMemFree( pBuf1 );

	// ノードネームリストデータ領域の解放
	smpMemFree( pBuf2 );

	{	// シェーダの開放
		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)
{
	NNS_MATRIX	viewmtx;	/* ビューマトリクス */
	NNS_MATRIX	objectmtx;	/* オブジェクトマトリクス */
	NNS_MATRIX	vomtx;		/* 描画用マトリクス */
	Float		vx, vz, vr;
	Angle32		vt;
	static NNS_VECTOR	otrans = { 0.f, 0.f, 0.f };	/* オブジェクト表示座標 */

	static Angle32	dx = NNM_DEGtoA32( 0.f );
	static Angle32	dz = NNM_DEGtoA32( 0.f );

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

	/* カメラ前進 */
	if ( pPad1->On & SMPPAD_BTN_TR ){
		gCamera.Position.x -= ( gCamera.Position.x - gCamera.Target.x ) * 0.02f;
		gCamera.Position.y -= ( gCamera.Position.y - gCamera.Target.y ) * 0.02f;
		gCamera.Position.z -= ( gCamera.Position.z - gCamera.Target.z ) * 0.02f;
	}
	/* カメラ後退 */
	if ( pPad1->On & SMPPAD_BTN_TL ){
		gCamera.Position.x += ( gCamera.Position.x - gCamera.Target.x ) * 0.02f;
		gCamera.Position.y += ( gCamera.Position.y - gCamera.Target.y ) * 0.02f;
		gCamera.Position.z += ( gCamera.Position.z - gCamera.Target.z ) * 0.02f;
	}
	/* カメラ左右回り込み */
	vx = gCamera.Position.x - gCamera.Target.x;
	vz = gCamera.Position.z - gCamera.Target.z;
	vt = nnArcTan2( vz, vx );
	vr = nnSqrt( vx * vx + vz * vz );

	if ( pPad1->On & (SMPPAD_BTN_L | SMPPAD_BTN_R)){
		if ( pPad1->On & SMPPAD_BTN_L ){
			vt += NNM_DEGtoA32( 3.f );
		} else {
			vt -= NNM_DEGtoA32( 3.f );
		}
		gCamera.Position.x = nnCos( vt ) * vr + gCamera.Target.x;
		gCamera.Position.z = nnSin( vt ) * vr + gCamera.Target.z;
	}

	/* ビューマトリクスの計算 */
	/* カメラからビューマトリクスを求める */
	nnMakeTargetRollCameraViewMatrix( &viewmtx, &gCamera );

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

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

	/* 地面の描画 */
	DrawGround( &viewmtx, dx, dz, GroundTime );
	GroundTime += 0.5f;

	/* 描画用マトリクス生成 */
	nnMultiplyMatrix( &vomtx, &viewmtx, &objectmtx );

	/* ノードステータスリストのクリア */
	nnSetUpNodeStatusList( pNodestatListObject0, pObject0->nNode, NND_NODESTATUS_NONE );


	/****************************/
	/* XSI IKを適用した階層計算 */
	/****************************/

#if ( XSIIK_FUNC_TEST_MODE == 0 || XSIIK_FUNC_TEST_MODE == 1 )
	/* TRSリスト計算 */
	if ( MotionMode == MOTION_OFF || NO_MOT ){	/* モーションなし */
		nnCalcTRSList( pTRSListObject0,  pObject0 );
	} else {
		nnCalcTRSListMotion( pTRSListObject0, pObject0, pMotion0, Frame );
	}

	/**************************************************/
	/* IK計算前にエフェクタをインタラクティブに動かす */
	/**************************************************/
	{
		Sint32 lfootidx, rfootidx, ltoeidx, rtoeidx;
		NNS_TRS *lfoottrs, *rfoottrs, *ltoetrs, *rtoetrs;
		Float lfootheight, rfootheight, lfootground, rfootground;
		Float ltoeheight, rtoeheight, ltoeground, rtoeground;
		Float lheightoffset, rheightoffset;
		static Float heightoffset = 0.f;

		/* XSI IKエフェクタの番号 */
		lfootidx = nnGetNodeIndex( pNodeNameListObject0, NODENAME_L_FOOT );
		rfootidx = nnGetNodeIndex( pNodeNameListObject0, NODENAME_R_FOOT );
		ltoeidx = nnGetNodeIndex( pNodeNameListObject0, NODENAME_L_TOE );
		rtoeidx = nnGetNodeIndex( pNodeNameListObject0, NODENAME_R_TOE );
		/* エフェクタのTRS（Tはグローバル座標系） */
		lfoottrs = &pTRSListObject0[ lfootidx ];
		rfoottrs = &pTRSListObject0[ rfootidx ];
		ltoetrs = &pTRSListObject0[ ltoeidx ];
		rtoetrs = &pTRSListObject0[ rtoeidx ];

		/* エフェクタの真下の地面高さ */
		lfootground = GroundHeight( lfoottrs->Translation.x, lfoottrs->Translation.z, dx, dz, GroundTime );
		rfootground = GroundHeight( rfoottrs->Translation.x, rfoottrs->Translation.z, dx, dz, GroundTime );
		ltoeground = GroundHeight( ltoetrs->Translation.x, ltoetrs->Translation.z, dx, dz, GroundTime );
		rtoeground = GroundHeight( rtoetrs->Translation.x, rtoetrs->Translation.z, dx, dz, GroundTime );

		/* エフェクタの高さ */
		lfootheight = lfoottrs->Translation.y;
		ltoeheight = ltoetrs->Translation.y;
		rfootheight = rfoottrs->Translation.y;
		rtoeheight = rtoetrs->Translation.y;

		/* オブジェクトの高さを決める */
		lheightoffset = lfootheight - lfootground;
		rheightoffset = rfootheight - rfootground;
		if ( MoveObjectRoot ){
			heightoffset = NNM_MIN( lheightoffset, rheightoffset );	// 高さを調整する
		}
		/* オブジェクトを持ち上げる */
		nnMakeTranslateMatrix( &objectmtx, 0.f, -heightoffset, 0.f );
		/* 描画用マトリクス */
		nnMultiplyMatrix( &vomtx, &viewmtx, &objectmtx );

		/* エフェクタが地面にめり込んでいるときは地面につける */
		lfootheight = lfoottrs->Translation.y - heightoffset;
		rfootheight = rfoottrs->Translation.y - heightoffset;
		if ( lfootground > lfootheight ){
			lfoottrs->Translation.y = lfootground + heightoffset;
		}
		if ( rfootground > rfootheight ){
			rfoottrs->Translation.y = rfootground + heightoffset;
		}
		ltoeheight = ltoetrs->Translation.y - heightoffset;
		rtoeheight = rtoetrs->Translation.y - heightoffset;
		if ( ltoeground > ltoeheight ){
			ltoetrs->Translation.y = ltoeground + heightoffset;
		}
		if ( rtoeground > rtoeheight ){
			rtoetrs->Translation.y = rtoeground + heightoffset;
		}
		ltoetrs->Translation.y = ltoeground + heightoffset;
		rtoetrs->Translation.y = rtoeground + heightoffset;
	}

	/********************************************/
	/* XSI IKを適用した階層マトリクスリスト計算 */
	/********************************************/
#if ( XSIIK_FUNC_TEST_MODE == 0 )
	/* マトリクスリスト経由版 */
	nnCalcMatrixListTRSList( pMtxListObject0, pObject0, pTRSListObject0, &vomtx );
	/* マトリクスパレットを生成 */
	nnCalcMatrixPaletteMatrixList( pMtxPalObject0, pObject0, pMtxListObject0, NULL );
#else	// XSIIK_FUNC_TEST_MODE == 1
	/* TRSリストから直接マトリクスパレットを生成 */
	nnCalcMatrixPaletteTRSList( pMtxPalObject0, NULL, pObject0, pTRSListObject0, &vomtx, &gMtxStack, 0 );
#endif

#elif ( XSIIK_FUNC_TEST_MODE == 2 )		//いきなりマトリクスパレットを求めるテスト（IKなし）

	/* 描画用マトリクス */
	nnMakeTranslateMatrix( &objectmtx, 0.f, 0.f, 0.f );
	nnMultiplyMatrix( &vomtx, &viewmtx, &objectmtx );
	if ( MotionMode == MOTION_OFF || NO_MOT ){	/* モーションなし */
		nnCalcMatrixPalette( pMtxPalObject0, pNodestatListObject0, pObject0,
								&vomtx, &gMtxStack, 0 );
	} else {
		nnCalcMatrixPaletteMotion( pMtxPalObject0, pNodestatListObject0, pObject0,
								pMotion0, Frame, &vomtx, &gMtxStack, 0 );
	}

#elif( XSIIK_FUNC_TEST_MODE == 3 )		//いきなりマトリクリストを求めるテスト（IKなし）

	/* 描画用マトリクス */
	nnMakeTranslateMatrix( &objectmtx, 0.f, 0.f, 0.f );
	nnMultiplyMatrix( &vomtx, &viewmtx, &objectmtx );
	if ( MotionMode == MOTION_OFF || NO_MOT ){	/* モーションなし */
		nnCalcMatrixList( pMtxListObject0, pObject0, &vomtx );
	} else {
		nnCalcMatrixListMotion( pMtxListObject0, pObject0, pMotion0, Frame, &vomtx );
	}
	/* マトリクスパレットを生成 */
	nnCalcMatrixPaletteMatrixList( pMtxPalObject0, pObject0, pMtxListObject0, NULL );

#endif

	/* モーションモード変更 */
	if ( pPad1->Press == SMPPAD_BTN_A ){
		switch ( MotionMode ) {
			case MOTION_OFF:
				MotionMode = MOTION_ON; break;
			case MOTION_ON:
				MotionMode = MOTION_STOP; break;
			case MOTION_STOP:
				MotionMode = MOTION_OFF; break;
		}
	}

#if !NO_MOT
	/* モーションを進める */
	if ( MotionMode == MOTION_ON ){
		Frame += pMotion0->FrameRate / 60.f;
	}
	/* マニュアルでモーションを進める */
	if ( MotionMode == MOTION_STOP ){
		if ( pPad1->Y1 > 50 ) {
			Frame += 0.2f;
		}
		if ( pPad1->Y1 < -50 ) {
			Frame -= 0.2f;
		}
	}
	/* モーションリピート */
	if( Frame >= pMotion0->EndFrame ){
		Frame = pMotion0->StartFrame;
	}
#endif

	/* サンプルパラメータ調整 */
	/* ボーン表示モード */
	if ( pPad1->Press & SMPPAD_BTN_B ){
		if ( DrawBoneMode == DRAW_BONE_NONE ){
			DrawBoneMode = DRAW_BONE_POLYGON;
		} else if ( DrawBoneMode == DRAW_BONE_POLYGON ){
			DrawBoneMode = DRAW_BONE_WIRE;
		} else {
			DrawBoneMode = DRAW_BONE_NONE;
		}
	}
	/* 高さ調整モード */
	if ( pPad1->Press & SMPPAD_BTN_X ){
		MoveObjectRoot = ( MoveObjectRoot == NNE_TRUE ? NNE_FALSE : NNE_TRUE );
	}
	/* 地面の角度 */
	if ( pPad1->Press & SMPPAD_BTN_U ){
		dz += NNM_DEGtoA32( 4.f );
		dx += NNM_DEGtoA32( 2.5f );
	}
	if ( pPad1->Press & SMPPAD_BTN_D ){
		dz += NNM_DEGtoA32( -4.f );
		dx += NNM_DEGtoA32( -2.5f );
	}
	/* カメラリセット */
	if( pPad1->Press & SMPPAD_BTN_ST ){
		gCamera.Position = gCamera.Target = pObject0->Center;
		gCamera.Position.x += pObject0->Radius * 2.f;
		gCamera.Position.z += pObject0->Radius * 4.f;
	}


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

	/* エンベロープオブジェクト計算 */
	nnCalcPliableObject( pPliableObject0, pMtxPalObject0, 0 );

	/* NNオブジェクト描画 */
	if ( DrawBoneMode == DRAW_BONE_NONE ){
		/* NNオブジェクト描画（不透明、リジッド）*/
		nnDrawObject( pObject0, pMtxPalObject0, pNodestatListObject0,
			NND_SUBOBJTYPE_OPAQUE | NND_SUBOBJTYPE_PUNCHTHROUGH | NND_SUBOBJTYPE_RIGID, drawflag );
		nnDrawPliableObject( pPliableObject0, NULL, pNodestatListObject0,
			NND_SUBOBJTYPE_OPAQUE | NND_SUBOBJTYPE_PUNCHTHROUGH, drawflag );
		/* NNオブジェクト描画（半透明）*/
		nnDrawObject( pObject0, pMtxPalObject0, pNodestatListObject0,
			NND_SUBOBJTYPE_TRANSPARENT | NND_SUBOBJTYPE_RIGID, drawflag );
		nnDrawPliableObject( pPliableObject0, NULL, pNodestatListObject0,
			NND_SUBOBJTYPE_TRANSPARENT, drawflag );
	}

	/*********************/
	/* XSIIKボーンの描画 */
	/*********************/
	if ( DrawBoneMode == DRAW_BONE_WIRE ){
		nnDrawSIIKBone( pObject0, NULL, pMtxListObject0, NND_DRAWSIIKBONE_WIRE );
	} else if ( DrawBoneMode == DRAW_BONE_POLYGON ){
		nnDrawSIIKBone( pObject0, NULL, pMtxListObject0, NND_DRAWSIIKBONE_POLY );
	}
}

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

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

	// 情報表示
	nnPrint( 3, 3, "NN "SAMPLENAME" SAMPLE" );
	nnPrint( 3, 5, "CPU Time%6d[us]", smpTmrDiff( endtick, starttick ) );
	nnPrint( 3, 6, "Frame  %7.1f", Frame );
	if ( MotionMode == MOTION_OFF ){
		nnPrint( 3, 8, "[Z] key : MOTION MODE  [OFF]" );
	} else if ( MotionMode == MOTION_STOP ){
		nnPrint( 3, 8, "[Z] key : MOTION MODE  [STOP]" );
	} else {
		nnPrint( 3, 8, "[Z] key : MOTION MODE  [ON]" );
	}
	if ( DrawBoneMode == DRAW_BONE_NONE ){
		nnPrint( 3, 9, "[X] key : DRAW BONE    [NONE]" );
	} else if ( DrawBoneMode == DRAW_BONE_WIRE ){
		nnPrint( 3, 9, "[X] key : DRAW BONE    [WIRE]" );
	} else {
		nnPrint( 3, 9, "[X] key : DRAW BONE    [POLYGON]" );
	}
	if ( MoveObjectRoot ){
		nnPrint( 3,10, "[A] key : MOVE ROOT    [ON]" );
	} else {
		nnPrint( 3,10, "[A] key : MOVE ROOT    [OFF]" );
	}
	nnPrint( 3, 11, "[Up/Down]   : CHANGE SLOPE" );
	nnPrint( 3, 12, "[Left/Right]: MOVE CAMERA" );

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