Files
UnrealEngineUWP/Engine/Source/Developer/MeshUtilities/Private/LayoutUV.cpp
Robert Manuszewski 5925a19dc9 Copying //UE4/Dev-Core to //UE4/Dev-Main (Source: //UE4/Dev-Core @ 3125172)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3053250 on 2016/07/18 by Steve.Robb

	TNot metafunction added.

Change 3053252 on 2016/07/18 by Steve.Robb

	New TIsEnumClass trait.

Change 3061345 on 2016/07/22 by Robert.Manuszewski

	Changing FMallocStomp::GetAllocationSize() to return the requested allocation size instead of the physical allocation size to make FMallocStomp work properly with FMallocPoisonProxy

Change 3061377 on 2016/07/22 by Graeme.Thornton

	Added bStripAnimationDataOnDedicatedServer option to UAnimationSettings which will remove all compressed data from cooked server data. Disabled by default

Change 3064592 on 2016/07/26 by Steven.Hutton

	Uploading repository files

Change 3064595 on 2016/07/26 by Steven.Hutton

	Assign crashes to buggs based not just on Callstack but also based on Error message.

	Error messages have "data" masked out and are then compared to a table of  error messages to find similar messages. Ensures are not currently filtered by error message.

Change 3066046 on 2016/07/27 by Graeme.Thornton

	Better dedicated client/server class exclusion during cooking
	 - Add class lists to cooker settings so they can be modified in the editor

Change 3066077 on 2016/07/27 by Graeme.Thornton

	Move Paragon NeedsLoadForServer calls over to the new config based system

Change 3066203 on 2016/07/27 by Chris.Wood

	CrashReportProcess logging and Slack reporting improvements to monitor disk space.
	[UE-31129] - Crash Report server need to alert on Slack when the PDB cache is full

Change 3066276 on 2016/07/27 by Graeme.Thornton

	Move simple NeedsLoadForClient implementations over to new config based system

Change 3068019 on 2016/07/28 by Graeme.Thornton

	Don't call ReleaseResources on UStaticMesh if we never render, and therefore never actually initialize the resources
	 - Corrects some bad stats

Change 3068218 on 2016/07/28 by Chris.Wood

	CrashReportProcess 1.1.18 passes BuildVersion to MinidumpDiagnostics
	[UE-31706] - Add new BuildVersion string to crash context and website

	Also modified command line log file ini settings to stop MDD stalling trying to sort and delete its logs.

Change 3071665 on 2016/08/01 by Robert.Manuszewski

	Moving RemoveNamesFromMasterList from UEnum destructor to BeginDestroy to avoid potential issues when its package has already been destroyed.

Change 3073388 on 2016/08/02 by Graeme.Thornton

	Invalidate string asset reference tags after finishing up loading of an async package

Change 3073745 on 2016/08/02 by Robert.Manuszewski

	Disabling logging to memory in shipping by default. From now on FOutputDeviceMemory will be an opt-in for games.

	#jira FORT-27839

Change 3074866 on 2016/08/03 by Robert.Manuszewski

	PR #2650: Fixed a bug where newline escape sequence wasnt written to the pipe (Contributed by ozturkhakki)

Change 3075128 on 2016/08/03 by Steve.Robb

	Static analysis fixes: error C2065: 'ThisOuterWorld': undeclared identifier

Change 3075130 on 2016/08/03 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'LODLevel'

Change 3075131 on 2016/08/03 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Owner'

Change 3075235 on 2016/08/03 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'AnimToOperateOn'

Change 3075248 on 2016/08/03 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ParentProfile'

Change 3075662 on 2016/08/03 by Steve.Robb

	Static analysis/buffer size fix: warning C28020: The expression '_Param_(7)>=sizeof(ICMP_ECHO_REPLY)+_Param_(4)+8' is not true at this call

Change 3075668 on 2016/08/03 by Steve.Robb

	Static analysis fix: warning C6326: Potential comparison of a constant with another constant

Change 3075679 on 2016/08/03 by Chris.Wood

	Added -NoTrimCallstack command line arg to MDD calls from CRP 1.1.19
	[OR-26335] - 29.1 crash reporter generating reports with no callstacks / info

	New MDD has -NoTrimCallstack option to leave possibly irrelevant entires in the stack. Trimming is somewhat arbitrary and based on string matching. I'd rather see the whole thing.

Change 3077070 on 2016/08/04 by Steve.Robb

	Dead array slack tracking code removed.

Change 3077113 on 2016/08/04 by Steve.Robb

	TEnumAsByte is now deprecated for enum classes.
	All current uses fixed (which tidies up that code anyway).
	New FArchive::op<< for enum classes.
	Generated code now never uses TEnumAsByte for enum classes.

Change 3077117 on 2016/08/04 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'DefaultSettings'

Change 3078709 on 2016/08/05 by Steve.Robb

	FUNCTION_NO_NULL_RETURN_* macros added to statically annotate a function to say that it never returns a null pointer.
	TObjectIterator annotated to never return null.
	NewObject annotated to never return null.

Change 3078836 on 2016/08/05 by Graeme.Thornton

	Silently skip creating exports from a package where the outer is also an export and has been filtered at runtime during loading

Change 3082217 on 2016/08/09 by Steve.Robb

	Missing #include for FUniqueNetIdRepl added.

Change 3083679 on 2016/08/10 by Chris.Wood

	CrashReportProcess performance improvements. CRP v1.1.22
	[UE-34402] - Crash Reporter: Improve CRP performance by allowing multiple MDD instances
	[UE-34403] - Crash Reporter: CRP should throw away crashes when backlog is too large to avoid runaway

	Passing lock details to MDD on command line and managing multiple MDD tasks in CRP.
	Configurable values for range of queue sizes that cause us to throw away crashes.

Change 3085362 on 2016/08/11 by Steve.Robb

	Rule-of-three fixes for FAIMessageObserver, to prevent accidents.

	From here: https://udn.unrealengine.com/questions/306507/tstaticarray-doesnt-call-destructor-on-elements-be.html

Change 3085396 on 2016/08/11 by Steve.Robb

	Swap can now be configured via the TUseBitwiseSwap trait to not use Memswap, which can be less optimal for certain types.

Change 3088840 on 2016/08/15 by Steve.Robb

	TRemoveReference moved to its own header.

Change 3088858 on 2016/08/15 by Steve.Robb

	TDecay moved to its own header.

Change 3088963 on 2016/08/15 by Steve.Robb

	Invoke function, for doing a generic call on a generic callable thing.  Equivalent to std::invoke.

Change 3089144 on 2016/08/15 by Steve.Robb

	Algo::Transform updated to use Invoke.  Algorithm tests updated to test the new features.

Change 3089147 on 2016/08/15 by Steve.Robb

	TLess and TGreater moved to their own headers and defaulted to void as a type-deducing version, as per std::.

Change 3090243 on 2016/08/16 by Steve.Robb

	New Algo::Sort and Algo::SortBy algorithms.

Change 3090387 on 2016/08/16 by Steve.Robb

	Improved bitwise swapping for Swap.

	See: https://udn.unrealengine.com/questions/306922/swap-is-painfully-slow.html

Change 3090444 on 2016/08/16 by Steve.Robb

	Ptr+Size overloads removed after discussion - MakeArrayView should be used instead.

Change 3090495 on 2016/08/16 by Steve.Robb

	Assert when FString::Mid is passed a negative count.

	#jira UE-18756

Change 3093455 on 2016/08/18 by Steve.Robb

	Debuggability and efficiency improvements to UScriptStruct::DeferCppStructOps.

Change 3094476 on 2016/08/19 by Steve.Robb

	BOM removed from InvariantCulture.h.

Change 3094697 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6237: (<zero> && <expression>) is always zero.  <expression> is never evaluated and might have side effects.

Change 3094702 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Interactor'.

Change 3094715 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6385: Reading invalid data from 'Order':  the readable size is '256' bytes, but '8160' bytes may be read.

Change 3094737 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'OwnedComponent'.
	warning C28182: Dereferencing NULL pointer. 'Child' contains the same NULL value as 'AttachToComponent' did.

