
import "sample_app.rs" ;


class	ShaderSprite	extends RenderableSprite
{
	protected SampleApp		m_app ;
	protected CustomShader	m_shader ;
	protected Vector3D 		m_vCameraPos ;		// 視点
	protected Vector3D 		m_vCameraTarget ;	// 注視点
	protected Vector3D 		m_vCameraTop ;		// 天頂方向
	protected Matrix3D		m_matTerrain ;
	protected Vector3D		m_vTerrain ;
	protected Vector3D		m_vLightDir ;
	protected Image			m_imgTerrain ;

	public ShaderSprite( SampleApp app )
	{
		super() ;
		m_app = app ;
	}


	// 表示領域矩形
	protected boolean getRenderRect( Rect rect )
	{
		rect.x = 0 ;
		rect.y = 0 ;
		rect.w = SampleApp.SCREEN_WIDTH ;
		rect.h = SampleApp.SCREEN_HEIGHT ;
		return	true ;
	}

	// マウスクリック当たり判定
	protected boolean isHitTest( double xLocal, double yLocal )
	{
		return	false ;
	}

	// 描画
	protected void onRender( RenderContext render, int s3dView )
	{
		// 3D レンダリングのための設定
		setPerspective( render ) ;
		setCameraTo( render ) ;

		// 3D 描画開始
		render.begin3DRenderer() ;
		render.pushTransformation() ;
		render.attachCustomShader( m_shader ) ;

		// シェーダーパラメータ設定
		Matrix3D	matITerrain = m_matTerrain.clone().inverse() ;
		Vector3D	vITerrain = matITerrain.mul( m_vTerrain.clone().mul(-1) ) ;

		render.setCustomShaderUniform
			( "u_mat3Terrain",
				CustomShader.uniformMatrix3x3,
				m_matTerrain, 1 ) ;
		render.setCustomShaderUniform
			( "u_mat3ITerrain",
				CustomShader.uniformMatrix3x3,
				matITerrain, 1 ) ;
		render.setCustomShaderUniform
			( "u_vITerrain",
				CustomShader.uniformVector3D,
				vITerrain, 1 ) ;
		render.setCustomShaderUniform
			( "u_vLightDir",
				CustomShader.uniformVector3D,
				m_vLightDir, 1 ) ;

		Int64Pointer	pTextures = new Int64Pointer( 1 ) ;
		pTextures[0] = m_imgTerrain.getNativePointer() ;
		render.setCustomShaderUniform
			( "u_texTerrain",
				CustomShader.uniformTexture,
				pTextures, 1 ) ;

		// 描画
		render.fillRectangle
			( 0, 0, SampleApp.SCREEN_WIDTH,
					SampleApp.SCREEN_HEIGHT, 0xffffffff ) ;

		// 3D 描画の終了とシェーディング設定を元に戻す
		render.popTransformation() ;
		render.end3DRenderer() ;
	}

	// タイマー処理
	protected void onTimer( int msecPast )
	{
		m_vTerrain.z -= msecPast * 0.001 ;
		postUpdate() ;
	}


	// 初期設定
	public boolean initialize( void )
	{
		m_vCameraPos = new Vector3D( 0.0, 0.0, -5.0 ) ;
		m_vCameraTarget = new Vector3D( 0.0, 0.0, 0.0 ) ;
		m_vCameraTop = new Vector3D( 0.0, -1.0, 0.0 ) ;

		m_matTerrain = new Matrix3D( 20, 0, 0,  0, 7, 0,  0, 0, 20 ) ;
		m_vTerrain = new Vector3D( 0, 8, 0 ) ;
		m_vLightDir = new Vector3D( 1.0, 1.0, 1.0 ) ;

		m_imgTerrain = new Image() ;
		if ( !m_imgTerrain.loadImage( "terrain.png" ) )
		{
			outputError( "terrain.png の読み込みに失敗しました。" ) ;
		}
		m_imgTerrain.normalizeToTexture( Image.bufferSampleTiling ) ;

		prepareShader() ;
	}

	// シェーダー準備
	public void prepareShader( void )
	{
		RenderDevice.ShaderDesc	shddsc = new RenderDevice.ShaderDesc() ;
		if ( !shddsc.loadDescriptor( "terrain_march.shddsc" ) )
		{
			outputError( "terrain_march.shddsc の読み込みに失敗しました。" ) ;
		}
		else
		{
			RenderDevice	device = m_app.getWindow().getRenderDevice() ;
			try
			{
				m_shader = device.buildCustomShader( "terrain_march", shddsc ) ;
			}
			catch ( RenderDevice.BuildShaderException e )
			{
				outputError
					( "シェーダーのビルドに失敗しました。\n>>> " + e.toString() ) ;
			}
		}
	}

	// 透視変換設定
	public void setPerspective( RenderContext render )
	{
		const double	degFOV = 65 ;
		const double	radHalfFOV = degFOV * 0.5 * Math.PI / 180 ;
		//
		Vector3D	vScreen = new Vector3D() ;
		vScreen.x = SampleApp.SCREEN_WIDTH * 0.5 ;
		vScreen.y = SampleApp.SCREEN_HEIGHT * 0.5 ;
		vScreen.z = vScreen.x / Math.tan(radHalfFOV) ;
		//
		render.setProjectionScreen( vScreen ) ;
		render.setZClipRange( 0.1, 100.0 ) ;
	}

	// カメラ設定
	public void setCameraTo( RenderContext render )
	{
		Matrix3D	matCamera = new Matrix3D() ;
		Vector3D	vCamera =
			matCamera.cameraAngleOf
				( m_vCameraTarget, m_vCameraPos, m_vCameraTop ) ;
		render.setCamera( matCamera, vCamera ) ;
	}

	// エラーメッセージ出力
	public void outputError( String err )
	{
		System.console().printf( "error: %s\n", err ) ;
	}
}


class	CustomShaderRunnable	implements Runnable
{
	public SampleApp	m_app ;
	public ShaderSprite	m_sprite ;

	public CustomShaderRunnable( SampleApp app )
	{
		m_app = app ;
	}

	public void run()
	{
		// RenderableSprite 準備
		m_sprite = new ShaderSprite( m_app ) ;
		m_sprite.setErrorMode( RenderableSprite.errorStdOut ) ;
		m_sprite.initialize() ;

		// 表示
		m_app.getWindow().addChild( m_sprite ) ;
		m_app.waitFor( function() : boolean { return false; } ) ;
	}

	public void finalize( void )
	{
		releaseRef() ;
	}

	public void releaseRef( void )
	{
		if ( m_app != null )
		{
			m_app.getWindow().detachChild( m_sprite ) ;
			m_sprite.dispose() ;
			m_sprite = null ;
			m_app = null ;
		}
	}
}


int main( String[] args )
{
	SampleApp	app = new SampleApp() ;
	app.initialize( "Custom Shader Sample" ) ;

//	CustomShaderRunnable	runnable = new CustomShaderRunnable(app) ;
	try
	{
		app.run( new CustomShaderRunnable(app) ) ;
	}
	finally
	{
//		runnable.releaseRef() ;
		app.release() ;
	}
	return	0 ;
}


