DXExample.cpp

<< Click to Display Table of Contents >>

Navigation:  Simplygon 7.1 examples >

DXExample.cpp

///////////////////////////////////////////////////////////////////////////
//
//  System:    Simplygon
//  File:      DXExample.cpp
//  Language:  C++
//
//  Copyright (c) 2015 Donya Labs AB. All rights reserved.
//
//  This is private property, and it is illegal to copy or distribute in
//  any form, without written authorization by the copyright owner(s).
//
///////////////////////////////////////////////////////////////////////////
//
//  #Description#
//
//  A simple DirectX9 example which loads an OBJ-file,
//  converts it to DirectX format (from right-handed to left-handed
//  coordinate system) and reduces the Simplygon mesh.
//  Both converted and reduced models are then displayed in a direct x window.
//
//  Note that manual linking to local dx9 libs is required for the example to run.
//
///////////////////////////////////////////////////////////////////////////
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include "../Common/Example.h"
#include <d3d9.h>
#include <d3dx9.h>
#include "Mesh.h"
#include <winerror.h>
// Define the screen resolution and start points
#define WINDOW_WIDTH  1000
#define WINDOW_HEIGHT 1000
#define WINDOW_START_X 150
#define WINDOW_START_Y 150
D3DVERTEXELEMENT9 dwDecl3[] =
    {
        {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
        {0, 12, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0},
        {0, 24, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
        {0, 36, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT,  0},
        {0, 48, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},
        D3DDECL_END()
    };
// Interface and device pointer
LPDIRECT3D9 D3D9Interface;                           // Pointer to D3D interface
LPDIRECT3DDEVICE9 D3D9Device;                       // Pointer to D3D device
// Mesh to be loaded and rendered
Mesh* original_d3d_mesh = NULL;
Mesh* lod_d3d_mesh = NULL;
//Simplygon geometry containers
spGeometryData original_simplygon_mesh = NULL;
spMaterialTable original_simplygon_materials = NULL;
ID3DXEffect* Shader = NULL;       // D3DX effect interface
bool ShowLOD = false;
bool visualize_debug = false;
// Camera settings
float OrbitRadius = 10.0f;
float OrbitRotation = 90.0f;
D3DXVECTOR3 OrbitCenter(0, 0, 0);
// Function prototypes
void InitializeD3D(HWND hWnd); // Initializes D3D
void DeInitializeD3D(void);   // De-initializes D3D
void Render(void);           // Render function
void RunProcessing( bool use_remeshing ); //Runs remeshing or reduction
bool LoadOBJ(const std::string& import_path, spGeometryData geom); //Reads an OBJ and its material from a file path and stores them as spGeometryData and spMaterialTable
bool InitializeGraphics(void);                                                         // Initialize graphics
HWND InitializeWindow( HINSTANCE hInstance, int nCmdShow );                           // Initialize window                                              
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);  // Callback function
bool CreateD3DMeshFromPackedGeometry(spPackedGeometryData PerVtxGeometry, Mesh * mesh);              // Simplygon geometry to dxGeometry
bool SetMaterials(spPackedGeometryData PerVtxGeometry, spMaterialTable MaterialTable, Mesh * mesh);  // Simplygon material to dxMaterial
// Desc: the main function
int WINAPI WinMain(HINSTANCE hInstance,    HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    // Initialize Simplygon SDK
    InitExample();
    // Sets the tangent space type. DirectX uses a left-handed coordinate system.
    sg->SetGlobalSetting( "DefaultTBNType" , SG_TANGENTSPACEMETHOD_ORTHONORMAL_LEFTHANDED);
    // Initialize window
    HWND hWnd = InitializeWindow(hInstance, nCmdShow);
    // Initialize Direct3D
    InitializeD3D(hWnd);
    // Initialize graphics, quit if initialization fails
    if(!InitializeGraphics())
        return -1;
    // Run a processing
    bool use_remeshing = false; // if false, do a mesh lod instead
    RunProcessing(use_remeshing);
    // Main loop
    MSG msg;
    while(TRUE)
        {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            }
        if(msg.message == WM_QUIT)
            {
            break;
            }
        else if(msg.message == WM_LBUTTONUP)
            {
            ShowLOD = false;
            }
        else if(msg.message == WM_LBUTTONDOWN)
            {
            ShowLOD = true;
            }
        else if(msg.message == WM_RBUTTONUP)
            {
            visualize_debug = false;
            }
        else if(msg.message == WM_RBUTTONDOWN)
            {
            visualize_debug = true;
            }
        // Render a frame
        Render();
        }
    // Deinitialize D3D
    DeInitializeD3D();
    delete original_d3d_mesh;
    delete lod_d3d_mesh;
    // Deinitialize Simplygon SDK
    original_simplygon_materials = NULL;
    original_simplygon_mesh = NULL;
    DeinitExample();
    return 0;
    }
