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 ========================== MAJOR FEATURES + CHANGES ========================== Change 3294073 on 2017/02/09 by Thomas.Sarkanen Delete old location of FriendViewModel.h that did not get picked up correctly in the merge Change 3354003 on 2017/03/20 by Thomas.Sarkanen Back out changelist 3353914 Change 3355932 on 2017/03/21 by Thomas.Sarkanen Back out changelist 3354003 Reinstating merge from Main: Merging //UE4/Dev-Main to Dev-AnimPhys (//UE4/Dev-AnimPhys) @ CL 3353839 Change 3358776 on 2017/03/22 by Ori.Cohen Recreate physics when changing mobility of components - fixes debug rendering issue #JIRA UE-39328 Change 3360075 on 2017/03/23 by James.Golding Back out changelist 3358776, as it was made after smoke testing. Will re-submit after copy to main. Change 3362695 on 2017/03/24 by Thomas.Sarkanen Use correct extender when building menus for AnimationEditor, SkeletalMeshEditor & SkeletonEditor #jira UE-41100 - Persona code calling incorrect extenders when building menus. Change 3362696 on 2017/03/24 by Thomas.Sarkanen Facial animation experimental feature now pops up restart editor toast #jira UE-40989 - User is not prompted to restart the editor when enabling the Facial Animation Importer Change 3362699 on 2017/03/24 by Thomas.Sarkanen Enable snapping for translation in animation viewports #jira UE-39747 - Translating sockets in Persona is not affected by Snap Options #jira UE-39748 - Translating bones in Persona is not affected by Snap Options Change 3362700 on 2017/03/24 by Thomas.Sarkanen Curve source nodes now dont update their source title if the source is a pin The title of the node is not recached when the pin default changes, so we cant display it in the title #jira UE-40108 - When changing a Curve Source Node doesn't update source binding name when typed in Change 3362721 on 2017/03/24 by Thomas.Sarkanen CVar r.SkeletalMeshLODBias no longer affects meshes in animation viewports #jira UE-35733 - GSkeletalMeshLODBias affects render LOD in Persona Change 3362724 on 2017/03/24 by Thomas.Sarkanen Fix animation preview scene keyboard bindings Also moved advanced preview scene into its own module #jira UE-41505 - Pressing the hotkey I and O do not hide the floor and environment if the Preview Scene Settings tab isn't opened. Change 3362783 on 2017/03/24 by Ori.Cohen Fix incorrect locking and check when an editor static shape is changed. Originally this code assumed it would only get called at runtime, where there is only 1 shape. In the case of the async scene and static shapes - there are two shapes and if you update the body setup in the editor this will crash. Change 3362796 on 2017/03/24 by Ori.Cohen Fix welded capsules not using the right rotation #JIRA UE-41546 Change 3362803 on 2017/03/24 by Ori.Cohen Recreate physics when changing mobility of components - fixes debug rendering issue #JIRA UE-39328 Change 3363013 on 2017/03/24 by Thomas.Sarkanen Fix CIS issue Change 3363202 on 2017/03/24 by Ori.Cohen Fix bad #endif Change 3363295 on 2017/03/24 by Ori.Cohen Remove bWelded and use WeldParent everywhere. This fixes a bad case where a kinematic is returned as the root welded of simulated children. #JIRA UE-40783 Change 3363435 on 2017/03/24 by Jon.Nabozny Fix infinite jump on client when Jump Max Hold Time is not 0. #jira UE-41004 Change 3363451 on 2017/03/24 by Jon.Nabozny Fix USkeletalMeshComponent::PoseTickedThisFrame to use a frame counter instead of world time (which isn't changed while paused). #jira UE-39567 Change 3363504 on 2017/03/24 by Jon.Nabozny Fix SCE_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A to treat result data as linked list instead of array. (Code originally came from SCE via UDN) #jira UE-41049 Change 3363532 on 2017/03/24 by Ori.Cohen Change default to SkipSimulatedBones. #JIRA UE-41404 Change 3363537 on 2017/03/24 by Ori.Cohen Remove the unsupported option of HideBone with DisableCollision. If we want this in the future we can add it. #JIRA UE-30826 Change 3363681 on 2017/03/24 by Jon.Nabozny Reapply 3292286 after it was clobbered by 3360002 (Only clobbered in Ocean). #jira UE-37512 Change 3363722 on 2017/03/24 by mason.seay Updated map to improve testing Change 3365351 on 2017/03/27 by Thomas.Sarkanen Fix non-PCH CIS errors #jira UE-43335 - CIS fail: 'FAdvancedPreviewSceneCommands is not a class name Change 3365422 on 2017/03/27 by Thomas.Sarkanen Fixed skinned mesh LOD not updating in editor under various physics-related circumstances #jira UE-42013 - Skeletal meshes with recently generated LODs will not change LODs in viewport Change 3365423 on 2017/03/27 by Thomas.Sarkanen Fix ensure when setting Spline IK Bone Axis to 'None' Node is currently not in use, so I've avoided deprecating this for now. Values should be preserved. #jira UE-39723 - Ensure when setting Bone Axis to None in Spline IK Change 3365424 on 2017/03/27 by Thomas.Sarkanen Fix control rigs being convertable to possessables Also prevent 'export to anim sequence' button from appearing on standard level sequences #jira UE-42070 - Crash converting a control rig to Possessable Change 3365425 on 2017/03/27 by Thomas.Sarkanen When selecting bones that are non-required, we dont render gizmos or text labels #jira UE-35737 - Bone labels flicker between two locations when removing from LOD levels in Persona Change 3365426 on 2017/03/27 by Thomas.Sarkanen Fixed foot placement IK doing line traces on worker threads Also exposed const access to required bones array (the proxy already does this, so I figure the instance should get it too). Note: Node is not actually used #jira UE-43179 - Fix FAnimNode_FootPlacementIK doing line trace on worker thread Change 3365428 on 2017/03/27 by Thomas.Sarkanen Skeleton selection no does not lag behind choice made in viewport #jira UE-42236 - Mesh Overlay always lags behind actual selection Change 3365429 on 2017/03/27 by Thomas.Sarkanen Fix anim BPs that contain state machines having blank palettes #jira UE-42172 - AnimBP Palette is blank on existing AnimBPs Change 3365435 on 2017/03/27 by Jurre.deBaare Undoing Channel selection in Paint mode does not work #fix use duplicated CDO object instead of CDO directly #jira UE-42621 Change 3365436 on 2017/03/27 by Jurre.deBaare Functionless undo/redo transactions created when importing Alembic file and changing conversion settings #fix use duplicated CDO object instead of CDO directly #jira UE-40347 Change 3365437 on 2017/03/27 by Jurre.deBaare Ensure that HLOD cannot be set up in sub-levels #fix added functionality to check whether or not a level is used as a sub/streaming level in any other content, and if so show a widget similar to the checkout notice to inform the user why the hlod outliner is disabled #jira UE-40301 Change 3365438 on 2017/03/27 by Jurre.deBaare 2D Blendspace breaks when aligning samples in a straight line #fix for some reason the number of samples to considerate was limit to 3 (old code) so removed it since it doesn't make sense #jira UE-42744 Change 3365439 on 2017/03/27 by Jurre.deBaare Color View Mode doesn't reset when closing/reopening editor #fix added a delegate to OnEditorClose to ensure we can reset the viewport modes while they still exist, this issue would happen if the user closes the editor with mesh paint mode open, exiting the mode would only happen after destructing most of the editor #jira UE-42466 Change 3365440 on 2017/03/27 by Jurre.deBaare LOD Model field scrubbing is very sensitive #fix made sure that max slider value is also driven by data #jira UE-42980 Change 3365441 on 2017/03/27 by Jurre.deBaare Replacing an asset in a sequence player node does not register in undo history #fix added transaction for dropping anim assets on player nodes #jira UE-39152 Change 3365442 on 2017/03/27 by Jurre.deBaare Crash on Mac importing some alembic files #fix disable multithread abc file reading with HDF5 backend #jira UE-39783 Change 3365443 on 2017/03/27 by Jurre.deBaare Moving over Alembic feature: "Add support for 'finding' materials according to the face-set names in abc files" Change 3365444 on 2017/03/27 by Jurre.deBaare Moving over Alembic fixes: - Time offset issues > changed how we determine frame indices and frame steps - Incorrect tangent issues > enforce tangent calculation during skeletal mesh import - Automatically set recompute tangent flag on skeletal mesh sections Change 3365451 on 2017/03/27 by Jurre.deBaare CIS: include needed for GetTransientPackage Change 3365460 on 2017/03/27 by Jurre.deBaare CIS: apparently CIS wants explicit cast to UObject Change 3365463 on 2017/03/27 by Jurre.deBaare CIS: final fix, was actually UPackage include missing ugh Change 3365486 on 2017/03/27 by Thomas.Sarkanen Fix case error in include for Linux builds Change 3365499 on 2017/03/27 by James.Golding Fix editing collision settings sometimes not 'sticking' - wait until after LoadProfileConfig (which modified Profiles array) to copy to 'SavedData' Fix FCollsiionProfileData typo #jira UE-42490 Change 3365500 on 2017/03/27 by Jurre.deBaare Turning on LOD Model Painting does not copy paint to other LODs on Skeletal Mesh #fix neede to pre edit / post edit the skeletal mesh to ensure the color buffers were re-initialised #jira UE-42637 Change 3365501 on 2017/03/27 by James.Golding Disable rather than hide anim picker when no mesh assigned #jira UE-35537 Change 3365505 on 2017/03/27 by James.Golding Fix USkinnedMeshComponent::FindClosestBone ignoring bRequirePhysicsAsset, because BodySetupIndexMap.FindRef would return 0 if bone not present https://udn.unrealengine.com/questions/353125/uskinnedmeshcomponentfindclosestbone-does-not-resp.html #jira UE-43061 Change 3365515 on 2017/03/27 by Jurre.deBaare Using Alt+Click to break Transition node connections cannot be undone using the Undo command #fix added transactions to breaking node links #jira UE-39068 Change 3365528 on 2017/03/27 by Jurre.deBaare Blendspaces don't update to reflect sync marker changes that have occurred on animations in the blend #fix added validation on blendspaces when removing or adding sync markers in animation editor #jira UE-39814 Change 3365535 on 2017/03/27 by Jurre.deBaare Camera moves at the same time as erasing paint when clicking Paint settings and back into Viewport #fix lock camera if erasing as well #jira UE-42474 Change 3365539 on 2017/03/27 by Jurre.deBaare Painting on animation sequence paints on invisible T-pose skeleton #fix during painting enforce bUseRefPoseOnInitAnim so you're always painting on a ref-pose skeletal mesh #jira UE-42484 Change 3365545 on 2017/03/27 by Jurre.deBaare Fill Tool does not fill the mesh with the selected Texture Weight Index when used in Weight Vertex Painting #fix added function to generate color according to texture weight settings and index, and fill using that color when the editor is in texture weight mode #jira UE-43091 Change 3365549 on 2017/03/27 by Jurre.deBaare CIS: include fix Change 3365555 on 2017/03/27 by Jurre.deBaare Missing change from 3365545 Change 3365561 on 2017/03/27 by Jurre.deBaare Missing change from 3365545 Change 3365918 on 2017/03/27 by Ori.Cohen PR #3424: fix compile error when PHYSX_MEMORY_STATS=1 (Contributed by kayama-shift) #JIRA UE-43337 Change 3366234 on 2017/03/27 by Ori.Cohen Moved call to UpdateHasValidBodies into OnRegister. Original suggested fix came from github, but I think the bounds need to update even if OnCreatePhysics isn't called. #JIRA UE-43339 #pr 3427 Change 3367252 on 2017/03/28 by Jurre.deBaare Vertex Weight Painting options are no longer present in MeshPaint mode #fix implemented singleton pattern for paint mode settings to ensure customization works correctly #jira UE-43357 Change 3367340 on 2017/03/28 by Jurre.deBaare GC fix for recent changes to painting / alembic settings Change 3367445 on 2017/03/28 by Thomas.Sarkanen Fix fallout from Main merge OrionEditor needs to depend on the new AdvancedPreviewScene module. Change 3367741 on 2017/03/28 by Ori.Cohen Remove unused physics warning when moving simulated object during physics #jira UE-41465 Change 3368072 on 2017/03/28 by Ori.Cohen Removed collision aggregates from ISMC collision. This is not a good candidate for aggregates as the bounding boxes generated could be huge (for example several trees that are very far away). This fixes perf regression from the PhysX 3.4 upgrade #JIRA UE-42956 Change 3368479 on 2017/03/28 by Ori.Cohen Fix GetClosestPoint incorrectly considering welded bodies. #JIRA UE-39329 Change 3368620 on 2017/03/28 by Lina.Halper Added code comment #rb:none #jira: UE-38836 Change 3368752 on 2017/03/28 by mason.seay Updated test map since UE-338836 came back as by design Change 3369370 on 2017/03/29 by James.Golding Fix StaticMesh Editor drawing complex collision if 'use simple as complex' is enabled Add separate options for showing simple and complex collision in StaticMesh Editor #jira UE-42844 Change 3369438 on 2017/03/29 by Lina.Halper Fix issue where it updates more often when you give too high update rate - i.e. 60 #jira: UE-38844 #code review: Laurent.Delayen Change 3369462 on 2017/03/29 by Thomas.Sarkanen Add preview mesh to animation blueprints Auto set preview mesh for animation assets and animation blueprints based off meshes. #jira UE-40032 - Creating animation assets from right-clicking skel mesh results in showing incorrect mesh in Persona Change 3369463 on 2017/03/29 by Thomas.Sarkanen We now select current preview mesh in the preview mesh dropdown Change 3369464 on 2017/03/29 by Thomas.Sarkanen Ticks now appear properly on anim sequence scrub bar #jira UE-41197 - No additional ticks appear on the timeline when you zoom in Change 3369465 on 2017/03/29 by Thomas.Sarkanen Persona now depends publicly on SkeletonEditor GitHub PR #3431 from kayama-shift #jira UE-43344 - GitHub 3431 : Persona module must depend on SkeletonEditor publically Change 3369466 on 2017/03/29 by Thomas.Sarkanen Blueprints are now only dirtied if edited properties are from objects in the Blueprint's package #jira UE-38956 - Changing Bone Transform between World and Local Location dirties Anim Blueprints Change 3369467 on 2017/03/29 by Thomas.Sarkanen Tweaked combo box alignment for world/local transform Appearance is now closer to that of the level editor Change 3369469 on 2017/03/29 by Thomas.Sarkanen Prevent switching to unsupported widget modes when editing control rigs #jira UE-42557 - When in Animation Mode, if you select a controller with the translation gizmo active, when you select a non animation controller the gizmo renders incorrectly Change 3369588 on 2017/03/29 by Thomas.Sarkanen Make import data and thumbnail data transactional #jira UE-41579 - Undo not working for several Asset Details options in Skeletal Mesh editor (Persona) Change 3369609 on 2017/03/29 by Thomas.Sarkanen Montages are now correctly initialized when created They can be used straight away without first opening them in the montage edtior. Moved initialization of composite sections from SMontageEditor to AnimMontageFactory. #jira UE-41219 - Animation montages created via Right click Create montage, that aren't opened in persona will not animate when used in an AnimBP Change 3369610 on 2017/03/29 by Thomas.Sarkanen Fixed anim class being set by name, causing duplicate-named anim BPs to be incorrectly resolved #jira UE-39833 - SkelMesh Component > Anim Class does not handle identical AnimBP names Change 3369647 on 2017/03/29 by Ori.Cohen Fix mass debug not working when moving components in the editor. #JIRA UE-43437 Change 3369875 on 2017/03/29 by Ori.Cohen Added the call into overlap instead of computePenetration when possible. There are edge cases that exist when computePenetration returns false but overlap returns true. Originally we were using computePenetration for all overlaps to make it consistent. However, computePenetration doesn't handle trimeshes correctly in certain cases. The reason for this is so that we can at least resolve a known issue with overlap events with complex collision. Nvidia may give us a flag for computePenetration to not do back-face culling which would give us a more consistent result. #JIRA UE-25923 Change 3370144 on 2017/03/29 by Lina.Halper Fix on the retargeting child anim BP #rb: Martin.Wilson #jira: UE-39928 Change 3371480 on 2017/03/30 by James.Golding Changes by BryanG at Nvidia required to get them up and running with plugins that override some skeletal mesh stuff. #jira UE-42132 Change 3371484 on 2017/03/30 by Thomas.Sarkanen We now propagate thread safety flags to child Animation Blueprints Change 3371486 on 2017/03/30 by Thomas.Sarkanen Fixed crash and effector selection issues for two bone IK Crash occured when selecting the root bone as the end of the chain. Viewport selection was not working at all as we werent creating any selection proxy actors any more. Change 3371497 on 2017/03/30 by Thomas.Sarkanen Added warning when Sequencer tries to write to properties that have changed type #jira UE-42098 - Sequencer writes rotation data to transform properties, causes assert Change3371614on 2017/03/30 by Martin.Wilson Clean up deprecated property value after load so we dont get extra anim blueprint references #jira UE-42501 Change 3371619 on 2017/03/30 by James.Golding Fix CIS by removing unneeded UNREALED_API from FImportSkeletalMeshArgs Change 3372452 on 2017/03/30 by Jon.Nabozny Add flag to CharacterMovementComponent to determine whether or not character should Sweep while using NavWalking, instead of relying on GenerateOverlaps flag. #jira UE-36546 Change 3373043 on 2017/03/30 by Lina.Halper - Consolidating to FastLerp to simply code - Added comment on FastLerp as the result is not normalized #rb: Michael.Noland, David.Hill Change 3373292 on 2017/03/30 by Jeff.Farris Made Actor.DispatchPhysicsCollisionHit virtual. (Copied CL 3261103 from Robo Recall to Dev-AnimPhys) Change 3373293 on 2017/03/30 by Jeff.Farris Added PhysicalAnimationComponent.GetBodyTargetTransform (Copied 3256175 from RoboRecall to Dev-AnimPhys) Change 3373318 on 2017/03/30 by Jeff.Farris Added option for components to opt out of physics impulses on damage. (Copied CL 3282585 from RoboRecall to Dev-AnimPhys) Change 3374064 on 2017/03/31 by Thomas.Sarkanen Renaming curves in animations is now saved properly Guids were not being set, so loading code obliterated the rename after an editor restart #jira UE-40582 - Curve name is not updated when user changes it in persona editor. Change 3374175 on 2017/03/31 by Thomas.Sarkanen Two bone IK UI consistency fixes Completely removed all bone select actors. They have a terrible UX anyway. We now rely only on our custom hit proxy and use a unified path to generate effector locations to draw and manipulate. #jira UE-42359 - Two Bone IK modifying effector location from viewport when bone is not set Change3374216on 2017/03/31 by Martin.Wilson Clear raw curve data during cooking + change Paragon game code so that it reads from the compressed curve data on Anim Sequences, not the raw data #jira UE-37897 Change 3374253 on 2017/03/31 by Thomas.Sarkanen Supplementary fix to CL 3371497 Adding name exposure for property types that we support Change 3374298 on 2017/03/31 by Jeff.Farris Code change from nVidia that improves physX perf (afterIntegration time). (Copied CL 3302239 from Robo Recall to Dev-AnimPhys) Change 3374432 on 2017/03/31 by Lina.Halper Make sure AcitveBoneIndices contains parent #jira: UE-14597 #rb: Martin.Wilson #code review: Benn.Gallagher, Alexis.Matte, James.Golding Change 3374836 on 2017/03/31 by Jon.Nabozny Fixup hits returned before start of trace in raycast_capsule. This change is copied from CL-3297212 #jira UE-42532 Change 3375195 on 2017/03/31 by Lina.Halper resave animation sequence for increasing info to warning #code review: Thomas.Sarkanen #jira: UE-40643 Change 3375282 on 2017/03/31 by Lina.Halper Fix odin content for animation : resave animation sequence for increasing info to warning #code review: Thomas.Sarkanen #jira: UE-40643 Change 3375288 on 2017/03/31 by Lina.Halper reverting code of degrading warning to info to avoid warning #jira: UE-40643 Change 3375865 on 2017/04/01 by Lina.Halper fix build error Change 3375868 on 2017/04/01 by Lina.Halper fix build warning on shadow var #code review: Thomas.Sarkanen Change 3376283 on 2017/04/03 by Thomas.Sarkanen Reworked, more generic property path handling in object change listener This allows all paths (auto vs manual key) to use the same code path, as well as regularizing the special-case handling of structs and arrays (and combinations thereof). The gist of the new implementation is to traverse the property path until we come across any property that satisfies our crirtera (exposed function, Interp flag etc.) Tweaked PropertyHandleToPropertyPath to correctly handle the slightly different way FPropertyNodes are arranged inside IPropertyHandle chains. IPropertyHandle chains contain arrays in a manner designed for display in the property editor, e.g. Container.Array.Array[ArrayIndex].StructInner. We need to collapse adjacent array properties as we are looking for Container.Array[ArrayIndex].StructInner to form a well-formed 'property path'. Small tweak to FPropertyChangedParams::GetPropertyValue. This is to handle property paths that end in an array (e.g. Struct.Array[0]). #jira UE-42271 - Pressing enter while the spine is in IK doesn't set any keys Change 3376320 on 2017/04/03 by Benn.Gallagher Merging from OR39 - Fix for clothing perf regressions due to LOD switching leaving simulation enties in the clothing solver #jira OR-36926 Change 3376413 on 2017/04/03 by Benn.Gallagher Merge from OR - Fixes to stability due to component space being the simulation space, games like Paragon treat the mesh and component transforms differently and cause issues with added energy in the system. #jira OR-36927 Change 3376421 on 2017/04/03 by Benn.Gallagher Missed file from OR cloth fixes merge Change 3376456 on 2017/04/03 by Lina.Halper Resave fortnite animation to remove warning #rb: none Change 3376479 on 2017/04/03 by Jon.Nabozny Touch PhysXLibs to ensure physx links properly. (Needed for CL-3374836) Change 3376610 on 2017/04/03 by Martin.Wilson Fix for crash when reimporting lodded mesh with virtual bones #jira UE-43557 Change3376618on 2017/04/03 by Jurre.deBaare Mesh Paint decrement rate is too slow when used with hotkeys #fix change way we increase/decrease brush value #jira UE-43402 Change 3376650 on 2017/04/03 by Benn.Gallagher Converted clothing asset editor warnings into toasts + logs so they are more discoverable by the user. #jira UE-41739 Change 3376771 on 2017/04/03 by Benn.Gallagher Fixed clothing selection dropdowns to no longer get out of sync with actual state (if the binding fails it falls back to the "None" selection correctly internally) #jira UE-41737 Change 3378117 on 2017/04/04 by Thomas.Sarkanen Prevented ensure by reverting to default unit vectors if zero-sized lookat/lookup vectors are specified Also added a warning to inform users of the issue. #jira UE-43360 - Enabling Use LookUp Axis causes handled ensure to occur Change 3378118 on 2017/04/04 by Thomas.Sarkanen Fix 2-bone IK flickering First changed non-triangular range check to be >= instead of >. Also removed fallback to doubled-back as this singularity also occurs at min stretch + float epsilon (which caused the flicker). We are OK to call ACos with out-of-range values as it clamps internally and the subsequent calculations work in these degenerate cases. #jira UE-43525 - Mesh Flicker when Two Bone IK is stretched Change 3378192 on 2017/04/04 by Benn.Gallagher Trivial fix to clothing dropdowns to unbind clothing assets when switching Change 3378206 on 2017/04/04 by Thomas.Sarkanen Fix IncludeTool static analysis errors Traced this back to the checkin that moved advanced preview scene into its own module. Further refined this to be the dependency on SharedSettingsWidgets. Simplest solution was to move the details customization back into the DetailsCustomizations module. #jira UE-43563 - //UE4/Dev-AnimPhys: Compile IncludeTool Static analysis Win64 completed with 1 error Change 3378207 on 2017/04/04 by Thomas.Sarkanen Fix static analysis warning Change 3378267 on 2017/04/04 by Benn.Gallagher Disabled "Accurate" wind mode selection for 4.16 by hiding enum option for windmode. Looks like there are some simulation issues that need resolved. Wind will act as it always has for 4.16. Change 3378268 on 2017/04/04 by Benn.Gallagher Fixed bounds calculation on clothing to stop dissapearances or flickering at the edge of the screen. #jira UE-40407 Change 3378342 on 2017/04/04 by Jurre.deBaare Bone LOD preview doesn't get update if change removed bones array directly in Skeletalmesh editor #fix ensure that we update the skeleton tree when changes to the bones are made #misc made sure the reapply button its visibility is dynamic #misc made sure we always re-add previously removed bones when necessary #jira UE-40500 Change 3378374 on 2017/04/04 by Thomas.Sarkanen Fixed animation not working for dynamically-bound wheeled-vehicle anim instances Vehicle anim instance now looks for a wheeled vehicle component when it initializes. #jira UE-41048 - Set Anim Instance Class no longer works with Wheeled Vehicle Pawns Change 3378518 on 2017/04/04 by Jon.Nabozny Fix jump height becoming frame dependent when JumpMaxHoldTime > 0. #jira UE-23695 Change 3378538 on 2017/04/04 by Martin.Wilson Speculative fix for unreproducable crash on loading animations #jira OR-37157 Change 3378592 on 2017/04/04 by Jurre.deBaare Mesh painting tools not working outside of QAGame #fix required a direct loadmodule call Change 3378622 on 2017/04/04 by Jurre.deBaare CIS shadow variable fix Change 3378742 on 2017/04/04 by Thomas.Sarkanen Fixed crash entering PIE in Orion Change 3378907 on 2017/04/04 by Benn.Gallagher Re-added wind adaptiive effects to legacy wind path. Fixed math error in damping calculations (log2/loge). #jira UE-42044 Change 3379240 on 2017/04/04 by Jon.Nabozny Fix SkeletalMeshComponent GetWeldedBodies not returning actual Skeletal Bodies Change 3379415 on 2017/04/04 by Jon.Nabozny Fix Skeletal Meshes imported with non-identity transform from reapplying transform when simulation is enabled / disabled. #JIRA UE-32598 Change 3379502 on 2017/04/04 by Lina.Halper Mirroring issue with flipping rotation due to not counting scale #jira: UE-33646, UE-34489 #rb: Ori.Cohen #code review: Zak.Middleton Change 3380544 on 2017/04/05 by Thomas.Sarkanen Fix warning spam when playing back UMG sequences animating visibility Reduced runtime overhead to a simple size check. This catches 90% of problem issues with much less runtime overhead. It would be well worth getting an FName-based type check working at some point, although we would need speciall case enum handling. Also removed message log usage in shipping/test builds. #jira UE-43621 - Large number of Sequencer warnings occur after opening Paragon in Pie - Property type mismatch for property 'Visibility'. Expected 'uint8', found 'ESlateVisibility'. Change 3380605 on 2017/04/05 by Benn.Gallagher Fix clothing simulation to correctly index a master pose component for clothing pose data. Fixes integration blocking crash in UT when picking up the flag which sets a master pose mid-game. #jira UE-43624 Change 3381025 on 2017/04/05 by Jon.Nabozny Change ensure in UpdateKinematicBonesToAnim to ensureMsgf so we can track down potential content issues. Change 3381475 on 2017/04/05 by Lina.Halper Fix crash on creating montage and setting slot node #jira: UE-43698 #rb: Ori.Cohen Change 3381558 on 2017/04/05 by Lina.Halper Better clean up of flipping issue of quaternion issue #jira: UE-33646, UE-34489 #rb: Zak.Middleton Change 3381789 on 2017/04/05 by Jon.Nabozny Revert InitAnim changes from CL-3379415 to fix cook warnings. (Will have proper fix for 4.17 instead). Change 3381978 on 2017/04/05 by Aaron.McLeran #jira UE-43654 Fixing oculus audio SDK sample rate issue Change 3383181 on 2017/04/06 by Lina.Halper Remove copy right of Dual quaternion since we no longer uses #code review:Leslie.Nivison Change 3383283 on 2017/04/06 by Ori.Cohen Fixed missing invalidation for body setup when modifying materials #JIRA UE-43680 Change 3383602 on 2017/04/06 by Lina.Halper Fixed scroller in retargeting window #jira: UE-43736 Change 3384369 on 2017/04/07 by Jurre.deBaare Wrong bone is sometimes highlighted in skeleton editor #fix new behaviour caused the user to try and select a bone if we didn't find on through hit proxies #jira UE-43735 Change 3384744 on 2017/04/07 by Jon.Nabozny Fix body rotation being killed on load. #JIRA UE-43771 [CL 3385232 by Thomas Sarkanen in Main branch]
1741 lines
59 KiB
C++
1741 lines
59 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MeshPaintHelpers.h"
|
|
|
|
#include "ComponentReregisterContext.h"
|
|
|
|
#include "MeshPaintTypes.h"
|
|
#include "MeshPaintSettings.h"
|
|
#include "IMeshPaintGeometryAdapter.h"
|
|
#include "MeshPaintAdapterFactory.h"
|
|
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "Engine/Texture2D.h"
|
|
#include "StaticMeshResources.h"
|
|
#include "RawMesh.h"
|
|
|
|
#include "GenericOctree.h"
|
|
#include "Utils.h"
|
|
|
|
#include "SlateApplication.h"
|
|
#include "SImportVertexColorOptions.h"
|
|
#include "EditorViewportClient.h"
|
|
#include "Interfaces/IMainFrameModule.h"
|
|
|
|
#include "Modules/ModuleManager.h"
|
|
#include "DesktopPlatformModule.h"
|
|
#include "EditorDirectories.h"
|
|
#include "PackageTools.h"
|
|
#include "FileHelpers.h"
|
|
#include "ISourceControlModule.h"
|
|
|
|
#include "Editor.h"
|
|
#include "LevelEditor.h"
|
|
#include "ILevelViewport.h"
|
|
#include "EditorViewportClient.h"
|
|
#include "LevelEditorViewport.h"
|
|
|
|
#include "VREditorMode.h"
|
|
#include "IVREditorModule.h"
|
|
#include "ViewportWorldInteraction.h"
|
|
#include "ViewportInteractableInterface.h"
|
|
#include "VREditorInteractor.h"
|
|
#include "EditorWorldExtension.h"
|
|
|
|
#include "ParallelFor.h"
|
|
|
|
void MeshPaintHelpers::RemoveInstanceVertexColors(UObject* Obj)
|
|
{
|
|
// Currently only static mesh component supports per instance vertex colors so only need to retrieve those and remove colors
|
|
AActor* Actor = Cast<AActor>(Obj);
|
|
if (Actor != NULL)
|
|
{
|
|
TArray<UStaticMeshComponent*> StaticMeshComponents;
|
|
Actor->GetComponents<UStaticMeshComponent>(StaticMeshComponents);
|
|
for (const auto& StaticMeshComponent : StaticMeshComponents)
|
|
{
|
|
if (StaticMeshComponent != NULL)
|
|
{
|
|
MeshPaintHelpers::RemoveComponentInstanceVertexColors(StaticMeshComponent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::RemoveComponentInstanceVertexColors(UStaticMeshComponent* StaticMeshComponent)
|
|
{
|
|
if (StaticMeshComponent != NULL && StaticMeshComponent->GetStaticMesh() != nullptr)
|
|
{
|
|
// Mark the mesh component as modified
|
|
StaticMeshComponent->Modify();
|
|
|
|
// If this is called from the Remove button being clicked the SMC wont be in a Reregister context,
|
|
// but when it gets called from a Paste or Copy to Source operation it's already inside a more specific
|
|
// SMCRecreateScene context so we shouldn't put it inside another one.
|
|
if (StaticMeshComponent->IsRenderStateCreated())
|
|
{
|
|
// Detach all instances of this static mesh from the scene.
|
|
FComponentReregisterContext ComponentReregisterContext(StaticMeshComponent);
|
|
|
|
StaticMeshComponent->RemoveInstanceVertexColors();
|
|
}
|
|
else
|
|
{
|
|
StaticMeshComponent->RemoveInstanceVertexColors();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool MeshPaintHelpers::PropagateColorsToRawMesh(UStaticMesh* StaticMesh, int32 LODIndex, FStaticMeshComponentLODInfo& ComponentLODInfo)
|
|
{
|
|
check(ComponentLODInfo.OverrideVertexColors);
|
|
check(StaticMesh->SourceModels.IsValidIndex(LODIndex));
|
|
check(StaticMesh->RenderData);
|
|
check(StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex));
|
|
|
|
bool bPropagatedColors = false;
|
|
FStaticMeshSourceModel& SrcModel = StaticMesh->SourceModels[LODIndex];
|
|
FStaticMeshRenderData& RenderData = *StaticMesh->RenderData;
|
|
FStaticMeshLODResources& RenderModel = RenderData.LODResources[LODIndex];
|
|
FColorVertexBuffer& ColorVertexBuffer = *ComponentLODInfo.OverrideVertexColors;
|
|
if (RenderData.WedgeMap.Num() > 0 && ColorVertexBuffer.GetNumVertices() == RenderModel.GetNumVertices())
|
|
{
|
|
// Use the wedge map if it is available as it is lossless.
|
|
FRawMesh RawMesh;
|
|
SrcModel.RawMeshBulkData->LoadRawMesh(RawMesh);
|
|
|
|
int32 NumWedges = RawMesh.WedgeIndices.Num();
|
|
if (RenderData.WedgeMap.Num() == NumWedges)
|
|
{
|
|
int32 NumExistingColors = RawMesh.WedgeColors.Num();
|
|
if (NumExistingColors < NumWedges)
|
|
{
|
|
RawMesh.WedgeColors.AddUninitialized(NumWedges - NumExistingColors);
|
|
}
|
|
for (int32 i = 0; i < NumWedges; ++i)
|
|
{
|
|
FColor WedgeColor = FColor::White;
|
|
int32 Index = RenderData.WedgeMap[i];
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
WedgeColor = ColorVertexBuffer.VertexColor(Index);
|
|
}
|
|
RawMesh.WedgeColors[i] = WedgeColor;
|
|
}
|
|
SrcModel.RawMeshBulkData->SaveRawMesh(RawMesh);
|
|
bPropagatedColors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there's no raw mesh data, don't try to do any fixup here
|
|
if (SrcModel.RawMeshBulkData->IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Fall back to mapping based on position.
|
|
FRawMesh RawMesh;
|
|
SrcModel.RawMeshBulkData->LoadRawMesh(RawMesh);
|
|
|
|
TArray<FColor> NewVertexColors;
|
|
FPositionVertexBuffer TempPositionVertexBuffer;
|
|
TempPositionVertexBuffer.Init(RawMesh.VertexPositions);
|
|
RemapPaintedVertexColors(
|
|
ComponentLODInfo.PaintedVertices,
|
|
*ComponentLODInfo.OverrideVertexColors,
|
|
RenderModel.PositionVertexBuffer,
|
|
RenderModel.VertexBuffer,
|
|
TempPositionVertexBuffer,
|
|
/*OptionalVertexBuffer=*/ NULL,
|
|
NewVertexColors
|
|
);
|
|
if (NewVertexColors.Num() == RawMesh.VertexPositions.Num())
|
|
{
|
|
int32 NumWedges = RawMesh.WedgeIndices.Num();
|
|
RawMesh.WedgeColors.Empty(NumWedges);
|
|
RawMesh.WedgeColors.AddZeroed(NumWedges);
|
|
for (int32 i = 0; i < NumWedges; ++i)
|
|
{
|
|
int32 Index = RawMesh.WedgeIndices[i];
|
|
RawMesh.WedgeColors[i] = NewVertexColors[Index];
|
|
}
|
|
SrcModel.RawMeshBulkData->SaveRawMesh(RawMesh);
|
|
bPropagatedColors = true;
|
|
}
|
|
}
|
|
return bPropagatedColors;
|
|
}
|
|
|
|
bool MeshPaintHelpers::PaintVertex(const FVector& InVertexPosition, const FMeshPaintParameters& InParams, FColor& InOutVertexColor)
|
|
{
|
|
float SquaredDistanceToVertex2D;
|
|
float VertexDepthToBrush;
|
|
if (MeshPaintHelpers::IsPointInfluencedByBrush(InVertexPosition, InParams, SquaredDistanceToVertex2D, VertexDepthToBrush))
|
|
{
|
|
// Compute amount of paint to apply
|
|
const float PaintAmount = ComputePaintMultiplier(SquaredDistanceToVertex2D, InParams.BrushStrength, InParams.InnerBrushRadius, InParams.BrushRadialFalloffRange, InParams.BrushDepth, InParams.BrushDepthFalloffRange, VertexDepthToBrush);
|
|
|
|
const FLinearColor OldColor = InOutVertexColor.ReinterpretAsLinear();
|
|
FLinearColor NewColor = OldColor;
|
|
|
|
if (InParams.PaintMode == EMeshPaintMode::PaintColors)
|
|
{
|
|
ApplyVertexColorPaint(InParams, OldColor, NewColor, PaintAmount);
|
|
|
|
}
|
|
else if (InParams.PaintMode == EMeshPaintMode::PaintWeights)
|
|
{
|
|
ApplyVertexWeightPaint(InParams, OldColor, PaintAmount, NewColor);
|
|
}
|
|
|
|
// Save the new color
|
|
InOutVertexColor.R = FMath::Clamp(FMath::RoundToInt(NewColor.R * 255.0f), 0, 255);
|
|
InOutVertexColor.G = FMath::Clamp(FMath::RoundToInt(NewColor.G * 255.0f), 0, 255);
|
|
InOutVertexColor.B = FMath::Clamp(FMath::RoundToInt(NewColor.B * 255.0f), 0, 255);
|
|
InOutVertexColor.A = FMath::Clamp(FMath::RoundToInt(NewColor.A * 255.0f), 0, 255);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Out of range
|
|
return false;
|
|
}
|
|
|
|
void MeshPaintHelpers::ApplyVertexColorPaint(const FMeshPaintParameters &InParams, const FLinearColor &OldColor, FLinearColor &NewColor, const float PaintAmount)
|
|
{
|
|
// Color painting
|
|
if (InParams.bWriteRed)
|
|
{
|
|
NewColor.R = (OldColor.R < InParams.BrushColor.R) ? FMath::Min(InParams.BrushColor.R, OldColor.R + PaintAmount) : FMath::Max(InParams.BrushColor.R, OldColor.R - PaintAmount);
|
|
}
|
|
|
|
if (InParams.bWriteGreen)
|
|
{
|
|
NewColor.G = (OldColor.G < InParams.BrushColor.G) ? FMath::Min(InParams.BrushColor.G, OldColor.G + PaintAmount) : FMath::Max(InParams.BrushColor.G, OldColor.G - PaintAmount);
|
|
}
|
|
|
|
if (InParams.bWriteBlue)
|
|
{
|
|
NewColor.B = (OldColor.B < InParams.BrushColor.B) ? FMath::Min(InParams.BrushColor.B, OldColor.B + PaintAmount) : FMath::Max(InParams.BrushColor.B, OldColor.B - PaintAmount);
|
|
}
|
|
|
|
if (InParams.bWriteAlpha)
|
|
{
|
|
NewColor.A = (OldColor.A < InParams.BrushColor.A) ? FMath::Min(InParams.BrushColor.A, OldColor.A + PaintAmount) : FMath::Max(InParams.BrushColor.A, OldColor.A - PaintAmount);
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ApplyVertexWeightPaint(const FMeshPaintParameters &InParams, const FLinearColor &OldColor, const float PaintAmount, FLinearColor &NewColor)
|
|
{
|
|
// Total number of texture blend weights we're using
|
|
check(InParams.TotalWeightCount > 0);
|
|
check(InParams.TotalWeightCount <= MeshPaintDefs::MaxSupportedWeights);
|
|
|
|
// True if we should assume the last weight index is composed of one minus the sum of all
|
|
// of the other weights. This effectively allows an additional weight with no extra memory
|
|
// used, but potentially requires extra pixel shader instructions to render.
|
|
//
|
|
// NOTE: If you change the default here, remember to update the MeshPaintWindow UI and strings
|
|
//
|
|
// NOTE: Materials must be authored to match the following assumptions!
|
|
const bool bUsingOneMinusTotal =
|
|
InParams.TotalWeightCount == 2 || // Two textures: Use a lerp() in pixel shader (single value)
|
|
InParams.TotalWeightCount == 5; // Five texture: Requires 1.0-sum( R+G+B+A ) in shader
|
|
check(bUsingOneMinusTotal || InParams.TotalWeightCount <= MeshPaintDefs::MaxSupportedPhysicalWeights);
|
|
|
|
// Prefer to use RG/RGB instead of AR/ARG when we're only using 2/3 physical weights
|
|
const int32 TotalPhysicalWeights = bUsingOneMinusTotal ? InParams.TotalWeightCount - 1 : InParams.TotalWeightCount;
|
|
const bool bUseColorAlpha =
|
|
TotalPhysicalWeights != 2 && // Two physical weights: Use RG instead of AR
|
|
TotalPhysicalWeights != 3; // Three physical weights: Use RGB instead of ARG
|
|
|
|
// Index of the blend weight that we're painting
|
|
check(InParams.PaintWeightIndex >= 0 && InParams.PaintWeightIndex < MeshPaintDefs::MaxSupportedWeights);
|
|
|
|
// Convert the color value to an array of weights
|
|
float Weights[MeshPaintDefs::MaxSupportedWeights];
|
|
{
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < InParams.TotalWeightCount; ++CurWeightIndex)
|
|
{
|
|
if (CurWeightIndex == TotalPhysicalWeights)
|
|
{
|
|
// This weight's value is one minus the sum of all previous weights
|
|
float OtherWeightsTotal = 0.0f;
|
|
for (int32 OtherWeightIndex = 0; OtherWeightIndex < CurWeightIndex; ++OtherWeightIndex)
|
|
{
|
|
OtherWeightsTotal += Weights[OtherWeightIndex];
|
|
}
|
|
Weights[CurWeightIndex] = 1.0f - OtherWeightsTotal;
|
|
}
|
|
else
|
|
{
|
|
switch (CurWeightIndex)
|
|
{
|
|
case 0:
|
|
Weights[CurWeightIndex] = bUseColorAlpha ? OldColor.A : OldColor.R;
|
|
break;
|
|
|
|
case 1:
|
|
Weights[CurWeightIndex] = bUseColorAlpha ? OldColor.R : OldColor.G;
|
|
break;
|
|
|
|
case 2:
|
|
Weights[CurWeightIndex] = bUseColorAlpha ? OldColor.G : OldColor.B;
|
|
break;
|
|
|
|
case 3:
|
|
check(bUseColorAlpha);
|
|
Weights[CurWeightIndex] = OldColor.B;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go ahead any apply paint!
|
|
Weights[InParams.PaintWeightIndex] += PaintAmount;
|
|
Weights[InParams.PaintWeightIndex] = FMath::Clamp(Weights[InParams.PaintWeightIndex], 0.0f, 1.0f);
|
|
|
|
|
|
// Now renormalize all of the other weights
|
|
float OtherWeightsTotal = 0.0f;
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < InParams.TotalWeightCount; ++CurWeightIndex)
|
|
{
|
|
if (CurWeightIndex != InParams.PaintWeightIndex)
|
|
{
|
|
OtherWeightsTotal += Weights[CurWeightIndex];
|
|
}
|
|
}
|
|
const float NormalizeTarget = 1.0f - Weights[InParams.PaintWeightIndex];
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < InParams.TotalWeightCount; ++CurWeightIndex)
|
|
{
|
|
if (CurWeightIndex != InParams.PaintWeightIndex)
|
|
{
|
|
if (OtherWeightsTotal == 0.0f)
|
|
{
|
|
Weights[CurWeightIndex] = NormalizeTarget / (InParams.TotalWeightCount - 1);
|
|
}
|
|
else
|
|
{
|
|
Weights[CurWeightIndex] = Weights[CurWeightIndex] / OtherWeightsTotal * NormalizeTarget;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The total of the weights should now always equal 1.0
|
|
float WeightsTotal = 0.0f;
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < InParams.TotalWeightCount; ++CurWeightIndex)
|
|
{
|
|
WeightsTotal += Weights[CurWeightIndex];
|
|
}
|
|
check(FMath::IsNearlyEqual(WeightsTotal, 1.0f, 0.01f));
|
|
|
|
// Convert the weights back to a color value
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < InParams.TotalWeightCount; ++CurWeightIndex)
|
|
{
|
|
// We can skip the non-physical weights as it's already baked into the others
|
|
if (CurWeightIndex != TotalPhysicalWeights)
|
|
{
|
|
switch (CurWeightIndex)
|
|
{
|
|
case 0:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.A = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.R = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.R = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.G = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.G = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.B = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
NewColor.B = Weights[CurWeightIndex];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FLinearColor MeshPaintHelpers::GenerateColorForTextureWeight(const int32 NumWeights, const int32 WeightIndex)
|
|
{
|
|
const bool bUsingOneMinusTotal =
|
|
NumWeights == 2 || // Two textures: Use a lerp() in pixel shader (single value)
|
|
NumWeights == 5; // Five texture: Requires 1.0-sum( R+G+B+A ) in shader
|
|
check(bUsingOneMinusTotal || NumWeights <= MeshPaintDefs::MaxSupportedPhysicalWeights);
|
|
|
|
// Prefer to use RG/RGB instead of AR/ARG when we're only using 2/3 physical weights
|
|
const int32 TotalPhysicalWeights = bUsingOneMinusTotal ? NumWeights - 1 : NumWeights;
|
|
const bool bUseColorAlpha =
|
|
TotalPhysicalWeights != 2 && // Two physical weights: Use RG instead of AR
|
|
TotalPhysicalWeights != 3; // Three physical weights: Use RGB instead of ARG
|
|
|
|
// Index of the blend weight that we're painting
|
|
check(WeightIndex >= 0 && WeightIndex < MeshPaintDefs::MaxSupportedWeights);
|
|
|
|
// Convert the color value to an array of weights
|
|
float Weights[MeshPaintDefs::MaxSupportedWeights];
|
|
{
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < NumWeights; ++CurWeightIndex)
|
|
{
|
|
if (CurWeightIndex == TotalPhysicalWeights)
|
|
{
|
|
// This weight's value is one minus the sum of all previous weights
|
|
float OtherWeightsTotal = 0.0f;
|
|
for (int32 OtherWeightIndex = 0; OtherWeightIndex < CurWeightIndex; ++OtherWeightIndex)
|
|
{
|
|
OtherWeightsTotal += Weights[OtherWeightIndex];
|
|
}
|
|
Weights[CurWeightIndex] = 1.0f - OtherWeightsTotal;
|
|
}
|
|
else
|
|
{
|
|
if (CurWeightIndex == WeightIndex)
|
|
{
|
|
Weights[CurWeightIndex] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
Weights[CurWeightIndex] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FLinearColor NewColor(FLinearColor::Black);
|
|
// Convert the weights back to a color value
|
|
for (int32 CurWeightIndex = 0; CurWeightIndex < NumWeights; ++CurWeightIndex)
|
|
{
|
|
// We can skip the non-physical weights as it's already baked into the others
|
|
if (CurWeightIndex != TotalPhysicalWeights)
|
|
{
|
|
switch (CurWeightIndex)
|
|
{
|
|
case 0:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.A = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.R = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.R = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.G = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (bUseColorAlpha)
|
|
{
|
|
NewColor.G = Weights[CurWeightIndex];
|
|
}
|
|
else
|
|
{
|
|
NewColor.B = Weights[CurWeightIndex];
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
NewColor.B = Weights[CurWeightIndex];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NewColor;
|
|
}
|
|
|
|
float MeshPaintHelpers::ComputePaintMultiplier(float SquaredDistanceToVertex2D, float BrushStrength, float BrushInnerRadius, float BrushRadialFalloff, float BrushInnerDepth, float BrushDepthFallof, float VertexDepthToBrush)
|
|
{
|
|
float PaintAmount = 1.0f;
|
|
|
|
// Compute the actual distance
|
|
float DistanceToVertex2D = 0.0f;
|
|
if (SquaredDistanceToVertex2D > KINDA_SMALL_NUMBER)
|
|
{
|
|
DistanceToVertex2D = FMath::Sqrt(SquaredDistanceToVertex2D);
|
|
}
|
|
|
|
// Apply radial-based falloff
|
|
if (DistanceToVertex2D > BrushInnerRadius)
|
|
{
|
|
const float RadialBasedFalloff = (DistanceToVertex2D - BrushInnerRadius) / BrushRadialFalloff;
|
|
PaintAmount *= 1.0f - RadialBasedFalloff;
|
|
}
|
|
|
|
// Apply depth-based falloff
|
|
if (VertexDepthToBrush > BrushInnerDepth)
|
|
{
|
|
const float DepthBasedFalloff = (VertexDepthToBrush - BrushInnerDepth) / BrushDepthFallof;
|
|
PaintAmount *= 1.0f - DepthBasedFalloff;
|
|
}
|
|
|
|
PaintAmount *= BrushStrength;
|
|
|
|
return PaintAmount;
|
|
}
|
|
|
|
bool MeshPaintHelpers::IsPointInfluencedByBrush(const FVector& InPosition, const FMeshPaintParameters& InParams, float& OutSquaredDistanceToVertex2D, float& OutVertexDepthToBrush)
|
|
{
|
|
// Project the vertex into the plane of the brush
|
|
FVector BrushSpaceVertexPosition = InParams.InverseBrushToWorldMatrix.TransformPosition(InPosition);
|
|
FVector2D BrushSpaceVertexPosition2D(BrushSpaceVertexPosition.X, BrushSpaceVertexPosition.Y);
|
|
|
|
// Is the brush close enough to the vertex to paint?
|
|
const float SquaredDistanceToVertex2D = BrushSpaceVertexPosition2D.SizeSquared();
|
|
if (SquaredDistanceToVertex2D <= InParams.SquaredBrushRadius)
|
|
{
|
|
// OK the vertex is overlapping the brush in 2D space, but is it too close or
|
|
// two far (depth wise) to be influenced?
|
|
const float VertexDepthToBrush = FMath::Abs(BrushSpaceVertexPosition.Z);
|
|
if (VertexDepthToBrush <= InParams.BrushDepth)
|
|
{
|
|
OutSquaredDistanceToVertex2D = SquaredDistanceToVertex2D;
|
|
OutVertexDepthToBrush = VertexDepthToBrush;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MeshPaintHelpers::IsPointInfluencedByBrush(const FVector2D& BrushSpacePosition, const float BrushRadius, float& OutInRangeValue)
|
|
{
|
|
const float DistanceToBrush = BrushSpacePosition.SizeSquared();
|
|
if (DistanceToBrush <= BrushRadius)
|
|
{
|
|
OutInRangeValue = DistanceToBrush / BrushRadius;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MeshPaintHelpers::RetrieveViewportPaintRays(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI, TArray<FPaintRay>& OutPaintRays)
|
|
{
|
|
checkf(View && Viewport && PDI, TEXT("Invalid Viewport data"));
|
|
FEditorViewportClient* ViewportClient = (FEditorViewportClient*)Viewport->GetClient();
|
|
checkf(ViewportClient != nullptr, TEXT("Unable to retrieve viewport client"));
|
|
|
|
if (ViewportClient->IsPerspective())
|
|
{
|
|
// If in VR mode retrieve possible viewport interactors and render widgets for them
|
|
UVREditorMode* VREditorMode = nullptr;
|
|
if (MeshPaintHelpers::IsInVRMode(ViewportClient))
|
|
{
|
|
VREditorMode = Cast<UVREditorMode>(GEditor->GetEditorWorldExtensionsManager()->GetEditorWorldExtensions(ViewportClient->GetWorld())->FindExtension(UVREditorMode::StaticClass()));
|
|
|
|
TArray<UViewportInteractor*> Interactors = VREditorMode->GetWorldInteraction().GetInteractors();
|
|
|
|
for (UViewportInteractor* Interactor : Interactors)
|
|
{
|
|
checkf(Interactor, TEXT("Invalid VR Interactor"));
|
|
|
|
// Don't draw visual cue if we're hovering over a viewport interact able, such as a dockable window selection bar
|
|
bool bShouldDrawInteractor = false;
|
|
FHitResult HitResult = Interactor->GetHitResultFromLaserPointer();
|
|
if (HitResult.Actor.IsValid())
|
|
{
|
|
UViewportWorldInteraction& WorldInteraction = VREditorMode->GetWorldInteraction();
|
|
|
|
if (WorldInteraction.IsInteractableComponent(HitResult.GetComponent()))
|
|
{
|
|
AActor* Actor = HitResult.Actor.Get();
|
|
|
|
// Make sure we're not hovering over some other viewport interactable, such as a dockable window selection bar or close button
|
|
IViewportInteractableInterface* ActorInteractable = Cast<IViewportInteractableInterface>(Actor);
|
|
bShouldDrawInteractor = (ActorInteractable == nullptr);
|
|
}
|
|
}
|
|
|
|
// Don't draw visual cue for paint brush when the interactor is hovering over UI
|
|
if (bShouldDrawInteractor && !Interactor->IsHoveringOverPriorityType())
|
|
{
|
|
FVector LaserPointerStart, LaserPointerEnd;
|
|
if (Interactor->GetLaserPointer( /* Out */ LaserPointerStart, /* Out */ LaserPointerEnd))
|
|
{
|
|
const FVector LaserPointerDirection = (LaserPointerEnd - LaserPointerStart).GetSafeNormal();
|
|
|
|
FPaintRay& NewPaintRay = *new(OutPaintRays) FPaintRay();
|
|
NewPaintRay.CameraLocation = VREditorMode->GetHeadTransform().GetLocation();
|
|
NewPaintRay.RayStart = LaserPointerStart;
|
|
NewPaintRay.RayDirection = LaserPointerDirection;
|
|
NewPaintRay.ViewportInteractor = Interactor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Else we're painting with mouse
|
|
// Make sure the cursor is visible OR we're flood filling. No point drawing a paint cue when there's no cursor.
|
|
if (Viewport->IsCursorVisible())
|
|
{
|
|
if (!PDI->IsHitTesting())
|
|
{
|
|
// Grab the mouse cursor position
|
|
FIntPoint MousePosition;
|
|
Viewport->GetMousePos(MousePosition);
|
|
|
|
// Is the mouse currently over the viewport? or flood filling
|
|
if ((MousePosition.X >= 0 && MousePosition.Y >= 0 && MousePosition.X < (int32)Viewport->GetSizeXY().X && MousePosition.Y < (int32)Viewport->GetSizeXY().Y))
|
|
{
|
|
// Compute a world space ray from the screen space mouse coordinates
|
|
FViewportCursorLocation MouseViewportRay(View, ViewportClient, MousePosition.X, MousePosition.Y);
|
|
|
|
FPaintRay& NewPaintRay = *new(OutPaintRays) FPaintRay();
|
|
NewPaintRay.CameraLocation = View->ViewMatrices.GetViewOrigin();
|
|
NewPaintRay.RayStart = MouseViewportRay.GetOrigin();
|
|
NewPaintRay.RayDirection = MouseViewportRay.GetDirection();
|
|
NewPaintRay.ViewportInteractor = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32 MeshPaintHelpers::GetVertexColorBufferSize(UStaticMeshComponent* MeshComponent, int32 LODIndex, bool bInstance)
|
|
{
|
|
checkf(MeshComponent != nullptr, TEXT("Invalid static mesh component ptr"));
|
|
|
|
uint32 SizeInBytes = 0;
|
|
|
|
// Retrieve component instance vertex color buffer size
|
|
if (bInstance)
|
|
{
|
|
if (MeshComponent->LODData.IsValidIndex(LODIndex))
|
|
{
|
|
const FStaticMeshComponentLODInfo& InstanceMeshLODInfo = MeshComponent->LODData[LODIndex];
|
|
if (InstanceMeshLODInfo.OverrideVertexColors)
|
|
{
|
|
SizeInBytes = InstanceMeshLODInfo.OverrideVertexColors->GetAllocatedSize();
|
|
}
|
|
}
|
|
}
|
|
// Retrieve static mesh asset vertex color buffer size
|
|
else
|
|
{
|
|
UStaticMesh* StaticMesh = MeshComponent->GetStaticMesh();
|
|
checkf(StaticMesh != nullptr, TEXT("Invalid static mesh ptr"));
|
|
if (StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
|
|
{
|
|
// count the base mesh color data
|
|
FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[LODIndex];
|
|
SizeInBytes = LODModel.ColorVertexBuffer.GetAllocatedSize();
|
|
}
|
|
}
|
|
|
|
return SizeInBytes;
|
|
}
|
|
|
|
TArray<FVector> MeshPaintHelpers::GetVerticesForLOD( const UStaticMesh* StaticMesh, int32 LODIndex)
|
|
{
|
|
checkf(StaticMesh != nullptr, TEXT("Invalid static mesh ptr"));
|
|
|
|
// Retrieve mesh vertices from Static mesh render data
|
|
TArray<FVector> Vertices;
|
|
if (StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
|
|
{
|
|
FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[LODIndex];
|
|
FPositionVertexBuffer* VertexBuffer = &LODModel.PositionVertexBuffer;
|
|
const uint32 NumVertices = VertexBuffer->GetNumVertices();
|
|
for (uint32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
|
|
{
|
|
Vertices.Add(VertexBuffer->VertexPosition(VertexIndex));
|
|
}
|
|
}
|
|
return Vertices;
|
|
}
|
|
|
|
TArray<FColor> MeshPaintHelpers::GetColorDataForLOD( const UStaticMesh* StaticMesh, int32 LODIndex)
|
|
{
|
|
checkf(StaticMesh != nullptr, TEXT("Invalid static mesh ptr"));
|
|
// Retrieve mesh vertex colors from Static mesh render data
|
|
TArray<FColor> Colors;
|
|
if (StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
|
|
{
|
|
const FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[LODIndex];
|
|
const FColorVertexBuffer& ColorBuffer = LODModel.ColorVertexBuffer;
|
|
const uint32 NumColors = ColorBuffer.GetNumVertices();
|
|
for (uint32 ColorIndex = 0; ColorIndex < NumColors; ++ColorIndex)
|
|
{
|
|
Colors.Add(ColorBuffer.VertexColor(ColorIndex));
|
|
}
|
|
}
|
|
return Colors;
|
|
}
|
|
|
|
TArray<FColor> MeshPaintHelpers::GetInstanceColorDataForLOD(const UStaticMeshComponent* MeshComponent, int32 LODIndex)
|
|
{
|
|
checkf(MeshComponent != nullptr, TEXT("Invalid static mesh component ptr"));
|
|
TArray<FColor> Colors;
|
|
|
|
// Retrieve mesh vertex colors from Static Mesh component instance data
|
|
if (MeshComponent->LODData.IsValidIndex(LODIndex))
|
|
{
|
|
const FStaticMeshComponentLODInfo& ComponentLODInfo = MeshComponent->LODData[LODIndex];
|
|
const FColorVertexBuffer* ColorBuffer = ComponentLODInfo.OverrideVertexColors;
|
|
if (ColorBuffer)
|
|
{
|
|
const uint32 NumColors = ColorBuffer->GetNumVertices();
|
|
for (uint32 ColorIndex = 0; ColorIndex < NumColors; ++ColorIndex)
|
|
{
|
|
Colors.Add(ColorBuffer->VertexColor(ColorIndex));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Colors;
|
|
}
|
|
|
|
void MeshPaintHelpers::SetInstanceColorDataForLOD(UStaticMeshComponent* MeshComponent, int32 LODIndex, const TArray<FColor>& Colors)
|
|
{
|
|
checkf(MeshComponent != nullptr, TEXT("Invalid static mesh component ptr"));
|
|
|
|
const UStaticMesh* Mesh = MeshComponent->GetStaticMesh();
|
|
if (Mesh)
|
|
{
|
|
const FStaticMeshLODResources& RenderData = Mesh->RenderData->LODResources[LODIndex];
|
|
FStaticMeshComponentLODInfo& ComponentLodInfo = MeshComponent->LODData[LODIndex];
|
|
|
|
// First release existing buffer
|
|
if (ComponentLodInfo.OverrideVertexColors)
|
|
{
|
|
ComponentLodInfo.ReleaseOverrideVertexColorsAndBlock();
|
|
}
|
|
|
|
// If we are adding colors to LOD > 0 we flag the component to have per-lod painted mesh colors
|
|
if (LODIndex > 0)
|
|
{
|
|
MeshComponent->bCustomOverrideVertexColorPerLOD = true;
|
|
}
|
|
|
|
// Initialize vertex buffer from given colors
|
|
ComponentLodInfo.OverrideVertexColors = new FColorVertexBuffer;
|
|
ComponentLodInfo.OverrideVertexColors->InitFromColorArray(Colors);
|
|
BeginInitResource(ComponentLodInfo.OverrideVertexColors);
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::SetInstanceColorDataForLOD(UStaticMeshComponent* MeshComponent, int32 LODIndex, const FColor FillColor)
|
|
{
|
|
checkf(MeshComponent != nullptr, TEXT("Invalid static mesh component ptr"));
|
|
|
|
const UStaticMesh* Mesh = MeshComponent->GetStaticMesh();
|
|
if (Mesh)
|
|
{
|
|
const FStaticMeshLODResources& RenderData = Mesh->RenderData->LODResources[LODIndex];
|
|
// Ensure we have enough LOD data structs
|
|
MeshComponent->SetLODDataCount(LODIndex + 1, MeshComponent->LODData.Num());
|
|
FStaticMeshComponentLODInfo& ComponentLodInfo = MeshComponent->LODData[LODIndex];
|
|
// First release existing buffer
|
|
if (ComponentLodInfo.OverrideVertexColors)
|
|
{
|
|
ComponentLodInfo.ReleaseOverrideVertexColorsAndBlock();
|
|
}
|
|
|
|
// If we are adding colors to LOD > 0 we flag the component to have per-lod painted mesh colors
|
|
if (LODIndex > 0)
|
|
{
|
|
MeshComponent->bCustomOverrideVertexColorPerLOD = true;
|
|
}
|
|
|
|
// Initialize vertex buffer from given color
|
|
ComponentLodInfo.OverrideVertexColors = new FColorVertexBuffer;
|
|
ComponentLodInfo.OverrideVertexColors->InitFromSingleColor(FillColor, RenderData.GetNumVertices() );
|
|
BeginInitResource(ComponentLodInfo.OverrideVertexColors);
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::FillVertexColors(UMeshComponent* MeshComponent, const FColor FillColor, bool bInstanced /*= false*/)
|
|
{
|
|
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent))
|
|
{
|
|
if (bInstanced)
|
|
{
|
|
UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
|
|
if (Mesh && Mesh->GetNumLODs() > 0)
|
|
{
|
|
const int32 NumLods = Mesh->GetNumLODs();
|
|
for (int32 LODIndex = 0; LODIndex < NumLods; ++LODIndex)
|
|
{
|
|
MeshPaintHelpers::SetInstanceColorDataForLOD(StaticMeshComponent, LODIndex, FillColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(MeshComponent))
|
|
{
|
|
TUniquePtr< FSkeletalMeshComponentRecreateRenderStateContext > RecreateRenderStateContext;
|
|
USkeletalMesh* Mesh = SkeletalMeshComponent->SkeletalMesh;
|
|
|
|
// Dirty the mesh
|
|
Mesh->SetFlags(RF_Transactional);
|
|
Mesh->Modify();
|
|
Mesh->bHasVertexColors = true;
|
|
|
|
// Release the static mesh's resources.
|
|
Mesh->ReleaseResources();
|
|
|
|
// Flush the resource release commands to the rendering thread to ensure that the build doesn't occur while a resource is still
|
|
// allocated, and potentially accessing the UStaticMesh.
|
|
Mesh->ReleaseResourcesFence.Wait();
|
|
|
|
if (Mesh && Mesh->LODInfo.Num() > 0)
|
|
{
|
|
RecreateRenderStateContext = MakeUnique<FSkeletalMeshComponentRecreateRenderStateContext>(Mesh);
|
|
const int32 NumLods = Mesh->LODInfo.Num();
|
|
for (int32 LODIndex = 0; LODIndex < NumLods; ++LODIndex)
|
|
{
|
|
MeshPaintHelpers::SetColorDataForLOD(Mesh, LODIndex, FillColor);
|
|
}
|
|
Mesh->InitResources();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::SetColorDataForLOD(USkeletalMesh* SkeletalMesh, int32 LODIndex, const FColor FillColor)
|
|
{
|
|
checkf(SkeletalMesh != nullptr, TEXT("Invalid Skeletal Mesh Ptr"));
|
|
FSkeletalMeshResource* Resource = SkeletalMesh->GetImportedResource();
|
|
if (Resource && Resource->LODModels.IsValidIndex(LODIndex))
|
|
{
|
|
FStaticLODModel& LOD = Resource->LODModels[LODIndex];
|
|
LOD.ColorVertexBuffer.InitFromSingleColor(FillColor, LOD.NumVertices);
|
|
BeginInitResource(&LOD.ColorVertexBuffer);
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ImportVertexColorsFromTexture(UMeshComponent* MeshComponent)
|
|
{
|
|
checkf(MeshComponent != nullptr, TEXT("Invalid mesh component ptr"));
|
|
|
|
// Get TGA texture filepath
|
|
FString ChosenFilename("");
|
|
FString ExtensionStr;
|
|
ExtensionStr += TEXT("TGA Files|*.tga|");
|
|
|
|
FString PromptTitle("Pick TGA Texture File");
|
|
|
|
// First, display the file open dialog for selecting the file.
|
|
TArray<FString> Filenames;
|
|
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
|
bool bOpen = false;
|
|
if (DesktopPlatform)
|
|
{
|
|
bOpen = DesktopPlatform->OpenFileDialog(
|
|
FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr),
|
|
PromptTitle,
|
|
TEXT(""),
|
|
TEXT(""),
|
|
*ExtensionStr,
|
|
EFileDialogFlags::None,
|
|
Filenames
|
|
);
|
|
}
|
|
|
|
if (bOpen && Filenames.Num() == 1)
|
|
{
|
|
// Valid file name picked
|
|
const FString FileName = Filenames[0];
|
|
UTexture2D* ColorTexture = ImportObject<UTexture2D>(GEngine, NAME_None, RF_Public, *FileName, NULL, NULL, TEXT("NOMIPMAPS=1 NOCOMPRESSION=1"));
|
|
|
|
if (ColorTexture && ColorTexture->Source.GetFormat() == TSF_BGRA8)
|
|
{
|
|
// Have a valid texture, now need user to specify options for importing
|
|
TSharedRef<SWindow> Window = SNew(SWindow)
|
|
.Title(FText::FromString(TEXT("Vertex Color Import Options")))
|
|
.SizingRule(ESizingRule::Autosized);
|
|
|
|
TSharedPtr<SImportVertexColorOptions> OptionsWindow = SNew(SImportVertexColorOptions).WidgetWindow(Window)
|
|
.WidgetWindow(Window)
|
|
.Component(MeshComponent)
|
|
.FullPath(FText::FromString(ChosenFilename));
|
|
|
|
Window->SetContent
|
|
(
|
|
OptionsWindow->AsShared()
|
|
);
|
|
|
|
TSharedPtr<SWindow> ParentWindow;
|
|
if (FModuleManager::Get().IsModuleLoaded("MainFrame"))
|
|
{
|
|
IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
|
|
ParentWindow = MainFrame.GetParentWindow();
|
|
}
|
|
FSlateApplication::Get().AddModalWindow(Window, ParentWindow, false);
|
|
|
|
if (OptionsWindow->ShouldImport())
|
|
{
|
|
// Options specified and start importing
|
|
UVertexColorImportOptions* Options = OptionsWindow->GetOptions();
|
|
|
|
if (MeshComponent->IsA<UStaticMeshComponent>())
|
|
{
|
|
UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent);
|
|
if (StaticMeshComponent)
|
|
{
|
|
if (Options->bImportToInstance)
|
|
{
|
|
// Import colors to static mesh / component
|
|
ImportVertexColorsToStaticMeshComponent(StaticMeshComponent, Options, ColorTexture);
|
|
}
|
|
else
|
|
{
|
|
if (StaticMeshComponent->GetStaticMesh())
|
|
{
|
|
ImportVertexColorsToStaticMesh(StaticMeshComponent->GetStaticMesh(), Options, ColorTexture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (MeshComponent->IsA<USkeletalMeshComponent>())
|
|
{
|
|
USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(MeshComponent);
|
|
|
|
if (SkeletalMeshComponent->SkeletalMesh)
|
|
{
|
|
// Import colors to skeletal mesh
|
|
ImportVertexColorsToSkeletalMesh(SkeletalMeshComponent->SkeletalMesh, Options, ColorTexture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!ColorTexture)
|
|
{
|
|
// Unable to import file
|
|
}
|
|
else if (ColorTexture && ColorTexture->Source.GetFormat() != TSF_BGRA8)
|
|
{
|
|
// Able to import file but incorrect format
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::SetViewportColorMode(EMeshPaintColorViewMode ColorViewMode, FEditorViewportClient* ViewportClient)
|
|
{
|
|
if (ViewportClient->IsPerspective())
|
|
{
|
|
// Update viewport show flags
|
|
{
|
|
// show flags forced on during vertex color modes
|
|
if (ColorViewMode == EMeshPaintColorViewMode::Normal)
|
|
{
|
|
ColorViewMode = EMeshPaintColorViewMode::Normal;
|
|
}
|
|
|
|
if (ColorViewMode == EMeshPaintColorViewMode::Normal)
|
|
{
|
|
if (ViewportClient->EngineShowFlags.VertexColors)
|
|
{
|
|
// If we're transitioning to normal mode then restore the backup
|
|
// Clear the flags relevant to vertex color modes
|
|
ViewportClient->EngineShowFlags.SetVertexColors(false);
|
|
|
|
// Restore the vertex color mode flags that were set when we last entered vertex color mode
|
|
ApplyViewMode(ViewportClient->GetViewMode(), ViewportClient->IsPerspective(), ViewportClient->EngineShowFlags);
|
|
GVertexColorViewMode = EVertexColorViewMode::Color;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ViewportClient->EngineShowFlags.SetMaterials(true);
|
|
ViewportClient->EngineShowFlags.SetLighting(false);
|
|
ViewportClient->EngineShowFlags.SetBSPTriangles(true);
|
|
ViewportClient->EngineShowFlags.SetVertexColors(true);
|
|
ViewportClient->EngineShowFlags.SetPostProcessing(false);
|
|
ViewportClient->EngineShowFlags.SetHMDDistortion(false);
|
|
|
|
switch (ColorViewMode)
|
|
{
|
|
case EMeshPaintColorViewMode::RGB:
|
|
{
|
|
GVertexColorViewMode = EVertexColorViewMode::Color;
|
|
}
|
|
break;
|
|
|
|
case EMeshPaintColorViewMode::Alpha:
|
|
{
|
|
GVertexColorViewMode = EVertexColorViewMode::Alpha;
|
|
}
|
|
break;
|
|
|
|
case EMeshPaintColorViewMode::Red:
|
|
{
|
|
GVertexColorViewMode = EVertexColorViewMode::Red;
|
|
}
|
|
break;
|
|
|
|
case EMeshPaintColorViewMode::Green:
|
|
{
|
|
GVertexColorViewMode = EVertexColorViewMode::Green;
|
|
}
|
|
break;
|
|
|
|
case EMeshPaintColorViewMode::Blue:
|
|
{
|
|
GVertexColorViewMode = EVertexColorViewMode::Blue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::SetRealtimeViewport(bool bRealtime)
|
|
{
|
|
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
|
|
TSharedPtr< ILevelViewport > ViewportWindow = LevelEditorModule.GetFirstActiveViewport();
|
|
const bool bRememberCurrentState = false;
|
|
if (ViewportWindow.IsValid())
|
|
{
|
|
FEditorViewportClient &Viewport = ViewportWindow->GetLevelViewportClient();
|
|
if (Viewport.IsPerspective())
|
|
{
|
|
if (bRealtime)
|
|
{
|
|
Viewport.SetRealtime(bRealtime, bRememberCurrentState);
|
|
}
|
|
else
|
|
{
|
|
const bool bAllowDisable = true;
|
|
Viewport.RestoreRealtime(bAllowDisable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool MeshPaintHelpers::IsInVRMode(const FEditorViewportClient* ViewportClient)
|
|
{
|
|
bool bIsInVRMode = false;
|
|
if (IVREditorModule::IsAvailable())
|
|
{
|
|
UVREditorMode* VREditorMode = Cast<UVREditorMode>(GEditor->GetEditorWorldExtensionsManager()->GetEditorWorldExtensions(ViewportClient->GetWorld())->FindExtension(UVREditorMode::StaticClass()));
|
|
if (VREditorMode != nullptr && VREditorMode->IsFullyInitialized() && VREditorMode->IsActive())
|
|
{
|
|
bIsInVRMode = true;
|
|
}
|
|
}
|
|
|
|
return bIsInVRMode;
|
|
}
|
|
|
|
void MeshPaintHelpers::ForceRenderMeshLOD(UMeshComponent* Component, int32 LODIndex)
|
|
{
|
|
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(Component))
|
|
{
|
|
StaticMeshComponent->ForcedLodModel = LODIndex + 1;
|
|
}
|
|
else if (USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(Component))
|
|
{
|
|
SkeletalMeshComponent->ForcedLodModel = LODIndex + 1;
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ClearMeshTextureOverrides(const IMeshPaintGeometryAdapter& GeometryInfo, UMeshComponent* InMeshComponent)
|
|
{
|
|
if (InMeshComponent != nullptr)
|
|
{
|
|
TArray<UTexture*> UsedTextures;
|
|
InMeshComponent->GetUsedTextures(/*out*/ UsedTextures, EMaterialQualityLevel::High);
|
|
|
|
for (UTexture* Texture : UsedTextures)
|
|
{
|
|
if (UTexture2D* Texture2D = Cast<UTexture2D>(Texture))
|
|
{
|
|
GeometryInfo.ApplyOrRemoveTextureOverride(Texture2D, nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ApplyVertexColorsToAllLODs(IMeshPaintGeometryAdapter& GeometryInfo, UMeshComponent* InMeshComponent)
|
|
{
|
|
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(InMeshComponent))
|
|
{
|
|
ApplyVertexColorsToAllLODs(GeometryInfo, StaticMeshComponent);
|
|
}
|
|
else if (USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(InMeshComponent))
|
|
{
|
|
ApplyVertexColorsToAllLODs(GeometryInfo, SkeletalMeshComponent);
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ApplyVertexColorsToAllLODs(IMeshPaintGeometryAdapter& GeometryInfo, UStaticMeshComponent* StaticMeshComponent)
|
|
{
|
|
// If a static mesh component was found, apply LOD0 painting to all lower LODs.
|
|
if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (StaticMeshComponent->LODData.Num() < 1)
|
|
{
|
|
//We need at least some painting on the base LOD to apply it to the lower LODs
|
|
return;
|
|
}
|
|
FStaticMeshComponentLODInfo& SourceCompLODInfo = StaticMeshComponent->LODData[0];
|
|
FStaticMeshLODResources& SourceRenderData = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[0];
|
|
//Make sure we have something paint in the LOD 0 to apply it to all lower LODs.
|
|
if (SourceCompLODInfo.OverrideVertexColors == nullptr && SourceCompLODInfo.PaintedVertices.Num() <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StaticMeshComponent->bCustomOverrideVertexColorPerLOD = false;
|
|
|
|
uint32 NumLODs = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources.Num();
|
|
StaticMeshComponent->Modify();
|
|
|
|
// Ensure LODData has enough entries in it, free not required.
|
|
StaticMeshComponent->SetLODDataCount(NumLODs, StaticMeshComponent->LODData.Num());
|
|
for (uint32 i = 1; i < NumLODs; ++i)
|
|
{
|
|
FStaticMeshComponentLODInfo* CurrInstanceMeshLODInfo = &StaticMeshComponent->LODData[i];
|
|
FStaticMeshLODResources& CurrRenderData = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[i];
|
|
// Destroy the instance vertex color array if it doesn't fit
|
|
if (CurrInstanceMeshLODInfo->OverrideVertexColors
|
|
&& CurrInstanceMeshLODInfo->OverrideVertexColors->GetNumVertices() != CurrRenderData.GetNumVertices())
|
|
{
|
|
CurrInstanceMeshLODInfo->ReleaseOverrideVertexColorsAndBlock();
|
|
}
|
|
|
|
if (CurrInstanceMeshLODInfo->OverrideVertexColors)
|
|
{
|
|
CurrInstanceMeshLODInfo->BeginReleaseOverrideVertexColors();
|
|
}
|
|
else
|
|
{
|
|
// Setup the instance vertex color array if we don't have one yet
|
|
CurrInstanceMeshLODInfo->OverrideVertexColors = new FColorVertexBuffer;
|
|
}
|
|
}
|
|
|
|
FlushRenderingCommands();
|
|
for (uint32 i = 1; i < NumLODs; ++i)
|
|
{
|
|
FStaticMeshComponentLODInfo& CurCompLODInfo = StaticMeshComponent->LODData[i];
|
|
FStaticMeshLODResources& CurRenderData = StaticMeshComponent->GetStaticMesh()->RenderData->LODResources[i];
|
|
|
|
check(CurCompLODInfo.OverrideVertexColors);
|
|
check(SourceCompLODInfo.OverrideVertexColors);
|
|
|
|
TArray<FColor> NewOverrideColors;
|
|
|
|
RemapPaintedVertexColors(
|
|
SourceCompLODInfo.PaintedVertices,
|
|
*SourceCompLODInfo.OverrideVertexColors,
|
|
SourceRenderData.PositionVertexBuffer,
|
|
SourceRenderData.VertexBuffer,
|
|
CurRenderData.PositionVertexBuffer,
|
|
&CurRenderData.VertexBuffer,
|
|
NewOverrideColors
|
|
);
|
|
|
|
if (NewOverrideColors.Num())
|
|
{
|
|
CurCompLODInfo.OverrideVertexColors->InitFromColorArray(NewOverrideColors);
|
|
}
|
|
|
|
// Initialize the vert. colors
|
|
BeginInitResource(CurCompLODInfo.OverrideVertexColors);
|
|
}
|
|
}
|
|
|
|
int32 MeshPaintHelpers::GetNumberOfLODs(const UMeshComponent* MeshComponent)
|
|
{
|
|
int32 NumLODs = 1;
|
|
|
|
if (const UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent))
|
|
{
|
|
const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh();
|
|
if (StaticMesh != nullptr)
|
|
{
|
|
NumLODs = StaticMesh->GetNumLODs();
|
|
}
|
|
}
|
|
else if (const USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(MeshComponent))
|
|
{
|
|
const USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
|
|
if (SkeletalMesh != nullptr)
|
|
{
|
|
NumLODs = SkeletalMesh->LODInfo.Num();
|
|
}
|
|
}
|
|
|
|
return NumLODs;
|
|
}
|
|
|
|
int32 MeshPaintHelpers::GetNumberOfUVs(const UMeshComponent* MeshComponent, int32 LODIndex)
|
|
{
|
|
int32 NumUVs = 0;
|
|
|
|
if (const UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent))
|
|
{
|
|
const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh();
|
|
if (StaticMesh != nullptr && StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
|
|
{
|
|
NumUVs = StaticMesh->RenderData->LODResources[LODIndex].GetNumTexCoords();
|
|
}
|
|
}
|
|
else if (const USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(MeshComponent))
|
|
{
|
|
const USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
|
|
if (SkeletalMesh != nullptr && SkeletalMesh->GetImportedResource() && SkeletalMesh->GetImportedResource()->LODModels.IsValidIndex(LODIndex))
|
|
{
|
|
NumUVs = SkeletalMesh->GetImportedResource()->LODModels[LODIndex].NumTexCoords;
|
|
}
|
|
}
|
|
|
|
return NumUVs;
|
|
}
|
|
|
|
bool MeshPaintHelpers::DoesMeshComponentContainPerLODColors(const UMeshComponent* MeshComponent)
|
|
{
|
|
bool bPerLODColors = false;
|
|
|
|
if (const UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(MeshComponent))
|
|
{
|
|
bPerLODColors = StaticMeshComponent->bCustomOverrideVertexColorPerLOD;
|
|
|
|
const int32 NumLODs = StaticMeshComponent->LODData.Num();
|
|
bool bInstancedLODColors = false;
|
|
for (int32 LODIndex = 1; LODIndex < NumLODs; ++LODIndex)
|
|
{
|
|
if (StaticMeshComponent->LODData[LODIndex].PaintedVertices.Num() > 0)
|
|
{
|
|
bInstancedLODColors = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bPerLODColors = bInstancedLODColors;
|
|
}
|
|
else if (const USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(MeshComponent))
|
|
{
|
|
USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
|
|
if (SkeletalMesh)
|
|
{
|
|
for ( const FSkeletalMeshLODInfo& Info : SkeletalMesh->LODInfo )
|
|
{
|
|
if (Info.bHasPerLODVertexColors)
|
|
{
|
|
bPerLODColors = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bPerLODColors;
|
|
}
|
|
|
|
void MeshPaintHelpers::GetInstanceColorDataInfo(const UStaticMeshComponent* StaticMeshComponent, int32 LODIndex, int32& OutTotalInstanceVertexColorBytes)
|
|
{
|
|
checkf(StaticMeshComponent, TEXT("Invalid StaticMeshComponent"));
|
|
OutTotalInstanceVertexColorBytes = 0;
|
|
|
|
const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh();
|
|
if (StaticMesh != nullptr && StaticMesh->GetNumLODs() > (int32)LODIndex && StaticMeshComponent->LODData.IsValidIndex(LODIndex))
|
|
{
|
|
// count the instance color data
|
|
const FStaticMeshComponentLODInfo& InstanceMeshLODInfo = StaticMeshComponent->LODData[LODIndex];
|
|
if (InstanceMeshLODInfo.OverrideVertexColors)
|
|
{
|
|
OutTotalInstanceVertexColorBytes += InstanceMeshLODInfo.OverrideVertexColors->GetAllocatedSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ImportVertexColorsToStaticMesh(UStaticMesh* StaticMesh, const UVertexColorImportOptions* Options, UTexture2D* Texture)
|
|
{
|
|
checkf(StaticMesh && Options && Texture, TEXT("Invalid ptr"));
|
|
|
|
// Extract color data from texture
|
|
TArray<uint8> SrcMipData;
|
|
Texture->Source.GetMipData(SrcMipData, 0);
|
|
const uint8* MipData = SrcMipData.GetData();
|
|
|
|
TUniquePtr< FStaticMeshComponentRecreateRenderStateContext > RecreateRenderStateContext = MakeUnique<FStaticMeshComponentRecreateRenderStateContext>(StaticMesh);
|
|
const int32 ImportLOD = Options->LODIndex;
|
|
FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[ImportLOD];
|
|
|
|
// Dirty the mesh
|
|
StaticMesh->Modify();
|
|
|
|
// Release the static mesh's resources.
|
|
StaticMesh->ReleaseResources();
|
|
|
|
// Flush the resource release commands to the rendering thread to ensure that the build doesn't occur while a resource is still
|
|
// allocated, and potentially accessing the UStaticMesh.
|
|
StaticMesh->ReleaseResourcesFence.Wait();
|
|
|
|
if (LODModel.ColorVertexBuffer.GetNumVertices() == 0)
|
|
{
|
|
// Mesh doesn't have a color vertex buffer yet! We'll create one now.
|
|
LODModel.ColorVertexBuffer.InitFromSingleColor(FColor::White, LODModel.GetNumVertices());
|
|
|
|
// @todo MeshPaint: Make sure this is the best place to do this
|
|
BeginInitResource(&LODModel.ColorVertexBuffer);
|
|
}
|
|
|
|
const int32 UVIndex = Options->UVIndex;
|
|
const FColor ColorMask = Options->CreateColorMask();
|
|
for (uint32 VertexIndex = 0; VertexIndex < LODModel.VertexBuffer.GetNumVertices(); ++VertexIndex)
|
|
{
|
|
const FVector2D UV = LODModel.VertexBuffer.GetVertexUV(VertexIndex, UVIndex);
|
|
LODModel.ColorVertexBuffer.VertexColor(VertexIndex) = PickVertexColorFromTextureData(MipData, UV, Texture, ColorMask);
|
|
}
|
|
|
|
// Make sure colors are saved into raw mesh
|
|
|
|
StaticMesh->InitResources();
|
|
}
|
|
|
|
void MeshPaintHelpers::ImportVertexColorsToStaticMeshComponent(UStaticMeshComponent* StaticMeshComponent, const UVertexColorImportOptions* Options, UTexture2D* Texture)
|
|
{
|
|
checkf(StaticMeshComponent && Options && Texture, TEXT("Invalid ptr"));
|
|
|
|
// Extract color data from texture
|
|
TArray<uint8> SrcMipData;
|
|
Texture->Source.GetMipData(SrcMipData, 0);
|
|
const uint8* MipData = SrcMipData.GetData();
|
|
|
|
TUniquePtr< FComponentReregisterContext > ComponentReregisterContext;
|
|
const UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
|
|
if (Mesh)
|
|
{
|
|
ComponentReregisterContext = MakeUnique<FComponentReregisterContext>(StaticMeshComponent);
|
|
StaticMeshComponent->Modify();
|
|
|
|
const int32 ImportLOD = Options->LODIndex;
|
|
FStaticMeshLODResources& LODModel = Mesh->RenderData->LODResources[ImportLOD];
|
|
|
|
if (!StaticMeshComponent->LODData.IsValidIndex(ImportLOD))
|
|
{
|
|
StaticMeshComponent->SetLODDataCount(ImportLOD + 1, StaticMeshComponent->LODData.Num());
|
|
}
|
|
|
|
FStaticMeshComponentLODInfo& InstanceMeshLODInfo = StaticMeshComponent->LODData[ImportLOD];
|
|
|
|
if (InstanceMeshLODInfo.OverrideVertexColors)
|
|
{
|
|
InstanceMeshLODInfo.ReleaseOverrideVertexColorsAndBlock();
|
|
}
|
|
|
|
// Setup the instance vertex color array
|
|
InstanceMeshLODInfo.OverrideVertexColors = new FColorVertexBuffer;
|
|
|
|
if ((int32)LODModel.ColorVertexBuffer.GetNumVertices() == LODModel.GetNumVertices())
|
|
{
|
|
// copy mesh vertex colors to the instance ones
|
|
InstanceMeshLODInfo.OverrideVertexColors->InitFromColorArray(&LODModel.ColorVertexBuffer.VertexColor(0), LODModel.GetNumVertices());
|
|
}
|
|
else
|
|
{
|
|
// Original mesh didn't have any colors, so just use a default color
|
|
MeshPaintHelpers::SetInstanceColorDataForLOD(StaticMeshComponent, ImportLOD, FColor::White);
|
|
}
|
|
|
|
const int32 UVIndex = Options->UVIndex;
|
|
const FColor ColorMask = Options->CreateColorMask();
|
|
for (uint32 VertexIndex = 0; VertexIndex < LODModel.VertexBuffer.GetNumVertices(); ++VertexIndex)
|
|
{
|
|
const FVector2D UV = LODModel.VertexBuffer.GetVertexUV(VertexIndex, UVIndex);
|
|
InstanceMeshLODInfo.OverrideVertexColors->VertexColor(VertexIndex) = PickVertexColorFromTextureData(MipData, UV, Texture, ColorMask);
|
|
}
|
|
|
|
BeginInitResource(InstanceMeshLODInfo.OverrideVertexColors);
|
|
}
|
|
else
|
|
{
|
|
// Error
|
|
}
|
|
}
|
|
|
|
void MeshPaintHelpers::ImportVertexColorsToSkeletalMesh(USkeletalMesh* SkeletalMesh, const UVertexColorImportOptions* Options, UTexture2D* Texture)
|
|
{
|
|
checkf(SkeletalMesh && Options && Texture, TEXT("Invalid ptr"));
|
|
|
|
// Extract color data from texture
|
|
TArray<uint8> SrcMipData;
|
|
Texture->Source.GetMipData(SrcMipData, 0);
|
|
const uint8* MipData = SrcMipData.GetData();
|
|
|
|
TUniquePtr< FSkeletalMeshComponentRecreateRenderStateContext > RecreateRenderStateContext;
|
|
FSkeletalMeshResource* Resource = SkeletalMesh->GetImportedResource();
|
|
const int32 ImportLOD = Options->LODIndex;
|
|
if (Resource && Resource->LODModels.IsValidIndex(ImportLOD))
|
|
{
|
|
RecreateRenderStateContext = MakeUnique<FSkeletalMeshComponentRecreateRenderStateContext>(SkeletalMesh);
|
|
SkeletalMesh->Modify();
|
|
SkeletalMesh->ReleaseResources();
|
|
SkeletalMesh->ReleaseResourcesFence.Wait();
|
|
|
|
FStaticLODModel& LODModel = Resource->LODModels[ImportLOD];
|
|
|
|
if (LODModel.ColorVertexBuffer.GetNumVertices() == 0)
|
|
{
|
|
LODModel.ColorVertexBuffer.InitFromSingleColor(FColor::White, LODModel.NumVertices);
|
|
BeginInitResource(&LODModel.ColorVertexBuffer);
|
|
}
|
|
|
|
const int32 UVIndex = Options->UVIndex;
|
|
const FColor ColorMask = Options->CreateColorMask();
|
|
for (uint32 VertexIndex = 0; VertexIndex < LODModel.NumVertices; ++VertexIndex)
|
|
{
|
|
const FVector2D UV = LODModel.VertexBufferGPUSkin.GetVertexUV(VertexIndex, UVIndex);
|
|
LODModel.ColorVertexBuffer.VertexColor(VertexIndex) = PickVertexColorFromTextureData(MipData, UV, Texture, ColorMask);
|
|
}
|
|
|
|
SkeletalMesh->InitResources();
|
|
}
|
|
else
|
|
{
|
|
// ERROR
|
|
}
|
|
}
|
|
|
|
FColor MeshPaintHelpers::PickVertexColorFromTextureData(const uint8* MipData, const FVector2D& UVCoordinate, const UTexture2D* Texture, const FColor ColorMask)
|
|
{
|
|
checkf(MipData, TEXT("Invalid texture MIP data"));
|
|
FColor VertexColor = FColor::Black;
|
|
|
|
if ((UVCoordinate.X >= 0.0f) && (UVCoordinate.X < 1.0f) && (UVCoordinate.Y >= 0.0f) && (UVCoordinate.Y < 1.0f))
|
|
{
|
|
const int32 X = Texture->GetSizeX()*UVCoordinate.X;
|
|
const int32 Y = Texture->GetSizeY()*UVCoordinate.Y;
|
|
|
|
const int32 Index = ((Y * Texture->GetSizeX()) + X) * 4;
|
|
VertexColor.B = MipData[Index + 0];
|
|
VertexColor.G = MipData[Index + 1];
|
|
VertexColor.R = MipData[Index + 2];
|
|
VertexColor.A = MipData[Index + 3];
|
|
|
|
VertexColor.DWColor() &= ColorMask.DWColor();
|
|
}
|
|
|
|
return VertexColor;
|
|
}
|
|
|
|
bool MeshPaintHelpers::ApplyPerVertexPaintAction(IMeshPaintGeometryAdapter* Adapter, const FVector& CameraPosition, const FVector& HitPosition, const UPaintBrushSettings* Settings, FPerVertexPaintAction Action)
|
|
{
|
|
// Retrieve components world matrix
|
|
const FMatrix& ComponentToWorldMatrix = Adapter->GetComponentToWorldMatrix();
|
|
|
|
// Compute the camera position in actor space. We need this later to check for back facing triangles.
|
|
const FVector ComponentSpaceCameraPosition(ComponentToWorldMatrix.InverseTransformPosition(CameraPosition));
|
|
const FVector ComponentSpaceBrushPosition(ComponentToWorldMatrix.InverseTransformPosition(HitPosition));
|
|
|
|
// @todo MeshPaint: Input vector doesn't work well with non-uniform scale
|
|
const float BrushRadius = Settings->GetBrushRadius();
|
|
const float ComponentSpaceBrushRadius = ComponentToWorldMatrix.InverseTransformVector(FVector(BrushRadius, 0.0f, 0.0f)).Size();
|
|
const float ComponentSpaceSquaredBrushRadius = ComponentSpaceBrushRadius * ComponentSpaceBrushRadius;
|
|
|
|
// Get a list of unique vertices indexed by the influenced triangles
|
|
TSet<int32> InfluencedVertices;
|
|
Adapter->GetInfluencedVertexIndices(ComponentSpaceSquaredBrushRadius, ComponentSpaceBrushPosition, ComponentSpaceCameraPosition, Settings->bOnlyFrontFacingTriangles, InfluencedVertices);
|
|
|
|
|
|
const int32 NumParallelFors = 4;
|
|
const int32 NumPerFor = FMath::CeilToInt((float)InfluencedVertices.Num() / NumParallelFors);
|
|
|
|
// Parallel applying
|
|
/*ParallelFor(NumParallelFors, [=](int32 Index)
|
|
{
|
|
const int32 Start = Index * NumPerFor;
|
|
const int32 End = FMath::Min(Start + NumPerFor, InfluencedVertices.Num());
|
|
|
|
for (int32 VertexIndex = Start; VertexIndex < End; ++VertexIndex)
|
|
{
|
|
Action.Execute(Adapter, VertexIndex);
|
|
}
|
|
});*/
|
|
|
|
for (int32 VertexIndex : InfluencedVertices)
|
|
{
|
|
// Apply the action!
|
|
Action.Execute(Adapter, VertexIndex);
|
|
}
|
|
|
|
return (InfluencedVertices.Num() > 0);
|
|
}
|
|
|
|
bool MeshPaintHelpers::ApplyPerTrianglePaintAction(IMeshPaintGeometryAdapter* Adapter, const FVector& CameraPosition, const FVector& HitPosition, const UPaintBrushSettings* Settings, FPerTrianglePaintAction Action)
|
|
{
|
|
// Retrieve components world matrix
|
|
const FMatrix& ComponentToWorldMatrix = Adapter->GetComponentToWorldMatrix();
|
|
|
|
// Compute the camera position in actor space. We need this later to check for back facing triangles.
|
|
const FVector ComponentSpaceCameraPosition(ComponentToWorldMatrix.InverseTransformPosition(CameraPosition));
|
|
const FVector ComponentSpaceBrushPosition(ComponentToWorldMatrix.InverseTransformPosition(HitPosition));
|
|
|
|
// @todo MeshPaint: Input vector doesn't work well with non-uniform scale
|
|
const float BrushRadius = Settings->GetBrushRadius();
|
|
const float ComponentSpaceBrushRadius = ComponentToWorldMatrix.InverseTransformVector(FVector(BrushRadius, 0.0f, 0.0f)).Size();
|
|
const float ComponentSpaceSquaredBrushRadius = ComponentSpaceBrushRadius * ComponentSpaceBrushRadius;
|
|
|
|
// Get a list of (optionally front-facing) triangles that are within a reasonable distance to the brush
|
|
TArray<uint32> InfluencedTriangles = Adapter->SphereIntersectTriangles(
|
|
ComponentSpaceSquaredBrushRadius,
|
|
ComponentSpaceBrushPosition,
|
|
ComponentSpaceCameraPosition,
|
|
Settings->bOnlyFrontFacingTriangles);
|
|
|
|
int32 TriangleIndices[3];
|
|
|
|
const TArray<uint32> VertexIndices = Adapter->GetMeshIndices();
|
|
for (uint32 TriangleIndex : InfluencedTriangles)
|
|
{
|
|
// Grab the vertex indices and points for this triangle
|
|
for (int32 TriVertexNum = 0; TriVertexNum < 3; ++TriVertexNum)
|
|
{
|
|
TriangleIndices[TriVertexNum] = VertexIndices[TriangleIndex * 3 + TriVertexNum];
|
|
}
|
|
|
|
Action.Execute(Adapter, TriangleIndex, TriangleIndices);
|
|
}
|
|
|
|
return (InfluencedTriangles.Num() > 0);
|
|
}
|
|
|
|
struct FPaintedMeshVertex
|
|
{
|
|
FVector Position;
|
|
FPackedNormal Normal;
|
|
FColor Color;
|
|
};
|
|
|
|
/** Helper struct for the mesh component vert position octree */
|
|
struct FVertexColorPropogationOctreeSemantics
|
|
{
|
|
enum { MaxElementsPerLeaf = 16 };
|
|
enum { MinInclusiveElementsPerNode = 7 };
|
|
enum { MaxNodeDepth = 12 };
|
|
|
|
typedef TInlineAllocator<MaxElementsPerLeaf> ElementAllocator;
|
|
|
|
/**
|
|
* Get the bounding box of the provided octree element. In this case, the box
|
|
* is merely the point specified by the element.
|
|
*
|
|
* @param Element Octree element to get the bounding box for
|
|
*
|
|
* @return Bounding box of the provided octree element
|
|
*/
|
|
FORCEINLINE static FBoxCenterAndExtent GetBoundingBox( const FPaintedMeshVertex& Element )
|
|
{
|
|
return FBoxCenterAndExtent( Element.Position, FVector::ZeroVector );
|
|
}
|
|
|
|
/**
|
|
* Determine if two octree elements are equal
|
|
*
|
|
* @param A First octree element to check
|
|
* @param B Second octree element to check
|
|
*
|
|
* @return true if both octree elements are equal, false if they are not
|
|
*/
|
|
FORCEINLINE static bool AreElementsEqual( const FPaintedMeshVertex& A, const FPaintedMeshVertex& B )
|
|
{
|
|
return ( A.Position == B.Position && A.Normal == B.Normal && A.Color == B.Color );
|
|
}
|
|
|
|
/** Ignored for this implementation */
|
|
FORCEINLINE static void SetElementId( const FPaintedMeshVertex& Element, FOctreeElementId Id )
|
|
{
|
|
}
|
|
};
|
|
typedef TOctree<FPaintedMeshVertex, FVertexColorPropogationOctreeSemantics> TVertexColorPropogationPosOctree;
|
|
|
|
void MeshPaintHelpers::ApplyVertexColorsToAllLODs(IMeshPaintGeometryAdapter& GeometryInfo, USkeletalMeshComponent* SkeletalMeshComponent)
|
|
{
|
|
checkf(SkeletalMeshComponent != nullptr, TEXT("Invalid Skeletal Mesh Component"));
|
|
USkeletalMesh* Mesh = SkeletalMeshComponent->SkeletalMesh;
|
|
if (Mesh)
|
|
{
|
|
FSkeletalMeshResource* Resource = Mesh->GetImportedResource();
|
|
if (Resource)
|
|
{
|
|
const int32 NumLODs = Resource->LODModels.Num();
|
|
if (NumLODs > 1)
|
|
{
|
|
const FStaticLODModel& BaseLOD = Resource->LODModels[0];
|
|
GeometryInfo.PreEdit();
|
|
|
|
FBox BaseBounds(EForceInit::ForceInitToZero);
|
|
TArray<FSoftSkinVertex> BaseVertices;
|
|
BaseLOD.GetVertices(BaseVertices);
|
|
|
|
TArray<FPaintedMeshVertex> PaintedVertices;
|
|
PaintedVertices.Empty(BaseVertices.Num());
|
|
|
|
FPaintedMeshVertex PaintedVertex;
|
|
for (int32 VertexIndex = 0; VertexIndex < BaseVertices.Num(); ++VertexIndex )
|
|
{
|
|
const FSoftSkinVertex& Vertex = BaseVertices[VertexIndex];
|
|
BaseBounds += Vertex.Position;
|
|
PaintedVertex.Position = Vertex.Position;
|
|
PaintedVertex.Normal = Vertex.TangentZ;
|
|
PaintedVertex.Color = BaseLOD.ColorVertexBuffer.VertexColor(VertexIndex);
|
|
PaintedVertices.Add(PaintedVertex);
|
|
}
|
|
|
|
for (int32 LODIndex = 1; LODIndex < NumLODs; ++LODIndex)
|
|
{
|
|
// Do something
|
|
FStaticLODModel& ApplyLOD = Resource->LODModels[LODIndex];
|
|
FBox CombinedBounds = BaseBounds;
|
|
Mesh->LODInfo[LODIndex].bHasPerLODVertexColors = false;
|
|
|
|
if (!ApplyLOD.ColorVertexBuffer.IsInitialized())
|
|
{
|
|
ApplyLOD.ColorVertexBuffer.InitFromSingleColor(FColor::White, ApplyLOD.NumVertices);
|
|
}
|
|
|
|
TArray<FSoftSkinVertex> ApplyVertices;
|
|
ApplyLOD.GetVertices(ApplyVertices);
|
|
for (const FSoftSkinVertex& Vertex : ApplyVertices)
|
|
{
|
|
CombinedBounds += Vertex.Position;
|
|
}
|
|
|
|
TVertexColorPropogationPosOctree VertPosOctree(CombinedBounds.GetCenter(), CombinedBounds.GetExtent().GetMax());
|
|
|
|
// Add each old vertex to the octree
|
|
for (const FPaintedMeshVertex& Vertex : PaintedVertices)
|
|
{
|
|
VertPosOctree.AddElement(Vertex);
|
|
}
|
|
|
|
// Iterate over each new vertex position, attempting to find the old vertex it is closest to, applying
|
|
// the color of the old vertex to the new position if possible.
|
|
const float DistanceOverNormalThreshold = KINDA_SMALL_NUMBER;
|
|
for (int32 VertexIndex = 0; VertexIndex < ApplyVertices.Num(); ++VertexIndex)
|
|
{
|
|
const FSoftSkinVertex& Vertex = ApplyVertices[VertexIndex];
|
|
TArray<FPaintedMeshVertex> PointsToConsider;
|
|
TVertexColorPropogationPosOctree::TConstIterator<> OctreeIter(VertPosOctree);
|
|
const FVector& CurPosition = Vertex.Position;
|
|
FVector CurNormal = Vertex.TangentZ;
|
|
|
|
// Iterate through the octree attempting to find the vertices closest to the current new point
|
|
while (OctreeIter.HasPendingNodes())
|
|
{
|
|
const TVertexColorPropogationPosOctree::FNode& CurNode = OctreeIter.GetCurrentNode();
|
|
const FOctreeNodeContext& CurContext = OctreeIter.GetCurrentContext();
|
|
|
|
// Find the child of the current node, if any, that contains the current new point
|
|
FOctreeChildNodeRef ChildRef = CurContext.GetContainingChild(FBoxCenterAndExtent(CurPosition, FVector::ZeroVector));
|
|
|
|
if (!ChildRef.IsNULL())
|
|
{
|
|
const TVertexColorPropogationPosOctree::FNode* ChildNode = CurNode.GetChild(ChildRef);
|
|
|
|
// If the specified child node exists and contains any of the old vertices, push it to the iterator for future consideration
|
|
if (ChildNode && ChildNode->GetInclusiveElementCount() > 0)
|
|
{
|
|
OctreeIter.PushChild(ChildRef);
|
|
}
|
|
// If the child node doesn't have any of the old vertices in it, it's not worth pursuing any further. In an attempt to find
|
|
// anything to match vs. the new point, add all of the children of the current octree node that have old points in them to the
|
|
// iterator for future consideration.
|
|
else
|
|
{
|
|
FOREACH_OCTREE_CHILD_NODE(OctreeChildRef)
|
|
{
|
|
if (CurNode.HasChild(OctreeChildRef))
|
|
{
|
|
OctreeIter.PushChild(OctreeChildRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add all of the elements in the current node to the list of points to consider for closest point calculations
|
|
PointsToConsider.Append(CurNode.GetElements());
|
|
OctreeIter.Advance();
|
|
}
|
|
|
|
// If any points to consider were found, iterate over each and find which one is the closest to the new point
|
|
if (PointsToConsider.Num() > 0)
|
|
{
|
|
int32 BestVertexIndex = 0;
|
|
FVector BestVertexNormal = PointsToConsider[BestVertexIndex].Normal;
|
|
|
|
float BestDistanceSquared = (PointsToConsider[BestVertexIndex].Position - CurPosition).SizeSquared();
|
|
float BestNormalDot = BestVertexNormal | CurNormal;
|
|
|
|
for (int32 ConsiderationIndex = 1; ConsiderationIndex < PointsToConsider.Num(); ++ConsiderationIndex)
|
|
{
|
|
FPaintedMeshVertex& CheckVertex = PointsToConsider[ConsiderationIndex];
|
|
FVector VertexNormal = CheckVertex.Normal;
|
|
|
|
const float DistSqrd = (CheckVertex.Position - CurPosition).SizeSquared();
|
|
const float NormalDot = VertexNormal | CurNormal;
|
|
if (DistSqrd < BestDistanceSquared - DistanceOverNormalThreshold)
|
|
{
|
|
BestVertexIndex = ConsiderationIndex;
|
|
|
|
BestDistanceSquared = DistSqrd;
|
|
BestNormalDot = NormalDot;
|
|
}
|
|
else if (DistSqrd < BestDistanceSquared + DistanceOverNormalThreshold && NormalDot > BestNormalDot)
|
|
{
|
|
BestVertexIndex = ConsiderationIndex;
|
|
BestDistanceSquared = DistSqrd;
|
|
BestNormalDot = NormalDot;
|
|
}
|
|
}
|
|
|
|
ApplyLOD.ColorVertexBuffer.VertexColor(VertexIndex) = PointsToConsider[BestVertexIndex].Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
GeometryInfo.PostEdit();
|
|
}
|
|
}
|
|
}
|
|
}
|