// RcCameraTransformExample.cpp : Implementation of CRcCameraTransformExample

#include "stdafx.h"
#include "RpExamplesPlugin.h"
#include "RcCameraTransformExample.h"
#include "math.h"

// CRcCameraTransformExample
STDMETHODIMP CRcCameraTransformExample::OnGetValue(CONID conID, IRsDataCache* pDataCache, VARIANT* pVtData, RtBool* pHandled)
{
    return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::OnSetValue(CONID con_id, IRsDataCache* pDataCache, VARIANT* pvtData, RtBool* pHandled)
{
    return S_OK;
}
STDMETHODIMP CRcCameraTransformExample::OnDefaultValue(CONID con_id, VARIANT* pVtData, RtBool* pHandled)
{
    return S_OK;
}
STDMETHODIMP CRcCameraTransformExample::OnInvalidate(CONID con_id, RtBool* pHandled)
{
    return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::OnInitialize(void)
{
    return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::OnLoadDefault(void)
{
    return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::OnDestroy(void)
{
    return S_OK;
}

HRESULT CRcCameraTransformExample::FinalConstruct()
{
    // get kernel package
    CComPtr<IRpKernel> spKernelPackage;
    HRESULT hr = CRpExamplesPlugin::GetKernelPackage(&spKernelPackage);
    // and create base node
    if (SUCCEEDED(hr))
        hr = spKernelPackage->CreateBaseNode(GetControllingUnknown(), &m_pUnkAgg);

    return hr;
}


#define DEGREESTORADS (3.141592f/180.0f)

static void EulerToMatrix(RtFloat fPitch, RtFloat fRoll, RtFloat fYaw, RtMatrixf *axes)
{
	float x, y, z;
	float cx, cy, cz;
	float sx, sy, sz;

	x = (float)DEGREESTORADS * fPitch;
	y = (float)DEGREESTORADS * fRoll;
	z = (float)DEGREESTORADS * fYaw;
	cx = (float)cos(x);
	sx = (float)sin(x);
	cy = (float)cos(y);
	sy = (float)sin(y);
	cz = (float)cos(z);
	sz = (float)sin(z);
	(*axes)[0][0] = cy*cz;
	(*axes)[0][1] = sx*sy*cz - cx*sz;
	(*axes)[0][2] = cx*sy*cz + sx*sz;
	(*axes)[0][3] = 0;

	(*axes)[1][0] = cy*sz;
	(*axes)[1][1] = sx*sy*sz + cx*cz;
	(*axes)[1][2] = cx*sy*sz - sx*cz;
	(*axes)[1][3] = 0;

	(*axes)[2][0] = -sy;
	(*axes)[2][1] = sx*cy;
	(*axes)[2][2] = cx*cy;
	(*axes)[2][3] = 0;

	(*axes)[3][0] = 0;
	(*axes)[3][1] = 0;
	(*axes)[3][2] = 0;
	(*axes)[3][3] = 1;
}


STDMETHODIMP CRcCameraTransformExample::Execute(void)
{
    // Get the BaseNode interface
    CComQIPtr<IRsBaseNode> spBase = m_pUnkAgg;   
    ATLASSERT(spBase);    

    CComPtr<IRpKernel>  spKernelPackage;
    CRpExamplesPlugin::GetKernelPackage(&spKernelPackage);

	//get tool manager 
	CComPtr<IRsNode> spViewNode;
	CComPtr<IRsSysCore> spSysCore;
	spKernelPackage->GetSysCore(&spSysCore);
	CComPtr<IRsSysToolManager> spToolMng;
	spSysCore->GetToolMng(&spToolMng);
	if (!spToolMng) 
		return E_FAIL;


	//read active view (last clicked)
	spToolMng->GetActiveViewNode(&spViewNode);
	CComQIPtr<IRnD3DWindowedRT> spView3d = spViewNode;
	if (!spView3d) {
		ATLTRACE("Active window is not D3D window, scanning through all windows with tool management");
		//this is not a view3d window. Search for first d3d window

		//enumerate all windows from tool manger
		CComPtr<IRsEnumToolManagers> spToolManagerEnum;
		spToolMng->EnumToolManagers(&spToolManagerEnum);
		RtUlong i, cnt;
		spToolManagerEnum->GetCount(&cnt);
		for (i = 0; i<cnt;i++) {
			CComPtr<IRsNode> spToolNode;
			spToolManagerEnum->GetViewNode(i, &spToolNode);
			CComQIPtr<IRnD3DWindowedRT> spViewNode3d = spToolNode;
			if (!spViewNode3d ) 
				continue;

			spViewNode = spToolNode;
			break;
		};
	}
	if (!spViewNode) 
		return E_FAIL;


	//find camera path for spViewNode
	CComVariant data;
	spViewNode->GetValue(CON_RENDERTARGET, &data);
	CComQIPtr<IRdRenderTarget> spRenderTarget;
	if (data.ChangeType(VT_UNKNOWN) == S_OK) 
		spRenderTarget = data.punkVal;
	RtRenderTargetInfo rtInfo;
	if (!spRenderTarget) 
		return E_FAIL;
	if (FAILED(spRenderTarget->GatherInfo(&rtInfo))) {
		return E_FAIL;
	};
	ATLTRACE("Active render window size is %d x %d\n", rtInfo.m_dwWidth, rtInfo.m_dwHeight);
	RtUlong i;
	RtLayerInfo layInfo;
	RtBool bSceneFound = false;
	for (i = rtInfo.m_dwLayerCount; i>0;) {
		i--;
		if (FAILED(spRenderTarget->GetLayerInfo(i, &layInfo))) {
			continue;
		};
		if (!wcscmp(layInfo.m_szUsage,L"SCENE")) {
			ATLTRACE("Scene layer render path: %S\n", layInfo.m_szRoot);
			ATLTRACE("Scene layer camera path: %S\n", layInfo.m_szCamera);
			bSceneFound = true;
			break;
		};
		::SysFreeString(layInfo.m_szRoot);
		::SysFreeString(layInfo.m_szUsage);
		::SysFreeString(layInfo.m_szCamera);
	};
	if (!bSceneFound) 
		return E_FAIL;

	CComBSTR szCameraName;
	szCameraName = layInfo.m_szCamera;
	::SysFreeString(layInfo.m_szRoot);
	::SysFreeString(layInfo.m_szUsage);
	::SysFreeString(layInfo.m_szCamera);


	//find camera in LE
	//get kernel root encapsulator
	CComPtr<IRsNode> spCameraNode;
	CComPtr<IRnKernel> spKernel;
	spKernelPackage->GetKernelNode(&spKernel);
	CComQIPtr<IRnEncapsulator> spKernelRoot = spKernel;
	spKernelRoot->FindNodeByName(szCameraName, &spCameraNode);
	if (!spCameraNode) 
		return E_FAIL;

	
	CComVariant vCamType;
	spCameraNode->GetValue(CON_VIEW3D_EYECAMERADATA_TYPE,&vCamType);
	vCamType.ChangeType(VT_INT);
	ReD3DEyeCameraType eType = (ReD3DEyeCameraType)vCamType.intVal;

	ATLTRACE("Camera type id = %d\n", eType);


	CComVariant vFov;
	//read camera FOV
	spCameraNode->GetValue(CON_SPACE3D_CAMERA_FOV,&vFov);

	CComQIPtr<IRdMatrixf> spTrans, spParent;
	CComVariant vParent, vTrans;
	//camera parent space transform
	spCameraNode->GetValue(CON_TRANSFORM_PARENT_MATRIX,&vParent);

	//camera world space transform
	spCameraNode->GetValue(CON_TRANSFORM_MATRIX,&vTrans);
	if (vParent.ChangeType(VT_UNKNOWN) == S_OK) 
		spParent = vParent.punkVal;
	if (vTrans.ChangeType(VT_UNKNOWN) == S_OK) 
		spTrans = vTrans.punkVal;


	if (!spTrans)
		return E_FAIL;

	float frx, fry, frz;
	float ftx, fty, ftz;
	spTrans->GetYaw(&frx);
	spTrans->GetPitch(&fry);
	spTrans->GetRoll(&frz);

	ATLTRACE("Camera world space rotation: %f, %f, %f\n", frx, fry, frz);

	spTrans->GetTransX(&ftx);
	spTrans->GetTransY(&fty);
	spTrans->GetTransZ(&ftz);
	ATLTRACE("Camera world space position: %f, %f, %f\n", ftx, fty, ftz);


	//set random position and rotation
	ftx = 1-(2.0f*rand()) / RAND_MAX;
	fty = 1-(2.0f*rand()) / RAND_MAX;
	ftz = 1-(2.0f*rand()) / RAND_MAX;

	frx = (360.0f*rand()) / RAND_MAX;
	fry = (180.0f*rand()) / RAND_MAX;
	frz = (360.0f*rand()) / RAND_MAX;

	//compose matrix at once
	RtMatrixf mat;
	EulerToMatrix(frx,fry,frz, &mat);
	mat[3][0] = ftx;
	mat[3][1] = fty;
	mat[3][2] = ftz;

	CComPtr<IRdMatrixf> spMat;
	CRpExamplesPlugin::CreateDataObject(&spMat);
	spMat->LoadMatrix(&mat);

	if (spParent) {
		//multiply by parent matrix if present to get local space transform
		RtMatrixf mInv;
		spParent->Invert(&mInv);
		spMat->Mult(&mInv);
	};

	//set new camera FOV
	spCameraNode->SetValue(CON_SPACE3D_CAMERA_FOV,CComVariant(0.5f));

	//set camera transform
	spCameraNode->SetValue(CON_OBJ_TRANSFORM_INP_MATRIX, CComVariant(spMat));

	//set also expanded connectors for safety
	spCameraNode->SetValue(CON_TRANSFORM_TX,CComVariant(ftx));
	spCameraNode->SetValue(CON_TRANSFORM_TY,CComVariant(fty));
	spCameraNode->SetValue(CON_TRANSFORM_TZ,CComVariant(ftz));
	spCameraNode->SetValue(CON_TRANSFORM_Z_YAW,CComVariant(frz));
	spCameraNode->SetValue(CON_TRANSFORM_Y_PITCH,CComVariant(fry));
	spCameraNode->SetValue(CON_TRANSFORM_X_ROLL,CComVariant(frx));

	return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::Undo(void)
{
    return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::Redo(void)
{
	return S_OK;
}

STDMETHODIMP CRcCameraTransformExample::HasUndo(RtBool* isUndoable)
{
    *isUndoable = false;
    return S_OK;
}