//Reads an OBJ and its material from a file path
//and stores them as spGeometryData and spMaterialTable
bool LoadOBJ(const std::string& import_path, spGeometryData geom)
    {
    spWavefrontImporter importer = sg->CreateWavefrontImporter();
    importer->SetImportFilePath( import_path.c_str() );
    if(!importer->RunImport())
        {
        MessageBox(NULL, "No geometry loaded!", "FAILED", 0);
        return false;
        }
    spGeometryData combined_geometry = importer->GetScene()->GetCombinedGeometry();
    geom->DeepCopy(combined_geometry, true);
    original_simplygon_materials = importer->GetScene()->GetMaterialTable();
    if(original_simplygon_materials.IsNull())
        {
        MessageBox(NULL, "Material assignment failed!", "FAILED", 0);
        return false;
        }
    return true;
    }
//Run a processing of the geometry
//If the use_remeshing flag is true, it performs a remeshing,
//otherwise a regular reduction
//The processed mesh is converted back to a
void RunProcessing( bool use_remeshing )
    {
    std::string exePath = GetExecutablePath();
    std::string output_filename = exePath + "object.obj";
    std::string output_material_filename = exePath + "object.mtl";
    std::string output_diffuse_filename = exePath + "object_diffuse.png";
    std::string output_normals_filename = exePath + "object_normals.png";
    // Make a copy of the geometry, we need the original for texture casting later.
    // The remesher will replace the data of the geometry after it has processed it.
    spGeometryData reduction_geometry = original_simplygon_mesh->NewCopy(true);
    // Create a Scene-object and a SceneMesh-object.
    // Place the reduction_geometry into the SceneMesh,
    // and then the SceneMesh as a child to the RootNode.
    spScene scene = sg->CreateScene();
    spSceneMesh mesh = sg->CreateSceneMesh();
    mesh->SetGeometry( reduction_geometry );
    mesh->SetName( reduction_geometry->GetName()->GetText() );
    scene->GetRootNode()->AddChild( mesh );
    spReductionProcessor red;
    spMappingImage mapping_image;
    if(use_remeshing)
        {
        // Remesh it
        spRemeshingProcessor remesher = sg->CreateRemeshingProcessor();
        // set settings
        remesher->GetRemeshingSettings()->SetOnScreenSize( 400 );
        remesher->GetMappingImageSettings()->SetGenerateMappingImage( true );
        remesher->GetMappingImageSettings()->SetWidth(512);
        remesher->GetMappingImageSettings()->SetHeight(512);
        remesher->GetMappingImageSettings()->SetTexCoordLevel(0);
        remesher->SetScene(scene);
        remesher->GetRemeshingSettings()->SetHardEdgeAngleInRadians(60.0);
        // run it
        remesher->RemeshGeometry();
        // Mapping image is needed later on for texture casting.
        mapping_image = remesher->GetMappingImage();
        }
    else
        {
        // Reduce it
        red = sg->CreateReductionProcessor();
        // set settings
        red->GetReductionSettings()->SetTriangleRatio( 0.7f );
        red->GetNormalCalculationSettings()->SetReplaceNormals(true);
        red->GetNormalCalculationSettings()->SetHardEdgeAngleInRadians(180.0);
        red->GetMappingImageSettings()->SetGenerateMappingImage( true );
        red->GetMappingImageSettings()->SetWidth(1024);
        red->GetMappingImageSettings()->SetHeight(1024);
        red->GetMappingImageSettings()->SetUseFullRetexturing(true);
        red->GetMappingImageSettings()->SetTexCoordLevel(0);
        red->GetReductionSettings()->SetDataCreationPreferences(SG_DATACREATIONPREFERENCES_PREFER_ORIGINAL_DATA);
        red->SetScene(scene);
        // run it
        red->RunProcessing();
        // Mapping image is needed later on for texture casting.
        mapping_image = red->GetMappingImage();
        }
    //Extract the processed scene mesh
    spSceneMesh topmesh = SimplygonSDK::Cast<ISceneMesh>(scene->GetRootNode()->GetChild(0));
    // Cast diffuse texture and normal map data into a new material
    // Create new material table.
    spMaterialTable output_materials = sg->CreateMaterialTable();
    // Create new material for the table.
    spMaterial output_material = sg->CreateMaterial();
    output_materials->AddMaterial( output_material );
    // Cast diffuse texture data
    // Cast the data using a color caster
    spColorCaster color_caster = sg->CreateColorCaster();
    color_caster->SetColorType( SG_MATERIAL_CHANNEL_DIFFUSE );
    color_caster->SetSourceMaterials( original_simplygon_materials );
    color_caster->SetMappingImage( mapping_image );     // The mapping image we got from the remeshing process.
    color_caster->SetOutputChannels( 3 );              // RGB, 3 channels! (1 would be for gray scale, and 4 would be for RGBA.)
    color_caster->SetOutputChannelBitDepth( 8 );      // 8 bits per channel. So in this case we will have 24bit colors RGB.
    color_caster->SetDilation( 10 );                 // To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree as well.
    color_caster->SetOutputFilePath( output_diffuse_filename.c_str() );  // Where the texture map will be saved to file.
    color_caster->CastMaterials();                              // Fetch!
    color_caster->SetIsSRGB(true);
    // set the material properties
    // Set the diffuse multiplier for the texture. 1 means it will not differ from original texture,
    // For example: 0 would ignore a specified color and 2 would make a color twice as pronounced as the others.
    output_material->SetDiffuseRed(1);
    output_material->SetDiffuseGreen(1);
    output_material->SetDiffuseBlue(1);
    // Set material to point to created texture filename.
    output_material->SetTexture( SG_MATERIAL_CHANNEL_DIFFUSE , output_diffuse_filename.c_str() );
    // cast normal map texture data
    // cast the data using a color caster
    spNormalCaster normal_caster = sg->CreateNormalCaster();
    normal_caster->SetSourceMaterials( original_simplygon_materials );
    normal_caster->SetGenerateTangentSpaceNormals( true );
    normal_caster->SetMappingImage( mapping_image );
    normal_caster->SetOutputChannels( 3 ); // RGB, 3 channels! (But really the x, y and z values for the normal)
    normal_caster->SetOutputChannelBitDepth( 8 );
    normal_caster->SetDilation( 10 );
    normal_caster->SetOutputFilePath( output_normals_filename.c_str() );
    normal_caster->CastMaterials();
    // Set material to point to created texture filename.
    output_material->SetTexture( SG_MATERIAL_CHANNEL_NORMALS , output_normals_filename.c_str() );
    // Create a packed, left-handed version of the simplygon LOD for rendering
    spGeometryData leftHandedGeom =  topmesh->GetGeometry()->NewCopy(true);
    leftHandedGeom->ConvertHandedness();
    spPackedGeometryData packed_geom = leftHandedGeom->NewPackedCopy();
    // fetch various data to be able to set up a new dxmesh
    D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH];                   
    const DWORD options = original_d3d_mesh->GetD3D9Mesh()->GetOptions();
    const DWORD triCount = packed_geom->GetTriangleCount();
    const DWORD vertCount = packed_geom->GetVertexCount();
    // fetch vertex declaration
    HRESULT hr = original_d3d_mesh->GetD3D9Mesh()->GetDeclaration( decl );
    if(FAILED(hr))
        {
        return;
        }
    // if lod dxmesh exists, delete
    if(lod_d3d_mesh)
        {
        delete lod_d3d_mesh;
        }
    // create a new dxmesh
    lod_d3d_mesh = new Mesh();
    bool result = lod_d3d_mesh->Create(triCount, vertCount, options, decl, D3D9Device);
    if(!result)
        {
        return;
        }
    // copy simplygon data to dxmesh
    result = CreateD3DMeshFromPackedGeometry(packed_geom, lod_d3d_mesh);
    if(!result)
        {
        return;
        }
    // copy simplygon material data to dxmesh
    result = SetMaterials(packed_geom, output_materials, lod_d3d_mesh);
    if(!result)
        {
        return;
        }
    }
