This commit is contained in:
walbourn_cp 2014-06-17 11:00:57 -07:00
Родитель eca6a37bff
Коммит a629c513d0
3 изменённых файлов: 988 добавлений и 0 удалений

375
DirectXMesh/DirectXMesh.h Normal file
Просмотреть файл

@ -0,0 +1,375 @@
//-------------------------------------------------------------------------------------
// DirectXMesh.h
//
// DirectX Mesh Geometry Library
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkID=324981
//-------------------------------------------------------------------------------------
#if defined(_MSC_VER) && (_MSC_VER > 1000)
#pragma once
#endif
// VS 2010's stdint.h conflicts with intsafe.h
#pragma warning(push)
#pragma warning(disable : 4005)
#include <stdint.h>
#pragma warning(pop)
#include <memory>
#include <string>
#include <vector>
#include <d3d11_1.h>
#include <directxmath.h>
#define DIRECTX_MESH_VERSION 001
namespace DirectX
{
//---------------------------------------------------------------------------------
// DXGI Format Utilities
bool IsValidVB( _In_ DXGI_FORMAT fmt );
bool IsValidIB( _In_ DXGI_FORMAT fmt );
size_t BytesPerElement( _In_ DXGI_FORMAT fmt );
//---------------------------------------------------------------------------------
// Input Layout Descriptor Utilities
bool IsValid( _In_reads_(nDecl) const D3D11_INPUT_ELEMENT_DESC* vbDecl, _In_ size_t nDecl );
void ComputeInputLayout( _In_reads_(nDecl) const D3D11_INPUT_ELEMENT_DESC* vbDecl, _In_ size_t nDecl,
_Out_writes_opt_(nDecl) uint32_t* offsets,
_Out_writes_opt_(D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT) uint32_t* strides );
//---------------------------------------------------------------------------------
// Attribute Utilities
std::vector<std::pair<size_t,size_t>> ComputeSubsets( _In_reads_opt_(nFaces) const uint32_t* attributes, _In_ size_t nFaces );
// Returns a list of face offset,counts for attribute groups
//---------------------------------------------------------------------------------
// Vertex Buffer Reader/Writer
class VBReader
{
public:
VBReader();
VBReader(VBReader&& moveFrom);
VBReader& operator= (VBReader&& moveFrom);
~VBReader();
HRESULT Initialize( _In_reads_(nDecl) const D3D11_INPUT_ELEMENT_DESC* vbDecl, _In_ size_t nDecl );
// Does not support VB decls with D3D11_INPUT_PER_INSTANCE_DATA
HRESULT AddStream( _In_reads_bytes_(stride*nVerts) const void* vb, _In_ size_t nVerts, _In_ size_t inputSlot, _In_ size_t stride = 0 );
// Add vertex buffer to reader
HRESULT Read( _Out_writes_(count) XMVECTOR* buffer, _In_z_ LPCSTR semanticName, _In_ UINT semanticIndex, _In_ size_t count ) const;
// Extracts data elements from vertex buffer
void Release();
private:
// Private implementation.
class Impl;
std::unique_ptr<Impl> pImpl;
// Prevent copying.
VBReader(VBReader const&);
VBReader& operator= (VBReader const&);
};
class VBWriter
{
public:
VBWriter();
VBWriter(VBWriter&& moveFrom);
VBWriter& operator= (VBWriter&& moveFrom);
~VBWriter();
HRESULT Initialize( _In_reads_(nDecl) const D3D11_INPUT_ELEMENT_DESC* vbDecl, _In_ size_t nDecl );
// Does not support VB decls with D3D11_INPUT_PER_INSTANCE_DATA
HRESULT AddStream( _Out_writes_bytes_(stride*nVerts) void* vb, _In_ size_t nVerts, _In_ size_t inputSlot, _In_ size_t stride = 0 );
// Add vertex buffer to writer
HRESULT Write( _In_reads_(count) const XMVECTOR* buffer, _In_z_ LPCSTR semanticName, _In_ UINT semanticIndex, _In_ size_t count ) const;
// Inserts data elements into vertex buffer
void Release();
private:
// Private implementation.
class Impl;
std::unique_ptr<Impl> pImpl;
// Prevent copying.
VBWriter(VBWriter const&);
VBWriter& operator= (VBWriter const&);
};
//---------------------------------------------------------------------------------
// Adjacency Computation
HRESULT GenerateAdjacencyAndPointReps( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_ float epsilon,
_Out_writes_opt_(nVerts) uint32_t* pointRep,
_Out_writes_opt_(nFaces*3) uint32_t* adjacency );
HRESULT GenerateAdjacencyAndPointReps( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_ float epsilon,
_Out_writes_opt_(nVerts) uint32_t* pointRep,
_Out_writes_opt_(nFaces*3) uint32_t* adjacency );
// If pointRep is null, it still generates them internally as they are needed for the final adjacency computation
HRESULT ConvertPointRepsToAdjacency( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_reads_opt_(nVerts) const uint32_t* pointRep,
_Out_writes_(nFaces*3) uint32_t* adjacency );
HRESULT ConvertPointRepsToAdjacency( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_reads_opt_(nVerts) const uint32_t* pointRep,
_Out_writes_(nFaces*3) uint32_t* adjacency );
// If pointRep is null, assumes an identity
HRESULT GenerateGSAdjacency( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* pointRep,
_In_reads_(nFaces*3) const uint32_t* adjacency, _In_ size_t nVerts,
_Out_writes_(nFaces*6) uint16_t* indicesAdj );
HRESULT GenerateGSAdjacency( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* pointRep,
_In_reads_(nFaces*3) const uint32_t* adjacency, _In_ size_t nVerts,
_Out_writes_(nFaces*6) uint32_t* indicesAdj );
// Generates an IB suitable for Geometry Shader using D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ
//---------------------------------------------------------------------------------
// Normals, Tangents, and Bi-Tangents Computation
enum CNORM_FLAGS
{
CNORM_DEFAULT = 0x0,
// Default is to compute normals using weight-by-angle
CNORM_WEIGHT_BY_AREA = 0x1,
// Computes normals using weight-by-area
CNORM_WEIGHT_EQUAL = 0x2,
// Compute normals with equal weights
CNORM_WIND_CW = 0x4,
// Vertices are clock-wise (defaults to CCW)
};
HRESULT ComputeNormals( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_(nVerts) XMFLOAT3* normals );
HRESULT ComputeNormals( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_(nVerts) XMFLOAT3* normals );
// Computes vertex normals
enum CTF_FLAGS
{
CTF_DEFAULT = 0x0,
// Default is to compute normals using weight-by-angle
CTF_WEIGHT_BY_AREA = 0x1,
// Computes normals using weight-by-area
CTF_WEIGHT_EQUAL = 0x2,
// Compute normals with equal weights
CTF_WIND_CW = 0x4,
// Vertices are clock-wise (defaults to CCW)
CTF_DONT_ORTHOGONALIZE = 0x10,
CTF_ORTHOGONALIZE_FROM_U = 0x20,
CTF_ORTHOGONALIZE_FROM_V = 0x40,
};
HRESULT ComputeTangentFrame( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions,
_In_reads_(nVerts) const XMFLOAT3* normals,
_In_reads_(nVerts) const XMFLOAT2* txtcoords, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_opt_(nVerts) XMFLOAT3* tangents,
_Out_writes_opt_(nVerts) XMFLOAT3* binormals );
HRESULT ComputeTangentFrame( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions,
_In_reads_(nVerts) const XMFLOAT3* normals,
_In_reads_(nVerts) const XMFLOAT2* txtcoords, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_opt_(nVerts) XMFLOAT3* tangents,
_Out_writes_opt_(nVerts) XMFLOAT3* binormals );
HRESULT ComputeTangentFrame( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions,
_In_reads_(nVerts) const XMFLOAT3* normals,
_In_reads_(nVerts) const XMFLOAT2* txtcoords, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_opt_(nVerts) XMFLOAT4* tangents,
_Out_writes_opt_(nVerts) XMFLOAT4* binormals );
HRESULT ComputeTangentFrame( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nVerts) const XMFLOAT3* positions,
_In_reads_(nVerts) const XMFLOAT3* normals,
_In_reads_(nVerts) const XMFLOAT2* txtcoords, _In_ size_t nVerts,
_In_ DWORD flags,
_Out_writes_opt_(nVerts) XMFLOAT4* tangents,
_Out_writes_opt_(nVerts) XMFLOAT4* binormals );
// Computes tangents and/or bi-normals (optionally with handedness stored in .w)
//---------------------------------------------------------------------------------
// Mesh clean-up and validation
enum VALIDATE_FLAGS
{
VALIDATE_DEFAULT = 0x0,
VALIDATE_BACKFACING = 0x1,
// Check for duplicate neighbor from triangle (requires adjacency)
VALIDATE_BOWTIES = 0x2,
// Check for two fans of triangles using the same vertex (requires adjacency)
VALIDATE_DEGENERATE = 0x4,
// Check for degenerate triangles
};
HRESULT Validate( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_ size_t nVerts, _In_reads_opt_(nFaces*3) const uint32_t* adjacency,
_In_ DWORD flags, _In_opt_ std::wstring* msgs = nullptr );
HRESULT Validate( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_ size_t nVerts, _In_reads_opt_(nFaces*3) const uint32_t* adjacency,
_In_ DWORD flags, _In_opt_ std::wstring* msgs = nullptr );
// Checks the mesh for common problems, return 'S_OK' if no problems were found
HRESULT Clean( _Inout_updates_all_(nFaces*3) uint16_t* indices, _In_ size_t nFaces,
_In_ size_t nVerts, _Inout_updates_all_opt_(nFaces*3) uint32_t* adjacency,
_In_reads_opt_(nFaces) const uint32_t* attributes,
_Inout_ std::vector<uint32_t>& dupVerts, _In_ bool breakBowties=false );
HRESULT Clean( _Inout_updates_all_(nFaces*3) uint32_t* indices, _In_ size_t nFaces,
_In_ size_t nVerts, _Inout_updates_all_opt_(nFaces*3) uint32_t* adjacency,
_In_reads_opt_(nFaces) const uint32_t* attributes,
_Inout_ std::vector<uint32_t>& dupVerts, _In_ bool breakBowties=false );
// Cleans the mesh, splitting vertices if needed
//---------------------------------------------------------------------------------
// Mesh Optimization
HRESULT AttributeSort( _In_ size_t nFaces, _Inout_updates_all_opt_(nFaces) uint32_t* attributes,
_Out_writes_(nFaces) uint32_t* faceRemap );
// Reorders faces by attribute id
enum OPTFACES
{
OPTFACES_V_DEFAULT = 12,
OPTFACES_R_DEFAULT = 7,
// Default vertex cache size and restart threshold which is considered 'device independent'
OPTFACES_V_STRIPORDER = 0,
// Indicates no vertex cache optimization, only reordering into strips
};
HRESULT OptimizeFaces( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nFaces*3) const uint32_t* adjacency,
_Out_writes_(nFaces) uint32_t* faceRemap,
_In_ uint32_t vertexCache = OPTFACES_V_DEFAULT,
_In_ uint32_t restart = OPTFACES_R_DEFAULT );
HRESULT OptimizeFaces( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nFaces*3) const uint32_t* adjacency,
_Out_writes_(nFaces) uint32_t* faceRemap,
_In_ uint32_t vertexCache = OPTFACES_V_DEFAULT,
_In_ uint32_t restart = OPTFACES_R_DEFAULT );
// Reorders faces to increase hit rate of vertex caches
HRESULT OptimizeFacesEx( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces,
_In_reads_(nFaces*3) const uint32_t* adjacency,
_In_reads_(nFaces) const uint32_t* attributes,
_Out_writes_(nFaces) uint32_t* faceRemap,
_In_ uint32_t vertexCache = OPTFACES_V_DEFAULT,
_In_ uint32_t restart = OPTFACES_R_DEFAULT );
HRESULT OptimizeFacesEx( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces,
_In_reads_(nFaces*3) const uint32_t* adjacency,
_In_reads_(nFaces) const uint32_t* attributes,
_Out_writes_(nFaces) uint32_t* faceRemap,
_In_ uint32_t vertexCache = OPTFACES_V_DEFAULT,
_In_ uint32_t restart = OPTFACES_R_DEFAULT );
// Attribute group version of OptimizeFaces
HRESULT OptimizeVertices( _In_reads_(nFaces*3) const uint16_t* indices, _In_ size_t nFaces, _In_ size_t nVerts,
_Out_writes_(nVerts) uint32_t* vertexRemap );
HRESULT OptimizeVertices( _In_reads_(nFaces*3) const uint32_t* indices, _In_ size_t nFaces, _In_ size_t nVerts,
_Out_writes_(nVerts) uint32_t* vertexRemap );
// Reorders vertices in order of use
//---------------------------------------------------------------------------------
// Remap functions
HRESULT ReorderIB( _In_reads_(nFaces*3) const uint16_t* ibin, _In_ size_t nFaces,
_In_reads_(nFaces) const uint32_t* faceRemap,
_Out_writes_(nFaces*3) uint16_t* ibout );
HRESULT ReorderIB( _Inout_updates_all_(nFaces*3) uint16_t* ib, _In_ size_t nFaces,
_In_reads_(nFaces) const uint32_t* faceRemap );
HRESULT ReorderIB( _In_reads_(nFaces*3) const uint32_t* ibin, _In_ size_t nFaces,
_In_reads_(nFaces) const uint32_t* faceRemap,
_Out_writes_(nFaces*3) uint32_t* ibout );
HRESULT ReorderIB( _Inout_updates_all_(nFaces*3) uint32_t* ib, _In_ size_t nFaces,
_In_reads_(nFaces) const uint32_t* faceRemap );
// Applies a face remap reordering to an index buffer
HRESULT ReorderIBAndAdjacency( _In_reads_(nFaces*3) const uint16_t* ibin, _In_ size_t nFaces, _In_reads_(nFaces*3) const uint32_t* adjin,
_In_reads_(nFaces) const uint32_t* faceRemap,
_Out_writes_(nFaces*3) uint16_t* ibout, _Out_writes_(nFaces*3) uint32_t* adjout );
HRESULT ReorderIBAndAdjacency( _Inout_updates_all_(nFaces*3) uint16_t* ib, _In_ size_t nFaces, _Inout_updates_all_(nFaces*3) uint32_t* adj,
_In_reads_(nFaces) const uint32_t* faceRemap );
HRESULT ReorderIBAndAdjacency( _In_reads_(nFaces*3) const uint32_t* ibin, _In_ size_t nFaces, _In_reads_(nFaces*3) const uint32_t* adjin,
_In_reads_(nFaces) const uint32_t* faceRemap,
_Out_writes_(nFaces*3) uint32_t* ibout, _Out_writes_(nFaces*3) uint32_t* adjout );
HRESULT ReorderIBAndAdjacency( _Inout_updates_all_(nFaces*3) uint32_t* ib, _In_ size_t nFaces, _Inout_updates_all_(nFaces*3) uint32_t* adj,
_In_reads_(nFaces) const uint32_t* faceRemap );
// Applies a face remap reordering to an index buffer and adjacency
HRESULT FinalizeIB( _In_reads_(nFaces*3) const uint16_t* ibin, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* vertexRemap, _In_ size_t nVerts,
_Out_writes_(nFaces*3) uint16_t* ibout );
HRESULT FinalizeIB( _Inout_updates_all_(nFaces*3) uint16_t* ib, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* vertexRemap, _In_ size_t nVerts );
HRESULT FinalizeIB( _In_reads_(nFaces*3) const uint32_t* ibin, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* vertexRemap, _In_ size_t nVerts,
_Out_writes_(nFaces*3) uint32_t* ibout );
HRESULT FinalizeIB( _Inout_updates_all_(nFaces*3) uint32_t* ib, _In_ size_t nFaces,
_In_reads_(nVerts) const uint32_t* vertexRemap, _In_ size_t nVerts );
// Applies a vertex remap reordering to an index buffer
HRESULT FinalizeVB( _In_reads_bytes_(nVerts*stride) const void* vbin, _In_ size_t stride, _In_ size_t nVerts,
_In_reads_opt_(nDupVerts) const uint32_t* dupVerts, _In_ size_t nDupVerts,
_In_reads_opt_(nVerts+nDupVerts) const uint32_t* vertexRemap,
_Out_writes_bytes_((nVerts+nDupVerts)*stride) void* vbout );
HRESULT FinalizeVB( _Inout_updates_bytes_all_(nVerts*stride) void* vb, _In_ size_t stride, _In_ size_t nVerts,
_In_reads_(nVerts) const uint32_t* vertexRemap );
// Applies a vertex remap and/or a vertex duplication set to a vertex buffer
HRESULT FinalizeVBAndPointReps( _In_reads_bytes_(nVerts*stride) const void* vbin, _In_ size_t stride, _In_ size_t nVerts,
_In_reads_(nVerts) const uint32_t* prin,
_In_reads_opt_(nDupVerts) const uint32_t* dupVerts, _In_ size_t nDupVerts,
_In_reads_opt_(nVerts+nDupVerts) const uint32_t* vertexRemap,
_Out_writes_bytes_((nVerts+nDupVerts)*stride) void* vbout,
_Out_writes_(nVerts+nDupVerts) uint32_t* prout );
HRESULT FinalizeVBAndPointReps( _Inout_updates_bytes_all_(nVerts*stride) void* vb, _In_ size_t stride, _In_ size_t nVerts,
_Inout_updates_all_(nVerts) uint32_t* pointRep,
_In_reads_(nVerts) const uint32_t* vertexRemap );
// Applies a vertex remap and/or a vertex duplication set to a vertex buffer and point representatives
#include "DirectXMesh.inl"
}; // namespace