Change 3094750 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Actor'.

Change 3094768 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'LevelSequence'.
	warning C6011: Dereferencing NULL pointer 'Actor'.

Change 3094774 on 2016/08/19 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'CallFunctionNode'.

Change 3094783 on 2016/08/19 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'TargetPin'.

Change 3094807 on 2016/08/19 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'SourceClass'.

Change 3094815 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'VarNode'.
	warning C6011: Dereferencing NULL pointer 'SourceClass'.

Change 3094840 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'TunnelGraph'.
	warning C28182: Dereferencing NULL pointer. 'GraphNode' contains the same NULL value as 'SourceNode' did.

Change 3094864 on 2016/08/19 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'SpawnClassPin'.

Change 3094880 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'PrevIfIndexMatchesStatement'.

Change 3094886 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'SpawnBlueprintPin'.

Change 3094903 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'K2Node'.

Change 3094916 on 2016/08/19 by Steve.Robb

	Static analysis fix: Dereferencing NULL pointer 'CompilerContext'.

Change 3094931 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'VariablePin'.

Change 3094935 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'CurrentPin'.

Change 3094943 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'Pin'.
	warning C28182: Dereferencing NULL pointer. 'Graph' contains the same NULL value as 'TargetGraph' did.

Change 3094960 on 2016/08/19 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'LastOutPin'.

Change 3095046 on 2016/08/19 by Steve.Robb

	Single parameter version of CastChecked tagged to never return null.

Change 3095054 on 2016/08/19 by Steve.Robb

	Committed wrong version in CL# 3095046.

Change 3095089 on 2016/08/19 by Steve.Robb

	Static analysis fixes:

	warning C6509: Invalid annotation: 'return' cannot be referenced in some contexts
	warning C6101: Returning uninitialized memory '*lpdwExitCode'.

Change 3096035 on 2016/08/22 by Steve.Robb

	Fix for static lighting in pixel inspector.

Change 3096039 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'World'.

Change 3096045 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Actor'.

Change 3096058 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OtherPin'.

Change 3096059 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'MainMesh'.

Change 3096066 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'SourceType'.

Change 3096070 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'LastPushStatement'.

Change 3096074 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OriginalDataTableInPin'.

Change 3096075 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'CurrentPin'.

Change 3096081 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'RunningPlatformData'.

Change 3096156 on 2016/08/22 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'BP'.
	warning C6011: Dereferencing NULL pointer 'Object'.

Change 3096308 on 2016/08/22 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'TopMipData'.
	warning C6011: Dereferencing NULL pointer 'MipCoverageData[0]'.

Change 3096315 on 2016/08/22 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'OldParent'.
	warning C6011: Dereferencing NULL pointer 'TestExecutionInfo'.

Change 3096318 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OwnerClass'.

Change 3096322 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'StaticMeshInstanceData'.

Change 3096337 on 2016/08/22 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'Pin'.
	warning C6011: Dereferencing NULL pointer 'SpawnVarPin'.

Change 3096345 on 2016/08/22 by Steve.Robb

	Static analysis fixes: warning C6246: Local declaration of 'NumTris' hides declaration of the same name in outer scope.

Change 3096356 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'InWorld'.

Change 3096387 on 2016/08/22 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'ExpressionPreviewMaterial'.
	warning C6011: Dereferencing NULL pointer 'MaterialNode->MaterialExpression'.

Change 3096400 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'FunctionInputs'.

Change 3096413 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'LODPackage' contains the same NULL value as 'AssetsOuter' did.

Change 3096416 on 2016/08/22 by Steve.Robb

	Static analysis fixes: warning C6237: (<zero> && <expression>) is always zero.  <expression> is never evaluated and might have side effects.

Change 3096423 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'RedirectorRefs.Redirector'.

Change 3096439 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'NewObject'.

Change 3096446 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'BaseClass'.

Change 3096454 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OldSkeleton'.

Change 3096464 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'MyNode'.

Change 3096469 on 2016/08/22 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'VRInteractor'.

Change 3097559 on 2016/08/23 by Steve.Robb

	Alternate fix to CL# 3096439.

Change 3097583 on 2016/08/23 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'SourceCategoryEnum'.
	warning C6011: Dereferencing NULL pointer 'CurrentWorld'.

Change 3097584 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'LocalizationTarget'.

Change 3097585 on 2016/08/23 by Steve.Robb

	Static analysis fixes:

	warning C28182: Dereferencing NULL pointer. 'VariableSetNode' contains the same NULL value as 'AssignmentNode' did.
	warning C6011: Dereferencing NULL pointer 'FirstNativeClass'.

Change 3097588 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OutputObjClass'.

Change 3097589 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'Term' contains the same NULL value as 'RValueTerm' did.

Change 3097591 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Schema'.

Change 3097597 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'LayerInfo'.

Change 3097598 on 2016/08/23 by Steve.Robb

	Const-correctness fix for ILandscapeEditorModule::GetHeightmapFormatByExtension and ILandscapeEditorModule::GetWeightmapFormatByExtension.

Change 3097600 on 2016/08/23 by Steve.Robb

	Fix for incorrect null check.

Change 3097605 on 2016/08/23 by Steve.Robb

	Spurious static analysis fix: warning C6011: Dereferencing NULL pointer 'TexDataPtr'.

	Bug filed here: https://connect.microsoft.com/VisualStudio/feedback/details/3078125

Change 3097609 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'ObjClass' contains the same NULL value as 'BaseClass' did.

Change 3097613 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'InEdGraph'.

Change 3097620 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ThisScalableFloat'.

Change 3097627 on 2016/08/23 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'AnimBlueprint'.

Change 3097629 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'Pin' contains the same NULL value as 'PoseNet' did.

Change 3097631 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'IPOverlayInfo.Brush'.

Change 3097634 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Survey'.

Change 3097639 on 2016/08/23 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Settings'.

Change 3097650 on 2016/08/23 by Steve.Robb

	Alternate fix for CL# 3097597.

Change 3097725 on 2016/08/23 by Steve.Robb

	Spurious static analysis fix: warning C6011: Dereferencing NULL pointer 'BodySetup'.

Change 3097764 on 2016/08/23 by Steve.Robb

	Spurious static analysis fix: warning C28182: Dereferencing NULL pointer. 'FoundMode' contains the same NULL value as 'ElementType * FoundMode=LoopModes.FindByPredicate(<lambda>)' did.

Change 3097770 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Triangle'.

Change 3097775 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'CurGroup'.

Change 3097796 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'SourceComponent'.

Change 3097797 on 2016/08/23 by Steve.Robb

	Spurious static analysis fix: warning C6011: Dereferencing NULL pointer 'HitComponent'.

Change 3097843 on 2016/08/23 by Steve.Robb

	Spurious static analysis fix: Dereferencing NULL pointer. 'MatchingNewPin' contains the same NULL value as 'UEdGraphPin ** MatchingNewPin=this->Pins.FindByPredicate(<lambda>)' did.

Change 3097864 on 2016/08/23 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'ObjectClass'.
	warning C6011: Dereferencing NULL pointer 'Client'.

Change 3097871 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'SMLightingMesh->StaticMesh' contains the same NULL value as 'StaticMesh' did.

Change 3098015 on 2016/08/23 by Steve.Robb

	Alternative fix for CL# 3094864.

Change 3098024 on 2016/08/23 by Steve.Robb

	Alternative fix for CL# 3094943.

Change 3098052 on 2016/08/23 by Steve.Robb

	Alternative fix for CL# 3094886.

Change 3098080 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'PrimitiveComponent' contains the same NULL value as 'ReplacementComponent' did.

Change 3098102 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'IndexTermPtr'.

Change 3098148 on 2016/08/23 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'Node'.
	warning C6011: Dereferencing NULL pointer 'OldNode'.
	warning C6011: Dereferencing NULL pointer 'LinkedPin'.
	warning C6011: Dereferencing NULL pointer 'RootNode'.

Change 3098156 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'BTGraphNode'.