// Message handler
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    switch(message)
        {
        case WM_DESTROY:
            {
            PostQuitMessage(0);
            return 0;
            }
            break;
        }
    return DefWindowProc (hWnd, message, wParam, lParam);
    }
// Initializes D3D
void InitializeD3D(HWND hWnd)
    {
    D3D9Interface = Direct3DCreate9(D3D_SDK_VERSION); // Create the D3D9 Interface
    D3DPRESENT_PARAMETERS D3D9PresentParameters;     // Create D3D9 parameter struct
    ZeroMemory(&D3D9PresentParameters, sizeof(D3D9PresentParameters)); // Zero out the memory
    D3D9PresentParameters.Windowed = TRUE;                            // Run window in windowed mode
    D3D9PresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    D3D9PresentParameters.hDeviceWindow = hWnd;
    D3D9PresentParameters.BackBufferFormat = D3DFMT_X8R8G8B8;
    D3D9PresentParameters.BackBufferWidth = WINDOW_WIDTH;
    D3D9PresentParameters.BackBufferHeight = WINDOW_HEIGHT;
    D3D9PresentParameters.EnableAutoDepthStencil = TRUE;                          // Automatically use the z-buffer
    D3D9PresentParameters.AutoDepthStencilFormat = D3DFMT_D16;                   // 16-bit pixel format for z-buffer
    D3D9PresentParameters.FullScreen_RefreshRateInHz = 0;                       // Default refresh rate
    D3D9PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;  // Vertical sync
    DWORD backBufferQualityLevels = 0;
    DWORD depthStencilQualityLevels = 0;
    D3DMULTISAMPLE_TYPE msaaType = D3DMULTISAMPLE_4_SAMPLES;
    // try to enable 4x msaa
    HRESULT hr = D3D9Interface->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3D9PresentParameters.BackBufferFormat,
        D3D9PresentParameters.Windowed, msaaType, &backBufferQualityLevels);
    if (SUCCEEDED(hr))
        {
        hr = D3D9Interface->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT,
            D3DDEVTYPE_HAL, D3D9PresentParameters.AutoDepthStencilFormat, D3D9PresentParameters.Windowed, msaaType,
            &depthStencilQualityLevels);
        if (SUCCEEDED(hr))
            {
            if (backBufferQualityLevels == depthStencilQualityLevels)
                {
                if (backBufferQualityLevels > 0)
                    {
                    D3D9PresentParameters.MultiSampleQuality = backBufferQualityLevels - 1;
                    D3D9PresentParameters.MultiSampleType = msaaType;
                    }
                else
                    {
                    D3D9PresentParameters.MultiSampleQuality = backBufferQualityLevels;
                    D3D9PresentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
                    }
                }
            }
        }
    // Initialize the D3D9 Device
    hr = D3D9Interface->CreateDevice(D3DADAPTER_DEFAULT,    D3DDEVTYPE_HAL,    hWnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING, &D3D9PresentParameters, &D3D9Device); 
    if(FAILED(hr))
        {
        return;
        }
    D3DCAPS9 caps;
    DWORD MaxAnisotrophy = 1;
    // Set max anisotrophy value
    if (!(D3D9Device->GetDeviceCaps(&caps)))
        {
        if (caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY)
            {
            MaxAnisotrophy = caps.MaxAnisotropy;
            }
        else
            {
            MaxAnisotrophy = 1;
            }
        }
    }
