#include "pch.hpp"
//===========================================================================//
// File:	 RenderToTexture.cpp											 //
// Project:  GameOS					                                         //
// Contents: Rendering using Blade onto a texture surface					 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 01/11/99  AG		Created								 					 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "3DRasterizer.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "Debugger.hpp"
#include "Texture Manager.hpp"


extern bool gNoBlade;

CTexInfo *pRenderTexture=NULL;

IDirectDrawSurface7* RenderTargetSurface=0;
IDirect3DDevice7* RenderDevice=0;
IDirect3DDevice7* TempRenderDevice=0;
IDirect3D7* RenderObject=0;

DDSURFACEDESC2 RenderTargetddsd;

DWORD RenderWidth,RenderHeight;


//
// Create a 565 render target for blade to use when rendering to a texture
//
void InitRenderToTexture()
{
	SPEW(( GROUP_DIRECTDRAW, "InitRenderToTexture()" ));
//
// Create a 256*256 render surfcae
//
	memset(&RenderTargetddsd,0,sizeof(RenderTargetddsd));
	RenderTargetddsd.dwSize = sizeof(RenderTargetddsd);
	RenderTargetddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT;
	RenderTargetddsd.ddsCaps.dwCaps= DDSCAPS_TEXTURE | DDSCAPS_3DDEVICE | DDSCAPS_SYSTEMMEMORY;
	RenderTargetddsd.dwWidth = 256;
	RenderTargetddsd.dwHeight = 256;
	RenderTargetddsd.ddpfPixelFormat.dwSize				= sizeof(DDPIXELFORMAT);
	RenderTargetddsd.ddpfPixelFormat.dwFlags			= DDPF_RGB;
	RenderTargetddsd.ddpfPixelFormat.dwRGBBitCount		= 16;
	RenderTargetddsd.ddpfPixelFormat.dwRGBAlphaBitMask	= 0x0000;
	RenderTargetddsd.ddpfPixelFormat.dwRBitMask			= 0xf800;
	RenderTargetddsd.ddpfPixelFormat.dwGBitMask			= 0x07e0;
	RenderTargetddsd.ddpfPixelFormat.dwBBitMask			= 0x001f;

	AllowFail=1;
	HRESULT result=wCreateSurface( CurrentDDobject, &RenderTargetddsd, &RenderTargetSurface, NULL );
	AllowFail=0;
	if( FAILED(result) )
	{
		RenderTargetddsd.ddsCaps.dwCaps= DDSCAPS_3DDEVICE | DDSCAPS_SYSTEMMEMORY;
		AllowFail=1;
		HRESULT result=wCreateSurface( CurrentDDobject, &RenderTargetddsd, &RenderTargetSurface, NULL );
		AllowFail=0;
		if( FAILED(result) )
		{
			RenderTargetSurface=0;
		}
	}
//
// Cache the ram address of this surface
//
	if( RenderTargetSurface )
	{
		RenderTargetddsd.dwSize = sizeof(RenderTargetddsd);
		wLock( RenderTargetSurface, NULL, &RenderTargetddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );
		wUnlock( RenderTargetSurface, NULL );
		RenderDevice=0;
//
// Make render target for blade
//
		wQueryInterface( CurrentDDobject, IID_IDirect3D7, (void**)&RenderObject );
		if( gNoBlade )
			wCreateDevice( RenderObject, IID_IDirect3DRGBDevice, RenderTargetSurface, &TempRenderDevice );
		else
			wCreateDevice( RenderObject, IID_IDirect3DBladeDevice, RenderTargetSurface, &TempRenderDevice );
	}
	SPEW(( GROUP_DIRECTDRAW, "InitRenderToTexture() - Finished" ));
}


//
//
//
void DestroyRenderToTexture()
{
	SPEW(( GROUP_DIRECTDRAW, "DestroyRenderToTexture()" ));

	if( 0==RenderTargetSurface )
		return;

	if( TempRenderDevice )
	{
		if( TempRenderDevice==RenderDevice )
			wEndScene( TempRenderDevice );
		wRelease( TempRenderDevice );
		TempRenderDevice=0;
	}
	if( RenderTargetSurface )
	{
		wRelease( RenderTargetSurface );
		RenderTargetSurface=0;
	}
	if( RenderObject )
	{
		wRelease( RenderObject );
		RenderObject=0;
	}
	RenderDevice=0;
	SPEW(( GROUP_DIRECTDRAW, "DestroyRenderToTexture() - Finished" ));
}