71
Utilities/ACMR.h Normal file
Просмотреть файл

@ -0,0 +1,71 @@
//-------------------------------------------------------------------------------------
// ACMR.h
//
// Function for computing the average cache miss ratio for vertex caches
//
// http://www.realtimerendering.com/blog/acmr-and-atvr/
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkID=324981
//-------------------------------------------------------------------------------------
#include <sal.h>
#include <stdint.h>
template<class index_t>
void CalculateMissRate( _In_reads_(nFaces*3) const index_t* indices, size_t nFaces, size_t nVerts, size_t cacheSize,
float& acmr, float& atvr )
{
acmr = -1.f;
atvr = -1.f;
if ( !indices || !nFaces || !nVerts || !cacheSize )
return;
if ( ( uint64_t(nFaces) * 3 ) > 0xFFFFFFFF )
return;
size_t misses = 0;
std::unique_ptr<uint32_t> fifo( new uint32_t[ cacheSize ] );
size_t tail = 0;
memset( fifo.get(), 0xff, sizeof(uint32_t) * cacheSize );
for( size_t j = 0; j < (nFaces * 3); ++j )
{
if ( indices[ j ] == index_t(-1) )
continue;
bool found = false;
for( size_t ptr = 0; ptr < cacheSize; ++ptr )
{
if ( fifo.get()[ ptr ] == indices[ j ] )
{
found = true;
break;
}
}
if ( !found )
{
++misses;
fifo.get()[ tail ] = indices[ j ];
++tail;
if ( tail == cacheSize ) tail = 0;
}
}
// ideal is 0.5, individual tris have 3.0
acmr = float( misses ) / float( nFaces );
// ideal is 1.0, worst case is 6.0
atvr = float( misses ) / float( nVerts );
}