// Renders the frame
void Render(void)
    {
    // Clear window to gray (also clear depth buffer)
    D3D9Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(10, 10, 10), 1.0f, 0);
    D3D9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    // Ready scene for rendering
    D3D9Device->BeginScene();
    D3DXMATRIX matRotateY;
    OrbitRotation += 0.003f;    // Increase rotation for each frame
    D3DXMatrixRotationY(&matRotateY, OrbitRotation);
    D3DXMATRIX matView;    // The view transform matrix
    D3DXMatrixLookAtLH(&matView,
        &D3DXVECTOR3 (0, OrbitRadius/2.0f, OrbitRadius*1.45f),    // The camera position
        &D3DXVECTOR3 (OrbitCenter),                           // The look-at position
        &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));                     // The up direction
    D3DXMATRIX matProjection;                                   // The projection transform matrix
    D3DXMatrixPerspectiveFovLH(&matProjection,
        D3DXToRadian(35),                                // The horizontal field of view
        (FLOAT)WINDOW_WIDTH / (FLOAT)WINDOW_HEIGHT,     // Aspect ratio
        1.0f,                                          // The near view-plane
        OrbitRadius*3);                               // The far view-plane
    D3DXMATRIX worldMatrix = matRotateY * matView;
    D3DXMATRIX finalMatrix = worldMatrix * matProjection;
    bool UseShader = true;
    // if shader, enable effects
    if(UseShader)
        {
        // send parameters to the shader
        Shader->SetTechnique("TransformTechnique");
        D3DXMATRIX m;
        Shader->SetMatrix("World", &worldMatrix);
        Shader->SetMatrix("WorldViewProj", &finalMatrix);
        Shader->SetVector("gLightDir",  &D3DXVECTOR4(1.0f, 1.0f, 0.0f, 1.0));
        Shader->SetBool("visualize_debug", visualize_debug); //Render only normal map shading
        // Apply the technique contained in the effect
        UINT NumberOfPasses;
        Shader->Begin( &NumberOfPasses, 0 );
        uint p = 0 ;
        for( UINT p = 0; p < NumberOfPasses; ++p )
            {
            // Render dx mesh
            if(original_d3d_mesh && !ShowLOD)
                {
                Shader->BeginPass( p );
                LPDIRECT3DTEXTURE9* DiffuseList = original_d3d_mesh->GetD3D9DiffuseTextures();
                LPDIRECT3DTEXTURE9 DiffuseTex = DiffuseList[0];
                Shader->SetTexture("gDiffuseTexture", DiffuseTex);
                LPDIRECT3DTEXTURE9* NormalsList = original_d3d_mesh->GetD3D9NormalsTextures();
                LPDIRECT3DTEXTURE9 NormalsTex = NormalsList[0];
                Shader->SetTexture("NormalMap", NormalsTex);
                original_d3d_mesh->Render(D3D9Device);
                Shader->EndPass();
                }
            else if(lod_d3d_mesh && ShowLOD)
                {
                Shader->BeginPass( p );
                LPDIRECT3DTEXTURE9* DiffuseList = lod_d3d_mesh->GetD3D9DiffuseTextures();
                LPDIRECT3DTEXTURE9 DiffuseTex = DiffuseList[0];
                Shader->SetTexture("gDiffuseTexture", DiffuseTex);
                LPDIRECT3DTEXTURE9* NormalsList = lod_d3d_mesh->GetD3D9NormalsTextures();
                LPDIRECT3DTEXTURE9 NormalsTex = NormalsList[0];
                Shader->SetTexture("NormalMap", NormalsTex);
                lod_d3d_mesh->Render(D3D9Device);
                Shader->EndPass();
                }
            Shader->EndPass();
            }
        Shader->End();
        }
    // Finish scene
    D3D9Device->EndScene();
    // Render the scene on screen
    D3D9Device->Present(NULL, NULL, NULL, NULL);
    }
