// DXHELP3.cpp : Contains routines shared by multiple 3D effects
#include "stdafx.h"
#include "dxhelp3.h"
#include <malloc.h>
#include <math.h>
#include <stdio.h>

void Rotate::Set(D3DVECTOR d3dvctrNormal, double dAngle)
{
#ifdef _DEBUG
    float NormalSum =   d3dvctrNormal.x * d3dvctrNormal.x + 
                        d3dvctrNormal.y * d3dvctrNormal.y + 
                        d3dvctrNormal.z * d3dvctrNormal.z;
    _ASSERT(NormalSum == 0.0f || (0.99f < NormalSum && NormalSum < 1.01f));
#endif

    // The following transformation matrix routines are from
    // Graphics Gems (the first one) page 466 (Converting 
    // between Matrix and Axis-Amount Representations)
    float s = (float)sin(dAngle), c = (float)cos(dAngle);
    float t = 1.0f - c;

    m_d3dvctrXComponent.x = t * d3dvctrNormal.x * d3dvctrNormal.x + c;
    m_d3dvctrXComponent.y = t * d3dvctrNormal.x * d3dvctrNormal.y - s * d3dvctrNormal.z;
    m_d3dvctrXComponent.z = t * d3dvctrNormal.x * d3dvctrNormal.z + s * d3dvctrNormal.y;

    m_d3dvctrYComponent.x = t * d3dvctrNormal.x * d3dvctrNormal.y + s * d3dvctrNormal.z;
    m_d3dvctrYComponent.y = t * d3dvctrNormal.y * d3dvctrNormal.y + c;
    m_d3dvctrYComponent.z = t * d3dvctrNormal.y * d3dvctrNormal.z - s * d3dvctrNormal.x;

    m_d3dvctrZComponent.x = t * d3dvctrNormal.x * d3dvctrNormal.z - s * d3dvctrNormal.y;
    m_d3dvctrZComponent.y = t * d3dvctrNormal.y * d3dvctrNormal.z + s * d3dvctrNormal.x;
    m_d3dvctrZComponent.z = t * d3dvctrNormal.z * d3dvctrNormal.z + c;
}