542
Utilities/WaveFrontReader.h Normal file
Просмотреть файл

@ -0,0 +1,542 @@
//--------------------------------------------------------------------------------------
// File: WaveFrontReader.h
//
// Code for loading basic mesh data from a WaveFront OBJ file
//
// http://en.wikipedia.org/wiki/Wavefront_.obj_file
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkID=324981
//--------------------------------------------------------------------------------------
#define NOMINMAX
#include <windows.h>
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_map>
#pragma warning(push)
#pragma warning(disable : 4005)
#include <stdint.h>
#pragma warning(pop)
#include <directxmath.h>
#include <directxcollision.h>
template<class index_t>
class WaveFrontReader
{
public:
typedef index_t index_t;
struct Vertex
{
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT3 normal;
DirectX::XMFLOAT2 textureCoordinate;
};
WaveFrontReader() : hasNormals(false), hasTexcoords(false) {}
HRESULT Load( _In_z_ const wchar_t* szFileName, bool ccw = true )
{
Clear();
static const size_t MAX_POLY = 64;
using namespace DirectX;
std::wifstream InFile( szFileName );
if( !InFile )
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
WCHAR fname[_MAX_FNAME];
_wsplitpath_s( szFileName, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0 );
name = fname;
std::vector<XMFLOAT3> positions;
std::vector<XMFLOAT3> normals;
std::vector<XMFLOAT2> texCoords;
VertexCache vertexCache;
Material defmat;
wcscpy_s( defmat.strName, L"default" );
materials.push_back( defmat );
uint32_t curSubset = 0;
WCHAR strCommand[256] = {0};
WCHAR strMaterialFilename[MAX_PATH] = {0};
for( ;; )
{
InFile >> strCommand;
if( !InFile )
break;
if( 0 == wcscmp( strCommand, L"#" ) )
{
// Comment
}
else if( 0 == wcscmp( strCommand, L"v" ) )
{
// Vertex Position
float x, y, z;
InFile >> x >> y >> z;
positions.push_back( XMFLOAT3( x, y, z ) );
}
else if( 0 == wcscmp( strCommand, L"vt" ) )
{
// Vertex TexCoord
float u, v;
InFile >> u >> v;
texCoords.push_back( XMFLOAT2( u, v ) );
hasTexcoords = true;
}
else if( 0 == wcscmp( strCommand, L"vn" ) )
{
// Vertex Normal
float x, y, z;
InFile >> x >> y >> z;
normals.push_back( XMFLOAT3( x, y, z ) );
hasNormals = true;
}
else if( 0 == wcscmp( strCommand, L"f" ) )
{
// Face
UINT iPosition, iTexCoord, iNormal;
Vertex vertex;
DWORD faceIndex[ MAX_POLY ];
size_t iFace = 0;
for(;;)
{
if ( iFace >= MAX_POLY )
{
// Too many polygon verts for the reader
return E_FAIL;
}
memset( &vertex, 0, sizeof( vertex ) );
// OBJ format uses 1-based arrays
InFile >> iPosition;
if ( iPosition > positions.size() )
return E_FAIL;
vertex.position = positions[ iPosition - 1 ];
if( '/' == InFile.peek() )
{
InFile.ignore();
if( '/' != InFile.peek() )
{
// Optional texture coordinate
InFile >> iTexCoord;
if ( iTexCoord > texCoords.size() )
return E_FAIL;
vertex.textureCoordinate = texCoords[ iTexCoord - 1 ];
}
if( '/' == InFile.peek() )
{
InFile.ignore();
// Optional vertex normal
InFile >> iNormal;
if ( iNormal > normals.size() )
return E_FAIL;
vertex.normal = normals[ iNormal - 1 ];
}
}
// If a duplicate vertex doesn't exist, add this vertex to the Vertices
// list. Store the index in the Indices array. The Vertices and Indices
// lists will eventually become the Vertex Buffer and Index Buffer for
// the mesh.
DWORD index = AddVertex( iPosition, &vertex, vertexCache );
if ( index == (DWORD)-1 )
return E_OUTOFMEMORY;
#pragma warning( suppress : 4127 )
if ( sizeof(index_t) == 2 && ( index >= 0xFFFF ) )
{
// Too many indices for 16-bit IB!
return E_FAIL;
}
else if ( sizeof(index_t) == 4 && ( index >= 0xFFFFFFFF ) )
{
// Too many indices for 32-bit IB!
return E_FAIL;
}
faceIndex[ iFace ] = index;
++iFace;
// Check for more face data or end of the face statement
bool faceEnd = false;
for(;;)
{
wchar_t p = InFile.peek();
if ( '\n' == p || !InFile )
{
faceEnd = true;
break;
}
else if ( isdigit( p ) )
break;
InFile.ignore();
}
if ( faceEnd )
break;
}
if ( iFace < 3 )
{
// Need at least 3 points to form a triangle
return E_FAIL;
}
// Convert polygons to triangles
DWORD i0 = faceIndex[0];
DWORD i1 = faceIndex[1];
for( size_t j = 2; j < iFace; ++ j )
{
DWORD index = faceIndex[ j ];
indices.push_back( static_cast<uint16_t>( i0 ) );
if ( ccw )
{
indices.push_back( static_cast<uint16_t>( i1 ) );
indices.push_back( static_cast<uint16_t>( index ) );
}
else
{
indices.push_back( static_cast<uint16_t>( index ) );
indices.push_back( static_cast<uint16_t>( i1 ) );
}
attributes.push_back( curSubset );
i1 = index;
}
assert( attributes.size()*3 == indices.size() );
}
else if( 0 == wcscmp( strCommand, L"mtllib" ) )
{
// Material library
InFile >> strMaterialFilename;
}
else if( 0 == wcscmp( strCommand, L"usemtl" ) )
{
// Material
WCHAR strName[MAX_PATH] = {0};
InFile >> strName;
bool bFound = false;
uint32_t count = 0;
for( auto it = materials.cbegin(); it != materials.cend(); ++it, ++count )
{
if( 0 == wcscmp( it->strName, strName ) )
{
bFound = true;
curSubset = count;
break;
}
}
if( !bFound )
{
Material mat;
curSubset = static_cast<uint32_t>( materials.size() );
wcscpy_s( mat.strName, MAX_PATH - 1, strName );
materials.push_back( mat );
}
}
else
{
// Unimplemented or unrecognized command
OutputDebugStringW( strCommand );
}
InFile.ignore( 1000, '\n' );
}
// Cleanup
InFile.close();
BoundingBox::CreateFromPoints( bounds, positions.size(), &positions.front(), sizeof(XMFLOAT3) );
// If an associated material file was found, read that in as well.
if( *strMaterialFilename )
{
WCHAR ext[_MAX_EXT];
_wsplitpath_s( strMaterialFilename, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT );
WCHAR drive[_MAX_DRIVE];
WCHAR dir[_MAX_DIR];
_wsplitpath_s( szFileName, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0 );
WCHAR szPath[ MAX_PATH ];
_wmakepath_s( szPath, MAX_PATH, drive, dir, fname, ext );
HRESULT hr = LoadMTL( szPath );
if ( FAILED(hr) )
return hr;
}
return S_OK;
}
HRESULT LoadMTL( _In_z_ const wchar_t* szFileName )
{
// Assumes MTL is in CWD along with OBJ
std::wifstream InFile( szFileName );
if( !InFile )
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
auto curMaterial = materials.end();
WCHAR strCommand[256] = {0};
for( ;; )
{
InFile >> strCommand;
if( !InFile )
break;
if( 0 == wcscmp( strCommand, L"newmtl" ) )
{
// Switching active materials
WCHAR strName[MAX_PATH] = {0};
InFile >> strName;
curMaterial = materials.end();
for( auto it = materials.begin(); it != materials.end(); ++it )
{
if( 0 == wcscmp( it->strName, strName ) )
{
curMaterial = it;
break;
}
}
}
// The rest of the commands rely on an active material
if( curMaterial == materials.end() )
continue;
if( 0 == wcscmp( strCommand, L"#" ) )
{
// Comment
}
else if( 0 == wcscmp( strCommand, L"Ka" ) )
{
// Ambient color
float r, g, b;
InFile >> r >> g >> b;
curMaterial->vAmbient = XMFLOAT3( r, g, b );
}
else if( 0 == wcscmp( strCommand, L"Kd" ) )
{
// Diffuse color
float r, g, b;
InFile >> r >> g >> b;
curMaterial->vDiffuse = XMFLOAT3( r, g, b );
}
else if( 0 == wcscmp( strCommand, L"Ks" ) )
{
// Specular color
float r, g, b;
InFile >> r >> g >> b;
curMaterial->vSpecular = XMFLOAT3( r, g, b );
}
else if( 0 == wcscmp( strCommand, L"d" ) ||
0 == wcscmp( strCommand, L"Tr" ) )
{
// Alpha
InFile >> curMaterial->fAlpha;
}
else if( 0 == wcscmp( strCommand, L"Ns" ) )
{
// Shininess
int nShininess;
InFile >> nShininess;
curMaterial->nShininess = nShininess;
}
else if( 0 == wcscmp( strCommand, L"illum" ) )
{
// Specular on/off
int illumination;
InFile >> illumination;
curMaterial->bSpecular = ( illumination == 2 );
}
else if( 0 == wcscmp( strCommand, L"map_Kd" ) )
{
// Texture
InFile >> curMaterial->strTexture;
}
else
{
// Unimplemented or unrecognized command
}
InFile.ignore( 1000, L'\n' );
}
InFile.close();
return S_OK;
}
void Clear()
{
vertices.clear();
indices.clear();
attributes.clear();
materials.clear();
name.clear();
hasNormals = false;
hasTexcoords = false;
bounds.Center.x = bounds.Center.y = bounds.Center.z = 0.f;
bounds.Extents.x = bounds.Extents.y = bounds.Extents.z = 0.f;
}
HRESULT LoadVBO( _In_z_ const wchar_t* szFileName )
{
Clear();
WCHAR fname[_MAX_FNAME];
_wsplitpath_s( szFileName, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0 );
name = fname;
Material defmat;
wcscpy_s( defmat.strName, L"default" );
materials.push_back( defmat );
std::ifstream vboFile(szFileName, std::ifstream::in | std::ifstream::binary);
if ( !vboFile.is_open() )
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
hasNormals = hasTexcoords = true;
uint32_t numVertices = 0;
uint32_t numIndices = 0;
vboFile.read( reinterpret_cast<char*>( &numVertices ), sizeof(uint32_t ) );
if ( !numVertices )
return E_FAIL;
vboFile.read( reinterpret_cast<char*>( &numIndices ), sizeof(uint32_t ) );
if ( !numIndices )
return E_FAIL;
vertices.resize( numVertices );
vboFile.read( reinterpret_cast<char*>( &vertices.front() ), sizeof(Vertex) * numVertices );
#pragma warning( suppress : 4127 )
if ( sizeof( index_t ) == 2 )
{
indices.resize( numIndices );
vboFile.read( reinterpret_cast<char*>( &indices.front() ), sizeof(uint16_t) * numIndices );
}
else
{
std::vector<uint16_t> tmp;
tmp.resize( numIndices );
vboFile.read( reinterpret_cast<char*>( &tmp.front() ), sizeof(uint16_t) * numIndices );
indices.reserve( numIndices );
for( auto it = tmp.cbegin(); it != tmp.cend(); ++it )
{
indices.push_back( *it );
}
}
BoundingBox::CreateFromPoints( bounds, vertices.size(), reinterpret_cast<const XMFLOAT3*>( &vertices.front() ), sizeof(Vertex) );
vboFile.close();
return S_OK;
}
struct Material
{
DirectX::XMFLOAT3 vAmbient;
DirectX::XMFLOAT3 vDiffuse;
DirectX::XMFLOAT3 vSpecular;
uint32_t nShininess;
float fAlpha;
bool bSpecular;
WCHAR strName[MAX_PATH];
WCHAR strTexture[MAX_PATH];
Material() :
vAmbient( 0.2f, 0.2f, 0.2f ),
vDiffuse( 0.8f, 0.8f, 0.8f ),
vSpecular( 1.0f, 1.0f, 1.0f ),
nShininess( 0 ),
fAlpha( 1.f ),
bSpecular( false )
{ memset(strName, 0, MAX_PATH); memset(strTexture, 0, MAX_PATH); }
};
std::vector<Vertex> vertices;
std::vector<index_t> indices;
std::vector<uint32_t> attributes;
std::vector<Material> materials;
std::wstring name;
bool hasNormals;
bool hasTexcoords;
DirectX::BoundingBox bounds;
private:
typedef std::unordered_multimap<UINT, UINT> VertexCache;
DWORD AddVertex( UINT hash, Vertex* pVertex, VertexCache& cache )
{
auto f = cache.equal_range( hash );
for( auto it = f.first; it != f.second; ++it )
{
auto& tv = vertices[ it->second ];
if ( 0 == memcmp( pVertex, &tv, sizeof(Vertex) ) )
{
return it->second;
}
}
DWORD index = static_cast<UINT>( vertices.size() );
vertices.push_back( *pVertex );
VertexCache::value_type entry( hash, index );
cache.insert( entry );
return index;
}
};