// Desc: cleans up Direct3D
void DeInitializeD3D(void)
    {
    D3D9Device->Release();        // Release device
    D3D9Interface->Release();    // Release interface
    }
// Desc: initializes the graphics (mesh)
bool InitializeGraphics(void)
    {
    std::string ImportGeometryPath = GetAssetPath() + "simplygonman.obj";
    std::string ImportShaderPath = GetAssetPath() + "shader.fx";
    //Load .obj to simplygon
    if(original_simplygon_mesh != NULL)
        original_simplygon_mesh->Release();
    //Load an OBJ from
    original_simplygon_mesh = sg->CreateGeometryData();
    if(!LoadOBJ(ImportGeometryPath, original_simplygon_mesh))
        return false;
    //Calculate D3D tangent space for original geometry
    spTangentCalculator tangentspacecalc = sg->CreateTangentCalculator();
    tangentspacecalc->SetTexCoordsSetId(0);
    tangentspacecalc->CalculateTangents(original_simplygon_mesh);
    //Create a packed, left-handed version of the right-handed simplygon geometry to use for DX rendering
    spGeometryData leftHandedOriginalGeom = original_simplygon_mesh->NewCopy(true);
    leftHandedOriginalGeom->ConvertHandedness();
    spPackedGeometryData packed_geom = leftHandedOriginalGeom->NewPackedCopy();
    const DWORD triCount = packed_geom->GetTriangleCount();
    const DWORD vertCount = packed_geom->GetVertexCount();
    //Create D3D mesh from simplygon geometry
    original_d3d_mesh = new Mesh();
    bool result = original_d3d_mesh->Create(triCount, vertCount, 0, dwDecl3, D3D9Device);
    if(!result)
        {
        return false;
        }
    CreateD3DMeshFromPackedGeometry(packed_geom, original_d3d_mesh);
    // copy simplygon material data to dxmesh
    result = SetMaterials(packed_geom, original_simplygon_materials, original_d3d_mesh);
    if(!result)
        {
        return false;
        }
    // Load a texture/normal map shader
    LPD3DXBUFFER pBufferErrors = NULL;
    HRESULT hr = D3DXCreateEffectFromFile( D3D9Device, ImportShaderPath.c_str(), NULL, NULL, 0, NULL, &Shader, &pBufferErrors );
    if( FAILED(hr) )
        {
        LPVOID pCompilErrors = pBufferErrors->GetBufferPointer();
        MessageBox(NULL, (const char*)pCompilErrors, "Effect Compile Error",
            MB_OK|MB_ICONEXCLAMATION);
        }
    original_simplygon_mesh->CalculateExtents(false);
    D3DXVECTOR3 AABBMin, AABBMax;
    real inf[3];
    real sup[3];
    original_simplygon_mesh->GetInf(inf);
    original_simplygon_mesh->GetSup(sup);
    AABBMin = D3DXVECTOR3(inf);
    AABBMax = D3DXVECTOR3(sup);
    OrbitRadius = D3DXVec3Length(&(AABBMax-AABBMin));
    OrbitCenter = AABBMin + ((AABBMax-AABBMin)/2.0f);
    return true;
    }