Change 3098176 on 2016/08/23 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'NewSection'.

Change 3098182 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Sprite'.

Change 3098197 on 2016/08/23 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Node'.
	Coding standards fixes.

Change 3098202 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ExistingEventNode'.

Change 3098208 on 2016/08/23 by Steve.Robb

	Static analysis fixes:

	warning C28182: Dereferencing NULL pointer. 'Graph' contains the same NULL value as 'GraphNew' did.
	warning C28182: Dereferencing NULL pointer. 'GoodGraph' contains the same NULL value as 'GraphNew' did.

Change 3098229 on 2016/08/23 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Property'.

Change 3099188 on 2016/08/24 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'SharedBaseClass'.

Change 3099195 on 2016/08/24 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'NodeProperty'.

Change 3099205 on 2016/08/24 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'VarDesc'.

Change 3099228 on 2016/08/24 by Steve.Robb

	Spurious static analysis fix: warning C28182: Dereferencing NULL pointer. 'Node' contains the same NULL value as 'ParentNode' did.

Change 3099539 on 2016/08/24 by Steve.Robb

	Spurious static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'InBlueprint'.
	warning C28182: Dereferencing NULL pointer. 'TestObj' contains the same NULL value as 'TestOuter' did.

	https://connect.microsoft.com/VisualStudio/feedback/details/3082362
	https://connect.microsoft.com/VisualStudio/feedback/details/3082622

Change 3099546 on 2016/08/24 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'OldNode'.

Change 3099561 on 2016/08/24 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ReferencedObject'.

Change 3099571 on 2016/08/24 by Steve.Robb

	Static analysis fix: Dereferencing NULL pointer. 'ObjClass' contains the same NULL value as 'CommonBaseClass' did.

Change 3099600 on 2016/08/24 by Steve.Robb

	Static analysis fix:

	warning C6385: Reading invalid data from 'this->Packages':  the readable size is '24' bytes, but '32' bytes may be read.
	warning C6385: Reading invalid data from 'Diff.ObjectSets':  the readable size is '24' bytes, but '32' bytes may be read.
	warning C6386: Buffer overrun while writing to 'Objects':  the writable size is '24' bytes, but '32' bytes might be written.

Change 3099912 on 2016/08/24 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'SharedBaseClass'.

Change 3099923 on 2016/08/24 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ThumbnailInfo'.

Change 3100977 on 2016/08/25 by Steve.Robb

	Static analysis fixes:

	warning C6001: Using uninitialized memory '*VectorRef'.
	warning C6001: Using uninitialized memory '*PointRef'.
	warning C6001: Using uninitialized memory '*PolyRef'.

	Coding standard fixes.

Change 3100985 on 2016/08/25 by Steve.Robb

	Static analyis fix: warning C6011: Dereferencing NULL pointer 'SpawnClassPin'.

Change 3100987 on 2016/08/25 by Steve.Robb

	Static analysis fixes:

	warning C28183: 'Resources.BitmapHandle' could be '0', and is a copy of the value found in 'CreateDIBSection()`829':  this does not adhere to the specification for the function 'SelectObject'.
	warning C6387: '_Param_(4)' could be '0':  this does not adhere to the specification for the function 'CreateDIBSection'.

Change 3100992 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6287: Redundant code:  the left and right sub-expressions are identical.

Change 3101000 on 2016/08/25 by Steve.Robb

	Static analysis fixes:

	warning C6001: Using uninitialized memory 'tmpMemReq'.
	warning C6001: Using uninitialized memory 'TmpCreateInfo'.

Change 3101004 on 2016/08/25 by Steve.Robb

	warning C28182: Dereferencing NULL pointer. 'FoliageActor' contains the same NULL value as 'Actor' did.

Change 3101009 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'StaticMeshComponent'.

Change 3101115 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Canvas'.

Change 3101120 on 2016/08/25 by Steve.Robb

	Fixes to previous fixes.

Change 3101128 on 2016/08/25 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Stream'.

Change 3101281 on 2016/08/25 by Steve.Robb

	Static analysis fixes:

	warning C6262: Function uses '99256' bytes of stack:  exceeds /analyze:stacksize '81940'.  Consider moving some data to heap.
	warning C6001: Using uninitialized memory 'Pixel'.

Change 3101292 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'BulkDataPointer'.

Change 3101299 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'UnrealMaterial'.

Change 3101300 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'AssetObject'.

Change 3101304 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'MeshRootNode'.

Change 3101311 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Cluster'.

Change 3101323 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'StartNode'.

Change 3101329 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Object'.

Change 3101333 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ArrayRef'.

Change 3101339 on 2016/08/25 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'ImportData'.
	warning C6011: Dereferencing NULL pointer 'CurveToImport'.

Change 3101485 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'ObjectProperty'.

Change 3101583 on 2016/08/25 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'UserDefinedStruct'.

Change 3105721 on 2016/08/30 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'SpawnClassPin'.

Change 3105724 on 2016/08/30 by Steven.Hutton

	Change users page to more responsive paginated version.

Change 3105725 on 2016/08/30 by Steven.Hutton

	Added field for crash processor failed

Change 3105786 on 2016/08/30 by Steve.Robb

	Reintroduced missing operator<< for enum classes.

Change 3105803 on 2016/08/30 by Steve.Robb

	Removal of obsolete code and state.  PrepareCppStructOps() has several unreachable blocks, one of which sets UScriptStruct::bCppStructOpsFromBaseClass which is otherwise never true, so it can be removed too.

Change 3106251 on 2016/08/30 by Steve.Robb

	Switch static analysis node to build editor instead of just the engine.

Change 3107556 on 2016/08/31 by Steven.Hutton

	Added build version data from CRP to DB as part of add crash #rb none

Change 3107557 on 2016/08/31 by Steven.Hutton

	Passed build version data to CRW through crash description #rb none

Change 3107634 on 2016/08/31 by Graeme.Thornton

	Only accept "log=<filename>" and "abslog=<filename>" command line values if the filename has a "log" or "txt" extension

	#jira UE-20147

Change 3107797 on 2016/08/31 by Steve.Robb

	Fix for UHT debugging manifest, after paths changed in CL# 3088416.

Change 3107964 on 2016/08/31 by Steve.Robb

	TCString::Strfind renamed to TCString::Strifind, as it is case-insensitive.
	New case-sensitive TCString::Strfind added, based on GitHub PR #2453.

Change 3108023 on 2016/08/31 by Steve.Robb

	Removal of test code which no longer compiles now that we emit errors on skipped preprocessor blocks.

Change 3108160 on 2016/08/31 by Steven.Hutton

	Update to add new filter to website front page #rb none

Change 3109556 on 2016/09/01 by Steven.Hutton

	Fixing compile warning #rb none

Change 3110001 on 2016/09/01 by Steve.Robb

	PR #2468: Fix for UnrealHeaderTool TArray<TScriptInterface<>> UFUNCTION parameters (Contributed by UnrealEverything)

Change 3111835 on 2016/09/02 by Steve.Robb

	Enforce uint8 on UENUM() enum classes.

	#jira UE-35224

Change 3111867 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6236: (<expression> || <non-zero constant>) is always a non-zero constant.

Change 3111880 on 2016/09/02 by Steve.Robb

	Static analysis fixes:

	warning C6386: Buffer overrun while writing to 'Views':  the writable size is 'ShaderBindings.ResourceViews.public: int __cdecl TArray<class TSlateD3DTypedShaderParameter<struct ID3D11ShaderResourceView> *,class FDefaultAllocator>::Num(void)const ()*8' bytes, but '16' bytes might be written.
	warning C6386: Buffer overrun while writing to 'ConstantBuffers':  the writable size is 'ShaderBindings.ConstantBuffers.public: int __cdecl TArray<class TSlateD3DTypedShaderParameter<struct ID3D11Buffer> *,class FDefaultAllocator>::Num(void)const ()*8' bytes, but '16' bytes might be written.

Change 3111886 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6386: Buffer overrun while writing to 'DistortionMeshIndices':  the writable size is 'NumIndices*2' bytes, but '4' bytes might be written.