static HRESULT DecoupleOneMeshBuilder(void *lpThis /* Ignored */, 
				      IDirect3DRMMeshBuilder3 *lpMeshBuilderOut,
				      IDirect3DRMMeshBuilder3 *lpMeshBuilderIn)
{
    HRESULT hr = lpMeshBuilderOut->SetAppData(lpMeshBuilderIn->GetAppData());

    if(FAILED(hr))
    {
	return hr;
    }

    const int ciOutputFaceCount = lpMeshBuilderOut->GetFaceCount();
    const int ciMaxFaceCount = lpMeshBuilderIn->GetFaceCount();
    if(!ciMaxFaceCount)
    {
	return hr;
    }
    CComPtr<IDirect3DRMFaceArray> cpD3DRMFaceArray;

    hr = lpMeshBuilderIn->GetFaces(&cpD3DRMFaceArray);
    if(hr != D3DRM_OK)
    {
	return hr;
    }

    D3DVECTOR *lpvctrPosition = NULL;
    D3DVECTOR *lpvctrFaceNormals = NULL;
    DWORD dwMaxVertexCount = 0;

    // By reserving space we speed things up by a factor of 20 or 30 X!

    DWORD dwTotalVertexCount = lpMeshBuilderIn->GetVertexCount();
    _ASSERT(dwTotalVertexCount >= 3);
    if (dwTotalVertexCount/ciMaxFaceCount < 3)
    {
	dwTotalVertexCount = ciMaxFaceCount * 3;  // There will be at least 3 vertices per face.
    }
    hr = lpMeshBuilderOut->ReserveSpace(dwTotalVertexCount, dwTotalVertexCount, ciMaxFaceCount);

    if(hr != D3DRM_OK)
    {
	return hr;
    }

    // Need to have seperate vertices for each face in the output mesh.
    int iFaceIndex = 0;
    do
    {
	CComPtr<IDirect3DRMFace> cpd3dInFaceTemp;
	CComPtr<IDirect3DRMFace2> cpd3dOutFace;

        if(FAILED(hr = cpD3DRMFaceArray->GetElement((DWORD)iFaceIndex, &cpd3dInFaceTemp)))
        {
            break;
        }
        _ASSERT(cpd3dInFaceTemp);

	CComPtr<IDirect3DRMFace2> cpd3dInFace;
	if(FAILED(hr = cpd3dInFaceTemp->QueryInterface(
		IID_IDirect3DRMFace2, (void **)&cpd3dInFace)))
	{
	    break;
	}

        DWORD dwVertexCount;

        cpd3dInFace->GetVertices(&dwVertexCount, NULL, NULL);
        _ASSERT(dwVertexCount >= 3);

        if(dwVertexCount > dwMaxVertexCount)
        {
            dwMaxVertexCount = dwVertexCount;

	    D3DVECTOR *lpTemp = lpvctrPosition;
            D3DVECTOR *lpNormalTemp = lpvctrFaceNormals;

	    lpvctrPosition = (D3DVECTOR *)realloc(lpvctrPosition, dwMaxVertexCount * sizeof (*lpvctrPosition));
	    lpvctrFaceNormals = (D3DVECTOR *)realloc(lpvctrFaceNormals, dwMaxVertexCount * sizeof (*lpvctrFaceNormals));

	    if(!lpvctrPosition || !lpvctrFaceNormals)
	    {
		free(lpTemp);
		free(lpNormalTemp);
		hr = E_OUTOFMEMORY;
		break;
	    }

	    // We may have a problem in the amount of space reserved in the output meshbuilder.
	    if((ciMaxFaceCount -  iFaceIndex) * dwMaxVertexCount > dwTotalVertexCount)
	    {
		dwTotalVertexCount = (ciMaxFaceCount -  iFaceIndex) * dwMaxVertexCount;
		hr = lpMeshBuilderOut->ReserveSpace(dwTotalVertexCount, dwTotalVertexCount, ciMaxFaceCount - iFaceIndex);

		if(hr != D3DRM_OK)
		{
		    break;
		}
	    }
        }

        if(FAILED(hr = cpd3dInFace->GetVertices(&dwVertexCount, lpvctrPosition, lpvctrFaceNormals)) ||
	   FAILED(hr = lpMeshBuilderOut->CreateFace(&cpd3dOutFace)) ||
	   FAILED(hr = cpd3dOutFace->SetAppData(cpd3dInFace->GetAppData())))
        {
            break;
        }


	CComPtr<IDirect3DRMMaterial2> cpMaterial;
        CComPtr<IDirect3DRMTexture3> cpTexture;
        BOOL bU, bV;

        if(hr != D3DRM_OK ||
          (hr = cpd3dInFace->GetMaterial(&cpMaterial)) != D3DRM_OK ||
          (hr = cpd3dOutFace->SetMaterial(cpMaterial)) != D3DRM_OK ||
          (hr = cpd3dOutFace->SetColor(cpd3dInFace->GetColor())) != D3DRM_OK ||
          (hr = cpd3dInFace->GetTexture(&cpTexture)) != D3DRM_OK ||
          (hr = cpd3dOutFace->SetTexture(cpTexture)) != D3DRM_OK ||
          (hr = cpd3dInFace->GetTextureTopology(&bU, &bV)) != D3DRM_OK ||
          (hr = cpd3dOutFace->SetTextureTopology(bU, bV)) != D3DRM_OK)
        {
            break;
        }

        DWORD dwInFaceVertex = 0;
        do
        {
            int iVIndex = lpMeshBuilderOut->AddVertex(lpvctrPosition[dwInFaceVertex].x, 
					 lpvctrPosition[dwInFaceVertex].y, 
					 lpvctrPosition[dwInFaceVertex].z);
            int iNIndex = lpMeshBuilderOut->AddNormal(lpvctrFaceNormals[dwInFaceVertex].x, 
					 lpvctrFaceNormals[dwInFaceVertex].y, 
					 lpvctrFaceNormals[dwInFaceVertex].z);

            D3DVALUE U, V;

            if(FAILED(hr = cpd3dOutFace->AddVertexAndNormalIndexed(iVIndex, iNIndex)) ||
               FAILED(hr = cpd3dInFace->GetTextureCoordinates(dwInFaceVertex, &U, &V)) ||
               FAILED(hr = cpd3dOutFace->SetTextureCoordinates(dwInFaceVertex, U, V)))
            {
                break;
            }
        } while (++dwInFaceVertex < dwVertexCount);
	dwTotalVertexCount -= dwVertexCount;
    } while (hr == D3DRM_OK && ++iFaceIndex < ciMaxFaceCount);

    free(lpvctrPosition);
    free(lpvctrFaceNormals);

    return hr;
}