HWND InitializeWindow( HINSTANCE hInstance, int nCmdShow )
    {
    // Setup window class
    HWND hWnd;
    WNDCLASSEX wc;
    // Zero out the memory
    ZeroMemory(&wc, sizeof(WNDCLASSEX));
    // Fill window class with information
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = "WindowClass";
    // Register window class
    RegisterClassEx(&wc);
    // Create the window
    hWnd = CreateWindowEx(NULL,    "WindowClass", "DirectX9 Processing example [press LMB to view LOD model. Press RMB to view only shading.]",
        WS_OVERLAPPEDWINDOW, WINDOW_START_X, WINDOW_START_Y, WINDOW_WIDTH, WINDOW_HEIGHT,
        NULL, NULL, hInstance, NULL);
    // Make the window visible
    ShowWindow(hWnd, nCmdShow);
    return hWnd;
    }
bool CreateD3DMeshFromPackedGeometry(spPackedGeometryData PerVtxGeometry, Mesh * mesh)
    {
    LPD3DXMESH D3D9Mesh = mesh->GetD3D9Mesh();
    LPDIRECT3DVERTEXBUFFER9 VertexBuffer = NULL;
    LPDIRECT3DINDEXBUFFER9 IndexBuffer = NULL;
    // Get vertex buffer
    HRESULT hResult = D3D9Mesh->GetVertexBuffer(&VertexBuffer);
    if( FAILED( hResult ) )
        {
        return false;
        }
    // Get index buffer
    hResult = D3D9Mesh->GetIndexBuffer(&IndexBuffer);
    if( FAILED( hResult ) )
        {
        return false;
        }
    uint NumberOfVertices = PerVtxGeometry->GetVertexCount();
    uint NumberOfFaces = PerVtxGeometry->GetTriangleCount();
    spRidArray VertexIDs = PerVtxGeometry->GetVertexIds();
    spRealArray Coords = PerVtxGeometry->GetCoords();
    spRealArray Normals = PerVtxGeometry->GetNormals();
    spRealArray TexCoords = PerVtxGeometry->GetTexCoords(0);
    spRealArray BiTangents;
    spRealArray Tangents;
    if(    PerVtxGeometry->GetTangents(0) )
        Tangents = PerVtxGeometry->GetTangents(0);
    if(    PerVtxGeometry->GetBitangents(0) )
        BiTangents = PerVtxGeometry->GetBitangents(0);
    // Get the original declaration of the mesh (as when created)
    std::vector<D3DVERTEXELEMENT9> vecDeclaration(MAX_FVF_DECL_SIZE);
    hResult = D3D9Mesh->GetDeclaration( &*vecDeclaration.begin() );
    if( FAILED( hResult ) )
        {
        return false;
        }
    void* VertexBufferPointer = NULL;
    // Get a writable handle to vertex buffer
    hResult = VertexBuffer->Lock(0, sizeof(VertexBuffer), (void**)&VertexBufferPointer, 0 );
    if(FAILED( hResult ) )
        {
        return false;
        }
    // Get vertex size
    DWORD BytesPerVertex = D3D9Mesh->GetNumBytesPerVertex();
    // Copy data from simplygon geometry to d3d geometry
    for(uint i = 0; i < NumberOfVertices; ++i)
        {
        char *vertex_start = &(((char*)VertexBufferPointer)[i*BytesPerVertex]);
        spRealData normals_xyz = sg->CreateRealData();
        spRealData tangents_xyz = sg->CreateRealData();
        spRealData bitangents_xyz = sg->CreateRealData();
        Normals->GetTuple(i, normals_xyz);
        Tangents->GetTuple(i, tangents_xyz);
        BiTangents->GetTuple(i, bitangents_xyz);
        uint atDeclaration = 0;
        for(uint j = 0; j < vecDeclaration.size(); ++j)
            {
            // Vertices
            if(vecDeclaration[j].Type == D3DDECLTYPE_FLOAT3 && vecDeclaration[j].Usage == D3DDECLUSAGE_POSITION)
                {
                // get simplygon data
                spRealData xyz = sg->CreateRealData();
                Coords->GetTuple(i, xyz);
                // set data to dxMesh
                float *dxyz = (float*) &(vertex_start[vecDeclaration[j].Offset]);
                dxyz[0] =  xyz[0];
                dxyz[1] =  xyz[1];
                dxyz[2] =  xyz[2];
                }
            // Normals
            if(vecDeclaration[j].Type == D3DDECLTYPE_FLOAT3 && vecDeclaration[j].Usage == D3DDECLUSAGE_NORMAL)
                {
                spRealData xyz = sg->CreateRealData();
                Normals->GetTuple(i, xyz);
               
                float *dxyz = (float*) &(vertex_start[vecDeclaration[j].Offset]);
                dxyz[0] = xyz[0];
                dxyz[1] = xyz[1];
                dxyz[2] = xyz[2];
                }
            // Texcoords
            if(vecDeclaration[j].Type == D3DDECLTYPE_FLOAT2 && vecDeclaration[j].Usage == D3DDECLUSAGE_TEXCOORD)
                {
                spRealData uv = sg->CreateRealData();
                TexCoords->GetTuple(i, uv);
                float *duv = (float*) &(vertex_start[vecDeclaration[j].Offset]);
                duv[0] = uv[0];
                duv[1] = uv[1];
                }
            // Bitangents
            if(vecDeclaration[j].Type == D3DDECLTYPE_FLOAT3 && vecDeclaration[j].Usage == D3DDECLUSAGE_BINORMAL)
                {
                spRealData xyz = sg->CreateRealData();
                if(BiTangents)
                    {
                    BiTangents->GetTuple(i, xyz);
                    float *duv = (float*) &(vertex_start[vecDeclaration[j].Offset]);
                    duv[0] = xyz[0];
                    duv[1] = xyz[1];
                    duv[2] = xyz[2];
                    }
                }
            // Tangents
            if(vecDeclaration[j].Type == D3DDECLTYPE_FLOAT3 && vecDeclaration[j].Usage == D3DDECLUSAGE_TANGENT)
                {
                spRealData xyz = sg->CreateRealData();
                if(Tangents)
                    {
                    Tangents->GetTuple(i, xyz);
                    float *duv = (float*) &(vertex_start[vecDeclaration[j].Offset]);
                    duv[0] = xyz[0];
                    duv[1] = xyz[1];
                    duv[2] = xyz[2];
                    }
                }
            // End of field, break
            if(vecDeclaration[j].Type == D3DDECLTYPE_UNUSED)
                {
                break;
                }
            }
        }
    // Unlock the vertex buffer
    VertexBuffer->Unlock();
    VertexBuffer->Release();
    // Fetch index buffer information
    D3DINDEXBUFFER_DESC IndexBufferDescription;
    if( FAILED(IndexBuffer->GetDesc( &IndexBufferDescription ) ))
        {
        return false;
        }
    // Set the index size (16 or 32 bits) to be able to traverse the index buffer
    int IndexSize;
    if(IndexBufferDescription.Format == D3DFMT_INDEX32)
        {
        IndexSize = 4;
        }
    else
        {
        IndexSize = 2;
        }
    BYTE* IndexBufferPointer = NULL;
    // Lock buffer to access index buffer
    if(FAILED(IndexBuffer->Lock(0, sizeof(IndexBuffer), (void**)&IndexBufferPointer, 0) ) )
        {
        return false;
        }
    // Copy index data
    DWORD CurrentTri[3] = {0,0,0};
    for(uint i = 0; i < NumberOfFaces*3; i+=3 )
        {
        CurrentTri[0] = VertexIDs->GetItem(i+0);
        CurrentTri[1] = VertexIDs->GetItem(i+1);
        CurrentTri[2] = VertexIDs->GetItem(i+2);
        for( uint e=0; e<3; ++e )
            {
            memcpy(IndexBufferPointer, &CurrentTri[e], IndexSize);
            IndexBufferPointer += IndexSize;
            }
        }
    // Unlock the index buffer
    IndexBuffer->Unlock();
    IndexBuffer->Release();
    real inf[3], sup[3];
    PerVtxGeometry->GetInf(inf);
    PerVtxGeometry->GetSup(sup);
    float dist = inf[2] - sup[2];
    dist = dist > inf[1] - sup[1] ? dist : inf[1] - sup[1];
    dist = dist > inf[0] - sup[0] ? dist : inf[0] - sup[0];
    return true;
    }