Change 3112025 on 2016/09/02 by Steve.Robb

	Static analysis fix:

	warning C6011: Dereferencing NULL pointer 'pInputProcessParameters'.
	warning C6011: Dereferencing NULL pointer 'pOutputProcessParameters'.

Change 3112051 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Command'.

Change 3112066 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'CurNetDriver'.

Change 3112093 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'byteArray'.

Change 3112110 on 2016/09/02 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'PersistentParty'.

Change 3112123 on 2016/09/02 by Steve.Robb

	Static analysis fixes:

	warning C6011: Dereferencing NULL pointer 'CurDriver'.
	warning C6011: Dereferencing NULL pointer 'CurNetDriver'.
	warning C6011: Dereferencing NULL pointer 'CurWorld'.

Change 3112157 on 2016/09/02 by Steve.Robb

	Static analysis fixes: warning C6011: Dereferencing NULL pointer 'UnitTest'.

Change 3112283 on 2016/09/02 by Steve.Robb

	Static analysis fixes:

	warning C6244: Local declaration of 'None' hides previous declaration at line '173' of 'netcodeunittest.h'.

Change 3113455 on 2016/09/05 by Chris.Wood

	CRP performance improvements (v1.1.25)

Change 3113468 on 2016/09/05 by Steve.Robb

	Reverting unnecessary merge in CL# 3112464.

Change 3113508 on 2016/09/05 by Steve.Robb

	Static analysis fix: warning C6031: Return value ignored: 'CoCreateGuid'.

Change 3113588 on 2016/09/05 by Steve.Robb

	Static analysis fix: warning C6244: Local declaration of 'hInstance' hides previous declaration

Change 3113863 on 2016/09/06 by Steve.Robb

	Fix for this error:

	Could not find a part of the path 'D:\Build\++UE4+Dev-Core+Compile\Sync\Engine\Plugins\2D\Paper2D\Binaries\Win64\UE4Editor.modules'.

Change 3113864 on 2016/09/06 by Steve.Robb

	Misc static analysis fixes for VS2015 Update 2.

Change 3113918 on 2016/09/06 by Ben.Marsh

	Explicitly check for version manifest existing before trying to delete it, rather than swallowing the exception.

Change 3114293 on 2016/09/06 by Steve.Robb

	Static analysis fixes for Visual Studio Update 2.

Change 3115732 on 2016/09/07 by Steve.Robb

	Static analysis fix: warning C6262: Function uses '121180' bytes of stack:  exceeds /analyze:stacksize '81940'.  Consider moving some data to heap.

Change 3115754 on 2016/09/07 by Steve.Robb

	GObjectArrayForDebugVisualizers init order fix.
	Removal of obsolete FName visualizer helper code.

Change 3115774 on 2016/09/07 by Steve.Robb

	Fix for ICE by moving static variables into their own file and removing const return types.

	#jira UE-35597

Change 3116061 on 2016/09/07 by Steve.Robb

	Redundant LOCTEXT_NAMESPACE removed - was missed in CL# 3115774.

Change 3117478 on 2016/09/08 by Steve.Robb

	Static analysis fixes in third party code, using a new macro-based system.

Change 3119152 on 2016/09/09 by Steve.Robb

	TArray::RemoveAt and RemoveAtSwap with a bool Count is now a compile error.

Change 3119200 on 2016/09/09 by Steve.Robb

	Fix for destructors not being called in TSparseArray move assignment.

Change 3119568 on 2016/09/09 by Steve.Robb

	Fix for TSparseArray visualizer.

Change 3119591 on 2016/09/09 by Steve.Robb

	New MakeShared function which allocates the object and reference controller in a single block.

Change 3120281 on 2016/09/09 by Steve.Robb

	Fix for ICE on static analysis build.

	#jira UE-35596

Change 3120786 on 2016/09/12 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'SavedGame'.

Change 3120787 on 2016/09/12 by Steve.Robb

	Removal of TEnumAsByte on enum classes.

Change 3120789 on 2016/09/12 by Steve.Robb

	Static analysis fixes:

	warning C6385: Reading invalid data from 'D3D11X_CERAM_OFFSET_BY_SET_STAGE':  the readable size is '28' bytes, but '64' bytes may be read.
	warning C6101: Returning uninitialized memory '*pDescriptorDst'.  A successful path through the function does not set the named _Out_ parameter.

Change 3121234 on 2016/09/12 by Steve.Robb

	Unused ToBuildInfoString function declaration removed.

Change 3122616 on 2016/09/13 by Steve.Robb

	Static analysis fix: warning C6011: Dereferencing NULL pointer 'Compiler'.

Change 3123070 on 2016/09/13 by Steve.Robb

	Static analysis fix: warning C28182: Dereferencing NULL pointer. 'top' contains the same NULL value as 'edge' did.

[CL 3126145 by Robert Manuszewski in Main branch]
2016-09-15 00:21:42 -04:00