HRESULT TraverseSubMeshes(  HRESULT (*lpCallBack)(void *lpThis,
						IDirect3DRMMeshBuilder3* lpOut,
						IDirect3DRMMeshBuilder3* lpIn),
			    void *lpThis, 
			    IDirect3DRMMeshBuilder3* lpMeshBuilderOut,
			    IDirect3DRMMeshBuilder3* lpMeshBuilderIn)
{
    HRESULT hr;

    _ASSERT(lpCallBack);
    if(FAILED(hr = (*lpCallBack)(lpThis, lpMeshBuilderOut, lpMeshBuilderIn)))
	return hr;

    DWORD dwInSubMeshCount;
    hr = lpMeshBuilderIn->GetSubMeshes(&dwInSubMeshCount, NULL);

    if(FAILED(hr) || dwInSubMeshCount == 0)
	return hr;

    DWORD dwOutSubMeshCount;
    if(FAILED(hr = lpMeshBuilderOut->GetSubMeshes(&dwOutSubMeshCount, NULL)))
    {
	return hr;
    }

    const BOOL bCreateOutMeshes = (dwOutSubMeshCount == 0);
    _ASSERT(bCreateOutMeshes || dwInSubMeshCount == dwOutSubMeshCount);

    LPUNKNOWN *lplpunkInSubMeshes =
	(LPUNKNOWN *)malloc(dwInSubMeshCount * sizeof(*lplpunkInSubMeshes));

    LPUNKNOWN *lplpunkOutSubMeshes = (bCreateOutMeshes? NULL:
	(LPUNKNOWN *)malloc(dwOutSubMeshCount * sizeof(*lplpunkOutSubMeshes)));

    if(!lplpunkInSubMeshes || !bCreateOutMeshes && !lplpunkOutSubMeshes)
    {
	return E_OUTOFMEMORY;
    }

    if(FAILED(hr = lpMeshBuilderIn->GetSubMeshes(&dwInSubMeshCount, lplpunkInSubMeshes)) ||
	!bCreateOutMeshes && 
	FAILED(hr = lpMeshBuilderOut->GetSubMeshes(&dwOutSubMeshCount, lplpunkOutSubMeshes)))
    {
	return hr;
    }

    CComPtr<IDirect3DRMMeshBuilder3> cpInSubMesh, cpOutSubMesh;

    DWORD dwSubMesh;
    for(dwSubMesh = 0; SUCCEEDED(hr) && dwSubMesh < dwInSubMeshCount; dwSubMesh++)
    {
	cpInSubMesh = NULL;
	cpOutSubMesh = NULL;

	// Get the current input submesh.
	hr = lplpunkInSubMeshes[dwSubMesh]->QueryInterface(IID_IDirect3DRMMeshBuilder3, 
		(void **)&cpInSubMesh);

	if(FAILED(hr))
	    break;

	if(bCreateOutMeshes)
	{
	    // Create a new submesh in the output mesh to match the input submesh.	    
	    LPUNKNOWN lpunkSubmesh;

	    if(FAILED(hr = lpMeshBuilderOut->CreateSubMesh(&lpunkSubmesh)))
		break;

	    hr = lpunkSubmesh->QueryInterface(IID_IDirect3DRMMeshBuilder3,
							(void **)&cpOutSubMesh);
	    lpunkSubmesh->Release();
	}
	else
	{
	    // Get the current output submesh.
	    hr = lplpunkOutSubMeshes[dwSubMesh]->QueryInterface(IID_IDirect3DRMMeshBuilder3,
		    (void **)&cpOutSubMesh);
	}
	if(FAILED(hr))
	    break;

	// Do all the children for these two MeshBuilders.
	hr = TraverseSubMeshes(lpCallBack, lpThis, cpOutSubMesh, cpInSubMesh);
    }

    for(dwSubMesh = 0; dwSubMesh < dwInSubMeshCount; dwSubMesh++)
    {
	lplpunkInSubMeshes[dwSubMesh]->Release();

	if(lplpunkOutSubMeshes)
	{
	    lplpunkOutSubMeshes[dwSubMesh]->Release();
	}
    }

    free(lplpunkInSubMeshes);
    free(lplpunkOutSubMeshes);

    return hr;
}

HRESULT DecoupleVertices(IDirect3DRMMeshBuilder3* lpMeshBuilderOut,
			 IDirect3DRMMeshBuilder3* lpMeshBuilderIn)
{
    DWORD dwSubMeshCount;

    HRESULT hr = lpMeshBuilderOut->GetSubMeshes(&dwSubMeshCount, NULL);

    if(FAILED(hr))
	return hr;

    if(lpMeshBuilderOut->GetFaceCount() || dwSubMeshCount)
    {
	if(FAILED(hr = lpMeshBuilderOut->Empty(0)))
	    return hr;
    }
    hr = TraverseSubMeshes(DecoupleOneMeshBuilder, NULL,
			    lpMeshBuilderOut, lpMeshBuilderIn);
    return hr;
}

float GetDlgItemFloat(HWND hDlg, int id)
{
    TCHAR *pEnd;
    TCHAR szItemText[20];
    GetDlgItemText(hDlg, id, szItemText, 20);
    return (float)strtod(szItemText, &pEnd);
}

BOOL SetDlgItemFloat( HWND hDlg, int id, float f )
{
    TCHAR szItem[20];
     _stprintf( szItem, _T("%.2f"), f );
    return SetDlgItemText( hDlg, id, szItem );
}

double GetDlgItemDouble(HWND hDlg, int id)
{
    TCHAR *pEnd;
    TCHAR szItemText[20];
    GetDlgItemText(hDlg, id, szItemText, 20);
    return strtod(szItemText, &pEnd);
}

BOOL SetDlgItemDouble( HWND hDlg, int id, double d )
{
    TCHAR szItem[20];
     _stprintf( szItem, _T("%.2d"), d );
    return SetDlgItemText( hDlg, id, szItem );
}