bool SetMaterials(spPackedGeometryData PerVtxGeometry, spMaterialTable MaterialTable, Mesh * mesh)
    {
    LPD3DXMESH D3D9Mesh = mesh->GetD3D9Mesh();
    uint NumberOfFaces = PerVtxGeometry->GetTriangleCount();
    // Lock attribute buffer
    DWORD* AttributeBufferPointer = NULL;
    HRESULT hResult = D3D9Mesh->LockAttributeBuffer(0, &AttributeBufferPointer);
    if(FAILED(hResult))
        {
        return false;
        }
    // Copy over material ids (Proxy Lods only have one material)
    for(uint a = 0; a <  NumberOfFaces; ++a)
        {
        AttributeBufferPointer[a] = 0;
        }
    // Unlock attribute buffer
    hResult = D3D9Mesh->UnlockAttributeBuffer();
    if(FAILED(hResult))
        {
        return false;
        }
    // Setup materials
    uint NumberOfMaterials = MaterialTable->GetMaterialsCount();
    mesh->SetNumMaterials(NumberOfMaterials);
    for(uint i = 0; i < NumberOfMaterials; ++i)
        {
        // get simplygon material
        spMaterial m = Cast<IMaterial>(MaterialTable->GetItem(i));
        // get diffuse and normal map
        std::string dpath = m->GetLayeredTexture(SG_MATERIAL_CHANNEL_DIFFUSE, 0)->GetText();
        std::string npath = m->GetLayeredTexture(SG_MATERIAL_CHANNEL_NORMALS, 0)->GetText();
        D3DMATERIAL9 mat;
        // Set the RGBA for diffuse reflection.
        mat.Diffuse.r = m->GetColorRed  (SG_MATERIAL_CHANNEL_DIFFUSE);
        mat.Diffuse.g = m->GetColorGreen(SG_MATERIAL_CHANNEL_DIFFUSE);
        mat.Diffuse.b = m->GetColorBlue (SG_MATERIAL_CHANNEL_DIFFUSE);
        mat.Diffuse.a = m->GetColorAlpha(SG_MATERIAL_CHANNEL_DIFFUSE);
        // store material and texture paths
        bool result = mesh->CreateTexture(D3D9Device, i, dpath, npath, mat);
        if(!result)
            {
            // material creation failed
            MessageBox(NULL, "material creation failed!", "FAILED", 0);
            return false;
            }
        }
    return true;
    }