You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
#rb none
#rnx
============================
MAJOR FEATURES & CHANGES
============================
Change 3783110 by Thomas.Sarkanen
Added support for logical negation when copying to array properties in the fast path
#jira UE-52043 - Anim fast path correctly doesn't handle copying to arrays of bools with logical negation
Change 3783112 by Thomas.Sarkanen
Fixed not being able to assign/unassign constraint and physical animation profiles from the context menu
#jira UE-50205 - Constraint cannot be assigned or unassigned from current profile when using "Unassign" from the context menu in the Skeleton Tree and Graph
Change 3783114 by Thomas.Sarkanen
Asset picker now only reports 'picks' on user interactions
In Ocean and other projects with lots of animation assets, the asset picker amortizes its filter queries over a number of frames. This causes the list to temporarily not include the currently selected item, which then gets reported to client code as a 'deselection'. To address this (and to maintain backwards compatibility) I've added a new delegate that gives more context for selections to the SAssetPicker, allowing us to not report selections that are 'Direct'.
#jira UE-46802 - Searching for an animation from the dropdown in persona causes it to instantly close
Change 3783118 by Thomas.Sarkanen
Added inline time/frame settings to notify context menu
#jira UE-45410 - Inline MontageSection/Notify Time Entry
Change 3783122 by Thomas.Sarkanen
Collision Response dropdown is now a checkbox
Added details customization to allow the enum to masquerate as a bool property
#jira UE-47916 - Drop down used for Collision Response enable/disable in Physics Asset Editor
Change 3783183 by Jurre.deBaare
Follow up fix for toggling post processing in the preview scene
Change 3783186 by Jurre.deBaare
Material Baking Options has two Mesh Settings sections
#fix Changed category names for to allow for two distinct categories (would have preferred to merge them, but am limited by the way detailsview handles multiple objects with containing same category)
#jira UE-52645
Change 3783188 by Jurre.deBaare
Duplicating:
"Simplygon Skelmesh "Regenerate" button remains for content that had Lods created in simplygon, even when it's not installed. On regenerating editor crashes - FPersonaMeshDetails::ApplyChanges()
#fix Ensure the regenerate button is disabled when there is no MeshReduction interface available
#jira UE-52641
Change 3783205 by Jurre.deBaare
Bounds are not shown correctly in Persona for imported alembic skeletal mesh
#fix Ensured that we do not add invalid of (0,0,0) bounds to outgoing mesh bounds
#jira UE-49338
Change 3783248 by Jurre.deBaare
Preview Scene Settings window is missing Search bar
#fix changed search bar flag on Details View
#jira UE-50063
Change 3783267 by Jurre.deBaare
Animation Modifiers: GetBonePosesForTime does not create valid data for GetBonePose
#fix changed the way we retrieve bone transforms, now take it directly from the raw data rather than from a pose
#jira UE-52057
Change 3783281 by Jurre.deBaare
Tool Tip issues in Animation Editor Preferences
#fix corrected typos in comments
#jira UE-51338
Change 3783373 by Thomas.Sarkanen
Added error-reporting to the profile name widgets
This means that the effect of renaming profiles the same as an existing one is clear (i.e. we deny it now).
#jira UE-48120 - Renaming Physics profiles the same as existing Physics profiles switches profiles and is confusing
Change 3783438 by Jurre.deBaare
Vertex paint fill tool fills all channels
#fix Ensure that the current channel fill selection in the Mesh Painter is also used for the fill tool, to allow the user masking out certain channels when using fill
#jira UE-49256
Change 3783583 by Thomas.Sarkanen
Correctly return whether a mesh section is shown or not when it has not been edited
This fixes skeletal->static mesh conversion
Change 3783598 by Thomas.Sarkanen
Fix multi-convex generastion for skeletal meshes
Some triangle indices were being missed out of the index buffer, so the mesh that the convex hull was generated from was corrupt. Removing an early-out fixes this.
#jira UE-52529 - Inaccurate Collision is Generated When Using Multi Convex Hull (PhysicsAsset Editor)
Change 3783615 by Jurre.deBaare
OpacityMask/Opacity bug in MeshUtilities
#fix ensured that material baking uses correct texture samples for Opacity Mask property
#misc deprecated all of this functionality as users should be using the MaterialBaking module
#jira UE-52382
Change 3783620 by Martin.Wilson
Fix crash due to oversampling animation during compression
#Jira UE-52713
Change 3783633 by Jurre.deBaare
Fix deprecation warnings on CIS
Change 3783636 by Benn.Gallagher
Fixed non-working tethers in clothing
Fixed clothing config not applying to active simulation after editing
Fixed and re-enabled accurate wind mode
#jira UE-50797, UE-43611
Change 3783637 by Benn.Gallagher
Github PR: Fix world to actor transform bug in anim dynamics
Fixed incorrect Actor-space calculations for simulation transforms and world vector transformations inside AnimDynamics highlighted when testing the above PR
#jira UE-48681
#3929
Change 3783638 by Benn.Gallagher
Fixed UBlendProfile properties not being correctly customized on anim nodes - and animation sequence references not being correctly filtered on anim nodes.
Change 3783660 by Danny.Bouimad
Fixing #UE-40686 Mass TranslatedMass Automated test, instead of decreasing tolrence I changed the content to preserve granularity.
Change 3783974 by Ori.Cohen
Refactor when sync components is called and how we pass the data into plugins. Simplifies how we handle physx data getting invalidated by UE4 during updates.
Also fixed crash when scene query returns a destructible component that's been destroyed, but final apex delete flush hasn't happened yet.
#jira UE-50215
Change 3784112 by Benn.Gallagher
Fixed subinstance nodes inside states failing to correctly create bridge variables on their skeleton class due to not correctly processing subgraphs in the anim blueprint compiler.
#jira UE-51000
Change 3784277 by Martin.Wilson
Fix socket name getting an appended _0
#jira UE-46625
Change 3785589 by Ori.Cohen
Fix cis
Change 3786336 by Martin.Wilson
Pushing skeleton to live link can now take source guid
-Message bus source pushes guid when sending skeleton
Change 3786778 by Martin.Wilson
Added ability for worker thread animation blueprint code to report to Message Log + added additive warning when playing an animation on an additive pose
#jira UE-49780
Change 3786847 by Martin.Wilson
Initialization and delta time for live link retargeter
#Jira UE-52112
Change 3786852 by Lina.Halper
Sequencer blending support
#jira: UE-52183
Change 3786924 by Lina.Halper
PR #4210: FIX: Incorrectly passing an unrelated bool rather than the expected pose ind. (Contributed by ruffenman)
Change 3787114 by Jurre.deBaare
Discrepancy in description of Preview Scene setting and keyboard shortcut
#fix Changed naming of the settings to match the Advanced Preview Scene panel
#jira UE-50060
Change 3787115 by Jurre.deBaare
Animation Editor Preferences do not update the preview scene
#fix Removed unused preference from PersonaOptions
#jira UE-51318
Change 3787117 by Jurre.deBaare
Off-by one error in frame time calculations
#fix Fixed up UAnimSequenceBase::GetTimeAtFrame and UAnimSequenceBase::GetFrameAtTime to return correct frame indices, similar to FAnimationRuntime::GetKeyIndicesFromTime
#jira UE-52037
Change 3787412 by Martin.Wilson
CIS Fix
Change 3787622 by Ethan.Geller
Include Google Resonance SDK.
Change 3787633 by Ethan.Geller
Promote AmibsonicsMixerPtr to FAudioDevice
Change 3788026 by Lina.Halper
Retarget source reference to soft object ptr
#jira: UE-48570
Change 3788252 by Ethan.Geller
Add blueprint functions for Resonance Global Reverb
Change 3788750 by Ethan.Geller
fix single file compile for Resonance plugin
Change 3788763 by Ethan.Geller
include IModularFeatures.h explicitly for incremental build
Change 3789108 by Martin.Wilson
Fix animations with scaled root bone generating incorrect root motion
#jira UE-52088
Change 3789642 by Martin.Wilson
Fix transition nodes from referencing the wrong state node due to non unique node guids being introduced by copy and paste
#jira UE-43891
Change 3790165 by Martin.Wilson
Fix marker sync position not being maintained across instant transitions
#jira UE-21355
Change 3790182 by Ethan.Geller
Final Resonance edits pass.
Change 3790184 by Lina.Halper
Fix issue with crash when montage is streamed out while event is queued.
https://udn.unrealengine.com/questions/404318/anim-montage-queued-montage-event-crash.html
Change 3790207 by dan.reynolds
#UE-50774 Updated AEOverviewReverb to not attempt to destroy Audio Component that has already been destroyed.
Change 3790215 by Martin.Wilson
CIS Fix
Change 3790953 by Ethan.Geller
#jira UE-53023 bypass filters when at max frequency for LPF, DC for HPF
Change 3791832 by Martin.Wilson
Don't load animations for preview tooltip in Persona
#jira UE-52118
Change 3792873 by David.Hill
Fix CIS. Remove timer from proxylod code.
Change 3793251 by David.Hill
ProxyLOD Thirdparty libs build cs files.
Update from ModuleRules.WindowsPlatform to ReadOnlyTargetRules.WindowsPlatform
-- The WindowsPlatform alias is deprecated in 4.18
Change 3793400 by Ethan.Geller
Update Resonance blueprint library to fit google naming conventions
Change 3794097 by Benn.Gallagher
Fixed clothing visualizations no longer functioning
#jira UE-52995
Change 3794250 by Danny.Bouimad
Regenerated ground truth on LODCurveLinkingTest1 and AnimatedCloth, expected change as a result of Ben fixing a bug.
Needed to update Owens cloth settings too.
Should resolve automation test CIS fails
Change 3794352 by David.Hill
ProxyLOD code:
Disable openvdb-centric warnings within the openvdb platform.h file.
C6326: Potential comparison of a constant with another constant
and add annotations
C28251: Inconsistent annotation for 'copysign'
also added a warning suppress for static analysis CA_SUPPRESS(6011) within the proxylod version of the simplifier.
Change 3794786 by Lina.Halper
Pose asset retarget source bug fix
#jira: UE-52429
Change 3794841 by Danny.Bouimad
Hopefully fixes the cloth automation CIS
Change 3795191 by Lina.Halper
Fix build issue
Change 3795486 by Ethan.Geller
re-enable android support for Oculus Audio
Change 3796162 by Danny.Bouimad
Third attempt to fix the cloth CIS error. Hopefuly this will solve it.
Change 3796311 by Martin.Wilson
Remove recompress animation from curve track actions. Allows smoother interaction on animations with slow recompress time.
#jira UE-51740
Change 3796321 by Thomas.Sarkanen
Duplicating CL 3770752 from 4.18:
Prevent crash when generating convex bodies fails
Note: speculative fix as the issue cannot be reproduced locally
#jira UE-52449 - [CrashReport] FPhysicsAssetUtils::CreateFromSkeletalMeshInternal()
Change 3797093 by Danny.Bouimad
Constrant node AnimBP Automated tests
Change 3797384 by Danny.Bouimad
Fixing CIS error caused by automated test lighting issue
Change 3800621 by Thomas.Sarkanen
Fix CIS: Shadowed variable warning
#jira UE-53253 - FMenuBuilder declaration shadows a local variable warning appears when building the editor on Linux
Change 3800690 by Danny.Bouimad
Checking in fix for CIS automation for ConstraintNode, set the screenshot tool to use BaseColor. This should fix the issue with the rendering fuzzyness
Change 3800874 by David.Hill
Clean up static analysis warnings
#jira: UE-53270
Change 3801227 by David.Hill
Allow proxylod to fail gracefully if the input mesh is way too big (e.g. sky sphere)
Added code to automatically compute the correct spatial sampling rate based on the geometry size, also allow the user to override.
#cl: UE-53155
Change 3801228 by David.Hill
UI: Mesh Proxy Dialog re-write. Make this more like MeshMerging, and share some code.
#cl UE-53155, UE-52787, UE-53106
Change 3801319 by Danny.Bouimad
Regenerated all the screen shots for the constraint tests.
Change 3801383 by Ethan.Geller
#jira UE-53311 fix additional #if PLATFORM_WINDOWS guards in Oculus Audio
Change 3801697 by Ethan.Geller
include AudioDevice.h directly to resolve FAudioDevice.
Change 3802180 by David.Hill
This should fix the Incremental UE4Editor Linux build.
Change 3802643 by David.Hill
ProxyLOD UI change. Add limits to the target screen size. They now reflect the values in the old version of the UI and the thirdparty tool.
#CL: UE-53313
Change 3802986 by Ethan.Geller
#jira UE-53330 Change vraudio to explicit library path
Change 3803448 by Danny.Bouimad
disabling constraint tests
Change 3803678 by Danny.Bouimad
#jira UE-53306 Fix
Change 3804333 by Ethan.Geller
#jira UE-53330 fix library paths for iOS on Resonance
Change 3804453 by David.Hill
Fix Shadow warning when compiling UE4Editor on linux:
FlattenedMaterials.
#CL: UE-53349
Change 3804510 by Lina.Halper
CIS warning on shadow vars
#jira: UE-53348, UE-53345
Change 3805451 by Lina.Halper
Fix build issue : Renamed variable -
https://ec-01.epicgames.net/commander/link/jobStepDetails/jobSteps/74095846?stepName=Incremental%20UnrealHeaderTool%20Win64&jobId=8173688&jobName=UE4%20Dev-AnimPhys%20-%20CL%203805429%20-%20Incremental%20Editor%20Win64&tabGroup=diagnosticHeader
Change 3805470 by Lina.Halper
Fix build issue
Change 3806524 by Martin.Wilson
Only use previous frame end position if it is valid for this frame
#jira UE-53414
Change 3792620 by David.Hill
Copying //UE4/Dev-ProxyLOD to Dev-AnimPhys-Minimal (//UE4/Dev-AnimPhys-Minimal)
Adding the ProxyLOD code to AnimPhys.
Change 3796059 by Thomas.Sarkanen
Persona viewport settings are now per-asset editor
This prevnets bone following (etc) being shared by all Persona asset editors
FOV & view type is no longer chared by all Persona asset editors
#jira UE-53005 - Viewport settings like bone following are shared between all animation sub-editors
[CL 3806814 by Marc Audy in Main branch]
2504 lines
71 KiB
C++
2504 lines
71 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: Mesh.cpp
|
|
//
|
|
// Mesh processing helper class
|
|
//
|
|
// 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 WIN32_LEAN_AND_MEAN
|
|
#define NOMINMAX
|
|
|
|
#include "mesh.h"
|
|
|
|
#include <DirectXPackedVector.h>
|
|
#include <DirectXCollision.h>
|
|
|
|
using namespace DirectX;
|
|
|
|
namespace
|
|
{
|
|
struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } };
|
|
|
|
typedef public std::unique_ptr<void, handle_closer> ScopedHandle;
|
|
|
|
inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; }
|
|
|
|
template<typename T> inline HRESULT write_file(HANDLE hFile, const T& value)
|
|
{
|
|
DWORD bytesWritten;
|
|
if (!WriteFile(hFile, &value, static_cast<DWORD>(sizeof(T)), &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != sizeof(T))
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
inline HRESULT write_file_string(HANDLE hFile, const wchar_t* value)
|
|
{
|
|
UINT length = (value) ? static_cast<UINT>( wcslen(value)+1 ) : 1;
|
|
|
|
DWORD bytesWritten;
|
|
if (!WriteFile(hFile, &length, static_cast<DWORD>(sizeof(UINT)), &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != sizeof(UINT))
|
|
return E_FAIL;
|
|
|
|
if (length > 0)
|
|
{
|
|
DWORD bytes = static_cast<DWORD>(sizeof(wchar_t) * length);
|
|
|
|
if (!WriteFile(hFile, value, bytes, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytes)
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
wchar_t nul = 0;
|
|
if (!WriteFile(hFile, &nul, sizeof(wchar_t), &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != sizeof(wchar_t))
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
inline UINT64 roundup4k(UINT64 value)
|
|
{
|
|
return ((value + 4095) / 4096) * 4096;
|
|
}
|
|
|
|
static const uint8_t g_padding[4096] = { 0 };
|
|
}
|
|
|
|
// Move constructor
|
|
Mesh::Mesh(Mesh&& moveFrom)
|
|
{
|
|
*this = std::move(moveFrom);
|
|
}
|
|
|
|
// Move operator
|
|
Mesh& Mesh::operator= (Mesh&& moveFrom)
|
|
{
|
|
if (this != &moveFrom)
|
|
{
|
|
mnFaces = moveFrom.mnFaces;
|
|
mnVerts = moveFrom.mnVerts;
|
|
mIndices.swap( moveFrom.mIndices );
|
|
mAttributes.swap( moveFrom.mAttributes );
|
|
mAdjacency.swap( moveFrom.mAdjacency );
|
|
mPositions.swap( moveFrom.mPositions );
|
|
mNormals.swap( moveFrom.mNormals );
|
|
mTangents.swap( moveFrom.mTangents );
|
|
mBiTangents.swap( moveFrom.mBiTangents );
|
|
mTexCoords.swap( moveFrom.mTexCoords );
|
|
mColors.swap( moveFrom.mColors );
|
|
mBlendIndices.swap( moveFrom.mBlendIndices );
|
|
mBlendWeights.swap( moveFrom.mBlendWeights );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
void Mesh::Clear()
|
|
{
|
|
mnFaces = mnVerts = 0;
|
|
|
|
// Release face data
|
|
mIndices.reset();
|
|
mAttributes.reset();
|
|
mAdjacency.reset();
|
|
|
|
// Release vertex data
|
|
mPositions.reset();
|
|
mNormals.reset();
|
|
mTangents.reset();
|
|
mBiTangents.reset();
|
|
mTexCoords.reset();
|
|
mColors.reset();
|
|
mBlendIndices.reset();
|
|
mBlendWeights.reset();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::SetIndexData( size_t nFaces, const uint16_t* indices, uint32_t* attributes )
|
|
{
|
|
if ( !nFaces || !indices )
|
|
return E_INVALIDARG;
|
|
|
|
if ( ( uint64_t(nFaces) * 3 ) >= UINT32_MAX )
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
|
|
// Release face data
|
|
mnFaces = 0;
|
|
mIndices.reset();
|
|
mAttributes.reset();
|
|
|
|
std::unique_ptr<uint32_t[]> ib(new (std::nothrow) uint32_t[nFaces * 3]);
|
|
if (!ib)
|
|
return E_OUTOFMEMORY;
|
|
|
|
for( size_t j = 0; j < (nFaces*3); ++j )
|
|
{
|
|
if ( indices[ j ] == uint16_t(-1) )
|
|
{
|
|
ib[ j ] = uint32_t(-1);
|
|
}
|
|
else
|
|
{
|
|
ib[ j ] = indices[ j ];
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<uint32_t[]> attr;
|
|
if ( attributes )
|
|
{
|
|
attr.reset( new (std::nothrow) uint32_t[ nFaces ] );
|
|
if ( !attr )
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy( attr.get(), attributes, sizeof(uint32_t) * nFaces);
|
|
}
|
|
|
|
mIndices.swap(ib);
|
|
mAttributes.swap(attr);
|
|
mnFaces = nFaces;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::SetIndexData( size_t nFaces, const uint32_t* indices, uint32_t* attributes )
|
|
{
|
|
if ( !nFaces || !indices )
|
|
return E_INVALIDARG;
|
|
|
|
if ( ( uint64_t(nFaces) * 3 ) >= UINT32_MAX )
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
|
|
mnFaces = 0;
|
|
mIndices.reset();
|
|
mAttributes.reset();
|
|
|
|
std::unique_ptr<uint32_t[]> ib( new (std::nothrow) uint32_t[ nFaces * 3] );
|
|
if ( !ib )
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy( ib.get(), indices, sizeof(uint32_t) * nFaces * 3 );
|
|
|
|
std::unique_ptr<uint32_t[]> attr;
|
|
if ( attributes )
|
|
{
|
|
attr.reset( new (std::nothrow) uint32_t[ nFaces ] );
|
|
if ( !attr )
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy( attr.get(), attributes, sizeof(uint32_t) * nFaces );
|
|
}
|
|
|
|
mIndices.swap(ib);
|
|
mAttributes.swap(attr);
|
|
mnFaces = nFaces;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::SetVertexData( _Inout_ DirectX::VBReader& reader, _In_ size_t nVerts )
|
|
{
|
|
if ( !nVerts )
|
|
return E_INVALIDARG;
|
|
|
|
// Release vertex data
|
|
mnVerts = 0;
|
|
mPositions.reset();
|
|
mNormals.reset();
|
|
mTangents.reset();
|
|
mBiTangents.reset();
|
|
mTexCoords.reset();
|
|
mColors.reset();
|
|
mBlendIndices.reset();
|
|
mBlendWeights.reset();
|
|
|
|
// Load positions (required)
|
|
std::unique_ptr<XMFLOAT3[]> pos( new (std::nothrow) XMFLOAT3[ nVerts ] );
|
|
if (!pos)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = reader.Read(pos.get(), "SV_Position", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Load normals
|
|
std::unique_ptr<XMFLOAT3[]> norms;
|
|
auto e = reader.GetElement11("NORMAL", 0);
|
|
if (e)
|
|
{
|
|
norms.reset(new (std::nothrow) XMFLOAT3[nVerts]);
|
|
if (!norms)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(norms.get(), "NORMAL", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load tangents
|
|
std::unique_ptr<XMFLOAT4[]> tans1;
|
|
e = reader.GetElement11("TANGENT", 0);
|
|
if (e)
|
|
{
|
|
tans1.reset(new (std::nothrow) XMFLOAT4[nVerts]);
|
|
if (!tans1)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(tans1.get(), "TANGENT", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load bi-tangents
|
|
std::unique_ptr<XMFLOAT3[]> tans2;
|
|
e = reader.GetElement11("BINORMAL", 0);
|
|
if (e)
|
|
{
|
|
tans2.reset(new (std::nothrow) XMFLOAT3[nVerts]);
|
|
if (!tans2)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(tans2.get(), "BINORMAL", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load texture coordinates
|
|
std::unique_ptr<XMFLOAT2[]> texcoord;
|
|
e = reader.GetElement11("TEXCOORD", 0);
|
|
if (e)
|
|
{
|
|
texcoord.reset(new (std::nothrow) XMFLOAT2[nVerts]);
|
|
if (!texcoord)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(texcoord.get(), "TEXCOORD", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load vertex colors
|
|
std::unique_ptr<XMFLOAT4[]> colors;
|
|
e = reader.GetElement11("COLOR", 0);
|
|
if (e)
|
|
{
|
|
colors.reset(new (std::nothrow) XMFLOAT4[nVerts]);
|
|
if (!colors)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(colors.get(), "COLOR", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load skinning bone indices
|
|
std::unique_ptr<XMFLOAT4[]> blendIndices;
|
|
e = reader.GetElement11("BLENDINDICES", 0);
|
|
if (e)
|
|
{
|
|
blendIndices.reset(new (std::nothrow) XMFLOAT4[nVerts]);
|
|
if (!blendIndices)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(blendIndices.get(), "BLENDINDICES", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Load skinning bone weights
|
|
std::unique_ptr<XMFLOAT4[]> blendWeights;
|
|
e = reader.GetElement11("BLENDWEIGHT", 0);
|
|
if (e)
|
|
{
|
|
blendWeights.reset(new (std::nothrow) XMFLOAT4[nVerts]);
|
|
if (!blendWeights)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = reader.Read(blendWeights.get(), "BLENDWEIGHT", 0, nVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Return values
|
|
mPositions.swap( pos );
|
|
mNormals.swap( norms );
|
|
mTangents.swap( tans1 );
|
|
mBiTangents.swap( tans2 );
|
|
mTexCoords.swap( texcoord );
|
|
mColors.swap( colors );
|
|
mBlendIndices.swap( blendIndices );
|
|
mBlendWeights.swap( blendWeights );
|
|
mnVerts = nVerts;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::Validate(DWORD flags, std::wstring* msgs) const
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts)
|
|
return E_UNEXPECTED;
|
|
|
|
return DirectX::Validate(mIndices.get(), mnFaces, mnVerts, mAdjacency.get(), flags, msgs);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::Clean()
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
std::vector<uint32_t> dups;
|
|
HRESULT hr = DirectX::Clean(mIndices.get(), mnFaces, mnVerts, mAdjacency.get(), mAttributes.get(), dups);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (dups.empty())
|
|
{
|
|
// No vertex duplication is needed for mesh clean
|
|
return S_OK;
|
|
}
|
|
|
|
size_t nNewVerts = mnVerts + dups.size();
|
|
|
|
std::unique_ptr<XMFLOAT3[]> pos(new (std::nothrow) XMFLOAT3[nNewVerts]);
|
|
if (!pos)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(pos.get(), mPositions.get(), sizeof(XMFLOAT3) * mnVerts);
|
|
|
|
std::unique_ptr<XMFLOAT3[]> norms;
|
|
if (mNormals)
|
|
{
|
|
norms.reset(new (std::nothrow) XMFLOAT3[nNewVerts]);
|
|
if (!norms)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(norms.get(), mNormals.get(), sizeof(XMFLOAT3) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT4[]> tans1;
|
|
if (mTangents)
|
|
{
|
|
tans1.reset(new (std::nothrow) XMFLOAT4[nNewVerts]);
|
|
if (!tans1)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(tans1.get(), mTangents.get(), sizeof(XMFLOAT4) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT3[]> tans2;
|
|
if (mBiTangents)
|
|
{
|
|
tans2.reset(new (std::nothrow) XMFLOAT3[nNewVerts]);
|
|
if (!tans2)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(tans2.get(), mBiTangents.get(), sizeof(XMFLOAT3) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT2[]> texcoord;
|
|
if (mTexCoords)
|
|
{
|
|
texcoord.reset(new (std::nothrow) XMFLOAT2[nNewVerts]);
|
|
if (!texcoord)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(texcoord.get(), mTexCoords.get(), sizeof(XMFLOAT2) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT4[]> colors;
|
|
if (mColors)
|
|
{
|
|
colors.reset(new (std::nothrow) XMFLOAT4[nNewVerts]);
|
|
if (!colors)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(colors.get(), mColors.get(), sizeof(XMFLOAT4) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT4[]> blendIndices;
|
|
if (mBlendIndices)
|
|
{
|
|
blendIndices.reset(new (std::nothrow) XMFLOAT4[nNewVerts]);
|
|
if (!blendIndices)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(blendIndices.get(), mBlendIndices.get(), sizeof(XMFLOAT4) * mnVerts);
|
|
}
|
|
|
|
std::unique_ptr<XMFLOAT4 []> blendWeights;
|
|
if (mBlendWeights)
|
|
{
|
|
blendWeights.reset(new (std::nothrow) XMFLOAT4[nNewVerts]);
|
|
if (!blendWeights)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(blendWeights.get(), mBlendWeights.get(), sizeof(XMFLOAT4) * mnVerts);
|
|
}
|
|
|
|
size_t j = mnVerts;
|
|
for (auto it = dups.begin(); it != dups.end() && (j < nNewVerts); ++it, ++j)
|
|
{
|
|
assert(*it < mnVerts);
|
|
|
|
pos[ j ] = mPositions[ *it ];
|
|
|
|
if (norms)
|
|
{
|
|
norms[ j ] = mNormals[ *it ];
|
|
}
|
|
|
|
if (tans1)
|
|
{
|
|
tans1[ j ] = mTangents[ *it ];
|
|
}
|
|
|
|
if (tans2)
|
|
{
|
|
tans2[ j ] = mBiTangents[*it];
|
|
}
|
|
|
|
if (texcoord)
|
|
{
|
|
texcoord.get() [ j] = mTexCoords[*it];
|
|
}
|
|
|
|
if (colors)
|
|
{
|
|
colors[j] = mColors[*it];
|
|
}
|
|
|
|
if (blendIndices)
|
|
{
|
|
blendIndices[j] = mBlendIndices[*it];
|
|
}
|
|
|
|
if (blendWeights)
|
|
{
|
|
blendWeights[j] = mBlendWeights[*it];
|
|
}
|
|
}
|
|
|
|
mPositions.swap(pos);
|
|
mNormals.swap(norms);
|
|
mTangents.swap(tans1);
|
|
mBiTangents.swap(tans2);
|
|
mTexCoords.swap(texcoord);
|
|
mColors.swap(colors);
|
|
mBlendIndices.swap(blendIndices);
|
|
mBlendWeights.swap(blendWeights);
|
|
mnVerts = nNewVerts;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::GenerateAdjacency( _In_ float epsilon )
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
|
|
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
|
|
mAdjacency.reset( new (std::nothrow) uint32_t[ mnFaces * 3 ] );
|
|
if ( !mAdjacency )
|
|
return E_OUTOFMEMORY;
|
|
|
|
return DirectX::GenerateAdjacencyAndPointReps(mIndices.get(), mnFaces, mPositions.get(), mnVerts, epsilon, nullptr, mAdjacency.get());
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::ComputeNormals( _In_ DWORD flags )
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
mNormals.reset( new (std::nothrow) XMFLOAT3[ mnVerts ] );
|
|
if (!mNormals)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return DirectX::ComputeNormals(mIndices.get(), mnFaces, mPositions.get(), mnVerts, flags, mNormals.get());
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::ComputeTangentFrame( _In_ bool bitangents )
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions || !mNormals || !mTexCoords)
|
|
return E_UNEXPECTED;
|
|
|
|
mTangents.reset();
|
|
mBiTangents.reset();
|
|
|
|
std::unique_ptr<XMFLOAT4[]> tan1( new (std::nothrow) XMFLOAT4[mnVerts] );
|
|
if (!tan1)
|
|
return E_OUTOFMEMORY;
|
|
|
|
std::unique_ptr<XMFLOAT3[]> tan2;
|
|
if (bitangents)
|
|
{
|
|
tan2.reset( new (std::nothrow) XMFLOAT3[mnVerts] );
|
|
if (!tan2)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = DirectX::ComputeTangentFrame(mIndices.get(), mnFaces, mPositions.get(), mNormals.get(), mTexCoords.get(), mnVerts,
|
|
tan1.get(), tan2.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
mBiTangents.reset();
|
|
|
|
HRESULT hr = DirectX::ComputeTangentFrame(mIndices.get(), mnFaces, mPositions.get(), mNormals.get(), mTexCoords.get(), mnVerts,
|
|
tan1.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
mTangents.swap( tan1 );
|
|
mBiTangents.swap( tan2 );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::Optimize( uint32_t vertexCache, uint32_t restart )
|
|
{
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions || !mAdjacency)
|
|
return E_UNEXPECTED;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
{
|
|
std::unique_ptr<uint32_t> remap(new (std::nothrow) uint32_t[mnFaces]);
|
|
if (!remap)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (mAttributes)
|
|
{
|
|
// Note that Clean handles vertex splits due to reuse between attributes
|
|
|
|
hr = AttributeSort(mnFaces, mAttributes.get(), remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = ReorderIBAndAdjacency(mIndices.get(), mnFaces, mAdjacency.get(), remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Optimize faces for pre-transform vertex cache
|
|
hr = OptimizeFacesEx(mIndices.get(), mnFaces, mAdjacency.get(), mAttributes.get(), remap.get(), vertexCache, restart);
|
|
}
|
|
else
|
|
{
|
|
// Optimize faces for pre-transform vertex cache
|
|
hr = OptimizeFaces(mIndices.get(), mnFaces, mAdjacency.get(), remap.get(), vertexCache, restart);
|
|
}
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = ReorderIBAndAdjacency(mIndices.get(), mnFaces, mAdjacency.get(), remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Optimize vertices for post-transform vertex cache
|
|
std::unique_ptr<uint32_t> remap(new (std::nothrow) uint32_t[mnVerts]);
|
|
if (!remap)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = OptimizeVertices(mIndices.get(), mnFaces, mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = FinalizeIB(mIndices.get(), mnFaces, remap.get(), mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = FinalizeVB(mPositions.get(), sizeof(XMFLOAT3), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (mNormals)
|
|
{
|
|
hr = FinalizeVB(mNormals.get(), sizeof(XMFLOAT3), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (mTangents)
|
|
{
|
|
hr = FinalizeVB(mTangents.get(), sizeof(XMFLOAT4), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (mBiTangents)
|
|
{
|
|
hr = FinalizeVB(mBiTangents.get(), sizeof(XMFLOAT3), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (mTexCoords)
|
|
{
|
|
hr = FinalizeVB(mTexCoords.get(), sizeof(XMFLOAT2), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (mColors)
|
|
{
|
|
hr = FinalizeVB(mColors.get(), sizeof(XMFLOAT4), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (mBlendIndices)
|
|
{
|
|
hr = FinalizeVB(mBlendIndices.get(), sizeof(XMFLOAT4), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
}
|
|
|
|
if (mBlendWeights)
|
|
{
|
|
hr = FinalizeVB(mBlendWeights.get(), sizeof(XMFLOAT4), mnVerts, remap.get());
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::ReverseWinding()
|
|
{
|
|
if (!mIndices || !mnFaces)
|
|
return E_UNEXPECTED;
|
|
|
|
auto iptr = mIndices.get();
|
|
for (size_t j = 0; j < mnFaces; ++j)
|
|
{
|
|
std::swap( *iptr, *(iptr + 2) );
|
|
iptr += 3;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::InvertUTexCoord()
|
|
{
|
|
if (!mTexCoords)
|
|
return E_UNEXPECTED;
|
|
|
|
auto tptr = mTexCoords.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++tptr)
|
|
{
|
|
tptr->x = 1.f - tptr->x;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::InvertVTexCoord()
|
|
{
|
|
if (!mTexCoords)
|
|
return E_UNEXPECTED;
|
|
|
|
auto tptr = mTexCoords.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++tptr)
|
|
{
|
|
tptr->y = 1.f - tptr->y;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::ReverseHandedness()
|
|
{
|
|
if (!mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
auto ptr = mPositions.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++ptr)
|
|
{
|
|
ptr->z = -ptr->z;
|
|
}
|
|
|
|
if (mNormals)
|
|
{
|
|
auto nptr = mNormals.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++nptr)
|
|
{
|
|
nptr->z = -nptr->z;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
bool Mesh::Is16BitIndexBuffer() const
|
|
{
|
|
if (!mIndices || !mnFaces)
|
|
return false;
|
|
|
|
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
|
|
return false;
|
|
|
|
const uint32_t* iptr = mIndices.get();
|
|
for (size_t j = 0; j < (mnFaces * 3); ++j )
|
|
{
|
|
uint32_t index = *(iptr++);
|
|
if (index != uint32_t(-1)
|
|
&& (index >= UINT16_MAX))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
std::unique_ptr<uint16_t[]> Mesh::GetIndexBuffer16() const
|
|
{
|
|
std::unique_ptr<uint16_t[]> ib;
|
|
|
|
if (!mIndices || !mnFaces)
|
|
return ib;
|
|
|
|
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
|
|
return ib;
|
|
|
|
size_t count = mnFaces * 3;
|
|
|
|
ib.reset(new (std::nothrow) uint16_t[count]);
|
|
if (!ib)
|
|
return ib;
|
|
|
|
const uint32_t* iptr = mIndices.get();
|
|
for (size_t j = 0; j < count; ++j)
|
|
{
|
|
uint32_t index = *(iptr++);
|
|
if (index == uint32_t(-1))
|
|
{
|
|
ib[j] = uint16_t(-1);
|
|
}
|
|
else if (index >= UINT16_MAX)
|
|
{
|
|
ib.reset();
|
|
return ib;
|
|
}
|
|
else
|
|
{
|
|
ib[j] = static_cast<uint16_t>(index);
|
|
}
|
|
}
|
|
|
|
return ib;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
HRESULT Mesh::GetVertexBuffer(_Inout_ DirectX::VBWriter& writer) const
|
|
{
|
|
if (!mnVerts || !mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
HRESULT hr = writer.Write(mPositions.get(), "SV_Position", 0, mnVerts );
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (mNormals)
|
|
{
|
|
auto e = writer.GetElement11("NORMAL", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mNormals.get(), "NORMAL", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mTangents)
|
|
{
|
|
auto e = writer.GetElement11("TANGENT", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mTangents.get(), "TANGENT", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mBiTangents)
|
|
{
|
|
auto e = writer.GetElement11("BINORMAL", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mBiTangents.get(), "BINORMAL", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mTexCoords)
|
|
{
|
|
auto e = writer.GetElement11("TEXCOORD", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mTexCoords.get(), "TEXCOORD", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mColors)
|
|
{
|
|
auto e = writer.GetElement11("COLOR", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mColors.get(), "COLOR", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mBlendIndices)
|
|
{
|
|
auto e = writer.GetElement11("BLENDINDICES", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mBlendIndices.get(), "BLENDINDICES", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (mBlendWeights)
|
|
{
|
|
auto e = writer.GetElement11("BLENDWEIGHT", 0);
|
|
if (e)
|
|
{
|
|
hr = writer.Write(mBlendWeights.get(), "BLENDWEIGHT", 0, mnVerts);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//======================================================================================
|
|
// VBO
|
|
//======================================================================================
|
|
|
|
namespace VBO
|
|
{
|
|
|
|
#pragma pack(push,1)
|
|
|
|
struct header_t
|
|
{
|
|
uint32_t numVertices;
|
|
uint32_t numIndices;
|
|
};
|
|
|
|
struct vertex_t
|
|
{
|
|
DirectX::XMFLOAT3 position;
|
|
DirectX::XMFLOAT3 normal;
|
|
DirectX::XMFLOAT2 textureCoordinate;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
static_assert(sizeof(header_t) == 8, "VBO header size mismatch");
|
|
static_assert(sizeof(vertex_t) == 32, "VBO vertex size mismatch");
|
|
}; // namespace
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::ExportToVBO( const wchar_t* szFileName ) const
|
|
{
|
|
using namespace VBO;
|
|
|
|
if ( !szFileName )
|
|
return E_INVALIDARG;
|
|
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions || !mNormals || !mTexCoords)
|
|
return E_UNEXPECTED;
|
|
|
|
if ( ( uint64_t(mnFaces) * 3 ) >= UINT32_MAX )
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
|
|
if ( mnVerts >= UINT16_MAX )
|
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
|
|
|
// Setup VBO header
|
|
header_t header;
|
|
header.numVertices = static_cast<uint32_t>( mnVerts );
|
|
header.numIndices = static_cast<uint32_t>( mnFaces*3 );
|
|
|
|
// Setup vertices/indices for VBO
|
|
|
|
std::unique_ptr<vertex_t[]> vb(new (std::nothrow) vertex_t[mnVerts]);
|
|
std::unique_ptr<uint16_t[]> ib(new (std::nothrow) uint16_t[header.numIndices]);
|
|
if (!vb || !ib)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Copy to VB
|
|
auto vptr = vb.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++vptr)
|
|
{
|
|
vptr->position = mPositions[j];
|
|
vptr->normal = mNormals[j];
|
|
vptr->textureCoordinate = mTexCoords[j];
|
|
}
|
|
|
|
// Copy to IB
|
|
auto iptr = ib.get();
|
|
for (size_t j = 0; j < header.numIndices; ++j, ++iptr)
|
|
{
|
|
uint32_t index = mIndices[j];
|
|
if (index == uint32_t(-1))
|
|
{
|
|
*iptr = uint16_t(-1);
|
|
}
|
|
else if (index >= UINT16_MAX)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
else
|
|
{
|
|
*iptr = static_cast<uint16_t>(index);
|
|
}
|
|
}
|
|
|
|
// Write header and data
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFileName, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
|
|
#else
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
|
|
#endif
|
|
if (!hFile)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
HRESULT hr = write_file( hFile.get(), header );
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
DWORD vertSize = static_cast<DWORD>( sizeof(vertex_t) * header.numVertices );
|
|
|
|
DWORD bytesWritten;
|
|
if (!WriteFile(hFile.get(), vb.get(), vertSize, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != vertSize)
|
|
return E_FAIL;
|
|
|
|
DWORD indexSize = static_cast<DWORD>( sizeof(uint16_t) * header.numIndices );
|
|
|
|
if (!WriteFile(hFile.get(), ib.get(), indexSize, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != indexSize)
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::CreateFromVBO( const wchar_t* szFileName, std::unique_ptr<Mesh>& result )
|
|
{
|
|
using namespace VBO;
|
|
|
|
if (!szFileName)
|
|
return E_INVALIDARG;
|
|
|
|
result.reset();
|
|
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
|
|
#else
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)));
|
|
#endif
|
|
if (!hFile)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// Get the file size
|
|
FILE_STANDARD_INFO fileInfo;
|
|
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// File is too big for 32-bit allocation, so reject read
|
|
if (fileInfo.EndOfFile.HighPart > 0)
|
|
return E_FAIL;
|
|
|
|
// Need at least enough data to read the header
|
|
if (fileInfo.EndOfFile.LowPart < sizeof(header_t))
|
|
return E_FAIL;
|
|
|
|
// Read VBO header
|
|
DWORD bytesRead = 0;
|
|
|
|
header_t header;
|
|
if (!ReadFile(hFile.get(), &header, sizeof(header_t), &bytesRead, nullptr))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (bytesRead != sizeof(header))
|
|
return E_FAIL;
|
|
|
|
if (!header.numVertices || !header.numIndices)
|
|
return E_FAIL;
|
|
|
|
result.reset( new (std::nothrow) Mesh );
|
|
if (!result)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Read vertices/indices from VBO
|
|
std::unique_ptr<vertex_t[]> vb(new (std::nothrow) vertex_t[header.numVertices]);
|
|
std::unique_ptr<uint16_t[]> ib( new (std::nothrow) uint16_t[ header.numIndices ] );
|
|
if (!vb || !ib)
|
|
return E_OUTOFMEMORY;
|
|
|
|
DWORD vertSize = static_cast<DWORD>( sizeof(vertex_t) * header.numVertices );
|
|
|
|
if (!ReadFile(hFile.get(), vb.get(), vertSize, &bytesRead, nullptr))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (bytesRead != vertSize)
|
|
return E_FAIL;
|
|
|
|
DWORD indexSize = static_cast<DWORD>(sizeof(uint16_t) * header.numIndices );
|
|
|
|
if (!ReadFile(hFile.get(), ib.get(), indexSize, &bytesRead, nullptr))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (bytesRead != indexSize)
|
|
return E_FAIL;
|
|
|
|
// Copy VB to result
|
|
std::unique_ptr<XMFLOAT3[]> pos(new (std::nothrow) XMFLOAT3[header.numVertices]);
|
|
std::unique_ptr<XMFLOAT3[]> norm(new (std::nothrow) XMFLOAT3[header.numVertices]);
|
|
std::unique_ptr<XMFLOAT2[]> texcoord(new (std::nothrow) XMFLOAT2[header.numVertices]);
|
|
if (!pos || !norm || !texcoord)
|
|
return E_OUTOFMEMORY;
|
|
|
|
auto vptr = vb.get();
|
|
for (size_t j = 0; j < header.numVertices; ++j, ++vptr)
|
|
{
|
|
pos[ j ] = vptr->position;
|
|
norm[ j ] = vptr->normal;
|
|
texcoord[ j] = vptr->textureCoordinate;
|
|
}
|
|
|
|
// Copy IB to result
|
|
std::unique_ptr<uint32_t[]> indices(new (std::nothrow) uint32_t[header.numIndices]);
|
|
if (!indices)
|
|
return E_OUTOFMEMORY;
|
|
|
|
auto iptr = ib.get();
|
|
for (size_t j = 0; j < header.numIndices; ++j, ++iptr)
|
|
{
|
|
uint16_t index = *iptr;
|
|
if (index == uint16_t(-1))
|
|
indices[ j ] = uint32_t(-1);
|
|
else
|
|
indices[ j ] = index;
|
|
}
|
|
|
|
result->mPositions.swap(pos);
|
|
result->mNormals.swap(norm);
|
|
result->mTexCoords.swap(texcoord);
|
|
result->mIndices.swap(indices);
|
|
result->mnVerts = header.numVertices;
|
|
result->mnFaces = header.numIndices / 3;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//======================================================================================
|
|
// Visual Studio CMO
|
|
//======================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// .CMO files are built by Visual Studio 2012 and an example renderer is provided
|
|
// in the VS Direct3D Starter Kit
|
|
// http://code.msdn.microsoft.com/Visual-Studio-3D-Starter-455a15f1
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
namespace VSD3DStarter
|
|
{
|
|
// .CMO files
|
|
|
|
// UINT - Mesh count
|
|
// { [Mesh count]
|
|
// UINT - Length of name
|
|
// wchar_t[] - Name of mesh (if length > 0)
|
|
// UINT - Material count
|
|
// { [Material count]
|
|
// UINT - Length of material name
|
|
// wchar_t[] - Name of material (if length > 0)
|
|
// Material structure
|
|
// UINT - Length of pixel shader name
|
|
// wchar_t[] - Name of pixel shader (if length > 0)
|
|
// { [8]
|
|
// UINT - Length of texture name
|
|
// wchar_t[] - Name of texture (if length > 0)
|
|
// }
|
|
// }
|
|
// BYTE - 1 if there is skeletal animation data present
|
|
// UINT - SubMesh count
|
|
// { [SubMesh count]
|
|
// SubMesh structure
|
|
// }
|
|
// UINT - IB Count
|
|
// { [IB Count]
|
|
// UINT - Number of USHORTs in IB
|
|
// USHORT[] - Array of indices
|
|
// }
|
|
// UINT - VB Count
|
|
// { [VB Count]
|
|
// UINT - Number of verts in VB
|
|
// Vertex[] - Array of vertices
|
|
// }
|
|
// UINT - Skinning VB Count
|
|
// { [Skinning VB Count]
|
|
// UINT - Number of verts in Skinning VB
|
|
// SkinningVertex[] - Array of skinning verts
|
|
// }
|
|
// MeshExtents structure
|
|
// [If skeleton animation data is not present, file ends here]
|
|
// UINT - Bone count
|
|
// { [Bone count]
|
|
// UINT - Length of bone name
|
|
// wchar_t[] - Bone name (if length > 0)
|
|
// Bone structure
|
|
// }
|
|
// UINT - Animation clip count
|
|
// { [Animation clip count]
|
|
// UINT - Length of clip name
|
|
// wchar_t[] - Clip name (if length > 0)
|
|
// float - Start time
|
|
// float - End time
|
|
// UINT - Keyframe count
|
|
// { [Keyframe count]
|
|
// Keyframe structure
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
#pragma pack(push,1)
|
|
|
|
struct Material
|
|
{
|
|
DirectX::XMFLOAT4 Ambient;
|
|
DirectX::XMFLOAT4 Diffuse;
|
|
DirectX::XMFLOAT4 Specular;
|
|
float SpecularPower;
|
|
DirectX::XMFLOAT4 Emissive;
|
|
DirectX::XMFLOAT4X4 UVTransform;
|
|
};
|
|
|
|
const uint32_t MAX_TEXTURE = 8;
|
|
|
|
struct SubMesh
|
|
{
|
|
UINT MaterialIndex;
|
|
UINT IndexBufferIndex;
|
|
UINT VertexBufferIndex;
|
|
UINT StartIndex;
|
|
UINT PrimCount;
|
|
};
|
|
|
|
const uint32_t NUM_BONE_INFLUENCES = 4;
|
|
|
|
struct Vertex
|
|
{
|
|
DirectX::XMFLOAT3 Position;
|
|
DirectX::XMFLOAT3 Normal;
|
|
DirectX::XMFLOAT4 Tangent;
|
|
UINT color;
|
|
DirectX::XMFLOAT2 TextureCoordinates;
|
|
};
|
|
|
|
struct SkinningVertex
|
|
{
|
|
UINT boneIndex[NUM_BONE_INFLUENCES];
|
|
float boneWeight[NUM_BONE_INFLUENCES];
|
|
};
|
|
|
|
struct MeshExtents
|
|
{
|
|
float CenterX, CenterY, CenterZ;
|
|
float Radius;
|
|
|
|
float MinX, MinY, MinZ;
|
|
float MaxX, MaxY, MaxZ;
|
|
};
|
|
|
|
struct Bone
|
|
{
|
|
INT ParentIndex;
|
|
DirectX::XMFLOAT4X4 InvBindPos;
|
|
DirectX::XMFLOAT4X4 BindPos;
|
|
DirectX::XMFLOAT4X4 LocalTransform;
|
|
};
|
|
|
|
struct Clip
|
|
{
|
|
float StartTime;
|
|
float EndTime;
|
|
UINT keys;
|
|
};
|
|
|
|
struct Keyframe
|
|
{
|
|
UINT BoneIndex;
|
|
float Time;
|
|
DirectX::XMFLOAT4X4 Transform;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
}; // namespace
|
|
|
|
static_assert(sizeof(VSD3DStarter::Material) == 132, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::SubMesh) == 20, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::Vertex) == 52, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::SkinningVertex) == 32, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::MeshExtents) == 40, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::Bone) == 196, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::Clip) == 12, "CMO Mesh structure size incorrect");
|
|
static_assert(sizeof(VSD3DStarter::Keyframe) == 72, "CMO Mesh structure size incorrect");
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::ExportToCMO(const wchar_t* szFileName, size_t nMaterials, const Material* materials) const
|
|
{
|
|
using namespace VSD3DStarter;
|
|
|
|
if (!szFileName)
|
|
return E_INVALIDARG;
|
|
|
|
if (nMaterials > 0 && !materials)
|
|
return E_INVALIDARG;
|
|
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions || !mNormals || !mTexCoords || !mTangents)
|
|
return E_UNEXPECTED;
|
|
|
|
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
|
|
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
|
|
if (mnVerts >= UINT16_MAX)
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
|
|
UINT nIndices = static_cast<UINT>( mnFaces * 3 );
|
|
|
|
// Setup vertices/indices for CMO
|
|
std::unique_ptr<Vertex []> vb(new (std::nothrow) Vertex[mnVerts]);
|
|
std::unique_ptr<uint16_t []> ib(new (std::nothrow) uint16_t[nIndices]);
|
|
if (!vb || !ib)
|
|
return E_OUTOFMEMORY;
|
|
|
|
std::unique_ptr<SkinningVertex []> vbSkin;
|
|
if (mBlendIndices && mBlendWeights)
|
|
{
|
|
vbSkin.reset(new (std::nothrow) SkinningVertex[mnVerts]);
|
|
if (!vbSkin)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Copy to VB
|
|
auto vptr = vb.get();
|
|
for (size_t j = 0; j < mnVerts; ++j, ++vptr)
|
|
{
|
|
vptr->Position = mPositions[j];
|
|
vptr->Normal = mNormals[j];
|
|
vptr->Tangent = mTangents[j];
|
|
vptr->TextureCoordinates = mTexCoords[j];
|
|
|
|
if (mColors)
|
|
{
|
|
XMVECTOR icolor = XMLoadFloat4(&mColors[j]);
|
|
PackedVector::XMUBYTEN4 rgba;
|
|
PackedVector::XMStoreUByteN4(&rgba, icolor);
|
|
vptr->color = rgba.v;
|
|
}
|
|
else
|
|
vptr->color = 0xFFFFFFFF;
|
|
}
|
|
|
|
// Copy to SkinVB
|
|
auto sptr = vbSkin.get();
|
|
if ( sptr )
|
|
{
|
|
for (size_t j = 0; j < mnVerts; ++j, ++sptr)
|
|
{
|
|
XMVECTOR v = XMLoadFloat4(&mBlendIndices[j]);
|
|
XMStoreUInt4( reinterpret_cast<XMUINT4*>( &sptr->boneIndex[0] ), v);
|
|
|
|
const XMFLOAT4* w = &mBlendWeights[j];
|
|
sptr->boneWeight[0] = w->x;
|
|
sptr->boneWeight[1] = w->y;
|
|
sptr->boneWeight[2] = w->z;
|
|
sptr->boneWeight[3] = w->w;
|
|
}
|
|
}
|
|
|
|
// Copy to IB
|
|
auto iptr = ib.get();
|
|
for (size_t j = 0; j < nIndices; ++j, ++iptr)
|
|
{
|
|
uint32_t index = mIndices[j];
|
|
if (index == uint32_t(-1))
|
|
{
|
|
*iptr = uint16_t(-1);
|
|
}
|
|
else if (index >= UINT16_MAX)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
else
|
|
{
|
|
*iptr = static_cast<uint16_t>(index);
|
|
}
|
|
}
|
|
|
|
// Create CMO file
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFileName, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
|
|
#else
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
|
|
#endif
|
|
if (!hFile)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
// Write 1 mesh, name based on the filename
|
|
UINT n = 1;
|
|
HRESULT hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
{
|
|
wchar_t fname[_MAX_FNAME];
|
|
_wsplitpath_s(szFileName, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0);
|
|
|
|
hr = write_file_string(hFile.get(), fname);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Write materials
|
|
static const Mesh::Material s_defMaterial = { L"default", false, 1.f, 1.f,
|
|
XMFLOAT3(0.2f, 0.2f, 0.2f), XMFLOAT3(0.8f, 0.8f, 0.8f),
|
|
XMFLOAT3(0.f, 0.f, 0.f), XMFLOAT3(0.f, 0.f, 0.f), L"" };
|
|
|
|
UINT materialCount = 1;
|
|
if (nMaterials > 0)
|
|
{
|
|
materialCount = static_cast<UINT>(nMaterials);
|
|
}
|
|
else
|
|
{
|
|
nMaterials = 1;
|
|
materials = &s_defMaterial;
|
|
}
|
|
|
|
hr = write_file(hFile.get(), materialCount);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
for (UINT j = 0; j < materialCount; ++j)
|
|
{
|
|
auto& m = materials[j];
|
|
|
|
if ( !m.name.empty() )
|
|
{
|
|
hr = write_file_string(hFile.get(), m.name.c_str() );
|
|
}
|
|
else
|
|
{
|
|
wchar_t name[64];
|
|
swprintf_s(name, L"material%03u\n", j);
|
|
hr = write_file_string(hFile.get(), name);
|
|
}
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
VSD3DStarter::Material mdata;
|
|
memset(&mdata, 0, sizeof(mdata));
|
|
|
|
mdata.Ambient.x = m.ambientColor.x;
|
|
mdata.Ambient.y = m.ambientColor.y;
|
|
mdata.Ambient.z = m.ambientColor.z;
|
|
mdata.Ambient.w = 1.f;
|
|
|
|
mdata.Diffuse.x = m.diffuseColor.x;
|
|
mdata.Diffuse.y = m.diffuseColor.y;
|
|
mdata.Diffuse.z = m.diffuseColor.z;
|
|
mdata.Diffuse.w = m.alpha;
|
|
|
|
if (m.specularColor.x > 0.f || m.specularColor.y > 0.f || m.specularColor.z > 0.f)
|
|
{
|
|
mdata.Specular.x = m.specularColor.x;
|
|
mdata.Specular.y = m.specularColor.y;
|
|
mdata.Specular.z = m.specularColor.z;
|
|
mdata.SpecularPower = ( m.specularPower <= 0.f ) ? 16.f : m.specularPower;
|
|
}
|
|
else
|
|
{
|
|
mdata.SpecularPower = 1.f;
|
|
}
|
|
mdata.Specular.w = 1.f;
|
|
|
|
mdata.Emissive.x = m.emissiveColor.x;
|
|
mdata.Emissive.y = m.emissiveColor.y;
|
|
mdata.Emissive.z = m.emissiveColor.z;
|
|
mdata.Emissive.w = 1.f;
|
|
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
XMStoreFloat4x4(&mdata.UVTransform, id);
|
|
|
|
hr = write_file(hFile.get(), mdata);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (m.specularColor.x > 0.f || m.specularColor.y > 0.f || m.specularColor.z > 0.f)
|
|
{
|
|
hr = write_file_string(hFile.get(), L"phong.dgsl");
|
|
}
|
|
else
|
|
{
|
|
hr = write_file_string(hFile.get(), L"lambert.dgsl");
|
|
}
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = write_file_string(hFile.get(), m.texture.c_str() );
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
for (size_t k = 1; k < MAX_TEXTURE; ++k)
|
|
{
|
|
hr = write_file_string(hFile.get(), L"" );
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
BYTE sd = 0; // No skeleton/animation data
|
|
hr = write_file(hFile.get(), sd);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (mAttributes)
|
|
{
|
|
auto subsets = ComputeSubsets(mAttributes.get(), mnFaces);
|
|
|
|
n = static_cast<UINT>(subsets.size());
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
size_t startIndex = 0;
|
|
for (auto it = subsets.cbegin(); it != subsets.end(); ++it)
|
|
{
|
|
SubMesh smesh;
|
|
smesh.MaterialIndex = mAttributes[it->first];
|
|
if (smesh.MaterialIndex >= nMaterials)
|
|
smesh.MaterialIndex = 0;
|
|
|
|
smesh.IndexBufferIndex = 0;
|
|
smesh.VertexBufferIndex = 0;
|
|
smesh.StartIndex = static_cast<UINT>( startIndex );
|
|
smesh.PrimCount = static_cast<UINT>( it->second );
|
|
hr = write_file(hFile.get(), smesh);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if ((startIndex + (it->second * 3)) > mnFaces * 3)
|
|
return E_FAIL;
|
|
|
|
startIndex += smesh.PrimCount * 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n = 1;
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
SubMesh smesh;
|
|
smesh.MaterialIndex = 0;
|
|
smesh.IndexBufferIndex = 0;
|
|
smesh.VertexBufferIndex = 0;
|
|
smesh.StartIndex = 0;
|
|
smesh.PrimCount = static_cast<UINT>(mnFaces);
|
|
|
|
hr = write_file(hFile.get(), smesh);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Write indices (one IB shared across submeshes)
|
|
n = 1;
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = write_file(hFile.get(), nIndices);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
DWORD indexSize = static_cast<DWORD>(sizeof(uint16_t) * nIndices);
|
|
|
|
DWORD bytesWritten;
|
|
if (!WriteFile(hFile.get(), ib.get(), indexSize, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != indexSize)
|
|
return E_FAIL;
|
|
|
|
// Write vertices (one VB shared across submeshes)
|
|
n = 1;
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
n = static_cast<UINT>( mnVerts );
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
DWORD vertSize = static_cast<DWORD>(sizeof(Vertex) * mnVerts);
|
|
|
|
if (!WriteFile(hFile.get(), vb.get(), vertSize, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != vertSize)
|
|
return E_FAIL;
|
|
|
|
// Write skinning vertices (one SkinVB shared across submeshes)
|
|
if ( vbSkin )
|
|
{
|
|
n = 1;
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
n = static_cast<UINT>(mnVerts);
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
DWORD skinVertSize = static_cast<DWORD>(sizeof(SkinningVertex) * mnVerts);
|
|
|
|
if (!WriteFile(hFile.get(), vbSkin.get(), skinVertSize, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != skinVertSize)
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
n = 0;
|
|
hr = write_file(hFile.get(), n);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Write extents
|
|
{
|
|
BoundingSphere sphere;
|
|
BoundingSphere::CreateFromPoints(sphere, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
|
|
|
|
BoundingBox box;
|
|
BoundingBox::CreateFromPoints(box, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
|
|
|
|
MeshExtents extents;
|
|
extents.CenterX = sphere.Center.x;
|
|
extents.CenterY = sphere.Center.y;
|
|
extents.CenterZ = sphere.Center.z;
|
|
extents.Radius = sphere.Radius;
|
|
|
|
extents.MinX = box.Center.x - box.Extents.x;
|
|
extents.MinY = box.Center.y - box.Extents.y;
|
|
extents.MinZ = box.Center.z - box.Extents.z;
|
|
|
|
extents.MaxX = box.Center.x + box.Extents.x;
|
|
extents.MaxY = box.Center.y + box.Extents.y;
|
|
extents.MaxZ = box.Center.z + box.Extents.z;
|
|
|
|
hr = write_file(hFile.get(), extents);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// No skeleton data, so no animations
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//======================================================================================
|
|
// SDKMESH
|
|
//======================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// SDKMESH format is generated by the legacy DirectX SDK's Content Exporter and
|
|
// originally rendered by the DXUT helper class SDKMesh
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkId=226208
|
|
//--------------------------------------------------------------------------------------
|
|
namespace DXUT
|
|
{
|
|
// .SDKMESH files
|
|
|
|
// SDKMESH_HEADER
|
|
// SDKMESH_VERTEX_BUFFER_HEADER header->VertexStreamHeadersOffset
|
|
// SDKMESH_INDEX_BUFFER_HEADER header->IndexStreamHeadersOffset
|
|
// SDKMESH_MESH header->MeshDataOffset
|
|
// SDKMESH_SUBSET header->SubsetDataOffset
|
|
// SDKMESH_FRAME header->FrameDataOffset
|
|
// SDKMESH_MATERIAL header->MaterialDataOffset
|
|
// [header->NonBufferDataSize]
|
|
// { [ header->NumVertexBuffers]
|
|
// VB data
|
|
// }
|
|
// { [ header->NumIndexBuffers]
|
|
// IB data
|
|
// }
|
|
|
|
|
|
// .SDDKANIM files
|
|
|
|
// SDKANIMATION_FILE_HEADER
|
|
// BYTE[] - Length of fileheader->AnimationDataSize
|
|
|
|
// .SDKMESH uses Direct3D 9 decls, but only a subset of these is ever generated by the legacy DirectX SDK Content Exporter
|
|
|
|
// D3DDECLUSAGE_POSITION / D3DDECLTYPE_FLOAT3
|
|
// (D3DDECLUSAGE_BLENDWEIGHT / D3DDECLTYPE_UBYTE4N
|
|
// D3DDECLUSAGE_BLENDINDICES / D3DDECLTYPE_UBYTE4)?
|
|
// (D3DDECLUSAGE_NORMAL / D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4)?
|
|
// (D3DDECLUSAGE_COLOR / D3DDECLTYPE_D3DCOLOR)?
|
|
// (D3DDECLUSAGE_TEXCOORD / D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2 or D3DDECLTYPE_FLOAT16_2, D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4, D3DDECLTYPE_FLOAT4 or D3DDECLTYPE_FLOAT16_4)*
|
|
// (D3DDECLUSAGE_TANGENT / D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4)?
|
|
// (D3DDECLUSAGE_BINORMAL / D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4)?
|
|
|
|
enum D3DDECLUSAGE
|
|
{
|
|
D3DDECLUSAGE_POSITION = 0,
|
|
D3DDECLUSAGE_BLENDWEIGHT = 1,
|
|
D3DDECLUSAGE_BLENDINDICES = 2,
|
|
D3DDECLUSAGE_NORMAL = 3,
|
|
D3DDECLUSAGE_TEXCOORD = 5,
|
|
D3DDECLUSAGE_TANGENT = 6,
|
|
D3DDECLUSAGE_BINORMAL = 7,
|
|
D3DDECLUSAGE_COLOR = 10,
|
|
};
|
|
|
|
enum D3DDECLTYPE
|
|
{
|
|
D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.)
|
|
D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.)
|
|
D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.)
|
|
D3DDECLTYPE_FLOAT4 = 3, // 4D float
|
|
D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range
|
|
// Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A)
|
|
D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte
|
|
D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0
|
|
D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1)
|
|
D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1)
|
|
D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values
|
|
|
|
D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused.
|
|
};
|
|
|
|
#pragma pack(push,4)
|
|
|
|
struct D3DVERTEXELEMENT9
|
|
{
|
|
WORD Stream; // Stream index
|
|
WORD Offset; // Offset in the stream in bytes
|
|
BYTE Type; // Data type
|
|
BYTE Method; // Processing method
|
|
BYTE Usage; // Semantics
|
|
BYTE UsageIndex; // Semantic index
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Hard Defines for the various structures
|
|
//--------------------------------------------------------------------------------------
|
|
const uint32_t SDKMESH_FILE_VERSION = 101;
|
|
const uint32_t MAX_VERTEX_ELEMENTS = 32;
|
|
const uint32_t MAX_VERTEX_STREAMS = 16;
|
|
const uint32_t MAX_FRAME_NAME = 100;
|
|
const uint32_t MAX_MESH_NAME = 100;
|
|
const uint32_t MAX_SUBSET_NAME = 100;
|
|
const uint32_t MAX_MATERIAL_NAME = 100;
|
|
const uint32_t MAX_TEXTURE_NAME = MAX_PATH;
|
|
const uint32_t MAX_MATERIAL_PATH = MAX_PATH;
|
|
const uint32_t INVALID_FRAME = uint32_t(-1);
|
|
const uint32_t INVALID_MESH = uint32_t(-1);
|
|
const uint32_t INVALID_MATERIAL = uint32_t(-1);
|
|
const uint32_t INVALID_SUBSET = uint32_t(-1);
|
|
const uint32_t INVALID_ANIMATION_DATA = uint32_t(-1);
|
|
const uint32_t INVALID_SAMPLER_SLOT = uint32_t(-1);
|
|
const uint32_t ERROR_RESOURCE_VALUE = 1;
|
|
|
|
template<typename TYPE> bool IsErrorResource(TYPE data)
|
|
{
|
|
if ((TYPE) ERROR_RESOURCE_VALUE == data)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Enumerated Types. These will have mirrors in both D3D9 and D3D11
|
|
//--------------------------------------------------------------------------------------
|
|
enum SDKMESH_PRIMITIVE_TYPE
|
|
{
|
|
PT_TRIANGLE_LIST = 0,
|
|
PT_TRIANGLE_STRIP,
|
|
PT_LINE_LIST,
|
|
PT_LINE_STRIP,
|
|
PT_POINT_LIST,
|
|
PT_TRIANGLE_LIST_ADJ,
|
|
PT_TRIANGLE_STRIP_ADJ,
|
|
PT_LINE_LIST_ADJ,
|
|
PT_LINE_STRIP_ADJ,
|
|
PT_QUAD_PATCH_LIST,
|
|
PT_TRIANGLE_PATCH_LIST,
|
|
};
|
|
|
|
enum SDKMESH_INDEX_TYPE
|
|
{
|
|
IT_16BIT = 0,
|
|
IT_32BIT,
|
|
};
|
|
|
|
enum FRAME_TRANSFORM_TYPE
|
|
{
|
|
FTT_RELATIVE = 0,
|
|
FTT_ABSOLUTE, //This is not currently used but is here to support absolute transformations in the future
|
|
};
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Structures.
|
|
//--------------------------------------------------------------------------------------
|
|
#pragma pack(push,8)
|
|
|
|
struct SDKMESH_HEADER
|
|
{
|
|
//Basic Info and sizes
|
|
UINT Version;
|
|
BYTE IsBigEndian;
|
|
UINT64 HeaderSize;
|
|
UINT64 NonBufferDataSize;
|
|
UINT64 BufferDataSize;
|
|
|
|
//Stats
|
|
UINT NumVertexBuffers;
|
|
UINT NumIndexBuffers;
|
|
UINT NumMeshes;
|
|
UINT NumTotalSubsets;
|
|
UINT NumFrames;
|
|
UINT NumMaterials;
|
|
|
|
//Offsets to Data
|
|
UINT64 VertexStreamHeadersOffset;
|
|
UINT64 IndexStreamHeadersOffset;
|
|
UINT64 MeshDataOffset;
|
|
UINT64 SubsetDataOffset;
|
|
UINT64 FrameDataOffset;
|
|
UINT64 MaterialDataOffset;
|
|
};
|
|
|
|
struct SDKMESH_VERTEX_BUFFER_HEADER
|
|
{
|
|
UINT64 NumVertices;
|
|
UINT64 SizeBytes;
|
|
UINT64 StrideBytes;
|
|
D3DVERTEXELEMENT9 Decl[MAX_VERTEX_ELEMENTS];
|
|
union
|
|
{
|
|
UINT64 DataOffset;
|
|
ID3D11Buffer* pVB11;
|
|
};
|
|
};
|
|
|
|
struct SDKMESH_INDEX_BUFFER_HEADER
|
|
{
|
|
UINT64 NumIndices;
|
|
UINT64 SizeBytes;
|
|
UINT IndexType;
|
|
union
|
|
{
|
|
UINT64 DataOffset;
|
|
ID3D11Buffer* pIB11;
|
|
};
|
|
};
|
|
|
|
struct SDKMESH_MESH
|
|
{
|
|
char Name[MAX_MESH_NAME];
|
|
BYTE NumVertexBuffers;
|
|
UINT VertexBuffers[MAX_VERTEX_STREAMS];
|
|
UINT IndexBuffer;
|
|
UINT NumSubsets;
|
|
UINT NumFrameInfluences; //aka bones
|
|
|
|
DirectX::XMFLOAT3 BoundingBoxCenter;
|
|
DirectX::XMFLOAT3 BoundingBoxExtents;
|
|
|
|
union
|
|
{
|
|
UINT64 SubsetOffset;
|
|
INT* pSubsets;
|
|
};
|
|
union
|
|
{
|
|
UINT64 FrameInfluenceOffset;
|
|
UINT* pFrameInfluences;
|
|
};
|
|
};
|
|
|
|
struct SDKMESH_SUBSET
|
|
{
|
|
char Name[MAX_SUBSET_NAME];
|
|
UINT MaterialID;
|
|
UINT PrimitiveType;
|
|
UINT64 IndexStart;
|
|
UINT64 IndexCount;
|
|
UINT64 VertexStart;
|
|
UINT64 VertexCount;
|
|
};
|
|
|
|
struct SDKMESH_FRAME
|
|
{
|
|
char Name[MAX_FRAME_NAME];
|
|
UINT Mesh;
|
|
UINT ParentFrame;
|
|
UINT ChildFrame;
|
|
UINT SiblingFrame;
|
|
DirectX::XMFLOAT4X4 Matrix;
|
|
UINT AnimationDataIndex; //Used to index which set of keyframes transforms this frame
|
|
};
|
|
|
|
struct SDKMESH_MATERIAL
|
|
{
|
|
char Name[MAX_MATERIAL_NAME];
|
|
|
|
// Use MaterialInstancePath
|
|
char MaterialInstancePath[MAX_MATERIAL_PATH];
|
|
|
|
// Or fall back to d3d8-type materials
|
|
char DiffuseTexture[MAX_TEXTURE_NAME];
|
|
char NormalTexture[MAX_TEXTURE_NAME];
|
|
char SpecularTexture[MAX_TEXTURE_NAME];
|
|
|
|
DirectX::XMFLOAT4 Diffuse;
|
|
DirectX::XMFLOAT4 Ambient;
|
|
DirectX::XMFLOAT4 Specular;
|
|
DirectX::XMFLOAT4 Emissive;
|
|
FLOAT Power;
|
|
|
|
union
|
|
{
|
|
UINT64 Force64_1; //Force the union to 64bits
|
|
ID3D11Texture2D* pDiffuseTexture11;
|
|
};
|
|
union
|
|
{
|
|
UINT64 Force64_2; //Force the union to 64bits
|
|
ID3D11Texture2D* pNormalTexture11;
|
|
};
|
|
union
|
|
{
|
|
UINT64 Force64_3; //Force the union to 64bits
|
|
ID3D11Texture2D* pSpecularTexture11;
|
|
};
|
|
|
|
union
|
|
{
|
|
UINT64 Force64_4; //Force the union to 64bits
|
|
ID3D11ShaderResourceView* pDiffuseRV11;
|
|
};
|
|
union
|
|
{
|
|
UINT64 Force64_5; //Force the union to 64bits
|
|
ID3D11ShaderResourceView* pNormalRV11;
|
|
};
|
|
union
|
|
{
|
|
UINT64 Force64_6; //Force the union to 64bits
|
|
ID3D11ShaderResourceView* pSpecularRV11;
|
|
};
|
|
};
|
|
|
|
struct SDKANIMATION_FILE_HEADER
|
|
{
|
|
UINT Version;
|
|
BYTE IsBigEndian;
|
|
UINT FrameTransformType;
|
|
UINT NumFrames;
|
|
UINT NumAnimationKeys;
|
|
UINT AnimationFPS;
|
|
UINT64 AnimationDataSize;
|
|
UINT64 AnimationDataOffset;
|
|
};
|
|
|
|
struct SDKANIMATION_DATA
|
|
{
|
|
DirectX::XMFLOAT3 Translation;
|
|
DirectX::XMFLOAT4 Orientation;
|
|
DirectX::XMFLOAT3 Scaling;
|
|
};
|
|
|
|
struct SDKANIMATION_FRAME_DATA
|
|
{
|
|
char FrameName[MAX_FRAME_NAME];
|
|
union
|
|
{
|
|
UINT64 DataOffset;
|
|
SDKANIMATION_DATA* pAnimationData;
|
|
};
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
}; // namespace
|
|
|
|
static_assert(sizeof(DXUT::D3DVERTEXELEMENT9) == 8, "Direct3D9 Decl structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_HEADER) == 104, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_VERTEX_BUFFER_HEADER) == 288, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_INDEX_BUFFER_HEADER) == 32, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_MESH) == 224, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_SUBSET) == 144, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_FRAME) == 184, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKMESH_MATERIAL) == 1256, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKANIMATION_FILE_HEADER) == 40, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKANIMATION_DATA) == 40, "SDK Mesh structure size incorrect");
|
|
static_assert(sizeof(DXUT::SDKANIMATION_FRAME_DATA) == 112, "SDK Mesh structure size incorrect");
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
_Use_decl_annotations_
|
|
HRESULT Mesh::ExportToSDKMESH(const wchar_t* szFileName, size_t nMaterials, const Material* materials) const
|
|
{
|
|
using namespace DXUT;
|
|
|
|
if (!szFileName)
|
|
return E_INVALIDARG;
|
|
|
|
if (nMaterials > 0 && !materials)
|
|
return E_INVALIDARG;
|
|
|
|
if (!mnFaces || !mIndices || !mnVerts || !mPositions)
|
|
return E_UNEXPECTED;
|
|
|
|
if ((uint64_t(mnFaces) * 3) >= UINT32_MAX)
|
|
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
|
|
|
// Build input layout/vertex decalaration
|
|
static const D3D11_INPUT_ELEMENT_DESC s_elements [] =
|
|
{
|
|
{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 0
|
|
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 1
|
|
{ "COLOR", 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 2
|
|
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 3
|
|
{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 4
|
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 5
|
|
{ "BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_UINT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 6
|
|
{ "BLENDWEIGHT", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // 7
|
|
};
|
|
|
|
static const D3DVERTEXELEMENT9 s_decls [] =
|
|
{
|
|
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0 }, // 0
|
|
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0 }, // 1
|
|
{ 0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0 }, // 2
|
|
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TANGENT, 0 }, // 3
|
|
{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BINORMAL, 0 }, // 4
|
|
{ 0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 }, // 5
|
|
{ 0, 0, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0 }, // 6
|
|
{ 0, 0, D3DDECLTYPE_UBYTE4N, 0, D3DDECLUSAGE_BLENDWEIGHT, 0 }, // 7
|
|
{ 0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0 },
|
|
};
|
|
|
|
static_assert((_countof(s_elements) + 1) == _countof(s_decls), "InputLayouts and Vertex Decls disagree");
|
|
|
|
SDKMESH_VERTEX_BUFFER_HEADER vbHeader = { 0 };
|
|
vbHeader.NumVertices = mnVerts;
|
|
vbHeader.Decl[0] = s_decls[0];
|
|
|
|
D3D11_INPUT_ELEMENT_DESC inputLayout[MAX_VERTEX_ELEMENTS] = { 0 };
|
|
inputLayout[0] = s_elements[0];
|
|
|
|
size_t nDecl = 1;
|
|
size_t stride = sizeof(XMFLOAT3);
|
|
|
|
if (mBlendIndices && mBlendWeights)
|
|
{
|
|
// BLENDWEIGHT
|
|
vbHeader.Decl[nDecl] = s_decls[7];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[7];
|
|
++nDecl;
|
|
stride += sizeof(UINT);
|
|
|
|
// BLENDINDICES
|
|
vbHeader.Decl[nDecl] = s_decls[6];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[6];
|
|
++nDecl;
|
|
stride += sizeof(UINT);
|
|
}
|
|
|
|
if (mNormals)
|
|
{
|
|
vbHeader.Decl[nDecl] = s_decls[1];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[1];
|
|
++nDecl;
|
|
stride += sizeof(XMFLOAT3);
|
|
}
|
|
|
|
if (mColors)
|
|
{
|
|
vbHeader.Decl[nDecl] = s_decls[2];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[2];
|
|
++nDecl;
|
|
stride += sizeof(UINT);
|
|
}
|
|
|
|
if (mTexCoords)
|
|
{
|
|
vbHeader.Decl[nDecl] = s_decls[5];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[5];
|
|
++nDecl;
|
|
stride += sizeof(XMFLOAT2);
|
|
}
|
|
|
|
if (mTangents)
|
|
{
|
|
vbHeader.Decl[nDecl] = s_decls[3];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[3];
|
|
++nDecl;
|
|
stride += sizeof(XMFLOAT3);
|
|
}
|
|
|
|
if (mBiTangents)
|
|
{
|
|
vbHeader.Decl[nDecl] = s_decls[4];
|
|
vbHeader.Decl[nDecl].Offset = static_cast<WORD>(stride);
|
|
inputLayout[nDecl] = s_elements[4];
|
|
++nDecl;
|
|
stride += sizeof(XMFLOAT3);
|
|
}
|
|
|
|
assert(nDecl < MAX_VERTEX_ELEMENTS);
|
|
vbHeader.Decl[nDecl] = s_decls[_countof(s_decls) - 1];
|
|
|
|
// Build vertex buffer
|
|
std::unique_ptr<uint8_t> vb(new (std::nothrow) uint8_t[mnVerts * stride]);
|
|
if (!vb)
|
|
return E_OUTOFMEMORY;
|
|
|
|
vbHeader.SizeBytes = mnVerts * stride;
|
|
vbHeader.StrideBytes = stride;
|
|
|
|
{
|
|
VBWriter writer;
|
|
|
|
HRESULT hr = writer.Initialize(inputLayout, nDecl);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = writer.AddStream(vb.get(), mnVerts, 0, stride);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = GetVertexBuffer(writer);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// Build index buffer
|
|
SDKMESH_INDEX_BUFFER_HEADER ibHeader = { 0 };
|
|
ibHeader.NumIndices = mnFaces * 3;
|
|
|
|
std::unique_ptr<uint16_t[]> ib16;
|
|
if (Is16BitIndexBuffer())
|
|
{
|
|
ibHeader.SizeBytes = mnFaces * 3 * sizeof(uint16_t);
|
|
ibHeader.IndexType = IT_16BIT;
|
|
|
|
ib16 = GetIndexBuffer16();
|
|
if (!ib16)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
ibHeader.SizeBytes = mnFaces * 3 * sizeof(uint32_t);
|
|
ibHeader.IndexType = IT_32BIT;
|
|
}
|
|
|
|
// Build materials buffer
|
|
std::unique_ptr<SDKMESH_MATERIAL[]> mats;
|
|
if (!nMaterials)
|
|
{
|
|
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[1]);
|
|
if (!mats)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memset(mats.get(), 0, sizeof(SDKMESH_MATERIAL));
|
|
|
|
strcpy_s(mats[0].Name, "default");
|
|
mats[0].Diffuse = XMFLOAT4(0.8f, 0.8f, 0.8f, 1.f);
|
|
mats[0].Ambient = XMFLOAT4(0.2f, 02.f, 0.2f, 1.f);
|
|
mats[0].Power = 1.f;
|
|
}
|
|
else
|
|
{
|
|
mats.reset(new (std::nothrow) SDKMESH_MATERIAL[nMaterials]);
|
|
if (!mats)
|
|
return E_OUTOFMEMORY;
|
|
|
|
for (size_t j = 0; j < nMaterials; ++j)
|
|
{
|
|
auto m0 = &materials[j];
|
|
auto m = &mats[j];
|
|
|
|
memset( m, 0, sizeof(SDKMESH_MATERIAL) );
|
|
|
|
int result = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
|
|
m0->name.c_str(), -1,
|
|
m->Name, MAX_MATERIAL_NAME, nullptr, FALSE);
|
|
if (!result)
|
|
{
|
|
*m->Name = 0;
|
|
}
|
|
|
|
result = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
|
|
m0->texture.c_str(), -1,
|
|
m->DiffuseTexture, MAX_TEXTURE_NAME, nullptr, FALSE);
|
|
if (!result)
|
|
{
|
|
*m->DiffuseTexture = 0;
|
|
}
|
|
|
|
m->Diffuse.x = m0->diffuseColor.x;
|
|
m->Diffuse.y = m0->diffuseColor.y;
|
|
m->Diffuse.z = m0->diffuseColor.z;
|
|
m->Diffuse.w = m0->alpha;
|
|
|
|
m->Ambient.x = m0->ambientColor.x;
|
|
m->Ambient.y = m0->ambientColor.y;
|
|
m->Ambient.z = m0->ambientColor.z;
|
|
m->Ambient.w = 1.f;
|
|
|
|
if (m0->specularColor.x > 0.f || m0->specularColor.y > 0.f || m0->specularColor.z > 0.f)
|
|
{
|
|
m->Specular.x = m0->specularColor.x;
|
|
m->Specular.y = m0->specularColor.y;
|
|
m->Specular.z = m0->specularColor.z;
|
|
m->Power = ( m0->specularPower <= 0.f ) ? 16.f : m0->specularPower;
|
|
}
|
|
else
|
|
{
|
|
m->Power = 1.f;
|
|
}
|
|
|
|
m->Emissive.x = m0->emissiveColor.x;
|
|
m->Emissive.y = m0->emissiveColor.y;
|
|
m->Emissive.z = m0->emissiveColor.z;
|
|
}
|
|
}
|
|
|
|
// Build subsets
|
|
std::vector<SDKMESH_SUBSET> submeshes;
|
|
std::vector<UINT> subsetArray;
|
|
if (mAttributes)
|
|
{
|
|
auto subsets = ComputeSubsets(mAttributes.get(), mnFaces);
|
|
|
|
UINT64 startIndex = 0;
|
|
for (auto it = subsets.cbegin(); it != subsets.cend(); ++it)
|
|
{
|
|
subsetArray.push_back(static_cast<UINT>(submeshes.size()));
|
|
|
|
SDKMESH_SUBSET s = { 0 };
|
|
s.MaterialID = mAttributes[it->first];
|
|
if (s.MaterialID >= nMaterials)
|
|
s.MaterialID = 0;
|
|
|
|
s.PrimitiveType = PT_TRIANGLE_LIST;
|
|
s.IndexStart = startIndex;
|
|
s.IndexCount = it->second * 3;
|
|
s.VertexCount = mnVerts;
|
|
submeshes.push_back(s);
|
|
|
|
if ((startIndex + s.IndexCount) > mnFaces * 3)
|
|
return E_FAIL;
|
|
|
|
startIndex += s.IndexCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SDKMESH_SUBSET s = { 0 };
|
|
s.PrimitiveType = PT_TRIANGLE_LIST;
|
|
s.IndexCount = mnFaces * 3;
|
|
s.VertexCount = mnVerts;
|
|
subsetArray.push_back(0);
|
|
submeshes.push_back(s);
|
|
}
|
|
|
|
// Create file
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFileName, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
|
|
#else
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
|
|
#endif
|
|
if (!hFile)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
// Write file header
|
|
SDKMESH_HEADER header = { 0 };
|
|
header.Version = SDKMESH_FILE_VERSION;
|
|
header.IsBigEndian = 0;
|
|
|
|
header.NumVertexBuffers = 1;
|
|
header.NumIndexBuffers = 1;
|
|
header.NumMeshes = 1;
|
|
header.NumTotalSubsets = static_cast<UINT>( submeshes.size() );
|
|
header.NumFrames = 1;
|
|
header.NumMaterials = (nMaterials > 0) ? static_cast<UINT>(nMaterials) : 1;
|
|
|
|
header.HeaderSize = sizeof(SDKMESH_HEADER) + sizeof(SDKMESH_VERTEX_BUFFER_HEADER) + sizeof(SDKMESH_INDEX_BUFFER_HEADER);
|
|
|
|
size_t staticDataSize = sizeof(SDKMESH_MESH)
|
|
+ header.NumTotalSubsets * sizeof(SDKMESH_SUBSET)
|
|
+ sizeof(SDKMESH_FRAME)
|
|
+ header.NumMaterials * sizeof(SDKMESH_MATERIAL);
|
|
|
|
header.NonBufferDataSize = staticDataSize + subsetArray.size() * sizeof(UINT) + sizeof(UINT);
|
|
|
|
header.BufferDataSize = roundup4k( vbHeader.SizeBytes ) + roundup4k( ibHeader.SizeBytes );
|
|
|
|
header.VertexStreamHeadersOffset = sizeof(SDKMESH_HEADER);
|
|
header.IndexStreamHeadersOffset = header.VertexStreamHeadersOffset + sizeof(SDKMESH_VERTEX_BUFFER_HEADER);
|
|
header.MeshDataOffset = header.IndexStreamHeadersOffset + sizeof(SDKMESH_INDEX_BUFFER_HEADER);
|
|
header.SubsetDataOffset = header.MeshDataOffset + sizeof(SDKMESH_MESH);
|
|
header.FrameDataOffset = header.SubsetDataOffset + header.NumTotalSubsets * sizeof(SDKMESH_SUBSET);
|
|
header.MaterialDataOffset = header.FrameDataOffset + sizeof(SDKMESH_FRAME);
|
|
|
|
HRESULT hr = write_file(hFile.get(), header);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write buffer headers
|
|
UINT64 offset = header.HeaderSize + header.NonBufferDataSize;
|
|
|
|
vbHeader.DataOffset = offset;
|
|
offset += roundup4k(vbHeader.SizeBytes);
|
|
|
|
hr = write_file(hFile.get(), vbHeader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ibHeader.DataOffset = offset;
|
|
offset += roundup4k(ibHeader.SizeBytes);
|
|
|
|
hr = write_file(hFile.get(), ibHeader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write mesh headers
|
|
assert(header.NumMeshes == 1);
|
|
offset = header.HeaderSize + staticDataSize;
|
|
|
|
SDKMESH_MESH meshHeader = { 0 };
|
|
meshHeader.NumVertexBuffers = 1;
|
|
meshHeader.NumFrameInfluences = 1;
|
|
|
|
{
|
|
BoundingBox box;
|
|
BoundingBox::CreateFromPoints(box, mnVerts, mPositions.get(), sizeof(XMFLOAT3));
|
|
|
|
meshHeader.BoundingBoxCenter = box.Center;
|
|
meshHeader.BoundingBoxExtents = box.Extents;
|
|
}
|
|
|
|
meshHeader.NumSubsets = static_cast<UINT>(submeshes.size());
|
|
meshHeader.SubsetOffset = offset;
|
|
offset += meshHeader.NumSubsets * sizeof(UINT);
|
|
meshHeader.FrameInfluenceOffset = offset;
|
|
offset += sizeof(UINT);
|
|
|
|
hr = write_file(hFile.get(), meshHeader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write subsets
|
|
DWORD bytesToWrite = static_cast<DWORD>( sizeof(SDKMESH_SUBSET) * submeshes.size() );
|
|
DWORD bytesWritten;
|
|
if ( !WriteFile(hFile.get(), submeshes.data(), bytesToWrite, &bytesWritten, nullptr) )
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
|
|
// Write frames
|
|
SDKMESH_FRAME frame = { 0 };
|
|
strcpy_s( frame.Name, "root");
|
|
frame.ParentFrame = frame.ChildFrame = frame.SiblingFrame = DWORD(-1);
|
|
frame.AnimationDataIndex = INVALID_ANIMATION_DATA;
|
|
XMMATRIX id = XMMatrixIdentity();
|
|
XMStoreFloat4x4(&frame.Matrix, id);
|
|
|
|
hr = write_file(hFile.get(), frame);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write materials
|
|
bytesToWrite = static_cast<DWORD>(sizeof(SDKMESH_MATERIAL) * ((nMaterials > 0) ? nMaterials : 1));
|
|
if (!WriteFile(hFile.get(), mats.get(), bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
|
|
// Write subset index list
|
|
assert(meshHeader.NumSubsets == subsetArray.size());
|
|
bytesToWrite = meshHeader.NumSubsets * sizeof(UINT);
|
|
if (!WriteFile(hFile.get(), subsetArray.data(), bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
|
|
// Write frame influence list
|
|
assert(meshHeader.NumFrameInfluences == 1);
|
|
UINT frameIndex = 0;
|
|
hr = write_file(hFile.get(), frameIndex);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Write VB data
|
|
bytesToWrite = static_cast<DWORD>(vbHeader.SizeBytes);
|
|
if (!WriteFile(hFile.get(), vb.get(), bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
|
|
bytesToWrite = static_cast<DWORD>( roundup4k(vbHeader.SizeBytes) - vbHeader.SizeBytes );
|
|
if (bytesToWrite > 0)
|
|
{
|
|
assert(bytesToWrite < sizeof(g_padding));
|
|
if (!WriteFile(hFile.get(), g_padding, bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Write IB data
|
|
bytesToWrite = static_cast<DWORD>(ibHeader.SizeBytes);
|
|
if (!WriteFile(hFile.get(), (ib16) ? static_cast<void*>( ib16.get() ) : static_cast<void*>( mIndices.get() ),
|
|
bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
|
|
bytesToWrite = static_cast<DWORD>(roundup4k(ibHeader.SizeBytes) - ibHeader.SizeBytes);
|
|
if (bytesToWrite > 0)
|
|
{
|
|
assert(bytesToWrite < sizeof(g_padding));
|
|
if (!WriteFile(hFile.get(), g_padding, bytesToWrite, &bytesWritten, nullptr))
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (bytesWritten != bytesToWrite)
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|