///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: DXExample.cpp
// Language: C++
//
// Copyright (c) 2019 Microsoft. 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
#define LocalScope if(true)
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;
spTextureTable original_simplygon_textable = 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, spTextureTable TextureTable, Mesh * mesh ); // Simplygon material to dxMaterial
// Desc: the main function
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// Initialize Simplygon SDK
try
{
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_textable = NULL;
original_simplygon_mesh = NULL;
DeinitExample();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -1;
}
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()->NewCombinedGeometry();
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;
}
original_simplygon_textable = importer->GetScene()->GetTextureTable();
if( original_simplygon_textable.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(1.047f);
// run it
remesher->RunProcessing();
// 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( 3.14159f );
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();
spTextureTable output_textures = sg->CreateTextureTable();
// 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->SetSourceTextures( original_simplygon_textable );
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->RunProcessing(); // Fetch!
color_caster->SetOutputSRGB( true );
// Set material to point to created texture filename.
AddSimplygonTexture( output_material, output_textures, 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->SetSourceTextures( original_simplygon_textable );
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->RunProcessing();
// Set material to point to created texture filename.
AddSimplygonTexture( output_material, output_textures, 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, output_textures, 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_simplygon_textable, 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, spTextureTable TextureTable, 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 ) );
std::string dpath = "";
std::string npath = "";
// get diffuse and normal map
spShadingNode diffNode = m->GetShadingNetwork( SG_MATERIAL_CHANNEL_DIFFUSE );
spShadingNode normNode = m->GetShadingNetwork( SG_MATERIAL_CHANNEL_NORMALS );
if( diffNode != nullptr )
{
//This node is either mult-node or tex-node. If mult, the texnode is one of the inputs. Ignore the rest of the network for this renderings sake.
spShadingTextureNode texNode;
texNode = SafeCast<IShadingTextureNode>( diffNode );
if( texNode == nullptr )
{
spShadingFilterNode filtNode = SafeCast<IShadingFilterNode>( diffNode );
if( filtNode != nullptr )
{
texNode = SafeCast<IShadingTextureNode>( filtNode->GetInput( 0 ) );
if( texNode == nullptr )
{
texNode = SafeCast<IShadingTextureNode>( filtNode->GetInput( 1 ) );
}
}
}
//If we found the node, check the path in the tex table and set it
if( texNode != nullptr )
{
spTexture tex = TextureTable->FindTexture( texNode->GetTextureName() );
if( tex != nullptr )
{
dpath = tex->GetFilePath();
}
}
}
if( normNode != nullptr )
{
//This node is either mult-node or tex-node. If mult, the texnode is one of the inputs. Ignore the rest of the network for this renderings sake.
spShadingTextureNode texNode;
texNode = SafeCast<IShadingTextureNode>( normNode );
if( texNode == nullptr )
{
spShadingFilterNode filtNode = SafeCast<IShadingFilterNode>( normNode );
if( filtNode != nullptr )
{
texNode = SafeCast<IShadingTextureNode>( filtNode->GetInput( 0 ) );
if( texNode == nullptr )
{
texNode = SafeCast<IShadingTextureNode>( filtNode->GetInput( 1 ) );
}
}
}
//If we found the node, check the path in the tex table and set it
if( texNode != nullptr )
{
spTexture tex = TextureTable->FindTexture( texNode->GetTextureName() );
if( tex != nullptr )
{
npath = tex->GetFilePath();
}
}
}
D3DMATERIAL9 mat;
// Set the RGBA for diffuse reflection.
mat.Diffuse.r = 1.f;
mat.Diffuse.g = 1.f;
mat.Diffuse.b = 1.f;
mat.Diffuse.a = 1.f;
// 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;
}