volatile __int64 TimeRenderTextures;
static __int64 TempTime;
//
// Render to a texture
//
void __stdcall gos_StartRenderToTexture( DWORD Handle )
{
	if( 0==RenderTargetSurface )
		return;

	gosASSERT( InsideBeginScene && !pRenderTexture );
	pRenderTexture = TEXHANDLE_TO_PTR(Handle);
//
// Do checks only on DEBUG/ARMOR builds
//
	#ifdef _DEBUG
	if( !pRenderTexture->ValidTexture() )
		STOP(( "Invalid texture handle" ));
	if( pRenderTexture->IsLocked() )
		STOP(( "Texture is currently locked" ));
	if( gos_Texture_Keyed != pRenderTexture->Format() )
		STOP(( "Must be a keyed texture" ));
	if( pRenderTexture->Description()->ddpfPixelFormat.dwRGBAlphaBitMask!=0x8000 )
		STOP(( "Keyed textures must be 1555 surfaces" ));
	if( 1!=pRenderTexture->MipMapLevels() )
		STOP(( "Must have no mipmap levels" ));
//	if( pRenderTexture->Flags()&gos_TextureFlag_Used )
//		STOP(( "Texture already used this frame" ));
	#endif
//
// Start time recording
//
	#ifdef LAB_ONLY
	TempTime=GetCycles();
	#endif
//
// Lock and clear render target (565 surface)
//
	WORD* pTexture=(WORD*)RenderTargetddsd.lpSurface;
	DWORD Pitch=RenderTargetddsd.lPitch>>1;
	RenderWidth=pRenderTexture->Width();
	RenderHeight=pRenderTexture->Height();

	for( int y=0; y<RenderHeight; y++ )
	{
		memset( pTexture, 0, RenderWidth*2 );
		pTexture+=Pitch;
	}
//
// Make sure all renderstates are current
//
	FlushRenderStates();
//
// Make blade the current renderer
//
	RenderDevice=TempRenderDevice;
//
// Create a viewport, attach it to the device
//
	D3DVIEWPORT7 viewData;
	viewData.dwX		= 0;
	viewData.dwY		= 0;
	viewData.dwWidth	= pRenderTexture->Width();
	viewData.dwHeight	= pRenderTexture->Height();
	viewData.dvMinZ		= 0.0f;
	viewData.dvMaxZ		= 1.0f;

    wSetViewport( RenderDevice, &viewData );

	wBeginScene( RenderDevice );

	wSetRenderState( RenderDevice, D3DRENDERSTATE_ZENABLE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_ZWRITEENABLE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_TEXTUREPERSPECTIVE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_SPECULARENABLE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_ALPHABLENDENABLE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_FOGENABLE, 0 );
	wSetRenderState( RenderDevice, D3DRENDERSTATE_CULLMODE, D3DCULL_NONE );
	wSetTexture( RenderDevice, 0, 0 );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
	wSetTextureStageState( RenderDevice, 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
	wSetTextureStageState( RenderDevice, 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
	wSetTextureStageState( RenderDevice, 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );

	wSetRenderState( RenderDevice, D3DRENDERSTATE_CLIPPING, 1 );
}

//
// When rendering to a texture is complete, this API must be called to process it.
//
void __stdcall gos_EndRenderToTexture( bool ClearBorder )
{
	if( 0==RenderTargetSurface )
		return;

	gosASSERT( InsideBeginScene && pRenderTexture && RenderDevice );
//
// Finish rendering with blade
//
	wEndScene( RenderDevice );
	RenderDevice=0;
//
// Clear a single pixel border around texture? (while doing the copy)
//
	if( ClearBorder )
	{
		DDSURFACEDESC2 DestSurface;

		DestSurface.dwSize = sizeof(DestSurface);

		wLock( pRenderTexture->GetSysMemSurf(false), NULL, &DestSurface, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );

		WORD* pTexture=(WORD*)RenderTargetddsd.lpSurface;
		DWORD sPitch=RenderTargetddsd.lPitch>>1;

		WORD* pDest=(WORD*)DestSurface.lpSurface;
		DWORD dPitch=DestSurface.lPitch>>1;

		memset( pDest, 0, RenderWidth*2 );
		memset( pDest+(RenderHeight-1)*dPitch, 0, RenderWidth*2 );

		for( int t1=1; t1<RenderHeight-1; t1++ )
		{
			pTexture+=sPitch;
			pDest+=dPitch;
			memcpy( pDest+1, pTexture+1, RenderWidth*2-2 );
			*pDest=0;
			*(pDest+RenderWidth-1)=0;
		}
		wUnlock( pRenderTexture->GetSysMemSurf(false), NULL );
	}
	else
	{
		DDSURFACEDESC2 DestSurface;

		DestSurface.dwSize = sizeof(DestSurface);

		wLock( pRenderTexture->GetSysMemSurf(false), NULL, &DestSurface, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );

		WORD* pTexture=(WORD*)RenderTargetddsd.lpSurface;
		DWORD sPitch=RenderTargetddsd.lPitch>>1;

		WORD* pDest=(WORD*)DestSurface.lpSurface;
		DWORD dPitch=DestSurface.lPitch>>1;

		for( int t1=0; t1<RenderHeight; t1++ )
		{
			memcpy( pDest, pTexture, RenderWidth*2 );
			pTexture+=sPitch;
			pDest+=dPitch;
		}
		wUnlock( pRenderTexture->GetSysMemSurf(false), NULL );
	}
//
// Copy the 'render target' to the final texture
//
//	RECT SourceRect={0,0,RenderWidth,RenderHeight};
//	wBlt( pRenderTexture->GetSysMemSurf(false), NULL, RenderTargetSurface, &SourceRect, DDBLT_WAIT, 0 ); 
//
// If loaded in vram, mark as unused
//
	pRenderTexture->InvalidateVidMem();

	pRenderTexture=NULL;
//
// End time recording
//
	#ifdef LAB_ONLY
	TimeRenderTextures+=GetCycles()-TempTime;
	#endif
}