1022 lines
27 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "MeshUtilitiesPrivate.h"
#include "LayoutUV.h"
#include "DisjointSet.h"
DEFINE_LOG_CATEGORY_STATIC(LogLayoutUV, Warning, All);
#define CHART_JOINING 1
FLayoutUV::FLayoutUV( FRawMesh* InMesh, uint32 InSrcChannel, uint32 InDstChannel, uint32 InTextureResolution )
: RawMesh( InMesh )
, SrcChannel( InSrcChannel )
, DstChannel( InDstChannel )
, TextureResolution( InTextureResolution )
, TotalUVArea( 0.0f )
, LayoutRaster( TextureResolution, TextureResolution )
, ChartRaster( TextureResolution, TextureResolution )
, ChartShader( &ChartRaster )
{}
void FLayoutUV::FindCharts( const TMultiMap<int32,int32>& OverlappingCorners )
{
double Begin = FPlatformTime::Seconds();
uint32 NumIndexes = RawMesh->WedgeIndices.Num();
uint32 NumTris = NumIndexes / 3;
TArray< int32 > TranslatedMatches;
TranslatedMatches.SetNumUninitialized( NumIndexes );
TexCoords.SetNumUninitialized( NumIndexes );
for( uint32 i = 0; i < NumIndexes; i++ )
{
TranslatedMatches[i] = -1;
TexCoords[i] = RawMesh->WedgeTexCoords[ SrcChannel ][i];
}
// Build disjoint set
FDisjointSet DisjointSet( NumTris );
for( uint32 i = 0; i < NumIndexes; i++ )
{
for( auto It = OverlappingCorners.CreateConstKeyIterator(i); It; ++It )
{
uint32 j = It.Value();
if( j > i )
{
const uint32 TriI = i/3;
const uint32 TriJ = j/3;
bool bUnion = false;
#if CHART_JOINING
bool bPositionMatch = PositionsMatch( i, j );
if( bPositionMatch )
{
uint32 i1 = 3 * TriI + (i + 1) % 3;
uint32 i2 = 3 * TriI + (i + 2) % 3;
uint32 j1 = 3 * TriJ + (j + 1) % 3;
uint32 j2 = 3 * TriJ + (j + 2) % 3;
bool bEdgeMatch21 = PositionsMatch( i2, j1 );
bool bEdgeMatch12 = PositionsMatch( i1, j2 );
if( bEdgeMatch21 || bEdgeMatch12 )
{
uint32 ie = bEdgeMatch21 ? i2 : i1;
uint32 je = bEdgeMatch21 ? j1 : j2;
bool bUVMatch = UVsMatch( i, j ) && UVsMatch( ie, je );
bool bUVWindingMatch = TriangleUVArea( TriI ) * TriangleUVArea( TriJ ) >= 0.0f;
if( bUVMatch && bUVWindingMatch )
{
bUnion = true;
}
else if( NormalsMatch( i, j ) && NormalsMatch( ie, je ) )
{
// Chart edge
FVector2D EdgeUVi = TexCoords[ie] - TexCoords[i];
FVector2D EdgeUVj = TexCoords[je] - TexCoords[j];
// Would these edges match if the charts were translated
bool bTranslatedUVMatch = ( EdgeUVi - EdgeUVj ).IsNearlyZero( THRESH_UVS_ARE_SAME );
if( bTranslatedUVMatch )
{
// Note: may be mirrored
// TODO should these be restricted to axis aligned edges?
uint32 EdgeI = bEdgeMatch21 ? i2 : i;
uint32 EdgeJ = bEdgeMatch21 ? j : j2;
// Only allow one match per edge
if( TranslatedMatches[ EdgeI ] < 0 &&
TranslatedMatches[ EdgeJ ] < 0 )
{
TranslatedMatches[ EdgeI ] = EdgeJ;
TranslatedMatches[ EdgeJ ] = EdgeI;
}
}
}
}
}
#else
if( VertsMatch( i, j ) )
{
// Edge must match as well (same winding)
if( VertsMatch( 3 * TriI + (i - 1) % 3, 3 * TriJ + (j + 1) % 3 ) ||
VertsMatch( 3 * TriI + (i + 1) % 3, 3 * TriJ + (j - 1) % 3 ) )
{
// Check for UV winding match too
if( TriangleUVArea( TriI ) * TriangleUVArea( TriJ ) >= 0.0f )
{
bUnion = true;
}
}
}
#endif
if( bUnion )
{
// TODO solve spiral case by checking sets for UV overlap
DisjointSet.Union( TriI, TriJ );
}
}
}
}
// Sort tris by chart
SortedTris.SetNumUninitialized( NumTris );
for( uint32 i = 0; i < NumTris; i++ )
{
// Flatten disjoint set path
DisjointSet.Find(i);
SortedTris[i] = i;
}
struct FCompareTris
{
FDisjointSet* DisjointSet;
FCompareTris( FDisjointSet* InDisjointSet )
: DisjointSet( InDisjointSet )
{}
FORCEINLINE bool operator()( uint32 A, uint32 B ) const
{
return (*DisjointSet)[A] < (*DisjointSet)[B];
}
};
SortedTris.Sort( FCompareTris( &DisjointSet ) );
TMap< uint32, int32 > DisjointSetToChartMap;
// Build Charts
for( uint32 Tri = 0; Tri < NumTris; )
{
int32 i = Charts.AddUninitialized();
FMeshChart& Chart = Charts[i];
Chart.MinUV = FVector2D( FLT_MAX, FLT_MAX );
Chart.MaxUV = FVector2D( -FLT_MAX, -FLT_MAX );
Chart.UVArea = 0.0f;
Chart.WorldScale = FVector2D::ZeroVector;
FMemory::Memset( Chart.Join, 0xff );
Chart.FirstTri = Tri;
uint32 ChartID = DisjointSet[ SortedTris[ Tri ] ];
DisjointSetToChartMap.Add( ChartID, i );
for( ; Tri < NumTris && DisjointSet[ SortedTris[ Tri ] ] == ChartID; Tri++ )
{
// Calculate chart bounds
FVector Positions[3];
FVector2D UVs[3];
for( int k = 0; k < 3; k++ )
{
uint32 Index = 3 * SortedTris[ Tri ] + k;
Positions[k] = RawMesh->GetWedgePosition( Index );
UVs[k] = TexCoords[ Index ];
Chart.MinUV.X = FMath::Min( Chart.MinUV.X, UVs[k].X );
Chart.MinUV.Y = FMath::Min( Chart.MinUV.Y, UVs[k].Y );
Chart.MaxUV.X = FMath::Max( Chart.MaxUV.X, UVs[k].X );
Chart.MaxUV.Y = FMath::Max( Chart.MaxUV.Y, UVs[k].Y );
}
FVector Edge1 = Positions[1] - Positions[0];
FVector Edge2 = Positions[2] - Positions[0];
float Area = 0.5f * ( Edge1 ^ Edge2 ).Size();
FVector2D EdgeUV1 = UVs[1] - UVs[0];
FVector2D EdgeUV2 = UVs[2] - UVs[0];
float UVArea = 0.5f * FMath::Abs( EdgeUV1.X * EdgeUV2.Y - EdgeUV1.Y * EdgeUV2.X );
FVector2D UVLength;
UVLength.X = ( EdgeUV2.Y * Edge1 - EdgeUV1.Y * Edge2 ).Size();
UVLength.Y = (-EdgeUV2.X * Edge1 + EdgeUV1.X * Edge2 ).Size();
Chart.WorldScale += UVLength;
Chart.UVArea += UVArea;
}
Chart.LastTri = Tri;
#if !CHART_JOINING
if( Chart.UVArea > 1e-4f )
{
Chart.WorldScale /= Chart.UVArea;
}
else
{
Chart.WorldScale = FVector2D::ZeroVector;
}
//Chart.WorldScale.Set(1,1);
TotalUVArea += Chart.UVArea * Chart.WorldScale.X * Chart.WorldScale.Y;
#endif
}
#if CHART_JOINING
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
for( uint32 Tri = Chart.FirstTri; Tri < Chart.LastTri; Tri++ )
{
for( int k = 0; k < 3; k++ )
{
uint32 Index = 3 * SortedTris[ Tri ] + k;
if( TranslatedMatches[ Index ] >= 0 )
{
checkSlow( TranslatedMatches[ TranslatedMatches[ Index ] ] == Index );
uint32 V0i = Index;
uint32 V0j = TranslatedMatches[ Index ];
uint32 TriI = V0i / 3;
uint32 TriJ = V0j / 3;
if( TriJ <= TriI )
{
// Only need to consider one direction
continue;
}
uint32 V1i = 3 * TriI + (V0i + 1) % 3;
uint32 V1j = 3 * TriJ + (V0j + 1) % 3;
int32 ChartI = i;
int32 ChartJ = DisjointSetToChartMap[ DisjointSet[ TriJ ] ];
FVector2D UV0i = TexCoords[ V0i ];
FVector2D UV1i = TexCoords[ V1i ];
FVector2D UV0j = TexCoords[ V0j ];
FVector2D UV1j = TexCoords[ V1j ];
FVector2D EdgeUVi = UV1i - UV0i;
FVector2D EdgeUVj = UV1j - UV0j;
bool bMirrored = TriangleUVArea( TriI ) * TriangleUVArea( TriJ ) < 0.0f;
FVector2D EdgeOffset0 = UV0i - UV1j;
FVector2D EdgeOffset1 = UV1i - UV0j;
checkSlow( ( EdgeOffset0 - EdgeOffset1 ).IsNearlyZero( THRESH_UVS_ARE_SAME ) );
FVector2D Translation = EdgeOffset0;
FMeshChart& ChartA = Charts[ ChartI ];
FMeshChart& ChartB = Charts[ ChartJ ];
for( uint32 Side = 0; Side < 4; Side++ )
{
// Join[] = { left, right, bottom, top }
// FIXME
if( bMirrored )
continue;
if( ChartA.Join[ Side ^ 0 ] != -1 ||
ChartB.Join[ Side ^ 1 ] != -1 )
{
// Already joined with something else
continue;
}
uint32 Sign = Side & 1;
uint32 Axis = Side >> 1;
bool bAxisAligned = FMath::Abs( EdgeUVi[ Axis ] ) < THRESH_UVS_ARE_SAME;
bool bBorderA = FMath::Abs( UV0i[ Axis ] - ( Sign ^ 0 ? Chart.MaxUV[ Axis ] : Chart.MinUV[ Axis ] ) ) < THRESH_UVS_ARE_SAME;
bool bBorderB = FMath::Abs( UV0j[ Axis ] - ( Sign ^ 1 ? Chart.MaxUV[ Axis ] : Chart.MinUV[ Axis ] ) ) < THRESH_UVS_ARE_SAME;
// FIXME mirrored
if( !bAxisAligned || !bBorderA || !bBorderB )
{
// Edges weren't on matching rectangle borders
continue;
}
FVector2D CenterA = 0.5f * ( ChartA.MinUV + ChartA.MaxUV );
FVector2D CenterB = 0.5f * ( ChartB.MinUV + ChartB.MaxUV );
FVector2D ExtentA = 0.5f * ( ChartA.MaxUV - ChartA.MinUV );
FVector2D ExtentB = 0.5f * ( ChartB.MaxUV - ChartB.MinUV );
// FIXME mirrored
CenterB += Translation;
FVector2D CenterDiff = CenterA - CenterB;
FVector2D ExtentDiff = ExtentA - ExtentB;
FVector2D Separation = ExtentA + ExtentB + CenterDiff * ( Sign ? 1.0f : -1.0f );
bool bCenterMatch = FMath::Abs( CenterDiff[ Axis ^ 1 ] ) < THRESH_UVS_ARE_SAME;
bool bExtentMatch = FMath::Abs( ExtentDiff[ Axis ^ 1 ] ) < THRESH_UVS_ARE_SAME;
bool bSeparate = FMath::Abs( Separation[ Axis ^ 0 ] ) < THRESH_UVS_ARE_SAME;
if( !bCenterMatch || !bExtentMatch || !bSeparate )
{
// Rectangles don't match up after translation
continue;
}
// Found a valid edge join
ChartA.Join[ Side ^ 0 ] = ChartJ;
ChartB.Join[ Side ^ 1 ] = ChartI;
break;
}
}
}
}
}
TArray< uint32 > JoinedSortedTris;
JoinedSortedTris.Reserve( NumTris );
// Detect loops
for( uint32 Axis = 0; Axis < 2; Axis++ )
{
uint32 Side = Axis << 1;
for( int32 i = 0; i < Charts.Num(); i++ )
{
int32 j = Charts[i].Join[ Side ^ 1 ];
while( j != -1 )
{
int32 Next = Charts[j].Join[ Side ^ 1 ];
if( Next == i )
{
// Break loop
Charts[i].Join[ Side ^ 0 ] = -1;
Charts[j].Join[ Side ^ 1 ] = -1;
break;
}
j = Next;
}
}
}
// Join rows first, then columns
for( uint32 Axis = 0; Axis < 2; Axis++ )
{
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& ChartA = Charts[i];
if( ChartA.FirstTri == ChartA.LastTri )
{
// Empty chart
continue;
}
for( uint32 Side = 0; Side < 4; Side++ )
{
if( ChartA.Join[ Side ] != -1 )
{
FMeshChart& ChartB = Charts[ ChartA.Join[ Side ] ];
check( ChartB.Join[ Side ^ 1 ] == i );
check( ChartB.FirstTri != ChartB.LastTri );
}
}
}
NumTris = 0;
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
NumTris += Chart.LastTri - Chart.FirstTri;
}
check( NumTris == SortedTris.Num() );
NumTris = 0;
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& ChartA = Charts[i];
if( ChartA.FirstTri == ChartA.LastTri )
{
// Empty chart
continue;
}
uint32 Side = Axis << 1;
// Find start (left, bottom)
if( ChartA.Join[ Side ^ 0 ] == -1 )
{
// Add original tris
NumTris += ChartA.LastTri - ChartA.FirstTri;
// Continue joining until no more to the (right, top)
int32 Next = ChartA.Join[ Side ^ 1 ];
while( Next != -1 )
{
FMeshChart& ChartB = Charts[ Next ];
NumTris += ChartB.LastTri - ChartB.FirstTri;
Next = ChartB.Join[ Side ^ 1 ];
}
}
}
check( NumTris == SortedTris.Num() );
#if 1
NumTris = 0;
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& ChartA = Charts[i];
if( ChartA.FirstTri == ChartA.LastTri )
{
// Empty chart
continue;
}
// Join[] = { left, right, bottom, top }
uint32 Side = Axis << 1;
// Find start (left, bottom)
if( ChartA.Join[ Side ^ 0 ] == -1 )
{
uint32 FirstTri = JoinedSortedTris.Num();
// Add original tris
for( uint32 Tri = ChartA.FirstTri; Tri < ChartA.LastTri; Tri++ )
{
JoinedSortedTris.Add( SortedTris[ Tri ] );
}
NumTris += ChartA.LastTri - ChartA.FirstTri;
// Continue joining until no more to the (right, top)
while( ChartA.Join[ Side ^ 1 ] != -1 )
{
FMeshChart& ChartB = Charts[ ChartA.Join[ Side ^ 1 ] ];
check( ChartB.FirstTri != ChartB.LastTri );
FVector2D Translation = ChartA.MinUV - ChartB.MinUV;
Translation[ Axis ] += ChartA.MaxUV[ Axis ] - ChartA.MinUV[ Axis ];
for( uint32 Tri = ChartB.FirstTri; Tri < ChartB.LastTri; Tri++ )
{
JoinedSortedTris.Add( SortedTris[ Tri ] );
for( int k = 0; k < 3; k++ )
{
TexCoords[ 3 * SortedTris[ Tri ] + k ] += Translation;
}
}
NumTris += ChartB.LastTri - ChartB.FirstTri;
ChartA.Join[ Side ^ 1 ] = ChartB.Join[ Side ^ 1 ];
ChartA.MaxUV[ Axis ] += ChartB.MaxUV[ Axis ] - ChartB.MinUV[ Axis ];
ChartA.WorldScale += ChartB.WorldScale;
ChartA.UVArea += ChartB.UVArea;
ChartB.FirstTri = 0;
ChartB.LastTri = 0;
ChartB.UVArea = 0.0f;
DisconnectChart( ChartB, Side ^ 2 );
DisconnectChart( ChartB, Side ^ 3 );
}
ChartA.FirstTri = FirstTri;
ChartA.LastTri = JoinedSortedTris.Num();
}
else
{
// Make sure a starting chart could connect to this
FMeshChart& ChartB = Charts[ ChartA.Join[ Side ^ 0 ] ];
check( ChartB.Join[ Side ^ 1 ] == i );
check( ChartB.FirstTri != ChartB.LastTri );
}
}
check( NumTris == SortedTris.Num() );
check( SortedTris.Num() == JoinedSortedTris.Num() );
Exchange( SortedTris, JoinedSortedTris );
JoinedSortedTris.Reset();
#endif
}
// Clean out empty charts
for( int32 i = 0; i < Charts.Num(); i++ )
{
while( i < Charts.Num() && Charts[i].FirstTri == Charts[i].LastTri )
{
Charts.RemoveAtSwap(i);
}
}
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
if( Chart.UVArea > 1e-4f )
{
Chart.WorldScale /= Chart.UVArea;
}
else
{
Chart.WorldScale = FVector2D::ZeroVector;
}
TotalUVArea += Chart.UVArea * Chart.WorldScale.X * Chart.WorldScale.Y;
}
#endif
double End = FPlatformTime::Seconds();
UE_LOG(LogLayoutUV, Display, TEXT("FindCharts: %s"), *FPlatformTime::PrettyTime(End - Begin) );
}
bool FLayoutUV::FindBestPacking()
{
if( (uint32)Charts.Num() > TextureResolution * TextureResolution )
{
// More charts than texels
return false;
}
const float LinearSearchStart = 0.5f;
const float LinearSearchStep = 0.5f;
const int32 BinarySearchSteps = 6;
float UVScaleFail = TextureResolution * FMath::Sqrt( 1.0f / TotalUVArea );
float UVScalePass = TextureResolution * FMath::Sqrt( LinearSearchStart / TotalUVArea );
// Linear search for first fit
while(1)
{
ScaleCharts( UVScalePass );
bool bFit = PackCharts();
if( bFit )
{
break;
}
UVScaleFail = UVScalePass;
UVScalePass *= LinearSearchStep;
}
// Binary search for best fit
for( int32 i = 0; i < BinarySearchSteps; i++ )
{
float UVScale = 0.5f * ( UVScaleFail + UVScalePass );
ScaleCharts( UVScale );
bool bFit = PackCharts();
if( bFit )
{
UVScalePass = UVScale;
}
else
{
UVScaleFail = UVScale;
}
}
// TODO store packing scale/bias separate so this isn't necessary
ScaleCharts( UVScalePass );
PackCharts();
return true;
}
void FLayoutUV::ScaleCharts( float UVScale )
{
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
Chart.UVScale = Chart.WorldScale * UVScale;
}
// Scale charts such that they all fit and roughly total the same area as before
#if 1
float UniformScale = 1.0f;
for( int i = 0; i < 1000; i++ )
{
uint32 NumMaxedOut = 0;
float ScaledUVArea = 0.0f;
for( int32 ChartIndex = 0; ChartIndex < Charts.Num(); ChartIndex++ )
{
FMeshChart& Chart = Charts[ChartIndex];
FVector2D ChartSize = Chart.MaxUV - Chart.MinUV;
FVector2D ChartSizeScaled = ChartSize * Chart.UVScale * UniformScale;
const float MaxChartEdge = TextureResolution - 1.0f;
const float LongestChartEdge = FMath::Max( ChartSizeScaled.X, ChartSizeScaled.Y );
const float Epsilon = 0.01f;
if( LongestChartEdge + Epsilon > MaxChartEdge )
{
// Rescale oversized charts to fit
Chart.UVScale.X = MaxChartEdge / FMath::Max( ChartSize.X, ChartSize.Y );
Chart.UVScale.Y = MaxChartEdge / FMath::Max( ChartSize.X, ChartSize.Y );
NumMaxedOut++;
}
else
{
Chart.UVScale.X *= UniformScale;
Chart.UVScale.Y *= UniformScale;
}
ScaledUVArea += Chart.UVArea * Chart.UVScale.X * Chart.UVScale.Y;
}
if( NumMaxedOut == 0 )
{
// No charts maxed out so no need to rebalance
break;
}
if( NumMaxedOut == Charts.Num() )
{
// All charts are maxed out
break;
}
// Scale up smaller charts to maintain expected total area
// Want ScaledUVArea == TotalUVArea * UVScale^2
float RebalanceScale = UVScale * FMath::Sqrt( TotalUVArea / ScaledUVArea );
if( RebalanceScale < 1.01f )
{
// Stop if further rebalancing is minor
break;
}
UniformScale = RebalanceScale;
}
#endif
#if 1
float NonuniformScale = 1.0f;
for( int i = 0; i < 1000; i++ )
{
uint32 NumMaxedOut = 0;
float ScaledUVArea = 0.0f;
for( int32 ChartIndex = 0; ChartIndex < Charts.Num(); ChartIndex++ )
{
FMeshChart& Chart = Charts[ChartIndex];
for( int k = 0; k < 2; k++ )
{
const float MaximumChartSize = TextureResolution - 1.0f;
const float ChartSize = Chart.MaxUV[k] - Chart.MinUV[k];
const float ChartSizeScaled = ChartSize * Chart.UVScale[k] * NonuniformScale;
const float Epsilon = 0.01f;
if( ChartSizeScaled + Epsilon > MaximumChartSize )
{
// Scale oversized charts to max size
Chart.UVScale[k] = MaximumChartSize / ChartSize;
NumMaxedOut++;
}
else
{
Chart.UVScale[k] *= NonuniformScale;
}
}
ScaledUVArea += Chart.UVArea * Chart.UVScale.X * Chart.UVScale.Y;
}
if( NumMaxedOut == 0 )
{
// No charts maxed out so no need to rebalance
break;
}
if( NumMaxedOut == Charts.Num() * 2 )
{
// All charts are maxed out in both dimensions
break;
}
// Scale up smaller charts to maintain expected total area
// Want ScaledUVArea == TotalUVArea * UVScale^2
float RebalanceScale = UVScale * FMath::Sqrt( TotalUVArea / ScaledUVArea );
if( RebalanceScale < 1.01f )
{
// Stop if further rebalancing is minor
break;
}
NonuniformScale = RebalanceScale;
}
#endif
// Sort charts from largest to smallest
struct FCompareCharts
{
FORCEINLINE bool operator()( const FMeshChart& A, const FMeshChart& B ) const
{
// Rect area
FVector2D ChartRectA = ( A.MaxUV - A.MinUV ) * A.UVScale;
FVector2D ChartRectB = ( B.MaxUV - B.MinUV ) * B.UVScale;
return ChartRectA.X * ChartRectA.Y > ChartRectB.X * ChartRectB.Y;
}
};
Charts.Sort( FCompareCharts() );
}
bool FLayoutUV::PackCharts()
{
uint32 RasterizeCycles = 0;
uint32 FindCycles = 0;
double BeginPackCharts = FPlatformTime::Seconds();
LayoutRaster.Clear();
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
// Try different orientations and pick best
int32 BestOrientation = -1;
FAllocator2D::FRect BestRect = { ~0u, ~0u, ~0u, ~0u };
for( int32 Orientation = 0; Orientation < 8; Orientation++ )
{
// TODO If any dimension is less than 1 pixel shrink dimension to zero
OrientChart( Chart, Orientation );
FVector2D ChartSize = Chart.MaxUV - Chart.MinUV;
ChartSize = ChartSize.X * Chart.PackingScaleU + ChartSize.Y * Chart.PackingScaleV;
// Only need half pixel dilate for rects
FAllocator2D::FRect Rect;
Rect.X = 0;
Rect.Y = 0;
Rect.W = FMath::CeilToInt( FMath::Abs( ChartSize.X ) + 1.0f );
Rect.H = FMath::CeilToInt( FMath::Abs( ChartSize.Y ) + 1.0f );
// Just in case lack of precision pushes it over
Rect.W = FMath::Min( TextureResolution, Rect.W );
Rect.H = FMath::Min( TextureResolution, Rect.H );
const bool bRectPack = false;
if( bRectPack )
{
if( LayoutRaster.Find( Rect ) )
{
// Is best?
if( Rect.X + Rect.Y * TextureResolution < BestRect.X + BestRect.Y * TextureResolution )
{
BestOrientation = Orientation;
BestRect = Rect;
}
}
else
{
continue;
}
}
else
{
uint32 BeginRasterize = FPlatformTime::Cycles();
RasterizeChart( Chart, Rect.W, Rect.H );
RasterizeCycles += FPlatformTime::Cycles() - BeginRasterize;
uint32 BeginFind = FPlatformTime::Cycles();
bool bFound = LayoutRaster.Find( Rect, ChartRaster );
FindCycles += FPlatformTime::Cycles() - BeginFind;
if( bFound )
{
// Is best?
if( Rect.X + Rect.Y * TextureResolution < BestRect.X + BestRect.Y * TextureResolution )
{
BestOrientation = Orientation;
BestRect = Rect;
}
}
else
{
continue;
}
}
}
if( BestOrientation >= 0 )
{
// Add chart to layout
OrientChart( Chart, BestOrientation );
uint32 BeginRasterize = FPlatformTime::Cycles();
RasterizeChart( Chart, BestRect.W, BestRect.H );
RasterizeCycles += FPlatformTime::Cycles() - BeginRasterize;
LayoutRaster.Alloc( BestRect, ChartRaster );
Chart.PackingBias.X += BestRect.X;
Chart.PackingBias.Y += BestRect.Y;
}
else
{
// Found no orientation that fit
return false;
}
}
double EndPackCharts = FPlatformTime::Seconds();
UE_LOG(LogLayoutUV, Display, TEXT("PackCharts: %s"), *FPlatformTime::PrettyTime(EndPackCharts - BeginPackCharts));
UE_LOG(LogLayoutUV, Display, TEXT(" Rasterize: %u"), RasterizeCycles);
UE_LOG(LogLayoutUV, Display, TEXT(" Find: %u"), FindCycles);
return true;
}
void FLayoutUV::OrientChart( FMeshChart& Chart, int32 Orientation )
{
switch( Orientation )
{
case 0:
// 0 degrees
Chart.PackingScaleU = FVector2D( Chart.UVScale.X, 0 );
Chart.PackingScaleV = FVector2D( 0, Chart.UVScale.Y );
Chart.PackingBias = -Chart.MinUV.X * Chart.PackingScaleU - Chart.MinUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 1:
// 0 degrees, flip x
Chart.PackingScaleU = FVector2D( -Chart.UVScale.X, 0 );
Chart.PackingScaleV = FVector2D( 0, Chart.UVScale.Y );
Chart.PackingBias = -Chart.MaxUV.X * Chart.PackingScaleU - Chart.MinUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 2:
// 90 degrees
Chart.PackingScaleU = FVector2D( 0, -Chart.UVScale.X );
Chart.PackingScaleV = FVector2D( Chart.UVScale.Y, 0 );
Chart.PackingBias = -Chart.MaxUV.X * Chart.PackingScaleU - Chart.MinUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 3:
// 90 degrees, flip x
Chart.PackingScaleU = FVector2D( 0, Chart.UVScale.X );
Chart.PackingScaleV = FVector2D( Chart.UVScale.Y, 0 );
Chart.PackingBias = -Chart.MinUV.X * Chart.PackingScaleU - Chart.MinUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 4:
// 180 degrees
Chart.PackingScaleU = FVector2D( -Chart.UVScale.X, 0 );
Chart.PackingScaleV = FVector2D( 0, -Chart.UVScale.Y );
Chart.PackingBias = -Chart.MaxUV.X * Chart.PackingScaleU - Chart.MaxUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 5:
// 180 degrees, flip x
Chart.PackingScaleU = FVector2D( Chart.UVScale.X, 0 );
Chart.PackingScaleV = FVector2D( 0, -Chart.UVScale.Y );
Chart.PackingBias = -Chart.MinUV.X * Chart.PackingScaleU - Chart.MaxUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 6:
// 270 degrees
Chart.PackingScaleU = FVector2D( 0, Chart.UVScale.X );
Chart.PackingScaleV = FVector2D( -Chart.UVScale.Y, 0 );
Chart.PackingBias = -Chart.MinUV.X * Chart.PackingScaleU - Chart.MaxUV.Y * Chart.PackingScaleV + 0.5f;
break;
case 7:
// 270 degrees, flip x
Chart.PackingScaleU = FVector2D( 0, -Chart.UVScale.X );
Chart.PackingScaleV = FVector2D( -Chart.UVScale.Y, 0 );
Chart.PackingBias = -Chart.MaxUV.X * Chart.PackingScaleU - Chart.MaxUV.Y * Chart.PackingScaleV + 0.5f;
break;
}
}
// Max of 2048x2048 due to precision
// Dilate in 28.4 fixed point. Half pixel dilation is conservative rasterization.
// Dilation same as Minkowski sum of triangle and square.
template< typename TShader, int32 Dilate >
void RasterizeTriangle( TShader& Shader, const FVector2D Points[3], int32 ScissorWidth, int32 ScissorHeight )
{
const FVector2D HalfPixel( 0.5f, 0.5f );
FVector2D p0 = Points[0] - HalfPixel;
FVector2D p1 = Points[1] - HalfPixel;
FVector2D p2 = Points[2] - HalfPixel;
// Correct winding
float Facing = ( p0.X - p1.X ) * ( p2.Y - p0.Y ) - ( p0.Y - p1.Y ) * ( p2.X - p0.X );
if( Facing < 0.0f )
{
Swap( p0, p2 );
}
// 28.4 fixed point
const int32 X0 = (int32)( 16.0f * p0.X + 0.5f );
const int32 X1 = (int32)( 16.0f * p1.X + 0.5f );
const int32 X2 = (int32)( 16.0f * p2.X + 0.5f );
const int32 Y0 = (int32)( 16.0f * p0.Y + 0.5f );
const int32 Y1 = (int32)( 16.0f * p1.Y + 0.5f );
const int32 Y2 = (int32)( 16.0f * p2.Y + 0.5f );
// Bounding rect
int32 MinX = ( FMath::Min3( X0, X1, X2 ) - Dilate + 15 ) / 16;
int32 MaxX = ( FMath::Max3( X0, X1, X2 ) + Dilate + 15 ) / 16;
int32 MinY = ( FMath::Min3( Y0, Y1, Y2 ) - Dilate + 15 ) / 16;
int32 MaxY = ( FMath::Max3( Y0, Y1, Y2 ) + Dilate + 15 ) / 16;
// Clip to image
MinX = FMath::Clamp( MinX, 0, ScissorWidth );
MaxX = FMath::Clamp( MaxX, 0, ScissorWidth );
MinY = FMath::Clamp( MinY, 0, ScissorHeight );
MaxY = FMath::Clamp( MaxY, 0, ScissorHeight );
// Deltas
const int32 DX01 = X0 - X1;
const int32 DX12 = X1 - X2;
const int32 DX20 = X2 - X0;
const int32 DY01 = Y0 - Y1;
const int32 DY12 = Y1 - Y2;
const int32 DY20 = Y2 - Y0;
// Half-edge constants
int32 C0 = DY01 * X0 - DX01 * Y0;
int32 C1 = DY12 * X1 - DX12 * Y1;
int32 C2 = DY20 * X2 - DX20 * Y2;
// Correct for fill convention
C0 += ( DY01 < 0 || ( DY01 == 0 && DX01 > 0 ) ) ? 0 : -1;
C1 += ( DY12 < 0 || ( DY12 == 0 && DX12 > 0 ) ) ? 0 : -1;
C2 += ( DY20 < 0 || ( DY20 == 0 && DX20 > 0 ) ) ? 0 : -1;
// Dilate edges
C0 += ( abs(DX01) + abs(DY01) ) * Dilate;
C1 += ( abs(DX12) + abs(DY12) ) * Dilate;
C2 += ( abs(DX20) + abs(DY20) ) * Dilate;
for( int32 y = MinY; y < MaxY; y++ )
{
for( int32 x = MinX; x < MaxX; x++ )
{
// same as Edge1 >= 0 && Edge2 >= 0 && Edge3 >= 0
int32 IsInside;
IsInside = C0 + (DX01 * y - DY01 * x) * 16;
IsInside |= C1 + (DX12 * y - DY12 * x) * 16;
IsInside |= C2 + (DX20 * y - DY20 * x) * 16;
if( IsInside >= 0 )
{
Shader.Process( x, y );
}
}
}
}
void FLayoutUV::RasterizeChart( const FMeshChart& Chart, uint32 RectW, uint32 RectH )
{
// Bilinear footprint is -1 to 1 pixels. If packed geometrically, only a half pixel dilation
// would be needed to guarantee all charts were at least 1 pixel away, safe for bilinear filtering.
// Unfortunately, with pixel packing a full 1 pixel dilation is required unless chart edges exactly
// align with pixel centers.
ChartRaster.Clear();
for( uint32 Tri = Chart.FirstTri; Tri < Chart.LastTri; Tri++ )
{
FVector2D Points[3];
for( int k = 0; k < 3; k++ )
{
const FVector2D& UV = TexCoords[ 3 * SortedTris[ Tri ] + k ];
Points[k] = UV.X * Chart.PackingScaleU + UV.Y * Chart.PackingScaleV + Chart.PackingBias;
}
RasterizeTriangle< FAllocator2DShader, 16 >( ChartShader, Points, RectW, RectH );
}
}
void FLayoutUV::CommitPackedUVs()
{
// Alloc new UV channel
RawMesh->WedgeTexCoords[ DstChannel ].SetNumUninitialized( TexCoords.Num() );
// Commit chart UVs
for( int32 i = 0; i < Charts.Num(); i++ )
{
FMeshChart& Chart = Charts[i];
Chart.PackingScaleU /= TextureResolution;
Chart.PackingScaleV /= TextureResolution;
Chart.PackingBias /= TextureResolution;
for( uint32 Tri = Chart.FirstTri; Tri < Chart.LastTri; Tri++ )
{
for( int k = 0; k < 3; k++ )
{
uint32 Index = 3 * SortedTris[ Tri ] + k;
const FVector2D& UV = TexCoords[ Index ];
RawMesh->WedgeTexCoords[ DstChannel ][ Index ] = UV.X * Chart.PackingScaleU + UV.Y * Chart.PackingScaleV + Chart.PackingBias;
}
}
}
}