From 185f601c08919506f48ea84675cd1d6befa3a7c2 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 19 Aug 2025 10:41:54 +0200 Subject: [PATCH] Auto-sync from SVN revision 64696 --- .../com.unity.animation.rigging/.signature | 5 + .../com.unity.animation.rigging/CHANGELOG.md | 208 +++++ .../CHANGELOG.md.meta | 7 + .../DocCodeExamples.meta | 8 + .../CustomPlayableGraphEvaluator.cs | 40 + .../CustomPlayableGraphEvaluator.cs.meta} | 2 +- .../CustomRigBuilderEvaluator.cs | 35 + .../CustomRigBuilderEvaluator.cs.meta | 3 + ...y.Animation.Rigging.DocCodeExamples.asmdef | 16 + ...mation.Rigging.DocCodeExamples.asmdef.meta | 7 + .../com.unity.animation.rigging/Editor.meta | 8 + .../Editor/AnimationRig.meta | 8 + .../Editor/AnimationRig/Constraints.meta | 8 + .../Constraints/BlendConstraintEditor.cs | 163 ++++ .../BlendConstraintEditor.cs.meta} | 2 +- .../Constraints/ChainIKConstraintEditor.cs | 156 ++++ .../ChainIKConstraintEditor.cs.meta} | 2 +- .../Constraints/DampedTransformEditor.cs | 122 +++ .../DampedTransformEditor.cs.meta} | 2 +- .../Constraints/MultiAimConstraintEditor.cs | 197 ++++ .../MultiAimConstraintEditor.cs.meta | 11 + .../MultiParentConstraintEditor.cs | 121 +++ .../MultiParentConstraintEditor.cs.meta | 11 + .../MultiPositionConstraintEditor.cs | 118 +++ .../MultiPositionConstraintEditor.cs.meta | 11 + .../MultiReferentialConstraintEditor.cs | 132 +++ .../MultiReferentialConstraintEditor.cs.meta | 11 + .../MultiRotationConstraintEditor.cs | 118 +++ .../MultiRotationConstraintEditor.cs.meta | 11 + .../Constraints/OverrideTransformEditor.cs | 152 +++ .../OverrideTransformEditor.cs.meta | 11 + .../Constraints/TwistChainConstraintEditor.cs | 143 +++ .../TwistChainConstraintEditor.cs.meta | 11 + .../Constraints/TwistCorrectionEditor.cs | 114 +++ .../Constraints/TwistCorrectionEditor.cs.meta | 11 + .../Constraints/TwoBoneIKConstraintEditor.cs | 301 ++++++ .../TwoBoneIKConstraintEditor.cs.meta | 11 + .../Editor/AnimationRig/RigBuilderEditor.cs | 61 ++ .../AnimationRig/RigBuilderEditor.cs.meta | 11 + .../Editor/AnimationRig/RigEditor.cs | 76 ++ .../Editor/AnimationRig/RigEditor.cs.meta | 11 + .../Editor/AnimationRig/RigLayerDrawer.cs | 40 + .../AnimationRig/RigLayerDrawer.cs.meta | 11 + .../Editor/AnimationRig/RigTransformEditor.cs | 12 + .../AnimationRig/RigTransformEditor.cs.meta | 11 + .../WeightedTransformArrayDrawer.cs | 249 +++++ .../WeightedTransformArrayDrawer.cs.meta | 3 + .../AnimationRig/WeightedTransformDrawer.cs | 59 ++ .../WeightedTransformDrawer.cs.meta | 11 + .../Editor/AssemblyInfo.cs | 2 + .../Editor/AssemblyInfo.cs.meta | 11 + .../Editor/Attributes.meta | 8 + .../Attributes/BakeParametersAttribute.cs | 116 +++ .../BakeParametersAttribute.cs.meta | 11 + .../Attributes/CustomOverlayAttribute.cs | 18 + .../Attributes/CustomOverlayAttribute.cs.meta | 11 + .../InverseRigConstraintAttribute.cs | 32 + .../InverseRigConstraintAttribute.cs.meta | 11 + .../Editor/Effectors.meta | 8 + .../Editor/Effectors/IRigEffector.cs | 12 + .../Editor/Effectors/IRigEffector.cs.meta | 11 + .../Editor/Effectors/IRigEffectorOverlay.cs | 10 + .../Effectors/IRigEffectorOverlay.cs.meta | 11 + .../Editor/Effectors/RigEffector.cs | 307 ++++++ .../Editor/Effectors/RigEffector.cs.meta | 11 + .../Editor/Effectors/RigEffectorOverlay.cs | 190 ++++ .../Effectors/RigEffectorOverlay.cs.meta | 11 + .../Editor/Effectors/RigEffectorRenderer.cs | 235 +++++ .../Effectors/RigEffectorRenderer.cs.meta | 11 + .../Editor/Effectors/RigEffectorWizard.cs | 68 ++ .../Effectors/RigEffectorWizard.cs.meta | 11 + .../Editor/Icons.meta | 8 + .../Editor/Icons/RuntimeRig@128.png | Bin 0 -> 6088 bytes .../Editor/Icons/RuntimeRig@128.png.meta | 120 +++ .../Editor/InverseSolve.meta | 8 + .../Editor/InverseSolve/AnimationJobs.meta | 8 + .../MultiAimInverseConstraintJob.cs | 130 +++ .../MultiAimInverseConstraintJob.cs.meta | 11 + .../MultiParentInverseConstraintJob.cs | 116 +++ .../MultiParentInverseConstraintJob.cs.meta | 11 + .../MultiPositionInverseConstraintJob.cs | 116 +++ .../MultiPositionInverseConstraintJob.cs.meta | 11 + .../MultiReferentialInverseConstraintJob.cs | 96 ++ ...ltiReferentialInverseConstraintJob.cs.meta | 11 + .../MultiRotationInverseConstraintJob.cs | 120 +++ .../MultiRotationInverseConstraintJob.cs.meta | 11 + .../TwistChainInverseConstraintJob.cs | 74 ++ .../TwistChainInverseConstraintJob.cs.meta | 11 + .../TwoBoneIKInverseConstraintJob.cs | 99 ++ .../TwoBoneIKInverseConstraintJob.cs.meta | 11 + .../Editor/InverseSolve/Constraints.meta | 8 + .../Constraints/MultiAimInverseConstraint.cs | 24 + .../MultiAimInverseConstraint.cs.meta | 11 + .../MultiParentInverseConstraint.cs | 25 + .../MultiParentInverseConstraint.cs.meta | 11 + .../MultiPositionInverseConstraint.cs | 24 + .../MultiPositionInverseConstraint.cs.meta | 11 + .../MultiReferentialInverseConstraint.cs | 23 + .../MultiReferentialInverseConstraint.cs.meta | 11 + .../MultiRotationInverseConstraint.cs | 24 + .../MultiRotationInverseConstraint.cs.meta | 11 + .../TwistChainInverseConstraint.cs | 24 + .../TwistChainInverseConstraint.cs.meta | 11 + .../Constraints/TwoBoneIKInverseConstraint.cs | 24 + .../TwoBoneIKInverseConstraint.cs.meta | 11 + .../Editor/Shaders.meta | 8 + .../Editor/Shaders/BoneHandles.shader | 67 ++ .../Editor/Shaders/BoneHandles.shader.meta | 9 + .../Editor/Shaders/RigEffector.shader | 66 ++ .../Editor/Shaders/RigEffector.shader.meta | 9 + .../Editor/Shapes.meta | 8 + .../Editor/Shapes/BallEffector.asset | 195 ++++ .../Editor/Shapes/BallEffector.asset.meta | 8 + .../Editor/Shapes/BoxEffector.asset | 175 ++++ .../Editor/Shapes/BoxEffector.asset.meta | 8 + .../Editor/Shapes/CircleEffector.asset | 165 ++++ .../Editor/Shapes/CircleEffector.asset.meta | 8 + .../Editor/Shapes/LocatorEffector.asset | 165 ++++ .../Editor/Shapes/LocatorEffector.asset.meta | 8 + .../Editor/Shapes/SquareEffector.asset | 165 ++++ .../Editor/Shapes/SquareEffector.asset.meta | 8 + .../Unity.Animation.Rigging.Editor.asmdef | 25 + ...Unity.Animation.Rigging.Editor.asmdef.meta | 7 + .../Editor/Utils.meta | 8 + .../Utils/AnimationRiggingContextMenus.cs | 23 + .../AnimationRiggingContextMenus.cs.meta | 11 + .../Utils/AnimationRiggingEditorUtils.cs | 147 +++ .../Utils/AnimationRiggingEditorUtils.cs.meta | 11 + .../Editor/Utils/AnimationRiggingMenu.cs | 90 ++ .../Editor/Utils/AnimationRiggingMenu.cs.meta | 11 + .../Editor/Utils/AnimationWindowUtils.cs | 85 ++ .../Editor/Utils/AnimationWindowUtils.cs.meta | 11 + .../Editor/Utils/BakeUtils.cs | 879 ++++++++++++++++++ .../Editor/Utils/BakeUtils.cs.meta | 11 + .../Editor/Utils/BoneRendererEditor.cs | 82 ++ .../Editor/Utils/BoneRendererEditor.cs.meta | 11 + .../Editor/Utils/BoneRendererUtils.cs | 498 ++++++++++ .../Editor/Utils/BoneRendererUtils.cs.meta | 11 + .../Editor/Utils/CommonContent.cs | 85 ++ .../Editor/Utils/CommonContent.cs.meta | 3 + .../Editor/Utils/EditorCurveBindingUtils.cs | 156 ++++ .../Utils/EditorCurveBindingUtils.cs.meta | 11 + .../Editor/Utils/EditorHelpers.cs | 221 +++++ .../Editor/Utils/EditorHelpers.cs.meta | 11 + .../Editor/Utils/ExpandChildrenDrawer.cs | 36 + .../Editor/Utils/ExpandChildrenDrawer.cs.meta | 11 + .../Editor/Utils/FoldoutState.cs | 43 + .../Editor/Utils/FoldoutState.cs.meta | 3 + .../Editor/Utils/Preferences.cs | 271 ++++++ .../Editor/Utils/Preferences.cs.meta | 11 + .../Editor/Utils/SceneViewOverlay.cs | 202 ++++ .../Editor/Utils/SceneViewOverlay.cs.meta | 11 + .../Editor/Utils/Vector3BoolDrawer.cs | 61 ++ .../Editor/Utils/Vector3BoolDrawer.cs.meta | 11 + .../com.unity.animation.rigging/LICENSE.md | 5 + .../LICENSE.md.meta | 7 + .../com.unity.animation.rigging/README.md | 5 + .../README.md.meta | 7 + .../com.unity.animation.rigging/Runtime.meta | 8 + .../Runtime/AnimationJobs.meta | 8 + .../AnimationJobs/AnimationJobCache.cs | 226 +++++ .../AnimationJobs/AnimationJobCache.cs.meta | 11 + .../AnimationJobs/BlendConstraintJob.cs | 156 ++++ .../AnimationJobs/BlendConstraintJob.cs.meta | 11 + .../AnimationJobs/ChainIKConstraintJob.cs | 188 ++++ .../ChainIKConstraintJob.cs.meta | 11 + .../AnimationJobs/DampedTransformJob.cs | 160 ++++ .../AnimationJobs/DampedTransformJob.cs.meta | 11 + .../AnimationJobs/IAnimatableProperty.cs | 511 ++++++++++ .../AnimationJobs/IAnimatableProperty.cs.meta | 11 + .../AnimationJobs/IAnimationJobBinder.cs | 111 +++ .../AnimationJobs/IAnimationJobBinder.cs.meta | 11 + .../AnimationJobs/IAnimationJobData.cs | 18 + .../AnimationJobs/IAnimationJobData.cs.meta | 11 + .../AnimationJobs/IWeightedAnimationJob.cs | 11 + .../IWeightedAnimationJob.cs.meta | 11 + .../AnimationJobs/MultiAimConstraintJob.cs | 355 +++++++ .../MultiAimConstraintJob.cs.meta | 11 + .../AnimationJobs/MultiParentConstraintJob.cs | 222 +++++ .../MultiParentConstraintJob.cs.meta | 11 + .../MultiPositionConstraintJob.cs | 180 ++++ .../MultiPositionConstraintJob.cs.meta | 11 + .../MultiReferentialConstraintJob.cs | 141 +++ .../MultiReferentialConstraintJob.cs.meta | 11 + .../MultiRotationConstraintJob.cs | 180 ++++ .../MultiRotationConstraintJob.cs.meta | 11 + .../AnimationJobs/OverrideTransformJob.cs | 241 +++++ .../OverrideTransformJob.cs.meta | 11 + .../AnimationJobs/RigSyncSceneToStreamJob.cs | 250 +++++ .../RigSyncSceneToStreamJob.cs.meta | 11 + .../Runtime/AnimationJobs/TransformHandle.cs | 312 +++++++ .../AnimationJobs/TransformHandle.cs.meta | 11 + .../AnimationJobs/TwistChainConstraintJob.cs | 155 +++ .../TwistChainConstraintJob.cs.meta | 11 + .../AnimationJobs/TwistCorrectionJob.cs | 141 +++ .../AnimationJobs/TwistCorrectionJob.cs.meta | 11 + .../AnimationJobs/TwoBoneIKConstraintJob.cs | 142 +++ .../TwoBoneIKConstraintJob.cs.meta | 11 + .../WeightedTransformArrayBinder.cs | 62 ++ .../WeightedTransformArrayBinder.cs.meta | 11 + .../Runtime/AnimationRig.meta | 8 + .../Runtime/AnimationRig/Constraints.meta | 8 + .../Constraints/BlendConstraint.cs | 92 ++ .../Constraints/BlendConstraint.cs.meta | 11 + .../Constraints/ChainIKConstraint.cs | 107 +++ .../Constraints/ChainIKConstraint.cs.meta | 11 + .../Constraints/DampedTransform.cs | 74 ++ .../Constraints/DampedTransform.cs.meta | 11 + .../Constraints/MultiAimConstraint.cs | 214 +++++ .../Constraints/MultiAimConstraint.cs.meta | 11 + .../Constraints/MultiParentConstraint.cs | 94 ++ .../Constraints/MultiParentConstraint.cs.meta | 11 + .../Constraints/MultiPositionConstraint.cs | 89 ++ .../MultiPositionConstraint.cs.meta | 11 + .../Constraints/MultiReferentialConstraint.cs | 91 ++ .../MultiReferentialConstraint.cs.meta | 11 + .../Constraints/MultiRotationConstraint.cs | 88 ++ .../MultiRotationConstraint.cs.meta | 11 + .../Constraints/OverrideTransform.cs | 95 ++ .../Constraints/OverrideTransform.cs.meta | 11 + .../Constraints/TwistChainConstraint.cs | 53 ++ .../Constraints/TwistChainConstraint.cs.meta | 11 + .../Constraints/TwistCorrection.cs | 101 ++ .../Constraints/TwistCorrection.cs.meta | 11 + .../Constraints/TwoBoneIKConstraint.cs | 91 ++ .../Constraints/TwoBoneIKConstraint.cs.meta | 11 + .../Runtime/AnimationRig/IRigConstraint.cs | 55 ++ .../AnimationRig/IRigConstraint.cs.meta | 11 + .../Runtime/AnimationRig/IRigLayer.cs | 46 + .../Runtime/AnimationRig/IRigLayer.cs.meta | 11 + .../AnimationRig/OverrideRigConstraint.cs | 106 +++ .../OverrideRigConstraint.cs.meta | 11 + .../Runtime/AnimationRig/OverrideRigLayer.cs | 129 +++ .../AnimationRig/OverrideRigLayer.cs.meta | 11 + .../Runtime/AnimationRig/Rig.cs | 46 + .../Runtime/AnimationRig/Rig.cs.meta | 11 + .../Runtime/AnimationRig/RigBuilder.cs | 374 ++++++++ .../Runtime/AnimationRig/RigBuilder.cs.meta | 11 + .../Runtime/AnimationRig/RigBuilderUtils.cs | 101 ++ .../AnimationRig/RigBuilderUtils.cs.meta | 11 + .../Runtime/AnimationRig/RigConstraint.cs | 109 +++ .../AnimationRig/RigConstraint.cs.meta | 11 + .../Runtime/AnimationRig/RigLayer.cs | 117 +++ .../Runtime/AnimationRig/RigLayer.cs.meta | 11 + .../Runtime/AnimationRig/RigTransform.cs | 11 + .../Runtime/AnimationRig/RigTransform.cs.meta | 11 + .../Runtime/AnimationRig/RigUtils.cs | 334 +++++++ .../Runtime/AnimationRig/RigUtils.cs.meta | 11 + .../AnimationRig/SyncSceneToStreamLayer.cs | 73 ++ .../SyncSceneToStreamLayer.cs.meta | 11 + .../Runtime/AnimationRig/WeightedTransform.cs | 75 ++ .../AnimationRig/WeightedTransform.cs.meta | 11 + .../AnimationRig/WeightedTransformArray.cs | 454 +++++++++ .../WeightedTransformArray.cs.meta | 11 + .../Runtime/AssemblyInfo.cs | 4 + .../Runtime/AssemblyInfo.cs.meta | 11 + .../Runtime/Attributes.meta | 8 + .../Attributes/ExpandChildrenAttribute.cs | 4 + .../ExpandChildrenAttribute.cs.meta | 3 + .../Attributes/SyncSceneToStreamAttribute.cs | 70 ++ .../SyncSceneToStreamAttribute.cs.meta | 11 + .../Attributes/WeightRangeAttribute.cs | 34 + .../Attributes/WeightRangeAttribute.cs.meta | 3 + .../Runtime/Effectors.meta | 8 + .../Runtime/Effectors/IRigEffectorHolder.cs | 28 + .../Effectors/IRigEffectorHolder.cs.meta | 11 + .../Runtime/Effectors/RigEffectorData.cs | 52 ++ .../Runtime/Effectors/RigEffectorData.cs.meta | 11 + .../Runtime/Unity.Animation.Rigging.asmdef | 12 + .../Unity.Animation.Rigging.asmdef.meta | 7 + .../Runtime/Utils.meta | 8 + .../Runtime/Utils/AffineTransform.cs | 114 +++ .../Runtime/Utils/AffineTransform.cs.meta | 11 + .../Runtime/Utils/AnimationRuntimeUtils.cs | 397 ++++++++ .../Utils/AnimationRuntimeUtils.cs.meta | 11 + .../Runtime/Utils/BoneRenderer.cs | 203 ++++ .../Runtime/Utils/BoneRenderer.cs.meta | 11 + .../Runtime/Utils/ConstraintsUtils.cs | 106 +++ .../Runtime/Utils/ConstraintsUtils.cs.meta | 11 + .../Runtime/Utils/QuaternionExt.cs | 77 ++ .../Runtime/Utils/QuaternionExt.cs.meta | 11 + .../Runtime/Utils/Vector3Bool.cs | 36 + .../Runtime/Utils/Vector3Bool.cs.meta | 11 + .../com.unity.animation.rigging/package.json | 41 + .../package.json.meta | 7 + .../Editor/Deprecated.cs | 22 + .../Lighting/ProbeVolume/ProbeGIBaking.cs | 8 +- .../RenderGraphViewer.SidePanel.cs | 11 +- ...aultVolumeProfileSettingsPropertyDrawer.cs | 6 +- .../Editor/Volume/VolumeComponentEditor.cs | 19 +- .../Volume/VolumeComponentListEditor.cs | 5 +- .../Runtime/Common/CoreUnsafeUtils.cs | 12 +- .../Runtime/Common/DynamicArray.cs | 3 +- .../Debug/DebugDisplayGPUResidentDrawer.cs | 156 +++- .../Runtime/GPUDriven/GPUResidentBatcher.cs | 15 +- .../Runtime/GPUDriven/GPUResidentDrawer.cs | 233 ++--- .../GPUDriven/GPUResidentDrawerBurst.cs | 80 ++ .../GPUDriven/GPUResidentDrawerBurst.cs.meta | 2 + .../GPUDriven/GPUResidentDrawerDebug.cs | 49 +- .../Runtime/GPUDriven/InstanceCuller.cs | 75 +- .../Runtime/GPUDriven/InstanceCullerBurst.cs | 19 + .../GPUDriven/InstanceCullerBurst.cs.meta | 2 + .../GPUDriven/InstanceCullingBatcher.cs | 399 +++----- .../GPUDriven/InstanceCullingBatcherBurst.cs | 301 ++++++ .../InstanceCullingBatcherBurst.cs.meta | 2 + .../InstanceData/InstanceDataSystem.Jobs.cs | 207 ----- .../InstanceData/InstanceDataSystem.cs | 17 +- .../InstanceData/InstanceDataSystemBurst.cs | 191 ++++ .../InstanceDataSystemBurst.cs.meta | 2 + .../GPUDriven/InstanceOcclusionCuller.cs | 10 +- .../GPUDriven/InstanceOcclusionCuller.cs.hlsl | 10 +- .../Runtime/GPUDriven/LODGroupDataPool.cs | 106 +-- .../GPUDriven/LODGroupDataPoolBurst.cs | 72 ++ .../GPUDriven/LODGroupDataPoolBurst.cs.meta | 2 + .../Lighting/ProbeVolume/ProbeBrickPool.cs | 1 + .../ProbeVolume/ProbeReferenceVolume.cs | 8 + .../Lighting/ProbeVolume/ProbeVolume.hlsl | 1 + .../ProbeVolume/ProbeVolumePerSceneData.cs | 4 +- .../PostProcessing/LensFlareDataSRP.cs | 1 - .../Compiler/NativePassCompiler.Debug.cs | 3 + .../RenderGraph/IRenderGraphBuilder.cs | 4 + .../Runtime/RenderGraph/RenderGraph.cs | 21 +- .../RenderGraph/RenderGraphBuilders.cs | 22 + .../RenderGraph/RenderGraphUtilsBlit.cs | 49 +- .../InstanceOcclusionCullingKernels.compute | 29 +- .../Runtime/Utilities/Blitter.cs | 36 + .../Runtime/Volume/VolumeManager.cs | 45 +- .../Runtime/XR/XRPass.cs | 18 + .../Runtime/XR/XRSystem.cs | 1 + .../Editor/FixedBufferStringQueueTests.cs | 26 + .../GPUDriven/GPUDrivenRenderingTests.cs | 42 +- .../Volumes/VolumeComponentCopyPasteTests.cs | 170 ---- .../VolumeComponentEditorSupportedOnTests.cs | 45 - .../Editor/Volumes/VolumeComponentTests.cs | 425 --------- .../Volumes/VolumeComponentVisibilityTests.cs | 68 -- .../Tests/Runtime/TestVolume.cs | 17 - .../Tests/Runtime/TestVolume.cs.meta | 11 - .../CopyPasteTestComponent1.cs | 26 - .../CopyPasteTestComponent1.cs.meta | 11 - .../CopyPasteTestComponent2.cs | 23 - .../CopyPasteTestComponent2.cs.meta | 11 - .../CopyPasteTestComponent3.cs | 24 - .../CopyPasteTestComponent3.cs.meta | 11 - ...olumeComponentCustomRenderPipelineAsset.cs | 12 - ...ComponentCustomRenderPipelineAsset.cs.meta | 11 - .../VolumeComponentNoSupportedOn.cs | 11 - .../VolumeComponentNoSupportedOn.cs.meta | 11 - .../VolumeComponentNotSpecifiedSupportedOn.cs | 12 - ...meComponentNotSpecifiedSupportedOn.cs.meta | 11 - .../VolumeComponentSupportedEverywhere.cs | 9 - ...VolumeComponentSupportedEverywhere.cs.meta | 11 - .../VolumeComponentSupportedOnAnySRP.cs | 10 - .../VolumeComponentSupportedOnAnySRP.cs.meta | 11 - .../VolumeComponentSupportedOnCustomSRP.cs | 10 - ...olumeComponentSupportedOnCustomSRP.cs.meta | 11 - .../Tests/Runtime/VolumeManagerTests.cs | 227 ----- .../Tests/Runtime/VolumeManagerTests.cs.meta | 11 - .../package.json | 2 +- .../package.json | 2 +- .../Material/Decal/DecalProjectorEditor.cs | 64 +- .../FogVolume/FogVolumePropertyBlock.cs | 4 + .../ShaderGraph/FogVolumeSubTarget.cs | 8 +- .../FogVolume/ShaderGraph/OverdrawDebug.hlsl | 2 +- .../ShaderGraph/ShaderPassPreview.hlsl | 2 +- .../ShaderGraph/ShaderPassVoxelize.hlsl | 13 +- .../ShaderGraph/VoxelizationTransforms.hlsl | 36 + .../VoxelizationTransforms.hlsl.meta} | 2 +- .../ShaderGraph/Templates/Pixel.template.hlsl | 8 + ...aultVolumeProfileSettingsPropertyDrawer.cs | 2 +- .../Camera/HDCameraUI.Rendering.Drawers.cs | 4 - .../CustomPass/CustomPassDrawer.cs | 17 + .../RenderPipeline/HDRenderPipelineUI.Skin.cs | 3 +- .../RenderPipeline/HDRenderPipelineUI.cs | 4 - .../Lighting/Shadow/HDShadowAlgorithms.hlsl | 6 + .../Runtime/Material/Lit/Lit.shader | 2 + .../RenderPipeline/HDRenderPipeline.Debug.cs | 6 + .../HDRenderPipeline.PostProcess.cs | 7 +- .../HDRenderPipeline.Prepass.cs | 4 +- .../HDRenderPipeline.RenderGraph.cs | 3 +- .../RenderPipeline/HDRenderPipeline.cs | 2 +- .../Shaders/RaytracingLightLoop.hlsl | 3 +- .../RenderPass/CustomPass/CustomPass.cs | 29 +- .../RenderPipeline/ShaderPass/ShaderPass.cs | 3 + .../ShaderPass/ShaderPass.cs.hlsl | 3 + .../HDRPDefaultVolumeProfileSetting.cs | 4 +- .../Runtime/Sky/SkyManager.cs | 58 +- .../VFXGraph/Shaders/VFXRayTracingCommon.hlsl | 151 --- .../Water/Shaders/SampleWaterSurface.hlsl | 4 + .../Water/Shaders/UnderWaterUtilities.hlsl | 2 +- .../package.json | 2 +- .../UnifiedRayTracing/AccelStructTests.cs | 1 + .../package.json | 2 +- .../Drawing/Manipulators/ResizeBorderFrame.cs | 71 -- .../Manipulators/ResizeBorderFrame.cs.meta | 11 - .../Drawing/Manipulators/ResizeSideHandle.cs | 365 -------- .../Manipulators/ResizeSideHandle.cs.meta | 11 - .../Editor/Drawing/MaterialGraphEditWindow.cs | 9 +- .../Editor/Resources/Styles/HelpBoxRow.uss | 65 -- .../Resources/Styles/HelpBoxRow.uss.meta | 11 - Packages/com.unity.shadergraph/package.json | 2 +- .../Editor/TMP/TMP_BaseShaderGUI.cs | 2 +- .../Editor/TMP/TMP_FontAssetEditor.cs | 15 +- .../TMP/TMPro_FontAssetCreatorWindow.cs | 7 +- .../Runtime/TMP/TMP_InputField.cs | 10 +- .../Runtime/TMP/TMP_SpriteAsset.cs | 2 +- .../Runtime/UGUI/UI/Core/Image.cs | 16 +- .../Runtime/UGUI/UI/Core/InputField.cs | 4 +- .../Runtime/UGUI/UI/Core/Slider.cs | 1 + Packages/com.unity.ugui/package.json | 2 +- .../GraphView/Views/VFXConvertSubgraph.cs | 6 + .../Editor/GraphView/Views/VFXView.cs | 82 +- .../Inspector/VFXSlotContainerEditor.cs | 1 + .../Editor/Models/VFXErrorManager.cs | 3 + .../Editor/Models/VFXGraph.cs | 45 +- .../Editor/UIResources/uss/PropertyRM.uss | 8 +- .../Editor/UIResources/uss/VFXDataAnchor.uss | 4 + .../Shaders/VFXRayTracingCommon.hlsl.meta | 2 +- .../Shaders/VFXVolumetricFogUpdate.template | 4 + .../com.unity.visualeffectgraph/package.json | 2 +- 419 files changed, 19991 insertions(+), 3000 deletions(-) create mode 100644 Packages/com.unity.animation.rigging/.signature create mode 100644 Packages/com.unity.animation.rigging/CHANGELOG.md create mode 100644 Packages/com.unity.animation.rigging/CHANGELOG.md.meta create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples.meta create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs rename Packages/{com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs.meta => com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs.meta} (83%) create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs.meta create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef create mode 100644 Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef.meta create mode 100644 Packages/com.unity.animation.rigging/Editor.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs rename Packages/{com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs.meta => com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs.meta} (83%) create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs rename Packages/{com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs.meta => com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs.meta} (83%) create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs rename Packages/{com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs.meta => com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs.meta} (83%) create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Icons.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png create mode 100644 Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shaders.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader create mode 100644 Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader create mode 100644 Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset create mode 100644 Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef create mode 100644 Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs create mode 100644 Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/LICENSE.md create mode 100644 Packages/com.unity.animation.rigging/LICENSE.md.meta create mode 100644 Packages/com.unity.animation.rigging/README.md create mode 100644 Packages/com.unity.animation.rigging/README.md.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Effectors.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef create mode 100644 Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs.meta create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs create mode 100644 Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs.meta create mode 100644 Packages/com.unity.animation.rigging/package.json create mode 100644 Packages/com.unity.animation.rigging/package.json.meta create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs create mode 100644 Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs.meta delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs delete mode 100644 Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs.meta create mode 100644 Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl rename Packages/com.unity.render-pipelines.high-definition/{Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl.meta => Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl.meta} (75%) delete mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl delete mode 100644 Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs delete mode 100644 Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs.meta delete mode 100644 Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs delete mode 100644 Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs.meta delete mode 100644 Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss delete mode 100644 Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss.meta diff --git a/Packages/com.unity.animation.rigging/.signature b/Packages/com.unity.animation.rigging/.signature new file mode 100644 index 00000000..5b3dacec --- /dev/null +++ b/Packages/com.unity.animation.rigging/.signature @@ -0,0 +1,5 @@ +{ + "timestamp": 1675097346, + "signature": "sLaf7UbDWeIQ1rbAZUqsDzJE5/0srDNWDpraziCbDW4CB1xkeWHyWhc+dfHkeB/vaJfm8lU2RHER8PwViFYDfsJoP6KzEpL2dlfFew2kyc5SldgxNylVg6dzfUy28niNlErhEd8nVizxROaTfUTDE8Qe3A66zdGWqUqz0j9R1TWAiuOLHVQOI4VzbkyjF3CL+b67Y2tRI9IBc9KcY5Mc7UCTji1QI1OQNAkVMbMOmdEBPkoiujnA5Vn3YLYYY24trSvNG0NGsFOOWWyus9QDJDWOjeMImiYl8Vc9k4s/pz11LLtVqKslsbRDc4V5AeD1mWV+p/QI2WlkDvdlKOLjt6/ITPp07LDNIJy5+A8E6VXAXCepcJ9z9Vm5HozelqU4WoPnScA1o2tVxeGVMXZjEesK/0WaMXTlqjOWNKEKtJtqoRcpdvGvqKnV7JTVTq/oWZDjcjdcNmE8ee6C2x6rijc/oYEsHbBVonco5OnOX1jynHW9gYquOcOkiPlojGeQ", + "publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg" +} \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/CHANGELOG.md b/Packages/com.unity.animation.rigging/CHANGELOG.md new file mode 100644 index 00000000..064221d5 --- /dev/null +++ b/Packages/com.unity.animation.rigging/CHANGELOG.md @@ -0,0 +1,208 @@ +# Changelog +All notable changes to this package will be documented in this file. + +## [1.3.0] - 2023-01-30 +- Added the `TransformHandle`'s function `GetLocalToParentMatrix` to get the matrix of an animation stream transform in local space. +- Added the `TransformHandle`'s function `GetLocalToWorldMatrix` to get the matrix of an animation stream transform in world space. +- Fixed handling negative scale in the `MultiAimConstraintJob` (case 1366549). +- Fixed transforms in animator hierarchy, but not children of avatar root not resolving properly (case 1373387). +- Fixed MultiAimConstraint evaluation with a world up axis (UM-1936). +- Fixed crash when calling `RigBuilder.Build` by preventing rebuilding the PlayableGraph when in a preview context (case UUM-8599). +- Fixed an issue where a misplaced `BoneHandles.shader` shader would cause the Scene View's Orientation Overlay to no longer render (case UUM-20874). + +## [1.2.0] - 2021-12-08 +- Updated package icons (case 1361823). +- Fixed BoneRenderer to refresh bones rendered in viewport on a mouse drag and drop event (case 1361819). +- Fixed potential performance issue with rig effectors when used in a deep hierarchy. +- Fixed NullReferenceException when previewing animation and layers were not initialized in `RigBuilder.StartPreview` (case 1367857). +- Added the function RigBuilder.SyncLayers to manually update rigs and constraints prior to PlayableGraph evaluation. +- Added the function RigBuilder.Evaluate(float) to manually update and evaluate the RigBuilder. +- Added the function RigBuilder.Build(PlayableGraph) to build the RigBuilder Playable nodes in an external PlayableGraph. +- Added effector visuals for auto setup of Two-Bone IK. +- Correctly positioned effectors for auto setup of Two-Bone IK. + +## [1.1.1] - 2021-08-23 +### Patch Update of *Animation Rigging*. +- Added missing tooltips to components (case 1265274). +- Labels on components now support localization in supported regions. +- Upgraded Animation Rigging overlay toolbox to use new Unity overlays. + +## [1.1.0] - 2021-04-09 +### Patch Update of *Animation Rigging*. +- Fixed Auto Setup on TwoBoneIK throwing an ArgumentNullException instead of a warning message. +- Fixed MultiReferential constraint not writing driver transform to stream. +- Added exceptions in ReadWriteTransformHandle and ReadOnlyTransformHandle to cover invalid Transform references (case 1275002). +- Added functions ConstructConstraintDataPropertyName and ConstructCustomPropertyName to ConstraintsUtils public API. +- Fixed Gizmo icons reimporting when switching platforms. +- Added missing icon to TwistChainConstraint. +- Built-in RigConstraint components now support multi-object editing in the Inspector. +- Fixed bug causing multi-target constraints to add a new target simply by viewing them in the Inspector. +- Fixed collapsing Source Objects and Settings foldouts in the Inspector going on the undo stack. + - Appearance of foldout groups now matches other Unity components. +- Appearance of WeightedTransformArray in the Inspector now matches default array control. + - Header now supports dragging and dropping Transforms. +- Validate constraint weights and source object weights are within supported range when deserializing built-in constraints in the Editor. +- Improvements to help when creating custom constraints: + - RigConstraint m_Data field no longer displays nested under a foldout in the Inspector by default. + - RigConstraint.OnValidate() is now overridable in sub-classes. + - Added WeightedTransformArray.OnValidate(). + - Added default PropertyDrawer for WeightedTransformArray that supports multi-select. + - Added optional WeightRangeAttribute. It should now be used in conjunction with WeightedTransformArray fields instead of RangeAttribute to specify the control should use a slider. +- Updated samples to standard Samples/PackageName/Version/SampleName structure (case 1276329). +- Fixed BoneRenderer and RigEffectors not respecting layer visibility flags (case 1238204). +- Added support for nested struct properties tagged with the SyncSceneToStream attribute. +- Fixed MultiParent and MultiPosition constraints evaluating with an offset when root game object scale is not one (case 1246893). +- Fixed unnormalized rotation in TwoBoneIK constraint when hint weight is lower than one (case 1311769). +- Removed editor only conditional compilation on serialized data (case 1324071). +- Updated Burst to version 1.4.1. + +## [1.0.3] - 2020-08-21 +### Patch Update of *Animation Rigging*. +- Updated Burst to version 1.3.4. + +## [1.0.2] - 2020-07-02 +### Patch Update of *Animation Rigging*. +- Updated minimum unity release to 2020.2.0a16. + +## [1.0.1] - 2020-06-26 +### Verified release of *Animation Rigging*. +- Added world up vector options for MultiAim. +- Removed use of `latest` keyword in links to documentation (case 1233972). +- Removed error log in CustomOverlayAttribute (case 1253862). +- Disabled alt-picking on BoneRenderer in Scene View. [AAA-102] +- Fixed unecessary GC Alloc in RigBuilder.Update(). [AAA-103] +- Fixed TwoBoneIK inverse constraint not taking dynamic bone lengths into account. +- Fixed ChainIK not applying chain rotation weight properly. + +## [0.3.3] - 2020-03-06 +### Patch Update of *Animation Rigging*. +- Improved RigEffector picking when using custom shapes. +- Improved RigEffector hover feedback when shape does not have line geometry. +- Adjusted MultiAim calculations to prevent rolling effects (case 1215736). +- Removed cached 'LimbLengths' in TwoBoneIKConstraintJob which lets users modify bone lengths dynamically. [AAA-97] +- Fixed erroneous link to TwistChain constraint documentation in the ConstraintSamples readme. [AAA-95] +- Fixed add and remove operations in ReorderableList when constraint is nested in prefab. [AAA-98] +- Fixed undo operations in ReorderableList. [AAA-98] +- Removed uses of Resources.Load in Runtime and Editor assemblies. + +## [0.3.2] - 2020-01-23 +### Patch Update of *Animation Rigging*. +- Updated documentation. + +## [0.3.1] - 2020-01-14 +### Patch Update of *Animation Rigging*. +- Removed unecessary files BakeUtils.cs.orig and BakeUtils.cs.orig.meta. +- Updated Burst to version 1.2.0-preview.12. + +## [0.3.0] - 2019-10-18 +### Patch Update of *Animation Rigging*. +- Added support for bidirectional-baking to existing constraints. + +## [0.2.5] - 2019-11-20 +### Patch Update of *Animation Rigging*. +- Adjusted calculations on MultiAim constrained axes. [AAA-86] + +## [0.2.4] - 2019-11-06 +### Patch Update of *Animation Rigging*. +- Added support for scene visibility and picking flags on BoneRenderer component and Rig effectors. [AAA-65] +- Fixed preview of disabled RigBuilder component in Animation Window and Timeline. [AAA-37] +- Fixed constrained axes in MultiAim not properly constraining local rotation. [AAA-86] +- Updated Animation Rigging samples and added a Readme file. + +## [0.2.3] - 2019-07-24 +### Patch Update of *Animation Rigging*. +- Increased the priority index of AnimationPlayableOutput in order for Animation Rigging to always evaluate after State Machine and Timeline. +- Fixed NullReferenceException in RigEffectorRenderer. +- Fixed TwoBoneIK evaluation when used on straight limbs by using hint target to define a valid IK plane. +- Fixed Multi-Parent, Multi-Rotation and Multi-Aim constraints to perform order independent rotation blending. [AAA-17] +- Fixed RigTransform component to work on all objects of an animator hierarchy not only specific to sub rig hierarchies. [AAA-18] +- Fixed crash in RigSyncSceneToStreamJob when rebuilding jobs after having deleted all valid rigs (case 1167624). +- Fixed undo/redo issues with Rig Effectors set on Prefab instances (case 1162002). +- Fixed missing links to package documentation for MonoBehaviour scripts. [AAA-16] +- Added Vector3IntProperty and Vector3BoolProperty helper structs. +- Updated Burst to version 1.1.1. + +## [0.2.2] - 2019-04-29 +### Patch Update of *Animation Rigging*. +- Added Rig Effector visualization toolkit for Animation Rigging. +- Fixed Animation Rigging align operations not using the same selection order in Scene View and Hierarchy. +- Updated Burst to version 1.0.4. + +## [0.2.1] - 2019-02-28 +### Patch Update of *Animation Rigging*. +- Added Burst support to existing constraints. The Animation Rigging package now depends on com.unity.burst. +- Upgraded weighted transform arrays in order for weights to be animatable. The following constraints were modified and will require a manual update: + - MultiAimConstraint + - MultiParentConstraint + - MultiPositionConstraint + - MultiReferentialConstraint + - TwistCorrection + +## [0.2.0] - 2019-02-12 + +### Keyframing support for *Animation Rigging*. +- Changed RigBuilder to build and update the PlayableGraph for Animation Window. +- Added attribute [NotKeyable] to properties that shouldn't be animated. +- Removed 'sync' property flag on transform fields for constraints. Syncing scene data to the animation stream is now performed by marking a constraint field with [SyncSceneToStream]. +- Fixed issue where constraint parameters were evaluated one frame late when animated. +- Added attribute [DisallowMultipleComponent] to constraints to avoid use of multiple constraints per Game Object. +- Updated constraints to use new AnimationStream API to reduce engine to script conversion. +- Added IAnimatableProperty helpers for Bool/Int/Float/Vector2/Vector3/Vector4 properties. +- Added ReadOnlyTransformHandle and ReadWriteTransformHandle. + +## [0.1.4] - 2018-12-21 + +### Patch Update of *Animation Rigging*. +- Fixed onSceneGUIDelegate deprecation warning in BoneRenderUtils + +## [0.1.3] - 2018-12-21 + +### Patch Update of *Animation Rigging*. +- Fixed stale bone rendering in prefab isolation view. +- Updated constraints to have a transform sync scene to stream toggle only on inputs. +- Fixed Twist Correction component to have twist nodes with weight varying between [-1, 1] +- Added Maintain Offset dropdown to TwoBoneIK, ChainIK, Blend and Multi-Parent constraints +- Added Rig Transform component in order to tag extra objects not specified by constraints to have an influence in the animation stream +- Updated documentation and samples to reflect component changes + +## [0.1.2] - 2018-11-29 + +### Patch Update of *Animation Rigging*. +- Added constraint examples to Sample folder (Samples/ConstraintExamples/AnimationRiggingExamples.unitypackage) +- Fixed links in documentation +- Updated package description + +## [0.1.1] - 2018-11-26 + +### Patch Update of *Animation Rigging*. +- Improved blend constraint UI layout +- Fixed jittering of DampedTransform when constraint weight was in between 0 and 1 +- Made generic interface of Get/Set AnimationJobCache functions +- Added separate size parameters for bones and tripods in BoneRenderer. +- Fixed NullReferenceException when deleting skeleton hierarchy while it's still being drawn by BoneRenderer. +- Fixed Reset and Undo operations on BoneRenderer not updating skeleton rendering. +- Reworked multi rig playable graph setup to have one initial scene to stream sync layer followed by subsequent rigs +- Prune duplicate rig references prior to creating sync to stream job +- Added passthrough conditions in animation jobs for proper stream values to be passed downstream when job weights are zero. Fixes a few major issues when character did not have a controller. +- Fixed bug in ChainIK causing chain to not align to full extent when target is out of reach +- Fixed TwoBoneIK bend normal strategy when limbs are collinear +- Reworked AnimationJobData classes to be declared as structs in order for their serialized members to be keyable within the Animation Window. +- Renamed component section and menu item "Runtime Rigging" to "Animation Rigging" +- Added check in SyncToStreamJob to make sure StreamHandle is still valid prior to reading it's values. +- Adding first draft of package documentation. + +## [0.1.0] - 2018-11-01 + +### This is the first release of *Animation Rigging*. +### Added +- RigBuilder component. +- Rig component. +- The following RuntimeRigConstraint components: + - BlendConstraint + - ChainIKConstraint + - MultiAimConstraint + - MultiParentConstraint + - MultiPositionConstraint + - MultiReferentialConstraint + - OverrideTransform + - TwistCorrection diff --git a/Packages/com.unity.animation.rigging/CHANGELOG.md.meta b/Packages/com.unity.animation.rigging/CHANGELOG.md.meta new file mode 100644 index 00000000..b8d0e8f9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 39c18d859abc65e41b8e5bf2267e013d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples.meta b/Packages/com.unity.animation.rigging/DocCodeExamples.meta new file mode 100644 index 00000000..95fc13fd --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: be78dea739b9e4fa68fe04d39c94b597 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs new file mode 100644 index 00000000..9c495e6f --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs @@ -0,0 +1,40 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using UnityEngine.Playables; + +namespace DocCodeExamples +{ + /// + /// Custom evaluator that manually evaluates the PlayableGraph in LateUpdate. + /// + #region custom-playable-graph-evaluator + [RequireComponent(typeof(RigBuilder))] + public class CustomPlayableGraphEvaluator : MonoBehaviour + { + private RigBuilder m_RigBuilder; + private PlayableGraph m_PlayableGraph; + + void OnEnable() + { + m_RigBuilder = GetComponent(); + m_PlayableGraph = PlayableGraph.Create(); + m_PlayableGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual); + + m_RigBuilder.Build(m_PlayableGraph); + } + + void OnDisable() + { + if (m_PlayableGraph.IsValid()) + m_PlayableGraph.Destroy(); + } + + void LateUpdate() + { + m_RigBuilder.UpdateLayers(); + m_PlayableGraph.Evaluate(Time.deltaTime); + m_RigBuilder.LateUpdateLayers(); + } + } + #endregion +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs.meta b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs.meta rename to Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs.meta index 3c9cb09e..6b52d127 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs.meta +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomPlayableGraphEvaluator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0fd9ee276a1023e439cf7a9c393195fa +guid: a2411ac2c7bbd4f58aaae3e6cefda2aa MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs new file mode 100644 index 00000000..5ba62153 --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs @@ -0,0 +1,35 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using UnityEngine.Playables; + +namespace DocCodeExamples +{ + /// + /// Custom Evaluator that manually evaluates the RigBuilder in LateUpdate. + /// + #region custom-rig-builder-evaluator + [RequireComponent(typeof(RigBuilder))] + public class CustomRigBuilderEvaluator : MonoBehaviour + { + private RigBuilder m_RigBuilder; + + void OnEnable() + { + m_RigBuilder = GetComponent(); + + // Disable the RigBuilder and set its PlayableGraph to manual update mode + // to let the script evaluate it instead. + m_RigBuilder.enabled = false; + if (m_RigBuilder.Build()) + { + m_RigBuilder.graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); + } + } + + void LateUpdate() + { + m_RigBuilder.Evaluate(Time.deltaTime); + } + } + #endregion +} diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs.meta b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs.meta new file mode 100644 index 00000000..effbe104 --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/CustomRigBuilderEvaluator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dc9f69976d6143e489b841bec6ac5cfd +timeCreated: 1636580728 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef b/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef new file mode 100644 index 00000000..26c72887 --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef @@ -0,0 +1,16 @@ +{ + "name": "Unity.Animation.Rigging.DocCodeExamples", + "rootNamespace": "", + "references": [ + "Unity.Animation.Rigging" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef.meta b/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef.meta new file mode 100644 index 00000000..51aa406c --- /dev/null +++ b/Packages/com.unity.animation.rigging/DocCodeExamples/Unity.Animation.Rigging.DocCodeExamples.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c2a5362f5f25b455f8f4eeea25441772 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor.meta b/Packages/com.unity.animation.rigging/Editor.meta new file mode 100644 index 00000000..e3b2dc19 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25a1bcc63261564418263e1b1fa0a713 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig.meta new file mode 100644 index 00000000..bd149142 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eade225a3b4a03d49af6485534a9dc23 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints.meta new file mode 100644 index 00000000..1d22186c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 754562bd98582a44e875ea6d688127ac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs new file mode 100644 index 00000000..cfef227f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(BlendConstraint))] + [CanEditMultipleObjects] + class BlendConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent sourceObjects = new GUIContent(CommonContent.sourceObjects.text); + public static readonly GUIContent sourceA = EditorGUIUtility.TrTextContent( + "Source A", + "The first source GameObject that influences the position and rotation of the Constrained Object." + ); + public static readonly GUIContent sourceB = EditorGUIUtility.TrTextContent( + "Source B", + "The second source GameObject that influences the position and rotation of the Constrained Object." + ); + public static readonly GUIContent settings = CommonContent.settings; + public static readonly GUIContent maintainOffset = CommonContent.maintainOffset; + public static readonly GUIContent blendPosition = EditorGUIUtility.TrTextContent( + "Blend A | B Position", + "If enabled, the constrained GameObject's position blends between those of Source A and Source B by the specified amount." + ); + public static readonly GUIContent blendRotation = EditorGUIUtility.TrTextContent( + "Blend A | B Rotation", + "If enabled, the constrained GameObject's rotation blends between those of Source A and Source B by the specified amount." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_SourceA; + SerializedProperty m_SourceB; + SerializedProperty m_BlendPosition; + SerializedProperty m_BlendRotation; + SerializedProperty m_PositionWeight; + SerializedProperty m_RotationWeight; + SerializedProperty m_MaintainPositionOffsets; + SerializedProperty m_MaintainRotationOffsets; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_SourceA = data.FindPropertyRelative("m_SourceA"); + m_SourceB = data.FindPropertyRelative("m_SourceB"); + m_BlendPosition = data.FindPropertyRelative("m_BlendPosition"); + m_BlendRotation = data.FindPropertyRelative("m_BlendRotation"); + m_PositionWeight = data.FindPropertyRelative("m_PositionWeight"); + m_RotationWeight = data.FindPropertyRelative("m_RotationWeight"); + m_MaintainPositionOffsets = data.FindPropertyRelative("m_MaintainPositionOffsets"); + m_MaintainRotationOffsets = data.FindPropertyRelative("m_MaintainRotationOffsets"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.sourceObjects); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_SourceA, Content.sourceA); + EditorGUILayout.PropertyField(m_SourceB, Content.sourceB); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, Content.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + + MaintainOffsetHelper.DoDropdown(Content.maintainOffset, m_MaintainPositionOffsets, m_MaintainRotationOffsets); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_BlendPosition, Content.blendPosition); + using (new EditorGUI.DisabledScope(!m_BlendPosition.boolValue)) + EditorGUILayout.PropertyField(m_PositionWeight, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_BlendRotation, Content.blendRotation); + using (new EditorGUI.DisabledScope(!m_BlendRotation.boolValue)) + EditorGUILayout.PropertyField(m_RotationWeight, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/BlendConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as BlendConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/BlendConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as BlendConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(BlendConstraint))] + class BlendConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => false; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, BlendConstraint constraint) + { + var bindings = new List(); + var sourceA = constraint.data.sourceObjectA; + var sourceB = constraint.data.sourceObjectB; + + if (constraint.data.blendPosition) + { + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, sourceA, bindings); + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, sourceB, bindings); + } + + if (constraint.data.blendRotation) + { + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, sourceA, bindings); + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, sourceB, bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, BlendConstraint constraint) + { + var bindings = new List(); + var constrained = constraint.data.constrainedObject; + + if (constraint.data.blendPosition) + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, constrained, bindings); + + if(constraint.data.blendRotation) + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, constrained, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs.meta rename to Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs.meta index 364037c2..3a37288b 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs.meta +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/BlendConstraintEditor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ff5ab0e3d6cd4a5bab92dab9c63e8962 +guid: a8670d948642f1546a4835c189aea303 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs new file mode 100644 index 00000000..8ccde887 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(ChainIKConstraint))] + [CanEditMultipleObjects] + class ChainIKConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent root = EditorGUIUtility.TrTextContent( + "Root", + "The root GameObject of the chain hierarchy." + ); + public static readonly GUIContent tip = EditorGUIUtility.TrTextContent( + "Tip", + "The final GameObject of the chain hierarchy. It must be a descendant of the Root GameObject." + ); + public static readonly GUIContent target = EditorGUIUtility.TrTextContent( + "Target", + "The GameObject that specifies the desired target transform for the chain's Tip." + ); + public static readonly GUIContent sourceObjects = new GUIContent(CommonContent.sourceObjects.text); + public static readonly GUIContent chainRotationWeight = EditorGUIUtility.TrTextContent( + "Chain Rotation Weight", + "The weight of rotations applied throughout the chain." + ); + public static readonly GUIContent tipRotationWeight = EditorGUIUtility.TrTextContent( + "Tip Rotation Weight", + "The weight of the rotation applied to the Tip." + ); + public static readonly GUIContent maxIterations = EditorGUIUtility.TrTextContent( + "Max Iterations", + "The maximum number of solver iterations to perform to try to make the Tip reach the Target within the specified Tolerance threshold." + ); + public static readonly GUIContent tolerance = EditorGUIUtility.TrTextContent( + "Tolerance", + "Distance tolerance between the Target and Tip GameObjects. " + + "The solver will finish its computation if the distance is less than this value at any point, even if Max Iterations has not been reached." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_Root; + SerializedProperty m_Tip; + SerializedProperty m_Target; + SerializedProperty m_ChainRotationWeight; + SerializedProperty m_TipRotationWeight; + SerializedProperty m_MaxIterations; + SerializedProperty m_Tolerance; + SerializedProperty m_MaintainTargetPositionOffset; + SerializedProperty m_MaintainTargetRotationOffset; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_Root = data.FindPropertyRelative("m_Root"); + m_Tip = data.FindPropertyRelative("m_Tip"); + m_Target = data.FindPropertyRelative("m_Target"); + m_ChainRotationWeight = data.FindPropertyRelative("m_ChainRotationWeight"); + m_TipRotationWeight = data.FindPropertyRelative("m_TipRotationWeight"); + m_MaxIterations = data.FindPropertyRelative("m_MaxIterations"); + m_Tolerance = data.FindPropertyRelative("m_Tolerance"); + m_MaintainTargetPositionOffset = data.FindPropertyRelative("m_MaintainTargetPositionOffset"); + m_MaintainTargetRotationOffset = data.FindPropertyRelative("m_MaintainTargetRotationOffset"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_Root, Content.root); + EditorGUILayout.PropertyField(m_Tip, Content.tip); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.sourceObjects); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_Target, Content.target); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + MaintainOffsetHelper.DoDropdown(CommonContent.maintainIKTargetOffset, m_MaintainTargetPositionOffset, m_MaintainTargetRotationOffset); + EditorGUILayout.PropertyField(m_ChainRotationWeight, Content.chainRotationWeight); + EditorGUILayout.PropertyField(m_TipRotationWeight, Content.tipRotationWeight); + EditorGUILayout.PropertyField(m_MaxIterations, Content.maxIterations); + EditorGUILayout.PropertyField(m_Tolerance, Content.tolerance); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/ChainIKConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as ChainIKConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/ChainIKConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as ChainIKConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(ChainIKConstraint))] + class ChainIKConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => false; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, ChainIKConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.target, bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, ChainIKConstraint constraint) + { + var bindings = new List(); + + var root = constraint.data.root; + var tip = constraint.data.tip; + + var tmp = tip; + while (tmp != root) + { + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, tmp, bindings); + tmp = tmp.parent; + } + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, root, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs.meta rename to Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs.meta index 4361a499..21f656b3 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs.meta +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/ChainIKConstraintEditor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 81dd1b9facda41f9a09fb997d6e64670 +guid: 63110210264e2ee4da7389733e5958ae MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs new file mode 100644 index 00000000..63d2218b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(DampedTransform))] + [CanEditMultipleObjects] + class DampedTransformEditor : Editor + { + static class Content + { + public static readonly GUIContent source = EditorGUIUtility.TrTextContent( + "Source", + "The GameObject that influences the Constrained Object's transform." + ); + public static readonly GUIContent dampPosition = EditorGUIUtility.TrTextContent( + "Damp Position", + "The weight of positional damping to apply to the Constrained Object." + ); + public static readonly GUIContent dampRotation = EditorGUIUtility.TrTextContent( + "Damp Rotation", + "The weight of rotational damping to apply to the Constrained Object." + ); + public static readonly GUIContent maintainAim = EditorGUIUtility.TrTextContent( + "Maintain Aim", + "Specifies whether to maintain the initial rotation offset between the Constrained Object and the Source Object." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_Source; + SerializedProperty m_DampPosition; + SerializedProperty m_DampRotation; + SerializedProperty m_MaintainAim; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_Source = data.FindPropertyRelative("m_Source"); + m_DampPosition = data.FindPropertyRelative("m_DampPosition"); + m_DampRotation = data.FindPropertyRelative("m_DampRotation"); + m_MaintainAim = data.FindPropertyRelative("m_MaintainAim"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.source); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_Source, Content.source); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_DampPosition, Content.dampPosition); + EditorGUILayout.PropertyField(m_DampRotation, Content.dampRotation); + EditorGUILayout.PropertyField(m_MaintainAim, Content.maintainAim); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/DampedTransform/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as DampedTransform; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/DampedTransform/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as DampedTransform; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(DampedTransform))] + class DampedTransformBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => false; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, DampedTransform constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.sourceObject, bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, DampedTransform constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs.meta rename to Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs.meta index 30e83ce7..46555b33 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs.meta +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/DampedTransformEditor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 172ced047a38440389ff238ad1d347ce +guid: 99a3cbe2e69614541bdf264c5e905f48 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs new file mode 100644 index 00000000..693217ae --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(MultiAimConstraint))] + [CanEditMultipleObjects] + class MultiAimConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent[] axisLabels = new []{ "X", "-X", "Y", "-Y", "Z", "-Z" } + .Select(c => new GUIContent(c)) + .ToArray(); + public static readonly GUIContent aimAxis = EditorGUIUtility.TrTextContent( + "Aim Axis", + "Specifies the local aim axis of the Constrained Object to use in order to orient its forward direction to the Source Objects." + ); + public static readonly GUIContent upAxis = EditorGUIUtility.TrTextContent( + "Up Axis", + "Specifies the local up axis of the Constrained Object to use in order to orient its upward direction (i.e., roll orientation)." + ); + public static readonly GUIContent worldUpType = EditorGUIUtility.TrTextContent( + "World Up Type", + "Specifies which mode to use to stabilize the upward direction (i.e., roll orientation) of the Constrained Object." + ); + public static readonly GUIContent worldUpAxis = EditorGUIUtility.TrTextContent( + "World Up Axis", + "A vector in some reference frame that is used to stabilize the upward direction of the Constrained Object. " + + "This value is used when World Up Type is either Vector or Object Rotation Up." + ); + public static readonly GUIContent worldUpObject = EditorGUIUtility.TrTextContent( + "World Up Object", + "A GameObject used as a reference frame for World Up Axis. " + + "This value is used when World Up Type is either Object Up or Object Rotation Up." + ); + public static readonly GUIContent sourceObjects = CommonContent.sourceObjectsWeightedRotation; + public static readonly GUIContent settings = CommonContent.settings; + public static readonly GUIContent maintainOffset = CommonContent.maintainRotationOffset; + public static readonly GUIContent minLimit = EditorGUIUtility.TrTextContent( + "Min Limit", + "Clamps the minimum rotation that may be applied about any of the constrained axes of rotation." + ); + public static readonly GUIContent maxLimit = EditorGUIUtility.TrTextContent( + "Max Limit", + "Clamps the maximum rotation that may be applied about any of the constrained axes of rotation." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_AimAxis; + SerializedProperty m_UpAxis; + SerializedProperty m_WorldUpType; + SerializedProperty m_WorldUpAxis; + SerializedProperty m_WorldUpObject; + SerializedProperty m_SourceObjects; + SerializedProperty m_MaintainOffset; + SerializedProperty m_Offset; + SerializedProperty m_ConstrainedAxes; + SerializedProperty m_MinLimit; + SerializedProperty m_MaxLimit; + + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_AimAxis = data.FindPropertyRelative("m_AimAxis"); + m_UpAxis = data.FindPropertyRelative("m_UpAxis"); + m_WorldUpType = data.FindPropertyRelative("m_WorldUpType"); + m_WorldUpAxis = data.FindPropertyRelative("m_WorldUpAxis"); + m_WorldUpObject = data.FindPropertyRelative("m_WorldUpObject"); + m_SourceObjects = data.FindPropertyRelative("m_SourceObjects"); + m_MaintainOffset = data.FindPropertyRelative("m_MaintainOffset"); + m_Offset = data.FindPropertyRelative("m_Offset"); + m_ConstrainedAxes = data.FindPropertyRelative("m_ConstrainedAxes"); + m_MinLimit = data.FindPropertyRelative("m_MinLimit"); + m_MaxLimit = data.FindPropertyRelative("m_MaxLimit"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + + ++EditorGUI.indentLevel; + DoAxisField(m_AimAxis, Content.aimAxis); + DoAxisField(m_UpAxis, Content.upAxis); + --EditorGUI.indentLevel; + + EditorGUILayout.PropertyField(m_WorldUpType, Content.worldUpType); + + var worldUpType = (MultiAimConstraintData.WorldUpType)m_WorldUpType.intValue; + + ++EditorGUI.indentLevel; + using (new EditorGUI.DisabledGroupScope(worldUpType != MultiAimConstraintData.WorldUpType.ObjectRotationUp && worldUpType != MultiAimConstraintData.WorldUpType.Vector)) + { + DoAxisField(m_WorldUpAxis, Content.worldUpAxis); + } + using (new EditorGUI.DisabledGroupScope(worldUpType != MultiAimConstraintData.WorldUpType.ObjectUp && worldUpType != MultiAimConstraintData.WorldUpType.ObjectRotationUp)) + { + EditorGUILayout.PropertyField(m_WorldUpObject, Content.worldUpObject); + } + --EditorGUI.indentLevel; + + EditorGUILayout.PropertyField(m_SourceObjects, Content.sourceObjects); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, Content.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_MaintainOffset, Content.maintainOffset); + EditorGUILayout.PropertyField(m_Offset, CommonContent.offsetRotation); + EditorGUILayout.PropertyField(m_ConstrainedAxes, CommonContent.constrainedAxesRotation); + EditorGUILayout.PropertyField(m_MinLimit, Content.minLimit); + EditorGUILayout.PropertyField(m_MaxLimit, Content.maxLimit); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + static void DoAxisField(SerializedProperty property, GUIContent label) + { + var rect = EditorGUILayout.GetControlRect(); + EditorGUI.BeginProperty(rect, label, property); + EditorGUI.BeginChangeCheck(); + var newValue = EditorGUI.Popup(rect, label, property.intValue, Content.axisLabels); + if (EditorGUI.EndChangeCheck()) + property.intValue = newValue; + EditorGUI.EndProperty(); + } + + [MenuItem("CONTEXT/MultiAimConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as MultiAimConstraint; + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/MultiAimConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as MultiAimConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/MultiAimConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/MultiAimConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as MultiAimConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(MultiAimConstraint))] + class MultiAimConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, MultiAimConstraint constraint) + { + var bindings = new List(); + + for (int i = 0; i < constraint.data.sourceObjects.Count; ++i) + { + var sourceObject = constraint.data.sourceObjects[i]; + + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, sourceObject.transform, bindings); + EditorCurveBindingUtils.CollectPropertyBindings(rigBuilder.transform, constraint, ((IMultiAimConstraintData)constraint.data).sourceObjectsProperty + ".m_Item" + i + ".weight", bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, MultiAimConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs.meta new file mode 100644 index 00000000..cb5cc525 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiAimConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a96277d4d9d2d04181fc5ce44119fdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs new file mode 100644 index 00000000..e44a15da --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs @@ -0,0 +1,121 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using System.Collections.Generic; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(MultiParentConstraint))] + [CanEditMultipleObjects] + class MultiParentConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent constrainedPositionAxes = new GUIContent( + L10n.Tr("Constrained Position Axes"), + CommonContent.constrainedAxesPosition.tooltip + ); + public static readonly GUIContent constrainedRotationAxes = new GUIContent( + L10n.Tr("Constrained Rotation Axes"), + CommonContent.constrainedAxesRotation.tooltip + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_ConstrainedPositionAxes; + SerializedProperty m_ConstrainedRotationAxes; + SerializedProperty m_SourceObjects; + SerializedProperty m_MaintainPositionOffset; + SerializedProperty m_MaintainRotationOffset; + + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_ConstrainedPositionAxes = data.FindPropertyRelative("m_ConstrainedPositionAxes"); + m_ConstrainedRotationAxes = data.FindPropertyRelative("m_ConstrainedRotationAxes"); + m_SourceObjects = data.FindPropertyRelative("m_SourceObjects"); + m_MaintainPositionOffset = data.FindPropertyRelative("m_MaintainPositionOffset"); + m_MaintainRotationOffset = data.FindPropertyRelative("m_MaintainRotationOffset"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + EditorGUILayout.PropertyField(m_SourceObjects, CommonContent.sourceObjects); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + MaintainOffsetHelper.DoDropdown(CommonContent.maintainOffset, m_MaintainPositionOffset, m_MaintainRotationOffset); + EditorGUILayout.PropertyField(m_ConstrainedPositionAxes, Content.constrainedPositionAxes); + EditorGUILayout.PropertyField(m_ConstrainedRotationAxes, Content.constrainedRotationAxes); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/MultiParentConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as MultiParentConstraint; + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/MultiParentConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as MultiParentConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/MultiParentConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/MultiParentConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as MultiParentConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(MultiParentConstraint))] + class MultiParentConstraintParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, MultiParentConstraint constraint) + { + var bindings = new List(); + + for (int i = 0; i < constraint.data.sourceObjects.Count; ++i) + { + var sourceObject = constraint.data.sourceObjects[i]; + + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, sourceObject.transform, bindings); + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, sourceObject.transform, bindings); + EditorCurveBindingUtils.CollectPropertyBindings(rigBuilder.transform, constraint, ((IMultiParentConstraintData)constraint.data).sourceObjectsProperty + ".m_Item" + i + ".weight", bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, MultiParentConstraint constraint) + { + var bindings = new List(); + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs.meta new file mode 100644 index 00000000..be085b9f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiParentConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2326ba71c8dbcba4ea941bf20e7e1bbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs new file mode 100644 index 00000000..716b0bdb --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using System.Collections.Generic; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(MultiPositionConstraint))] + [CanEditMultipleObjects] + class MultiPositionConstraintEditor : Editor + { + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_ConstrainedAxes; + SerializedProperty m_SourceObjects; + SerializedProperty m_MaintainOffset; + SerializedProperty m_Offset; + + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_ConstrainedAxes = data.FindPropertyRelative("m_ConstrainedAxes"); + m_SourceObjects = data.FindPropertyRelative("m_SourceObjects"); + m_MaintainOffset = data.FindPropertyRelative("m_MaintainOffset"); + m_Offset = data.FindPropertyRelative("m_Offset"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + EditorGUILayout.PropertyField(m_ConstrainedAxes, CommonContent.constrainedAxesPosition); + EditorGUILayout.PropertyField(m_SourceObjects, CommonContent.sourceObjectsWeightedPosition); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_MaintainOffset, CommonContent.maintainPositionOffset); + EditorGUILayout.PropertyField(m_Offset, CommonContent.offsetPosition); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/MultiPositionConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as MultiPositionConstraint; + + var axesMask = new Vector3( + System.Convert.ToSingle(constraint.data.constrainedXAxis), + System.Convert.ToSingle(constraint.data.constrainedYAxis), + System.Convert.ToSingle(constraint.data.constrainedZAxis)); + + if (Vector3.Dot(axesMask, axesMask) < 3f) + { + Debug.LogWarning("Multi-Position constraint with one or more Constrained Axes toggled off may lose precision when transferring its motion to constraint."); + } + + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/MultiPositionConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as MultiPositionConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/MultiPositionConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/MultiPositionConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as MultiPositionConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(MultiPositionConstraint))] + class MultiPositionConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, MultiPositionConstraint constraint) + { + var bindings = new List(); + + for (int i = 0; i < constraint.data.sourceObjects.Count; ++i) + { + var sourceObject = constraint.data.sourceObjects[i]; + + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, sourceObject.transform, bindings); + EditorCurveBindingUtils.CollectPropertyBindings(rigBuilder.transform, constraint, ((IMultiPositionConstraintData)constraint.data).sourceObjectsProperty + ".m_Item" + i + ".weight", bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, MultiPositionConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs.meta new file mode 100644 index 00000000..a13c4826 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiPositionConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ca0517db96fdfc41b4230a34d84b37c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs new file mode 100644 index 00000000..b2349677 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(MultiReferentialConstraint))] + [CanEditMultipleObjects] + class MultiReferentialConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent driving = EditorGUIUtility.TrTextContent( + "Driving", + "An object from the list of Referenced Objects, whose motion drives that of all other Referenced Objects." + ); + public static readonly GUIContent referenceObjects = EditorGUIUtility.TrTextContent( + "Reference Objects", + "A list of GameObjects to be driven by the specified Driving object." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_Driver; + SerializedProperty m_SourceObjects; + SerializedProperty m_SourceObjectsSize; + + GUIContent[] m_DrivingLabels = Array.Empty(); + int m_PreviousSourceSize; + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_Driver = data.FindPropertyRelative("m_Driver"); + m_SourceObjects = data.FindPropertyRelative("m_SourceObjects"); + m_SourceObjectsSize = m_SourceObjects.FindPropertyRelative("Array.size"); + m_PreviousSourceSize = m_SourceObjectsSize.intValue; + + UpdateDrivingLabels(); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + + Rect rect = EditorGUILayout.GetControlRect(); + EditorGUI.BeginProperty(rect, Content.driving, m_Driver); + EditorGUI.BeginChangeCheck(); + var newValue = EditorGUI.Popup(rect, Content.driving, m_Driver.intValue, m_DrivingLabels); + if (EditorGUI.EndChangeCheck()) + m_Driver.intValue = newValue; + EditorGUI.EndProperty(); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_SourceObjects, Content.referenceObjects); + // also check if size has changed, because drag/drop on default control and Reset do not trigger change + if (EditorGUI.EndChangeCheck() || m_PreviousSourceSize != m_SourceObjectsSize.intValue) + { + UpdateDrivingLabels(); + m_PreviousSourceSize = m_SourceObjectsSize.intValue; + } + + serializedObject.ApplyModifiedProperties(); + } + + void UpdateDrivingLabels() + { + Array.Resize(ref m_DrivingLabels, m_SourceObjects.arraySize); + for (int i = 0; i < m_DrivingLabels.Length; ++i) + { + var element = m_SourceObjects.GetArrayElementAtIndex(i); + var name = element.objectReferenceValue == null ? "None" : element.objectReferenceValue.name; + m_DrivingLabels[i] = new GUIContent($"{i} : {name}"); + } + } + + [MenuItem("CONTEXT/MultiReferentialConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as MultiReferentialConstraint; + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/MultiReferentialConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as MultiReferentialConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/MultiReferentialConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/MultiReferentialConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as MultiReferentialConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(MultiReferentialConstraint))] + class MultiReferentialConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, MultiReferentialConstraint constraint) + { + var bindings = new List(); + + var sources = constraint.data.sourceObjects; + for (int i = 1; i < sources.Count; ++i) + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, sources[i], bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, MultiReferentialConstraint constraint) + { + var bindings = new List(); + + var transform = constraint.data.sourceObjects[0]; + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, transform, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs.meta new file mode 100644 index 00000000..f37db2ed --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiReferentialConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74cfdd6a1e6ff244496d38de0c2bbee5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs new file mode 100644 index 00000000..7e04ebf7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using System.Collections.Generic; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(MultiRotationConstraint))] + [CanEditMultipleObjects] + class MultiRotationConstraintEditor : Editor + { + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_ConstrainedAxes; + SerializedProperty m_SourceObjects; + SerializedProperty m_MaintainOffset; + SerializedProperty m_Offset; + + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_ConstrainedAxes = data.FindPropertyRelative("m_ConstrainedAxes"); + m_SourceObjects = data.FindPropertyRelative("m_SourceObjects"); + m_MaintainOffset = data.FindPropertyRelative("m_MaintainOffset"); + m_Offset = data.FindPropertyRelative("m_Offset"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + EditorGUILayout.PropertyField(m_ConstrainedAxes, CommonContent.constrainedAxesRotation); + EditorGUILayout.PropertyField(m_SourceObjects, CommonContent.sourceObjectsWeightedRotation); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_MaintainOffset, CommonContent.maintainRotationOffset); + EditorGUILayout.PropertyField(m_Offset, CommonContent.offsetRotation); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/MultiRotationConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as MultiRotationConstraint; + + var axesMask = new Vector3( + System.Convert.ToSingle(constraint.data.constrainedXAxis), + System.Convert.ToSingle(constraint.data.constrainedYAxis), + System.Convert.ToSingle(constraint.data.constrainedZAxis)); + + if (Vector3.Dot(axesMask, axesMask) < 3f) + { + Debug.LogWarning("Multi-Rotation constraint with one or more Constrained Axes toggled off may lose precision when transferring its motion to constraint."); + } + + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/MultiRotationConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as MultiRotationConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/MultiRotationConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/MultiRotationConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as MultiRotationConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(MultiRotationConstraint))] + class MultiRotationConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, MultiRotationConstraint constraint) + { + var bindings = new List(); + + for (int i = 0; i < constraint.data.sourceObjects.Count; ++i) + { + var sourceObject = constraint.data.sourceObjects[i]; + + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, sourceObject.transform, bindings); + EditorCurveBindingUtils.CollectPropertyBindings(rigBuilder.transform, constraint, ((IMultiRotationConstraintData)constraint.data).sourceObjectsProperty + ".m_Item" + i + ".weight", bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, MultiRotationConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs.meta new file mode 100644 index 00000000..581d7e15 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/MultiRotationConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91a61b91969f07d448049f1b2c1bf3c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs new file mode 100644 index 00000000..f078e31b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs @@ -0,0 +1,152 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(OverrideTransform))] + [CanEditMultipleObjects] + class OverrideTransformEditor : Editor + { + static class Content + { + public static readonly GUIContent sourceObjects = new GUIContent(CommonContent.sourceObjects.text); + public static readonly GUIContent overrideSource = EditorGUIUtility.TrTextContent( + "Override Source", + "The GameObject that influences the Constrained Object's transform. " + + "If specified, Override Position and Override Rotation fields are ignored." + ); + public static readonly GUIContent overridePosition = EditorGUIUtility.TrTextContent( + "Override Position", + "A specific position value to apply to the Constrained Object. " + + "This value is ignored if an Override Source is specified." + ); + public static readonly GUIContent overrideRotation = EditorGUIUtility.TrTextContent( + "Override Rotation", + "A specific rotation value to apply to the Constrained Object. " + + "This value is ignored if an Override Source is specified." + ); + public static readonly GUIContent space = EditorGUIUtility.TrTextContent( + "Space", + "Specifies how the Override Source's local transform values (or manual Override Position and Rotation) should be applied to Constrained Object." + ); + public static readonly GUIContent positionWeight = EditorGUIUtility.TrTextContent( + "Position Weight", + "The weight of the position influence." + ); + public static readonly GUIContent rotationWeight = EditorGUIUtility.TrTextContent( + "Rotation Weight", + "The weight of the rotation influence." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_ConstrainedObject; + SerializedProperty m_OverrideSource; + SerializedProperty m_OverridePosition; + SerializedProperty m_OverrideRotation; + SerializedProperty m_Space; + SerializedProperty m_PositionWeight; + SerializedProperty m_RotationWeight; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_ConstrainedObject = data.FindPropertyRelative("m_ConstrainedObject"); + m_OverrideSource = data.FindPropertyRelative("m_OverrideSource"); + m_OverridePosition = data.FindPropertyRelative("m_OverridePosition"); + m_OverrideRotation = data.FindPropertyRelative("m_OverrideRotation"); + m_Space = data.FindPropertyRelative("m_Space"); + m_PositionWeight = data.FindPropertyRelative("m_PositionWeight"); + m_RotationWeight = data.FindPropertyRelative("m_RotationWeight"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_ConstrainedObject, CommonContent.constrainedObject); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.sourceObjects); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_OverrideSource, Content.overrideSource); + using (new EditorGUI.DisabledScope(m_OverrideSource.objectReferenceValue != null)) + { + EditorGUILayout.PropertyField(m_OverridePosition, Content.overridePosition); + EditorGUILayout.PropertyField(m_OverrideRotation, Content.overrideRotation); + } + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_Space, Content.space); + EditorGUILayout.PropertyField(m_PositionWeight, Content.positionWeight); + EditorGUILayout.PropertyField(m_RotationWeight, Content.rotationWeight); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/OverrideTransform/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as OverrideTransform; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/OverrideTransform/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as OverrideTransform; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(OverrideTransform))] + class OverrideTransformBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => false; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, OverrideTransform constraint) + { + var bindings = new List(); + + if (constraint.data.sourceObject != null) + { + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.sourceObject, bindings); + } + else + { + var data = (IOverrideTransformData)constraint.data; + EditorCurveBindingUtils.CollectVector3Bindings(rigBuilder.transform, constraint, data.positionVector3Property, bindings); + EditorCurveBindingUtils.CollectVector3Bindings(rigBuilder.transform, constraint, data.rotationVector3Property, bindings); + } + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, OverrideTransform constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRSBindings(rigBuilder.transform, constraint.data.constrainedObject, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs.meta new file mode 100644 index 00000000..58744aba --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/OverrideTransformEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16cd7f77b75e35946b191bd98757e5a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs new file mode 100644 index 00000000..5fe40f9d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(TwistChainConstraint))] + [CanEditMultipleObjects] + class TwistChainConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent root = EditorGUIUtility.TrTextContent( + "Root", + "The root GameObject of the chain hierarchy." + ); + public static readonly GUIContent tip = EditorGUIUtility.TrTextContent( + "Tip", + "The final GameObject of the chain hierarchy. It must be a descendant of the Root GameObject." + ); + public static readonly GUIContent sourceObjects = new GUIContent(CommonContent.sourceObjects.text); + public static readonly GUIContent rootTarget = EditorGUIUtility.TrTextContent( + "Root Target", + "The GameObject that specifies the desired target rotation for the chain's Root." + ); + public static readonly GUIContent tipTarget = EditorGUIUtility.TrTextContent( + "Tip Target", + "The GameObject that specifies the desired target rotation for the chain's Tip." + ); + public static readonly GUIContent curve = EditorGUIUtility.TrTextContent( + "Curve", + "A curve with a normalized domain and range, specifying how the twist rotation should be distributed down the length of the chain." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_Root; + SerializedProperty m_Tip; + SerializedProperty m_RootTarget; + SerializedProperty m_TipTarget; + SerializedProperty m_Curve; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_RootTarget = data.FindPropertyRelative("m_RootTarget"); + m_TipTarget = data.FindPropertyRelative("m_TipTarget"); + m_Root = data.FindPropertyRelative("m_Root"); + m_Tip = data.FindPropertyRelative("m_Tip"); + m_Curve = data.FindPropertyRelative("m_Curve"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_Root, Content.root); + EditorGUILayout.PropertyField(m_Tip, Content.tip); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.sourceObjects); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_RootTarget, Content.rootTarget); + EditorGUILayout.PropertyField(m_TipTarget, Content.tipTarget); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_Curve, Content.curve); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/TwistChainConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as TwistChainConstraint; + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/TwistChainConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as TwistChainConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/TwistChainConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/TwistChainConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as TwistChainConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(TwistChainConstraint))] + class TwistChainConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, TwistChainConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.rootTarget, bindings); + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.tipTarget, bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, TwistChainConstraint constraint) + { + var bindings = new List(); + + // Retrieve chain in-between root and tip transforms. + Transform[] chain = ConstraintsUtils.ExtractChain(constraint.data.root, constraint.data.tip); + + for (int i = 0; i < chain.Length; ++i) + { + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, chain[i], bindings); + } + + return bindings; + } + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs.meta new file mode 100644 index 00000000..ec4e4a4c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistChainConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6f51e9e105d754014895d13788b539ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs new file mode 100644 index 00000000..dedaed1c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs @@ -0,0 +1,114 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using System.Collections.Generic; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(TwistCorrection))] + [CanEditMultipleObjects] + class TwistCorrectionEditor : Editor + { + static class Content + { + public static readonly GUIContent source = EditorGUIUtility.TrTextContent( + "Source", + "The GameObject that influences the Twist Nodes to rotate around a specific Twist Axis." + ); + public static readonly GUIContent twistAxis = EditorGUIUtility.TrTextContent( + "Twist Axis", + "Specifies the axis on the Source object from which the rotation is extracted and then redistributed to the Twist Nodes." + ); + public static readonly GUIContent twistNodes = EditorGUIUtility.TrTextContent( + "Twist Nodes", + "The list of GameObjects that will be influenced by the Source GameObject, and the cumulative percentage of the Source's twist rotation they should inherit. " + + "They are generally expected to all be leaf nodes in the hierarchy (i.e., they have a common parent and no children), and to have their twist axes oriented the same as the Source object in their initial pose." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_Source; + SerializedProperty m_TwistAxis; + SerializedProperty m_TwistNodes; + SerializedProperty m_TwistNodesLength; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + var data = serializedObject.FindProperty("m_Data"); + m_Source = data.FindPropertyRelative("m_Source"); + m_TwistAxis = data.FindPropertyRelative("m_TwistAxis"); + m_TwistNodes = data.FindPropertyRelative("m_TwistNodes"); + m_TwistNodesLength = m_TwistNodes.FindPropertyRelative("m_Length"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + + // by default, the first WeightedTransform element a user adds has a weight of 1 + // for this constraint, the first twist node usually should not have a value of 1 + // TODO: make drag/drop auto-distribute weights + EditorGUI.BeginChangeCheck(); + var oldLength = m_TwistNodesLength.intValue; + EditorGUILayout.PropertyField(m_TwistNodes, Content.twistNodes); + if (EditorGUI.EndChangeCheck() && oldLength == 0 && m_TwistNodesLength.intValue != oldLength) + m_TwistNodes.FindPropertyRelative("m_Item0.weight").floatValue = 0f; + + EditorGUILayout.PropertyField(m_TwistAxis, Content.twistAxis); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.source); + { + ++EditorGUI.indentLevel; + EditorGUILayout.PropertyField(m_Source); + --EditorGUI.indentLevel; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/TwistCorrection/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as TwistCorrection; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/TwistCorrection/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as TwistCorrection; + return BakeUtils.TransferMotionValidate(constraint); + } + } + + [BakeParameters(typeof(TwistCorrection))] + class TwistCorrectionBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => false; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, TwistCorrection constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, constraint.data.sourceObject, bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, TwistCorrection constraint) + { + var bindings = new List(); + + foreach (var node in constraint.data.twistNodes) + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, node.transform, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs.meta new file mode 100644 index 00000000..1989cd56 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwistCorrectionEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e52e5d5c12dea4e4397e6781cbeac36d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs new file mode 100644 index 00000000..094dca79 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs @@ -0,0 +1,301 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(TwoBoneIKConstraint))] + [CanEditMultipleObjects] + class TwoBoneIKConstraintEditor : Editor + { + static class Content + { + public static readonly GUIContent root = EditorGUIUtility.TrTextContent( + "Root", + "The root GameObject of the limb hierarchy." + ); + public static readonly GUIContent mid = EditorGUIUtility.TrTextContent( + "Mid", + "The middle GameObject of the limb hierarchy. It must be a child of the Root GameObject." + ); + public static readonly GUIContent tip = EditorGUIUtility.TrTextContent( + "Tip", + "The final GameObject of the limb hierarchy. It must be a child of the Mid GameObject." + ); + public static readonly GUIContent sourceObjects = new GUIContent(CommonContent.sourceObjects.text); + public static readonly GUIContent target = EditorGUIUtility.TrTextContent( + "Target", + "Source GameObject that specifies the desired position of the Tip." + ); + public static readonly GUIContent hint = EditorGUIUtility.TrTextContent( + "Hint", + "Optional Source GameObject, whose position is used to specify the direction the limb should be oriented when it bends." + ); + public static readonly GUIContent targetPositionWeight = EditorGUIUtility.TrTextContent( + "Target Position Weight", + "The weight to apply for calculating the desired position when reaching for the Target." + ); + public static readonly GUIContent targetRotationWeight = EditorGUIUtility.TrTextContent( + "Target Rotation Weight", + "The weight of the rotation applied to the Tip." + ); + public static readonly GUIContent hintWeight = EditorGUIUtility.TrTextContent( + "Hint Weight", + "The amount of influence the Hint has on the configuration of the hierarchy." + ); + } + + SerializedProperty m_Weight; + SerializedProperty m_Root; + SerializedProperty m_Mid; + SerializedProperty m_Tip; + SerializedProperty m_Target; + SerializedProperty m_Hint; + SerializedProperty m_TargetPositionWeight; + SerializedProperty m_TargetRotationWeight; + SerializedProperty m_HintWeight; + SerializedProperty m_MaintainTargetPositionOffset; + SerializedProperty m_MaintainTargetRotationOffset; + + readonly FoldoutState m_SourceObjectsToggle = FoldoutState.ForSourceObjects(); + readonly FoldoutState m_SettingsToggle = FoldoutState.ForSettings(); + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + + var data = serializedObject.FindProperty("m_Data"); + m_Root = data.FindPropertyRelative("m_Root"); + m_Mid = data.FindPropertyRelative("m_Mid"); + m_Tip = data.FindPropertyRelative("m_Tip"); + m_Target = data.FindPropertyRelative("m_Target"); + m_Hint = data.FindPropertyRelative("m_Hint"); + m_TargetPositionWeight = data.FindPropertyRelative("m_TargetPositionWeight"); + m_TargetRotationWeight = data.FindPropertyRelative("m_TargetRotationWeight"); + m_HintWeight = data.FindPropertyRelative("m_HintWeight"); + m_MaintainTargetPositionOffset = data.FindPropertyRelative("m_MaintainTargetPositionOffset"); + m_MaintainTargetRotationOffset = data.FindPropertyRelative("m_MaintainTargetRotationOffset"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_Weight, CommonContent.weight); + EditorGUILayout.PropertyField(m_Root, Content.root); + EditorGUILayout.PropertyField(m_Mid, Content.mid); + EditorGUILayout.PropertyField(m_Tip, Content.tip); + + m_SourceObjectsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SourceObjectsToggle.value, Content.sourceObjects); + if (m_SourceObjectsToggle.value) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_Target, Content.target); + EditorGUILayout.PropertyField(m_Hint, Content.hint); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + m_SettingsToggle.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SettingsToggle.value, CommonContent.settings); + if (m_SettingsToggle.value) + { + EditorGUI.indentLevel++; + MaintainOffsetHelper.DoDropdown(CommonContent.maintainIKTargetOffset, m_MaintainTargetPositionOffset, m_MaintainTargetRotationOffset); + EditorGUILayout.PropertyField(m_TargetPositionWeight, Content.targetPositionWeight); + EditorGUILayout.PropertyField(m_TargetRotationWeight, Content.targetRotationWeight); + EditorGUILayout.PropertyField(m_HintWeight, Content.hintWeight); + EditorGUI.indentLevel--; + } + EditorGUILayout.EndFoldoutHeaderGroup(); + + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/TwoBoneIKConstraint/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var constraint = command.context as TwoBoneIKConstraint; + BakeUtils.TransferMotionToConstraint(constraint); + } + + [MenuItem("CONTEXT/TwoBoneIKConstraint/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var constraint = command.context as TwoBoneIKConstraint; + BakeUtils.TransferMotionToSkeleton(constraint); + } + + [MenuItem("CONTEXT/TwoBoneIKConstraint/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/TwoBoneIKConstraint/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var constraint = command.context as TwoBoneIKConstraint; + return BakeUtils.TransferMotionValidate(constraint); + } + + [MenuItem("CONTEXT/TwoBoneIKConstraint/Auto Setup from Tip Transform", false, 631)] + public static void TwoBoneIKAutoSetup(MenuCommand command) + { + var constraint = command.context as TwoBoneIKConstraint; + var tip = constraint.data.tip; + var animator = constraint.GetComponentInParent()?.transform; + var dirty = false; + + if (!tip) + { + var selection = Selection.transforms; + var constraintInSelection = false; + + // Take transform from selection that is part of the animator hierarchy & not the constraint transform. + if (animator) + { + for (int i = 0; i < selection.Length; i++) + { + if (selection[i].IsChildOf(animator)) + { + if (selection[i] != constraint.transform) + { + tip = selection[i]; + break; + } + else + { + constraintInSelection = true; + } + } + } + } + + // If the constraint itself was selected and we haven't found anything use that. + if (!tip && constraintInSelection) + tip = constraint.transform; + + // If there is still no tip return. + if (!tip) + { + Debug.LogWarning("Please provide a tip before running auto setup!"); + return; + } + else + { + Undo.RecordObject(constraint, "Setup tip bone from user selection"); + constraint.data.tip = tip; + dirty = true; + } + } + + if (!constraint.data.mid) + { + Undo.RecordObject(constraint, "Setup mid bone for TwoBoneIK"); + constraint.data.mid = tip.parent; + dirty = true; + } + + if (!constraint.data.root) + { + Undo.RecordObject(constraint, "Setup root bone for TwoBoneIK"); + constraint.data.root = tip.parent.parent; + dirty = true; + } + + if (!constraint.data.target) + { + var target = constraint.transform.Find(constraint.gameObject.name + "_target"); + if (target == null) + { + var t = new GameObject(); + Undo.RegisterCreatedObjectUndo(t, "Created target"); + t.name = constraint.gameObject.name + "_target"; + t.transform.localScale = .1f * t.transform.localScale; + Undo.SetTransformParent(t.transform, constraint.transform, "Set new parent"); + target = t.transform; + } + constraint.data.target = target; + dirty = true; + } + + if (!constraint.data.hint) + { + var hint = constraint.transform.Find(constraint.gameObject.name + "_hint"); + if (hint == null) + { + var t = new GameObject(); + Undo.RegisterCreatedObjectUndo(t, "Created hint"); + t.name = constraint.gameObject.name + "_hint"; + t.transform.localScale = .1f * t.transform.localScale; + Undo.SetTransformParent(t.transform, constraint.transform, "Set new parent"); + hint = t.transform; + } + constraint.data.hint = hint; + dirty = true; + } + + Vector3 rootPosition = constraint.data.root.position; + Vector3 midPosition = constraint.data.mid.position; + Vector3 tipPosition = constraint.data.tip.position; + Quaternion tipRotation = constraint.data.tip.rotation; + Vector3 targetPosition = constraint.data.target.position; + Quaternion targetRotation = constraint.data.target.rotation; + Vector3 hintPosition = constraint.data.hint.position; + float posWeight = constraint.data.targetPositionWeight; + float rotWeight = constraint.data.targetRotationWeight; + float hintWeight = constraint.data.hintWeight; + AffineTransform targetOffset = new AffineTransform(Vector3.zero, Quaternion.identity); + + AnimationRuntimeUtils.InverseSolveTwoBoneIK(rootPosition, midPosition, tipPosition, tipRotation, + ref targetPosition, ref targetRotation, ref hintPosition, true, posWeight, rotWeight, hintWeight, targetOffset); + + constraint.data.target.position = targetPosition; + constraint.data.target.rotation = targetRotation; + constraint.data.hint.position = hintPosition; + + Rig rig = constraint.GetComponentInParent(); + if (rig != null) + { + RigEffectorData.Style targetStyle = RigEffector.defaultStyle; + targetStyle.shape = EditorHelper.LoadShape("BoxEffector.asset"); + rig.AddEffector(constraint.data.target, targetStyle); + RigEffectorData.Style hintStyle = RigEffector.defaultStyle; + hintStyle.shape = EditorHelper.LoadShape("BallEffector.asset"); + rig.AddEffector(constraint.data.hint, hintStyle); + } + + if (dirty && PrefabUtility.IsPartOfPrefabInstance(constraint)) + EditorUtility.SetDirty(constraint); + } + } + + [BakeParameters(typeof(TwoBoneIKConstraint))] + class TwoBoneIKConstraintBakeParameters : BakeParameters + { + public override bool canBakeToSkeleton => true; + public override bool canBakeToConstraint => true; + + public override IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, TwoBoneIKConstraint constraint) + { + var bindings = new List(); + + EditorCurveBindingUtils.CollectTRBindings(rigBuilder.transform, constraint.data.target, bindings); + + if (constraint.data.hint != null) + EditorCurveBindingUtils.CollectPositionBindings(rigBuilder.transform, constraint.data.hint, bindings); + + return bindings; + } + + public override IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, TwoBoneIKConstraint constraint) + { + var bindings = new List(); + + var root = constraint.data.root; + var mid = constraint.data.mid; + var tip = constraint.data.tip; + + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, root, bindings); + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, mid, bindings); + EditorCurveBindingUtils.CollectRotationBindings(rigBuilder.transform, tip, bindings); + + return bindings; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs.meta new file mode 100644 index 00000000..1d743596 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/Constraints/TwoBoneIKConstraintEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d47d5b10876eed042af2bccb184f9a5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs new file mode 100644 index 00000000..884461b3 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs @@ -0,0 +1,61 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; +using UnityEditorInternal; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(RigBuilder))] + class RigBuilderEditor : Editor + { + static readonly GUIContent k_RigLabel = new GUIContent("Rig Layers"); + + SerializedProperty m_Rigs; + ReorderableList m_ReorderableList; + + void OnEnable() + { + m_Rigs = serializedObject.FindProperty("m_RigLayers"); + m_ReorderableList = ReorderableListHelper.Create(serializedObject, m_Rigs, true, true); + if (m_ReorderableList.count == 0) + ((RigBuilder)serializedObject.targetObject).layers.Add(new RigLayer(null)); + + m_ReorderableList.drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, k_RigLabel); + + m_ReorderableList.onAddCallback = (ReorderableList list) => + { + ((RigBuilder)(serializedObject.targetObject)).layers.Add(new RigLayer(null, true)); + }; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + EditorGUILayout.Separator(); + m_ReorderableList.DoLayoutList(); + serializedObject.ApplyModifiedProperties(); + } + + [MenuItem("CONTEXT/RigBuilder/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var rigBuilder = command.context as RigBuilder; + BakeUtils.TransferMotionToConstraint(rigBuilder); + } + + [MenuItem("CONTEXT/RigBuilder/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var rigBuilder = command.context as RigBuilder; + BakeUtils.TransferMotionToSkeleton(rigBuilder); + } + + [MenuItem("CONTEXT/RigBuilder/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/RigBuilder/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var rigBuilder = command.context as RigBuilder; + return BakeUtils.TransferMotionValidate(rigBuilder); + } + + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs.meta new file mode 100644 index 00000000..2f332b36 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigBuilderEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f920510daf5fba46b137a815f4e9a1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs new file mode 100644 index 00000000..04ef7351 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs @@ -0,0 +1,76 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(Rig))] + [CanEditMultipleObjects] + class RigEditor : Editor + { + SerializedProperty m_Weight; + + void OnEnable() + { + m_Weight = serializedObject.FindProperty("m_Weight"); + } + + public override void OnInspectorGUI() + { + bool isEditingMultipleObjects = targets.Length > 1; + + serializedObject.Update(); + EditorGUILayout.Separator(); + EditorGUILayout.PropertyField(m_Weight); + serializedObject.ApplyModifiedProperties(); + + if (!isEditingMultipleObjects) + { + var rig = target as Rig; + var rigBuilder = rig.GetComponentInParent(); + + if (rigBuilder == null) + { + EditorGUILayout.HelpBox("Rig component is not child of a GameObject with a RigBuilder component.", MessageType.Warning, true); + } + else + { + var inRigBuilder = false; + var layers = rigBuilder.layers; + + for (int i = 0; i < layers.Count; ++i) + { + if (layers[i].rig == rig && layers[i].active) + { + inRigBuilder = true; + break; + } + } + + if (!inRigBuilder) + EditorGUILayout.HelpBox("Rig component is not referenced or not active in the RigBuilder component.", MessageType.Warning, true); + } + } + } + + [MenuItem("CONTEXT/Rig/Transfer motion to constraint", false, 611)] + public static void TransferMotionToConstraint(MenuCommand command) + { + var rig = command.context as Rig; + BakeUtils.TransferMotionToConstraint(rig); + } + + [MenuItem("CONTEXT/Rig/Transfer motion to skeleton", false, 612)] + public static void TransferMotionToSkeleton(MenuCommand command) + { + var rig = command.context as Rig; + BakeUtils.TransferMotionToSkeleton(rig); + } + + [MenuItem("CONTEXT/Rig/Transfer motion to constraint", true)] + [MenuItem("CONTEXT/Rig/Transfer motion to skeleton", true)] + public static bool TransferMotionValidate(MenuCommand command) + { + var rig = command.context as Rig; + return BakeUtils.TransferMotionValidate(rig); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs.meta new file mode 100644 index 00000000..a52e0e64 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6856dd80c42d3244b9d9eb1844f8715f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs new file mode 100644 index 00000000..d5c1db6a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs @@ -0,0 +1,40 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomPropertyDrawer(typeof(RigLayer))] + class RigLayerDrawer : PropertyDrawer + { + const int k_Padding = 6; + const int k_TogglePadding = 30; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(rect, label, property); + + var w = rect.width - k_TogglePadding; + var weightRect = new Rect(rect.x + w + k_Padding, rect.y, rect.width - w - k_Padding, rect.height); + rect.width = w; + + EditorGUI.BeginChangeCheck(); + EditorGUI.PropertyField(rect, property.FindPropertyRelative("m_Rig"), label); + + var indentLvl = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUI.PropertyField(weightRect, property.FindPropertyRelative("m_Active"), GUIContent.none); + EditorGUI.indentLevel = indentLvl; + + if (EditorGUI.EndChangeCheck()) + property.serializedObject.ApplyModifiedProperties(); + + EditorGUI.EndProperty(); + } + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs.meta new file mode 100644 index 00000000..93d03ef2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigLayerDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfddd68cbf078454ab5dfd53ed554127 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs new file mode 100644 index 00000000..ad3a84af --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs @@ -0,0 +1,12 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(RigTransform))] + class RigTransformEditor : Editor + { + public override void OnInspectorGUI() + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs.meta new file mode 100644 index 00000000..3b891bef --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/RigTransformEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab480ae8b7295e54c9c72a011401317e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs new file mode 100644 index 00000000..70a903ac --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditorInternal; +using UnityEngine; +using UnityEngine.Animations.Rigging; +using UnityObject = UnityEngine.Object; + +namespace UnityEditor.Animations.Rigging +{ + [CustomPropertyDrawer(typeof(WeightedTransformArray))] + sealed class WeightedTransformArrayDrawer : PropertyDrawer + { + const string k_LengthPath = "m_Length"; + const string k_ItemTransformPath = nameof(WeightedTransform.transform); + const string k_ItemWeightPath = nameof(WeightedTransform.weight); + static readonly string[] k_ItemPropertyPaths = + Enumerable.Range(0, WeightedTransformArray.k_MaxLength).Select(i => $"m_Item{i}").ToArray(); + + // one reorderable list per unique property path + readonly Dictionary m_GUIState = + new Dictionary(); + + // function to get WeightedTransform from item property + static WeightedTransform GetValueSingle(SerializedProperty item) => new WeightedTransform + { + transform = item.FindPropertyRelative(k_ItemTransformPath).objectReferenceValue as Transform, + weight = item.FindPropertyRelative(k_ItemWeightPath).floatValue + }; + + // function to modify a list of items per target + static void ModifyItemsSingle(SerializedProperty parentProperty, Action> modifyList) + { + foreach (var target in parentProperty.serializedObject.targetObjects) + { + using (var so = new SerializedObject(target)) + { + var sp = so.FindProperty(parentProperty.propertyPath); + var length = sp.FindPropertyRelative(k_LengthPath); + + // create a live list of items + var items = + Enumerable.Range(0, length.intValue) + .Select(i => GetValueSingle(sp.FindPropertyRelative(k_ItemPropertyPaths[i]))) + .ToList(); + + // modify the list + modifyList(items); + + // write the results back to the serialized data stream + for (var i = 0; i < items.Count; ++i) + { + var item = sp.FindPropertyRelative(k_ItemPropertyPaths[i]); + item.FindPropertyRelative(k_ItemTransformPath).objectReferenceValue = items[i].transform; + item.FindPropertyRelative(k_ItemWeightPath).floatValue = items[i].weight; + } + // clear other items + for (var i = items.Count; i < WeightedTransformArray.k_MaxLength; ++i) + { + var item = sp.FindPropertyRelative(k_ItemPropertyPaths[i]); + item.FindPropertyRelative(k_ItemTransformPath).objectReferenceValue = default; + item.FindPropertyRelative(k_ItemWeightPath).floatValue = default; + } + + // synchronize length property + length.intValue = items.Count; + + // write back results + so.ApplyModifiedProperties(); + } + } + // update parent property's serialized data stream to get new (shared) values for all targets + parentProperty.serializedObject.Update(); + } + + (ReorderableList listControl, SerializedProperty lengthProperty) GetGUIState(SerializedProperty property, GUIContent label) + { + var lengthProperty = property.FindPropertyRelative(k_LengthPath); + + // create a new reorderable list if one does not exist + if (!m_GUIState.TryGetValue(property.propertyPath, out var guiState)) + { + // bind the control to a proxy list + var proxy = Enumerable.Range(0, lengthProperty.intValue) + .ToList(); + var reorderableList = new ReorderableList(proxy, typeof(int)); + + reorderableList.headerHeight = Styles.minHeaderHeight; + + // default array control only allocates single line height, but that leaves no spacing between object fields + reorderableList.elementHeight = + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + var attr = fieldInfo.GetCustomAttribute(); + var legacyAttr = fieldInfo.GetCustomAttribute(); + var min = attr?.min ?? legacyAttr?.min ?? float.NaN; + var max = attr?.max ?? legacyAttr?.max ?? float.NaN; + var spacing = EditorGUIUtility.standardVerticalSpacing / 2f; + reorderableList.drawElementCallback += (rect, index, active, focused) => + { + rect = new Rect(rect) { height = EditorGUIUtility.singleLineHeight, y = rect.y + spacing }; + WeightedTransformDrawer.DoGUI(rect, property.FindPropertyRelative(k_ItemPropertyPaths[index]), min, max); + }; + + reorderableList.onCanAddCallback += list => + !Application.isPlaying + && !AnimationMode.InAnimationMode() + && lengthProperty.intValue < WeightedTransformArray.k_MaxLength; + + reorderableList.onCanRemoveCallback += list => + !Application.isPlaying + && !AnimationMode.InAnimationMode() + && lengthProperty.intValue > 0; + + reorderableList.onAddCallback += list => + { + ModifyItemsSingle(property, items => + { + int insertIndex = Math.Max(0, reorderableList.index >= 0 ? reorderableList.index : items.Count - 1); + if (items.Count < WeightedTransformArray.k_MaxLength) + items.Insert(insertIndex, insertIndex < items.Count ? items[insertIndex] : WeightedTransform.Default(1f)); + }); + proxy.Add(proxy.Count); + }; + + reorderableList.onRemoveCallback += list => + { + ModifyItemsSingle(property, items => + { + int removeIndex = Math.Max(0, reorderableList.index >= 0 ? reorderableList.index : items.Count - 1); + if (removeIndex >= 0) + items.RemoveAt(removeIndex); + }); + proxy.RemoveAt(proxy.Count - 1); + }; + + reorderableList.onReorderCallbackWithDetails += (list, srcIndex, dstIndex) => + ModifyItemsSingle(property, items => + { + var moved = items[srcIndex]; + items.RemoveAt(srcIndex); + items.Insert(dstIndex, moved); + }); + + guiState = m_GUIState[property.propertyPath] = (reorderableList, lengthProperty.Copy()); + } + + // synchronize proxy list to serialized length + var proxyList = guiState.listControl.list; + while (proxyList.Count < lengthProperty.intValue) + proxyList.Add(proxyList.Count); + while (proxyList.Count > lengthProperty.intValue) + proxyList.RemoveAt(proxyList.Count - 1); + + return guiState; + } + + static class Styles + { + // cf. ReorderableList.Defaults.minHeaderHeight; + public static float minHeaderHeight = 2f; + // cf. ReorderableListWrapper.cs + public const float headerPadding = 3f; + public const float arraySizeWidth = 50f; // 48 in ReorderableListWrapper, but EditorGUI.Slider() field is 50 + public const float defaultFoldoutHeaderHeight = 18f; + public static readonly GUIContent sizeLabel = EditorGUIUtility.TrTextContent("", "Length"); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => + Styles.defaultFoldoutHeaderHeight + + (property.isExpanded ? Styles.headerPadding + GetGUIState(property, label).listControl.GetHeight() : 0f); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + var guiState = GetGUIState(property, label); + + Rect headerRect = new Rect(position) { height = EditorGUIUtility.singleLineHeight }; + Rect sizeRect = new Rect(headerRect) { xMin = headerRect.xMax - Styles.arraySizeWidth }; + + EventType prevType = Event.current.type; + if (Event.current.type == EventType.MouseUp && sizeRect.Contains(Event.current.mousePosition)) + { + Event.current.type = EventType.Used; + } + + property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(headerRect, property.isExpanded, label); + EditorGUI.EndFoldoutHeaderGroup(); + + if (Event.current.type == EventType.Used && sizeRect.Contains(Event.current.mousePosition)) Event.current.type = prevType; + + EditorGUI.BeginChangeCheck(); + EditorGUI.DelayedIntField(sizeRect, guiState.lengthProperty, Styles.sizeLabel); + if (EditorGUI.EndChangeCheck()) + guiState.lengthProperty.intValue = Mathf.Clamp(guiState.lengthProperty.intValue, 0, WeightedTransformArray.k_MaxLength); + EditorGUI.LabelField(sizeRect, Styles.sizeLabel); + + if (headerRect.Contains(Event.current.mousePosition)) + { + if (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform) + { + OnDropObjects(property, DragAndDrop.objectReferences, guiState.listControl); + DragAndDrop.AcceptDrag(); + Event.current.Use(); + } + } + + if (Event.current.type == EventType.DragExited) + { + DragAndDrop.visualMode = DragAndDropVisualMode.None; + Event.current.Use(); + } + + if (property.isExpanded) + guiState.listControl.DoList(new Rect(position) { yMin = headerRect.yMax + Styles.headerPadding }); + } + + static void OnDropObjects(SerializedProperty property, UnityObject[] objectReferences, ReorderableList listControl) + { + foreach (var o in objectReferences) + { + var go = o as GameObject; + var c = o as Component; + + if (go == null && c == null) + continue; + + if (listControl.list.Count >= WeightedTransformArray.k_MaxLength) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; + continue; + } + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + var t = c == null ? go.transform : c.transform; + if (Event.current.type == EventType.DragPerform) + { + ModifyItemsSingle(property, items => + { + var weight = items.Count == 0 ? 1f : items[items.Count - 1].weight; + items.Add(new WeightedTransform(t, weight)); + }); + listControl.list.Add(listControl.list.Count); + } + } + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs.meta new file mode 100644 index 00000000..637b707a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformArrayDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ee7818cdc02e4548adb5ae6bc5901523 +timeCreated: 1607593832 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs new file mode 100644 index 00000000..7bd617ec --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs @@ -0,0 +1,59 @@ +using System.Reflection; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomPropertyDrawer(typeof(WeightedTransform))] + class WeightedTransformDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => + EditorGUIUtility.singleLineHeight; + + (WeightRangeAttribute attr, RangeAttribute legacyAttr)? m_RangeAttributes; + + public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) + { + var (attr, legacyAttr) = m_RangeAttributes ??= ( + fieldInfo.GetCustomAttribute(), + fieldInfo.GetCustomAttribute() + ); + + float min = attr?.min ?? legacyAttr?.min ?? float.NaN; + float max = attr?.max ?? legacyAttr?.max ?? float.NaN; + + DoGUI(rect, property, min, max); + } + + static class Styles + { + public static float transformFieldWidthScale = 0.65f; + public static readonly int horizontalMargin = ( + EditorStyles.objectField.margin.right + GUI.skin.horizontalSlider.margin.left + ) / 2; + } + + internal static void DoGUI(Rect rect, SerializedProperty property, float min, float max) + { + EditorGUI.BeginProperty(rect, GUIContent.none, property); + + var w = rect.width * Styles.transformFieldWidthScale; + var weightRect = new Rect(rect.x + w, rect.y, rect.width - w, EditorGUIUtility.singleLineHeight); + rect.width = w; + + var transformRect = new Rect(rect.x, rect.y, rect.width - Styles.horizontalMargin, EditorGUIUtility.singleLineHeight); + + EditorGUI.PropertyField(transformRect, property.FindPropertyRelative("transform"), GUIContent.none); + + var indentLvl = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + if (float.IsNaN(max) || float.IsNaN(min)) + EditorGUI.PropertyField(weightRect, property.FindPropertyRelative("weight"), GUIContent.none); + else + EditorGUI.Slider(weightRect, property.FindPropertyRelative("weight"), min, max, GUIContent.none); + EditorGUI.indentLevel = indentLvl; + + EditorGUI.EndProperty(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs.meta b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs.meta new file mode 100644 index 00000000..5a8e32ee --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AnimationRig/WeightedTransformDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a015bc034afc6b742996a3d23fa85902 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs b/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs new file mode 100644 index 00000000..773a17ae --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs @@ -0,0 +1,2 @@ +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Unity.Animation.Rigging.EditorTests")] diff --git a/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs.meta b/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs.meta new file mode 100644 index 00000000..6aee2bc8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e9f01c83e9859644bb453f6f07af6d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes.meta b/Packages/com.unity.animation.rigging/Editor/Attributes.meta new file mode 100644 index 00000000..54a394a4 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f53358c1055884db9ba27bfa39aae0a0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs b/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs new file mode 100644 index 00000000..ced9a0d6 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// Attribute that can be placed on BakeParameters. The attribute is used to declare to which RigConstraint the BakeParameters belong. + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class BakeParametersAttribute : Attribute + { + /// + /// Constructor. + /// + /// The RigConstraint to which the BakeParameters belong. + public BakeParametersAttribute(Type constraintType) + { + if (constraintType == null || !typeof(IRigConstraint).IsAssignableFrom(constraintType)) + Debug.LogError("Invalid constraint for InverseRigConstraint attribute."); + + this.constraintType = constraintType; + } + + /// + /// The RigConstraint to which the BakeParameters belong. + /// + public Type constraintType { get; } + } + + /// + /// Class that holds bi-directional baking capabilities and curve bindings of a RigConstraint. + /// + /// The Type of RigConstraint the parameters belong to. + public abstract class BakeParameters : IBakeParameters + where T : IRigConstraint + { + /// + /// Boolean used to determine if the RigConstraint can transfer motion from itself to the skeleton + /// + public abstract bool canBakeToSkeleton { get; } + /// + /// Boolean used to determine if the RigConstraint can transfer motion to itself from the skeleton. + /// + public abstract bool canBakeToConstraint { get; } + + /// + /// Collects the editor curve bindings for all the properties that this RigConstraint modifies when transferring motion to the skeleton. + /// + /// The RigBuilder which the constraint is part of. + /// The RigConstraint for which the bindings should be collected. + /// + public abstract IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, T constraint); + /// + /// Collects the editor curve bindings for all the properties that this RigConstraint modifies when transferring motion to this constraint. + /// + /// The RigBuilder which the constraint is part of. + /// The RigConstraint for which the bindings should be collected. + /// + public abstract IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, T constraint); + + + /// + bool IBakeParameters.canBakeToSkeleton => canBakeToSkeleton; + /// + bool IBakeParameters.canBakeToConstraint => canBakeToConstraint; + + /// + IEnumerable IBakeParameters.GetSourceCurveBindings(RigBuilder rigBuilder, IRigConstraint constraint) + { + Debug.Assert(constraint is T); + T tConstraint = (T)constraint; + return GetSourceCurveBindings(rigBuilder, tConstraint); + } + + /// + IEnumerable IBakeParameters.GetConstrainedCurveBindings(RigBuilder rigBuilder, IRigConstraint constraint) + { + Debug.Assert(constraint is T); + T tConstraint = (T)constraint; + return GetConstrainedCurveBindings(rigBuilder, tConstraint); + } + } + + /// + /// This is the base interface for BakeParameters. + /// + public interface IBakeParameters + { + /// + /// Boolean used to determine if the RigConstraint can transfer motion from itself to the skeleton + /// + bool canBakeToSkeleton { get; } + /// + /// Boolean used to determine if the RigConstraint can transfer motion to itself from the skeleton. + /// + bool canBakeToConstraint { get; } + + /// + /// Collects the editor curve bindings for all the properties that this RigConstraint modifies when transferring motion to the skeleton. + /// + /// The RigBuilder which the constraint is part of. + /// The RigConstraint for which the bindings should be collected. + /// + IEnumerable GetSourceCurveBindings(RigBuilder rigBuilder, IRigConstraint constraint); + /// + /// Collects the editor curve bindings for all the properties that this RigConstraint modifies when transferring motion to this constraint. + /// + /// The RigBuilder which the constraint is part of. + /// The RigConstraint for which the bindings should be collected. + /// + IEnumerable GetConstrainedCurveBindings(RigBuilder rigBuilder, IRigConstraint constraint); + } + +} diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs.meta b/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs.meta new file mode 100644 index 00000000..bff83cb2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/BakeParametersAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc4c7bdb3fbef4e6f8eb6ea619b7c442 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs b/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs new file mode 100644 index 00000000..0ca3a772 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs @@ -0,0 +1,18 @@ +using System; +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + internal sealed class CustomOverlayAttribute : Attribute + { + private Type m_EffectorType; + + public CustomOverlayAttribute(Type effectorType) + { + m_EffectorType = effectorType; + } + + public Type effectorType { get => m_EffectorType; } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs.meta b/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs.meta new file mode 100644 index 00000000..8256fb71 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/CustomOverlayAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54d375d3ab6254023a5ab55153af9c84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs b/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs new file mode 100644 index 00000000..e888aee7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs @@ -0,0 +1,32 @@ +using System; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The [InverseRigConstraint] attribute allows to match an inverse constraint (inverse solve) to its + /// base constraint (forward solve) counterpart. This is used in bi-directional baking to override + /// constraints when baking animations to constraints. + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class InverseRigConstraintAttribute : Attribute + { + /// + /// Constructor. + /// + /// The base constraint type. + public InverseRigConstraintAttribute(Type targetBinderType) + { + if (targetBinderType == null || !typeof(IRigConstraint).IsAssignableFrom(targetBinderType)) + Debug.LogError("Invalid constraint for InverseRigConstraint attribute."); + + this.baseConstraint = targetBinderType; + } + + /// + /// Retrieves the base constraint type. + /// + public Type baseConstraint { get; } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs.meta b/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs.meta new file mode 100644 index 00000000..bf2253d9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Attributes/InverseRigConstraintAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 28f235e117a7e453fa6d4c1cb6094700 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors.meta b/Packages/com.unity.animation.rigging/Editor/Effectors.meta new file mode 100644 index 00000000..6f62dea0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fbd5015e3b81f464aa1b87826a3eaf55 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs new file mode 100644 index 00000000..59171879 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + internal interface IRigEffector + { + Transform transform { get; } + bool visible { get; set; } + + void OnSceneGUI(); + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs.meta new file mode 100644 index 00000000..7cb4778d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5aa7c79e34bb34aab80024d95fafe526 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs new file mode 100644 index 00000000..5d09955e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs @@ -0,0 +1,10 @@ +using System; + +namespace UnityEditor.Animations.Rigging +{ + interface IRigEffectorOverlay : IDisposable + { + bool IsValid(); + void OnSceneGUIOverlay(); + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs.meta new file mode 100644 index 00000000..2bb28123 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/IRigEffectorOverlay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4231b7e96c3094051a12f91bae5c7c90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs new file mode 100644 index 00000000..f6b0c1b8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [Serializable] + internal class RigEffector : ScriptableObject, IRigEffector + { + [SerializeField] private RigEffectorData m_Data; + + public Transform transform + { + get => m_Data.transform; + } + + public bool visible + { + get => m_Data.visible; + set => m_Data.visible = value; + } + + private static int s_ButtonHash = "RigEffector".GetHashCode(); + + public void Initialize(RigEffectorData data) + { + m_Data = data; + } + + private static Material s_Material; + + public static Material material + { + get + { + if (!s_Material) + { + var shader = EditorHelper.LoadShader("BoneHandles.shader"); + s_Material = new Material(shader); + s_Material.hideFlags = HideFlags.HideAndDontSave; + } + + return s_Material; + } + } + + public static RigEffectorData.Style defaultStyle + { + get + { + var style = new RigEffectorData.Style() + { + shape = EditorHelper.LoadShape("LocatorEffector.asset"), + color = new Color(1f, 0f, 0f, 0.5f), + size = 0.10f, + position = Vector3.zero, + rotation = Vector3.zero + }; + + return style; + } + } + + public void OnSceneGUI() + { + if (!m_Data.visible) + return; + + // Might happen if we delete transform while effector still exists. + if (transform == null) + return; + + var style = m_Data.style; + + // Disregard effectors without shapes. + if (style.shape == null) + return; + + if (SceneVisibilityManager.instance.IsHidden(transform.gameObject, false)) + return; + + var mask = UnityEditor.Tools.visibleLayers; + if ((mask & (1 << transform.gameObject.layer)) == 0) + return; + + int id = GUIUtility.GetControlID(s_ButtonHash, FocusType.Passive); + Event evt = Event.current; + + switch (evt.GetTypeForControl(id)) + { + case EventType.Layout: + { + HandleUtility.AddControl(id, DistanceToEffector(transform, style.shape, style.position, style.rotation, style.size)); + break; + } + case EventType.MouseDown: + { + if (evt.alt) + break; + + if (HandleUtility.nearestControl == id && evt.button == 0) + { + GameObject targetGameObject = transform.gameObject; + if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false)) + { + GUIUtility.hotControl = id; // Grab mouse focus + EditorHelper.HandleClickSelection(targetGameObject, evt); + evt.Use(); + } + } + break; + } + case EventType.MouseDrag: + { + if (!evt.alt && GUIUtility.hotControl == id) + { + GameObject targetGameObject = transform.gameObject; + if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false)) + { + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new UnityEngine.Object[] {transform}; + DragAndDrop.StartDrag(ObjectNames.GetDragAndDropTitle(transform)); + + GUIUtility.hotControl = 0; + + evt.Use(); + } + } + break; + } + case EventType.MouseUp: + { + if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2)) + { + GUIUtility.hotControl = 0; + evt.Use(); + } + break; + } + case EventType.Repaint: + { + Matrix4x4 matrix = GetEffectorMatrix(transform, style.position, style.rotation, style.size); + + Color highlight = style.color; + + bool hoveringEffector = GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id; + hoveringEffector = hoveringEffector && + !SceneVisibilityManager.instance.IsPickingDisabled(transform.gameObject, false); + + if (hoveringEffector) + { + highlight = Handles.preselectionColor; + } + else if (Selection.Contains(transform.gameObject) || Selection.activeObject == transform.gameObject) + { + highlight = Handles.selectedColor; + } + + try + { + Material mat = material; + + var shapeHighlight = MeshHasWireframeShapes(style.shape) ? style.color : highlight; + var wireHighlight = new Color(highlight.r, highlight.g, highlight.b, 1f); + + if (style.shape.subMeshCount > 0) + { + // Draw every sub meshes separately to control highlight vs shape colors. + for (int i = 0; i < style.shape.subMeshCount; ++i) + { + MeshTopology topology = style.shape.GetTopology(i); + bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads); + + mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight); + mat.SetPass(0); + + Graphics.DrawMeshNow(style.shape, matrix, i); + } + } + else + { + MeshTopology topology = style.shape.GetTopology(0); + bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads); + + mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight); + mat.SetPass(0); + + Graphics.DrawMeshNow(style.shape, matrix); + } + } + catch (Exception exception) + { + Debug.LogException(exception); + } + } + break; + } + } + + private bool FindMeshTopology(Mesh shape, IEnumerable topologies) + { + if (shape.subMeshCount > 0) + { + for (int i = 0; i < shape.subMeshCount; ++i) + { + MeshTopology topology = shape.GetTopology(i); + foreach (var topologyQuery in topologies) + { + if (topologyQuery == topology) + return true; + } + } + } + else + { + var topology = shape.GetTopology(0); + foreach (var topologyQuery in topologies) + { + if (topologyQuery == topology) + return true; + } + } + + return false; + } + + private bool MeshHasFilledShapes(Mesh shape) => + FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Triangles, MeshTopology.Quads}); + + private bool MeshHasWireframeShapes(Mesh shape) => + FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Lines, MeshTopology.LineStrip}); + + private Matrix4x4 GetEffectorMatrix(Transform transform, Vector3 position, Vector3 rotation, float size) + { + return Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one) * Matrix4x4.TRS(position, Quaternion.Euler(rotation), new Vector3(size, size, size)); + } + + private float DistanceToEffector(Transform transform, Mesh shape, Vector3 position, Vector3 rotation, float size) + { + Matrix4x4 matrix = GetEffectorMatrix(transform, position, rotation, size); + Vector3 origin = matrix.MultiplyPoint(Vector3.zero); + + if (shape == null) + return HandleUtility.DistanceToCircle(origin, size * 0.5f); + + if (MeshHasFilledShapes(shape)) + { + var bounds = shape.bounds; + var extents = Mathf.Max( Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z); + return HandleUtility.DistanceToCircle(origin + bounds.center * size, extents * size); + } + + float nearestDistance = Mathf.Infinity; + + Vector3[] vertices = shape.vertices; + + for (int i = 0; i < shape.subMeshCount; ++i) + { + MeshTopology topology = shape.GetTopology(i); + if (topology == MeshTopology.Lines) + { + int[] indices = shape.GetIndices(i); + + int count = 0; + while (count < indices.Length) + { + float d = HandleUtility.DistanceToLine(matrix.MultiplyPoint(vertices[indices[count]]), matrix.MultiplyPoint(vertices[indices[count+1]])); + if (d < nearestDistance) + nearestDistance = d; + + count += 2; + } + } + else if (topology == MeshTopology.LineStrip) + { + int[] indices = shape.GetIndices(i); + + if (indices.Length > 0) + { + int count = 0; + float d = 0f; + Vector3 v0 = matrix.MultiplyPoint(vertices[indices[count]]); + Vector3 v1 = v0; + + while (++count < indices.Length) + { + Vector3 v2 = matrix.MultiplyPoint(vertices[indices[count]]); + + d = HandleUtility.DistanceToLine(v1, v2); + if (d < nearestDistance) + nearestDistance = d; + + v1 = v2; + } + + d = HandleUtility.DistanceToLine(v1, v0); + if (d < nearestDistance) + nearestDistance = d; + } + } + } + + return nearestDistance; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs.meta new file mode 100644 index 00000000..1aa0f3cb --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff03b96c5bd3745c28814ed6eb4341cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs new file mode 100644 index 00000000..a384838f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs @@ -0,0 +1,190 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomOverlay(typeof(RigEffector))] + class RigEffectorOverlay : IRigEffectorOverlay + { + private Object[] m_TargetObjects; + private SerializedObject m_SerializedObject; + + private SerializedProperty m_Visible; + private SerializedProperty m_Shape; + private SerializedProperty m_Color; + private SerializedProperty m_Size; + private SerializedProperty m_Position; + private SerializedProperty m_Rotation; + + private bool m_ExpandOverlay; + + private static GUIContent s_VisibleLabel = new GUIContent("Visible"); + private static GUIContent s_ShapeLabel = new GUIContent("Shape"); + private static GUIContent s_ColorLabel = new GUIContent("Color"); + private static GUIContent s_SizeLabel = new GUIContent("Size"); + private static GUIContent s_PositionLabel = new GUIContent("Position"); + private static GUIContent s_RotationLabel = new GUIContent("Rotation"); + + private static GUIContent s_RemoveLabel = new GUIContent("Remove"); + + private static string s_ExpandOverlayPrefKey = "AnimationRigging.ExpandOverlay"; + + private static GUILayoutOption s_FixedWidth = GUILayout.Width(210f); + + public void Initialize(Object[] effectors) + { + m_TargetObjects = effectors; + m_SerializedObject = new SerializedObject(effectors); + + SerializedProperty data = m_SerializedObject.FindProperty("m_Data"); + + m_Visible = data.FindPropertyRelative("m_Visible"); + + SerializedProperty style = data.FindPropertyRelative("m_Style"); + + m_Shape = style.FindPropertyRelative("shape"); + m_Color = style.FindPropertyRelative("color"); + m_Size = style.FindPropertyRelative("size"); + m_Position = style.FindPropertyRelative("position"); + m_Rotation = style.FindPropertyRelative("rotation"); + + m_ExpandOverlay = EditorPrefs.GetBool(s_ExpandOverlayPrefKey, true); + } + + private IRigEffectorHolder FetchRigEffectorHolder(Transform transform) + { + var rigBuilder = EditorHelper.GetClosestComponent(transform); + var rig = EditorHelper.GetClosestComponent(transform, (rigBuilder != null) ? rigBuilder.transform : null); + + if (rigBuilder.ContainsEffector(transform)) + { + return rigBuilder; + } + else if (rig.ContainsEffector(transform)) + { + return rig; + } + + return null; + } + + public bool IsValid() => m_SerializedObject.targetObject != null; + + public void OnSceneGUIOverlay() + { + if (!IsValid()) + return; + + m_SerializedObject.Update(); + + GameObject targetGameObject = null; + if (!m_SerializedObject.isEditingMultipleObjects) + { + RigEffector rigEffector = m_SerializedObject.targetObject as RigEffector; + if (rigEffector != null && rigEffector.transform != null) + { + targetGameObject = rigEffector.transform.gameObject; + } + } + + GUILayout.BeginHorizontal(s_FixedWidth); + + EditorGUI.BeginChangeCheck(); + m_ExpandOverlay = EditorGUILayout.Toggle(m_ExpandOverlay, EditorStyles.foldout, GUILayout.Width(12)); + if (EditorGUI.EndChangeCheck()) + { + EditorPrefs.SetBool(s_ExpandOverlayPrefKey, m_ExpandOverlay); + } + + GUILayout.BeginHorizontal(EditorStyles.toolbar); + + EditorGUILayout.PropertyField(m_Visible, GUIContent.none, GUILayout.Width(17)); + + GUILayout.Label((targetGameObject != null) ? targetGameObject.name : "(Multiple objects)"); + + if (GUILayout.Button(GUIContent.none, "OL Minus", GUILayout.Width(17))) + { + UnityEngine.Object[] targetObjects = m_SerializedObject.targetObjects; + foreach(var targetObject in targetObjects) + { + var effector = targetObject as IRigEffector; + Transform transform = effector.transform; + + IRigEffectorHolder holder = FetchRigEffectorHolder(transform); + if (holder != null) + { + var holderObject = holder as UnityEngine.Object; + + Undo.RecordObject(holderObject, "Remove Effector"); + + if (PrefabUtility.IsPartOfPrefabInstance(holderObject)) + EditorUtility.SetDirty(holderObject); + + holder.RemoveEffector(transform); + } + } + } + + GUILayout.EndHorizontal(); + GUILayout.EndHorizontal(); + + if (m_ExpandOverlay) + { + EditorGUILayout.LabelField(s_ShapeLabel, s_FixedWidth); + EditorGUILayout.PropertyField(m_Shape, GUIContent.none, s_FixedWidth); + + Rect rect = GUILayoutUtility.GetRect(s_ColorLabel, EditorStyles.colorField, s_FixedWidth); + + // Shenanigans to bypass color picker bug. + var evt = Event.current; + if (evt.type == EventType.MouseUp) + { + if (rect.Contains(evt.mousePosition)) + { + GUIUtility.hotControl = 0; + } + } + + EditorGUI.BeginProperty(rect, s_ColorLabel, m_Color); + EditorGUI.BeginChangeCheck(); + Color newColor = EditorGUI.ColorField(rect, s_ColorLabel, m_Color.colorValue, false, true, false); + if (EditorGUI.EndChangeCheck()) + { + m_Color.colorValue = newColor; + } + EditorGUI.EndProperty(); + + EditorGUILayout.PropertyField(m_Size, s_SizeLabel, s_FixedWidth); + EditorGUILayout.PropertyField(m_Position, s_PositionLabel, s_FixedWidth); + EditorGUILayout.PropertyField(m_Rotation, s_RotationLabel, s_FixedWidth); + } + + if (m_SerializedObject.hasModifiedProperties) + { + UnityEngine.Object[] targetObjects = m_SerializedObject.targetObjects; + foreach(var targetObject in targetObjects) + { + var effector = targetObject as IRigEffector; + Transform transform = effector.transform; + + IRigEffectorHolder holder = FetchRigEffectorHolder(transform); + if (holder != null) + { + var holderObject = holder as UnityEngine.Object; + Undo.RecordObject(holderObject, "Edit Effector"); + + if (PrefabUtility.IsPartOfPrefabInstance(holderObject)) + EditorUtility.SetDirty(holderObject); + } + } + + m_SerializedObject.ApplyModifiedProperties(); + } + } + + public void Dispose() + { + m_SerializedObject?.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs.meta new file mode 100644 index 00000000..d481b8b7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorOverlay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 128d92bb7c7e949569b14ae35b4b229e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs new file mode 100644 index 00000000..bfde8047 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs @@ -0,0 +1,235 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; +#if SUPPORTS_SCENE_VIEW_OVERLAYS +using UnityEditor.Overlays; +#endif +using UnityEditor.Experimental.SceneManagement; // required for 2020.2 +using UnityEditor.SceneManagement; + +namespace UnityEditor.Animations.Rigging +{ + [InitializeOnLoad] + static class RigEffectorRenderer + { + const string k_OverlayId = "Scene View/Animation Rigging"; + const string k_DisplayName = "Animation Rigging"; + + static GUIContent s_OverlayTitle = new GUIContent(k_DisplayName); + + static List s_RigBuilders = new List(); + static Dictionary s_Effectors = new Dictionary(); + + static Transform[] s_ActiveSelection = null; + static List s_ActiveEffectors = null; + static IRigEffectorOverlay s_ActiveOverlay = null; + + static bool s_ActiveOverlayDirtied = true; + + static RigEffectorRenderer() + { + RigBuilder.onAddRigBuilder += OnAddRigBuilder; + RigBuilder.onRemoveRigBuilder += OnRemoveRigBuilder; + + SceneView.duringSceneGui += OnSceneGUI; + Selection.selectionChanged += OnSelectionChange; + ObjectFactory.componentWasAdded += OnComponentAdded; + } + + static void OnSelectionChange() + { + s_ActiveOverlayDirtied = true; + } + + static void OnComponentAdded(Component component) + { + if (!(component is Rig) && !(component is RigBuilder)) + return; + + s_ActiveOverlayDirtied = true; + } + + static void FetchOrCreateEffectors(IRigEffectorHolder holder) + { + foreach(var effectorData in holder.effectors) + { + if (s_Effectors.ContainsKey(effectorData)) + { + s_ActiveEffectors.Add(s_Effectors[effectorData]); + } + else + { + var newEffector = ScriptableObject.CreateInstance(); + newEffector.Initialize(effectorData); + + s_Effectors.Add(effectorData, newEffector); + s_ActiveEffectors.Add(newEffector); + } + } + } + + static void FetchOrCreateEffectors() + { + s_ActiveEffectors = new List(); + + PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + + for (int i = 0; i < s_RigBuilders.Count; i++) + { + var rigBuilder = s_RigBuilders[i]; + + if (rigBuilder == null) + continue; + + if (prefabStage != null) + { + StageHandle stageHandle = prefabStage.stageHandle; + if (stageHandle.IsValid() && !stageHandle.Contains(rigBuilder.gameObject)) + continue; + } + + FetchOrCreateEffectors(rigBuilder); + + var rigs = rigBuilder.GetComponentsInChildren(); + if (rigs != null) + { + foreach(var rig in rigs) + { + FetchOrCreateEffectors(rig); + } + } + } + } + + static IRigEffectorOverlay FetchOrCreateEffectorOverlay() + { + if (!s_ActiveOverlayDirtied && s_ActiveOverlay != null && s_ActiveOverlay.IsValid()) + return s_ActiveOverlay; + + s_ActiveOverlay?.Dispose(); + + Transform[] transforms = Selection.GetTransforms(SelectionMode.ExcludePrefab | SelectionMode.Editable); + var inspectedEffectors = new List(); + + for (int i = 0; i < s_ActiveEffectors.Count; ++i) + { + var effector = s_ActiveEffectors[i]; + if (effector != null && effector.transform != null) + { + if (Selection.Contains(effector.transform) || Selection.Contains(effector.transform.gameObject)) + { + inspectedEffectors.Add(s_ActiveEffectors[i]); + } + } + } + + if (inspectedEffectors.Count > 0) + { + var overlay = new RigEffectorOverlay(); + overlay.Initialize(inspectedEffectors.ToArray()); + + s_ActiveOverlay = overlay; + } + else + { + RigEffectorWizard wizard = null; + + foreach(var transform in transforms) + { + RigBuilder rigBuilder = EditorHelper.GetClosestComponent(transform); + Rig rig = EditorHelper.GetClosestComponent(transform, (rigBuilder != null) ? rigBuilder.transform : null); + IRigEffectorHolder holder = (rig != null) ? (IRigEffectorHolder)rig : (IRigEffectorHolder)rigBuilder; + + if (holder == null) + continue; + + if (wizard == null) + wizard = new RigEffectorWizard(); + + wizard.Add(holder, transform); + } + + if (wizard != null) + { + s_ActiveOverlay = wizard; + } + else + { + s_ActiveOverlay = null; + } + } + + s_ActiveSelection = transforms; + + s_ActiveOverlayDirtied = false; + return s_ActiveOverlay; + } + + static void OnSceneGUI(SceneView sceneView) + { + // Fetch effectors and overlay once in Layout before processing events. + if (Event.current.type == EventType.Layout) + { + FetchOrCreateEffectors(); + FetchOrCreateEffectorOverlay(); + } + + // Process effector events. + if (s_ActiveEffectors != null) + { + for (int i = 0; i < s_ActiveEffectors.Count; ++i) + { + var effector = s_ActiveEffectors[i]; + if (effector == null) + continue; + + effector.OnSceneGUI(); + } + } + + #if !SUPPORTS_SCENE_VIEW_OVERLAYS + // Process overlay events. + if (s_ActiveOverlay != null) + { + SceneViewOverlay.Begin(sceneView); + SceneViewOverlay.Window(s_OverlayTitle, SceneViewGUICallback, 1200); + SceneViewOverlay.End(); + } + #endif + } + + static void OnAddRigBuilder(RigBuilder rigBuilder) + { + s_RigBuilders.Add(rigBuilder); + } + + static void OnRemoveRigBuilder(RigBuilder rigBuilder) + { + s_RigBuilders.Remove(rigBuilder); + s_Effectors.Clear(); + } + + private static void SceneViewGUICallback(UnityEngine.Object target, SceneView sceneView) + { + if (s_ActiveOverlay != null) + s_ActiveOverlay.OnSceneGUIOverlay(); + } + + #if SUPPORTS_SCENE_VIEW_OVERLAYS + [Overlay(typeof(SceneView), k_OverlayId, k_DisplayName)] + class Overlay : IMGUIOverlay, ITransientOverlay + { + public bool visible + { + get => s_ActiveOverlay != null; + } + + public override void OnGUI() + { + if (s_ActiveOverlay != null) + s_ActiveOverlay.OnSceneGUIOverlay(); + } + } + #endif + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs.meta new file mode 100644 index 00000000..8a002d8a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aedff7ad35cc7477a9786519f4791c00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs new file mode 100644 index 00000000..d2b4efca --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + class RigEffectorWizard : IRigEffectorOverlay + { + private static GUIContent s_CreateEffectorLabel = new GUIContent("Create Effector"); + + private struct HolderTransformPair + { + public IRigEffectorHolder holder; + public Transform transform; + } + + private List m_Transforms = new List(); + + public void Add(IRigEffectorHolder holder, Transform transform) + { + m_Transforms.Add(new HolderTransformPair() { holder = holder, transform = transform }); + } + + public bool IsValid() => true; + + public void OnSceneGUIOverlay() + { + string labelName = "(no selection)"; + if (m_Transforms.Count > 1) + { + labelName = "(Multiple objects)"; + } + else if (m_Transforms.Count > 0) + { + if (m_Transforms[0].transform.gameObject != null) + { + labelName = m_Transforms[0].transform.gameObject.name; + } + } + + GUILayout.BeginHorizontal(EditorStyles.toolbar, GUILayout.Width(210.0f)); + + GUILayout.Label(labelName); + + if (GUILayout.Button(GUIContent.none, "OL Plus", GUILayout.Width(17))) + { + foreach (var pair in m_Transforms) + { + var targetObject = pair.holder as UnityEngine.Object; + + Undo.RecordObject(targetObject, "Add Effector"); + + if (PrefabUtility.IsPartOfPrefabInstance(targetObject)) + EditorUtility.SetDirty(targetObject); + + pair.holder.AddEffector(pair.transform, RigEffector.defaultStyle); + } + } + + GUILayout.EndHorizontal(); + } + + public void Dispose() + { + // nothing to do. + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs.meta b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs.meta new file mode 100644 index 00000000..c3767bb7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Effectors/RigEffectorWizard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51459fd0b87be44cbb33be7842186a5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Icons.meta b/Packages/com.unity.animation.rigging/Editor/Icons.meta new file mode 100644 index 00000000..31b79fa9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Icons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 756c4b6dd3c75480781e03b559e98355 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png b/Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png new file mode 100644 index 0000000000000000000000000000000000000000..0773d0c2c79b03efd04f2f4ed9d149247826509d GIT binary patch literal 6088 zcmcIo2Ut_d_D_iPE}(QnP(U%H2MC~a0*DlmqO_C?L_!jh(2Iawnj&JOxgxsK*M^E1 zAB%#lh=_=l*cMPAEX{)8yAfpFee2u({^fq(&AoTdZ_fG6nRCv}WRKT+7ezU3IS2%z z=t_3<0iWsOTSf}}ZC*oZ2OqK=av%=^kyjJnP{`3DO|Y$k;TymY@K{Hpv7^l>bT$<* zONi!xXb8l5Z32fvivsv?DiFb7*&sw$t|8zIx(&jg;DPqwH~^6hauOHVkhI>HmJ~&^ zq$Ad@fmAoNzd-m=HV8YhL3n_N7ui(8 zG?Io!6XE8TW*8#goPb*m$D*+q6dH@d5Re!QiGU?xaPavH0b1kI!%04l&hxgwFB?Q8 zpU)woQ1S8cX7M;PHa7x=v9z=l>%d}>AOgutWbr8pNEXlVs|H7aN8>U$d&F=EwGzXr6B(2SE7%Ja#OX1~|om77f1=31AWcmRKYfi#JD72?PR?3gGGC7#s#?3Cytt`#Z8}vErZ- z_n*rzI-3USSeOlx1u;CFO0cj%T40DoP!S%3q|yig5{skKh*%00N2i#7W#h?ZfGJL4 zeydf?iVkYTq6v6nI1Puiq+qDXZ~~r+w7^o)NCJjN#o?%+1VcfIRNLb=$5Xg~T?FXkzs(EO z_sH`oasQnFTCRC62+3D7JkR3Z&ez=adP4*fr=|0#?9C)DTN zN{ghhA^>n1LLt687#f=u2XMcc3l4=#0aqV@%d*yz49X9X)26FWTQ! z@xjsM2nH@5N#5{-tM|FuIoe%qG|Am<+hoKU%oB+^1Xq*O;!9Ke`bHvtHRp*&*rhhf zQ=j5W`>p$nPO-WRjufaLiet!o<(r*^suy)ZROgAYK^eXssaL5{IYo|nCgqt_h_rTwh5#GJ%N#SdM`{UF z!WNAZgfeBN&GnP5VURVUual$Fodjz$BwJ0m9wmxp`W8x>up*g>^}?c=Ri(I9pKGN{ zAo@}zj-L5J`(0p~Qbp25N-1VQU=mDiRjG%7DGUi?9JRl+@6h@%=`h_o&6V#EUDrM! z2QywJ>9<)bwG}gdVnK@2T?J8s83AQ*@5^J&`x{a zmwkg(SM7#9eScOr_aXF?UV=)3davfTpd~gL2h#7QvmwKJxy7#qQgzQNUmnpSEC0B!0;=Gse_nP?(=;NfTVLT*Zgy;W9K|YM z{r-mJ;(|1hb zN#?{7Q&%m)Z2_=xiVZW9x`DV8ggmQp+i1P>gXox*U7M?xQy>q@vg&)YD|n}j;O>PD zda)nRNFE7t`=xeUgQC!W!s2XzyzY))yRuqSgl~OCOV`;YW$&<16eb9BS55oFTwtgc z69@f(4AQR3Gb#N_uVS)`8Wh6dV|O;s{&xS|Q$w@GQTx{T=}bR9k^3^kyVODn)(hJ~ zjwMXhx*QJ*jCmQpLy?Snn==!fVd*$T%x#9G?HPXlST05W>a_c&vr7hVYRl?nPv~fQ z>j+i~9BF;TFg!uURR`C;ucXT0g`=?h^!g!XIVs__tf{_+j_z3N`O_ z^a3f4&Q!q&Q|PZ+QYo{pWlD3(@uQN5uM~Q*)nS?^r8FSYI14FkaW?qbI;LIozUb$(ON;O z!6P%q?NoAYNKj_i(_&c~B%+KH33k3l2?{(ZJ01IYX^W4A+6_n=l^L3yk9Yf_-@7~% zWhBW!pN6JrS$U5arbjzpZ;SK+kb8|fsBbn0te(-|JCdblrx?~)e^vE%z{!^4(+cZK zZ}p*6GR`ii{c=4{$QohFzX&?NI?pd zPW!E7cVx%jHpHnPT2H*qu-Q=r=RwOec;us^ye$v7fZSK8DGY~*aC4Ft?9kH$ofUE-y;PV zXSBSQ)3by{?9leKULLNRLrAA(4`*e>>pi|D5i3ycjC$_oi!eel|1~9>J^FZeB~+Ke z{9H-13Z!{<+okN0X|7%@=(-YHx=HmQ)cR%bdcVQEo!R5wN})Z8k9yO#_eljQlv#1> z&e5e;alG8KA%*Y=pPo0WBe$-Njz~|~y{q&HQV_fesJgM0P-E?#_oS_IU#U$N>-A@< z@qzbmWG-t8;I!oGU5W7y$Nf<%8xQBJQ<~c;z=6ZQnoL**j91o1O&? zs0{V3l$3knV&>P%d%e6n^~1qjRg*u5$^qCmOR&A5L# zzv+(l~k#?)KfL9rqAn`c|xKZ>Mzz`C0plJgkAF- zo6M_veyR388T9R@{f2qZeZA|UCnL5>CYY_zTAf!~tKl0I0@cYL?pf}Q9ACL=G6|W~ zf68@R{nIg(k)2skJW0;0@)sw`+N#9&<@IUlIcZ9{|AHuSBMt9V*{a#PDvCZ)2d~|| zbKNX8UE@YvpFyi7AU{e|H#~g)nDT|$YB43LFsE4s$E;O7+K)3rPaG7CEI%f{G1hJ@ z@wur_d#q+d2Q4{yteEn$MFQU(ullefUNiTdP0%hIW}o5%+no_P!!DK=4s7&0fvq;o zc)hayg|xdU>l0D91>)Cy)9S(j!qKM}9lTL%Q2|L8(@s}&6k|iem}_+<;pI(BY7DLJ zVnv5&)0RKE#h1sUmb{Ptc~Z7cL6kH#oG!AsaFBS%;N7rtTiDeXOrf`tlzd* zh3MrM&K!6hNj5(^^nRq-CofoW$PVxQ`dFE);x)_#k#e?dZ@79;`j~o;fpG^~H|b|f z3Vv!)=hK=T$=eCRd-Dw%_d12_T#ZrGziAc{Y#K0mq_j4rNajEn9VwS+!SUH0=gw@( z$@FY&upw`wD1;H?-rqZK@YsyuVI?Hd`bdvL_CI0bO-iY#Abhq8VaVP3$nJ?ZE!7rr zvqxp8O^b7_Jwu+P-&xd>PaKqe5;>H7;7$AF#uZi2l+2lcM>-9b;tFdtH1IaGY+7Zv z$+LU!!aR#hb_8#(`+1oQ&8oLhEo|VTskf#;X{bf+O{b6XQ-p=5>fl~Eht07$#s)Le zMrTSsrr@jJIKj}!AI8;luGF2V5xIbszrf+Vd))!m8xjW$t^HLGXJ1w7bS@WlIx-+4 zpVWbPwJ8O^wh`T|3Gi^M+Oo=dxavrjjc2FoK}z)`daO>tF6k$~*@j~>%{?zhdxEp| z_IGJ~cze~YqmYcsGq6dl&)gk)GDcJ25_#j6o0e!BRc>#Q?;9sn`O?bPZol#eWcRV3 zU)P4__?)aZxMdq=(21%T@6Tx>FY}Y^(u)k<=KsT*m?Aae)*B%~iwo}lq*6+Vjyh}? zxg*xJS-Sk@&6gI5PjGSf>VjL!j0HILcs^R9Wz;`;afnNHTEX%P=Bcnd+LA$PdffUR7^Jr7UoPQycMw{$HX`p|UMBadG4IhHA&B8Ogp4UsqM4e<&rIx2U&JWvbs-Hx1UqD zQkFEoaIm>YaWehT50aJRRGHz<#aG<-e~LcXwRz{n{shTI35+Jtnv0^$asYu0%+WYH#uv0p}XJw1s*FPd~{Xr9|0Z9cS4Z`wr5V z;dO`e^!pt$po=_HQq_=%7;~*kVxbcZvU1Q2AGc=N>*h;0RL;4ahbU$HV$FLD@}BYS zx3g}#`hA2fDTN;zgb3Fa>^mx52v3xTf50V<8%%>EBzDJ;bb;lbhRKpY5)CdXIdD4= z((QxvNe!=@U-1KNf|y7Xg4{rH=IXb1O@tJoE0@!}Io|~q8Ls1#cJ>`pM%zwzkDP6b zYJ7vz?LL%!@%<;LqT&cxWZO@-PF-Tz#djw!+nzM8*h;wv9%IrgN**8IFYu%L9$Qta zc>2ZE+eoBf>2HB)1kdh#=($%m6Q;rYYBV1jp^63hhfTgY6#ePUG`^L?=E>nCH<^q7 OYj<^8?^t6WmicdEt+TfP literal 0 HcmV?d00001 diff --git a/Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png.meta b/Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png.meta new file mode 100644 index 00000000..f6478e8a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Icons/RuntimeRig@128.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: ca41524ec86f0d84ab5675c9622cc1a2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 1 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve.meta new file mode 100644 index 00000000..0f0fc6bd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afac13c76cd0741e4b5af60059fba0e8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs.meta new file mode 100644 index 00000000..f216e800 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f72a409037e7942fc86acf5dcd9f4716 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs new file mode 100644 index 00000000..b8e100b3 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs @@ -0,0 +1,130 @@ +using Unity.Collections; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The MultiAim inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiAimInverseConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadOnlyTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-rotation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// Local axis of the constrained object Transform. + public Vector3 aimAxis; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + var lRot = driven.GetLocalRotation(stream); + var offset = drivenOffset.Get(stream); + + if (Vector3.Dot(offset, offset) > 0f) + lRot *= Quaternion.Inverse(Quaternion.Euler(offset)); + + var localToWorld = Quaternion.identity; + var wPos = driven.GetPosition(stream); + if (drivenParent.IsValid(stream)) + localToWorld = drivenParent.GetRotation(stream); + + for (int i = 0; i < sourceTransforms.Length; ++i) + { + sourceWeights[i].SetFloat(stream, 1f); + + var sourceTransform = sourceTransforms[i]; + + sourceTransform.SetPosition(stream, wPos + localToWorld * sourceOffsets[i] * lRot * aimAxis); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + } + } + } + + /// + /// The MultiAim inverse constraint job binder. + /// + /// The constraint data type + public class MultiAimInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiAimConstraintData + { + /// + public override MultiAimInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiAimInverseConstraintJob(); + + job.driven = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + job.aimAxis = data.aimAxis; + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + for (int i = 0; i < sourceObjects.Count; ++i) + { + if (data.maintainOffset) + { + var aimDirection = data.constrainedObject.rotation * data.aimAxis; + var dataToSource = sourceObjects[i].transform.position - data.constrainedObject.position; + var rot = QuaternionExt.FromToRotation(dataToSource, aimDirection); + job.sourceOffsets[i] = Quaternion.Inverse(rot); + } + else + { + job.sourceOffsets[i] = Quaternion.identity; + } + } + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + return job; + } + + /// + public override void Destroy(MultiAimInverseConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs.meta new file mode 100644 index 00000000..7ee27566 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiAimInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05face438934c204d87b0e772c7c0a71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs new file mode 100644 index 00000000..90f5a507 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs @@ -0,0 +1,116 @@ +using Unity.Collections; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The MultiParent inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiParentInverseConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadOnlyTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + driven.GetGlobalTR(stream, out Vector3 currentWPos, out Quaternion currentWRot); + var drivenTx = new AffineTransform(currentWPos, currentWRot); + + for (int i = 0; i < sourceTransforms.Length; ++i) + { + sourceWeights[i].SetFloat(stream, 1f); + + var sourceTransform = sourceTransforms[i]; + sourceTransform.GetGlobalTR(stream, out var sourcePosition, out var sourceRotation); + + var result = drivenTx; + result *= sourceOffsets[i]; + + sourceTransform.SetGlobalTR(stream, result.translation, result.rotation); + sourceTransforms[i] = sourceTransform; + } + } + } + + /// + /// The MultiParent inverse constraint job binder. + /// + /// The constraint data type + public class MultiParentInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiParentConstraintData + { + /// + public override MultiParentInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiParentInverseConstraintJob(); + + job.driven = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + var drivenTx = new AffineTransform(data.constrainedObject.position, data.constrainedObject.rotation); + for (int i = 0; i < sourceObjects.Count; ++i) + { + var sourceTransform = sourceObjects[i].transform; + + var srcTx = new AffineTransform(sourceTransform.position, sourceTransform.rotation); + var srcOffset = AffineTransform.identity; + var tmp = srcTx.InverseMul(drivenTx); + + if (data.maintainPositionOffset) + srcOffset.translation = tmp.translation; + if (data.maintainRotationOffset) + srcOffset.rotation = tmp.rotation; + + srcOffset = srcOffset.Inverse(); + + job.sourceOffsets[i] = srcOffset; + } + + return job; + } + + /// + public override void Destroy(MultiParentInverseConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs.meta new file mode 100644 index 00000000..054f33f0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiParentInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 288c55302720e3e498d76fcd7909373d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs new file mode 100644 index 00000000..bcf99656 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs @@ -0,0 +1,116 @@ +using Unity.Collections; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The MultiPosition inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiPositionInverseConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadOnlyTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-translation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + var parentTx = new AffineTransform(); + if (drivenParent.IsValid(stream)) + { + drivenParent.GetGlobalTR(stream, out Vector3 parentWPos, out Quaternion parentWRot); + parentTx = new AffineTransform(parentWPos, parentWRot); + } + + var drivenPos = driven.GetPosition(stream); + drivenPos = parentTx.InverseTransform(drivenPos); + + var offset = drivenOffset.Get(stream); + + var lPos = drivenPos - offset; + + var wPos = parentTx.Transform(lPos); + for (int i = 0; i < sourceTransforms.Length; ++i) + { + sourceWeights[i].SetFloat(stream, 1f); + + ReadWriteTransformHandle sourceTransform = sourceTransforms[i]; + + sourceTransform.SetPosition(stream, wPos - sourceOffsets[i]); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + } + } + } + + /// + /// The MultiPosition inverse constraint job binder. + /// + /// The constraint data type + public class MultiPositionInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiPositionConstraintData + { + /// + public override MultiPositionInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiPositionInverseConstraintJob(); + + job.driven = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + Vector3 drivenPos = data.constrainedObject.position; + for (int i = 0; i < sourceObjects.Count; ++i) + { + job.sourceOffsets[i] = data.maintainOffset ? (drivenPos - sourceObjects[i].transform.position) : Vector3.zero; + } + + return job; + } + + /// + public override void Destroy(MultiPositionInverseConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs.meta new file mode 100644 index 00000000..e30caf5e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiPositionInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd9bd1ef777641d4b8ec163f15579bf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs new file mode 100644 index 00000000..34e4d762 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs @@ -0,0 +1,96 @@ +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; +using Unity.Collections; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The MultiReferential inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiReferentialInverseConstraintJob : IWeightedAnimationJob + { + /// The list of Transforms that are affected by the specified driver. + public NativeArray sources; + /// List of AffineTransform to apply to driven source objects. + public NativeArray offsetTx; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + sources[0].GetGlobalTR(stream, out Vector3 driverWPos, out Quaternion driverWRot); + var driverTx = new AffineTransform(driverWPos, driverWRot); + + int offset = 0; + for (int i = 1; i < sources.Length; ++i) + { + var tx = driverTx * offsetTx[offset]; + + var src = sources[i]; + src.GetGlobalTR(stream, out Vector3 srcWPos, out Quaternion srcWRot); + src.SetGlobalTR(stream, tx.translation, tx.rotation); + offset++; + + sources[i] = src; + } + } + } + + /// + /// The MultiReferential inverse constraint job binder. + /// + /// The constraint data type + public class MultiReferentialInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiReferentialConstraintData + { + /// + public override MultiReferentialInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiReferentialInverseConstraintJob(); + + var sources = data.sourceObjects; + job.sources = new NativeArray(sources.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.offsetTx = new NativeArray(sources.Length - 1, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + var sourceBindTx = new AffineTransform[sources.Length]; + + for (int i = 0; i < sources.Length; ++i) + { + job.sources[i] = ReadWriteTransformHandle.Bind(animator, sources[i].transform); + sourceBindTx[i] = new AffineTransform(sources[i].position, sources[i].rotation); + } + + int offset = 0; + var invDriverTx = sourceBindTx[0].Inverse(); + for (int i = 1; i < sourceBindTx.Length; ++i) + { + job.offsetTx[offset] = invDriverTx * sourceBindTx[i]; + offset++; + } + + return job; + } + + /// + public override void Destroy(MultiReferentialInverseConstraintJob job) + { + job.sources.Dispose(); + job.offsetTx.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs.meta new file mode 100644 index 00000000..64a5f693 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiReferentialInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07e9ec99cb928a0428b3391e587dfa9c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs new file mode 100644 index 00000000..46e8a95d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs @@ -0,0 +1,120 @@ +using Unity.Collections; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The MultiRotation inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiRotationInverseConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadOnlyTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-rotation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + var lRot = driven.GetLocalRotation(stream); + var offset = drivenOffset.Get(stream); + + if (Vector3.Dot(offset, offset) > 0f) + lRot *= Quaternion.Inverse(Quaternion.Euler(offset)); + + var wRot = lRot; + if (drivenParent.IsValid(stream)) + { + wRot = drivenParent.GetRotation(stream) * wRot; + } + + for (int i = 0; i < sourceTransforms.Length; ++i) + { + sourceWeights[i].SetFloat(stream, 1f); + + ReadWriteTransformHandle sourceTransform = sourceTransforms[i]; + + sourceTransform.SetRotation(stream, wRot * sourceOffsets[i]); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + } + } + } + + /// + /// The MultiRotation inverse constraint job binder. + /// + /// The constraint data type + public class MultiRotationInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiRotationConstraintData + { + /// + public override MultiRotationInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiRotationInverseConstraintJob(); + + job.driven = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + Quaternion drivenRotInv = Quaternion.Inverse(data.constrainedObject.rotation); + for (int i = 0; i < sourceObjects.Count; ++i) + { + job.sourceOffsets[i] = data.maintainOffset ? + (drivenRotInv * sourceObjects[i].transform.rotation) : Quaternion.identity; + } + + return job; + } + + /// + public override void Destroy(MultiRotationInverseConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs.meta new file mode 100644 index 00000000..cdb1f361 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/MultiRotationInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e68f0e2daa979074da79ea4fa6e40660 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs new file mode 100644 index 00000000..53ca0b4d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs @@ -0,0 +1,74 @@ +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The TwistChain inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct TwistChainInverseConstraintJob : IWeightedAnimationJob + { + /// The Transform handle for the root Transform of the chain. + public ReadOnlyTransformHandle root; + /// The Transform handle for the tip Transform of the chain. + public ReadOnlyTransformHandle tip; + + /// The Transform handle for the root target Transform. + public ReadWriteTransformHandle rootTarget; + /// The Transform handle for the tip target Transform. + public ReadWriteTransformHandle tipTarget; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + rootTarget.SetPosition(stream, root.GetPosition(stream)); + rootTarget.SetRotation(stream, root.GetRotation(stream)); + + tipTarget.SetPosition(stream, tip.GetPosition(stream)); + tipTarget.SetRotation(stream, tip.GetRotation(stream)); + } + } + + /// + /// The TwistChain inverse constraint job binder. + /// + /// The constraint data type + public class TwistChainInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, ITwistChainConstraintData + { + /// + public override TwistChainInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new TwistChainInverseConstraintJob(); + + job.root = ReadOnlyTransformHandle.Bind(animator, data.root); + job.tip = ReadOnlyTransformHandle.Bind(animator, data.tip); + + job.rootTarget = ReadWriteTransformHandle.Bind(animator, data.rootTarget); + job.tipTarget = ReadWriteTransformHandle.Bind(animator, data.tipTarget); + + return job; + } + + /// + public override void Destroy(TwistChainInverseConstraintJob job) + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs.meta new file mode 100644 index 00000000..8435ed39 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwistChainInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c669908f1bcf4a829cb78b6398d008f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs new file mode 100644 index 00000000..5eadd5ed --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs @@ -0,0 +1,99 @@ +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// The TwoBoneIK inverse constraint job. + /// + [Unity.Burst.BurstCompile] + public struct TwoBoneIKInverseConstraintJob : IWeightedAnimationJob + { + const float k_SqrEpsilon = 1e-8f; + + /// The transform handle for the root transform. + public ReadOnlyTransformHandle root; + /// The transform handle for the mid transform. + public ReadOnlyTransformHandle mid; + /// The transform handle for the tip transform. + public ReadOnlyTransformHandle tip; + + /// The transform handle for the hint transform. + public ReadWriteTransformHandle hint; + /// The transform handle for the target transform. + public ReadWriteTransformHandle target; + + /// The offset applied to the target transform if maintainTargetPositionOffset or maintainTargetRotationOffset is enabled. + public AffineTransform targetOffset; + + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty targetPositionWeight; + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty targetRotationWeight; + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty hintWeight; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + jobWeight.Set(stream, 1f); + + AnimationRuntimeUtils.InverseSolveTwoBoneIK(stream, root, mid, tip, target, hint, + targetPositionWeight.Get(stream), + targetRotationWeight.Get(stream), + hintWeight.Get(stream), targetOffset); + } + } + + /// + /// The TwoBoneIK inverse constraint job binder. + /// + /// The constraint data type + public class TwoBoneIKInverseConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, ITwoBoneIKConstraintData + { + /// + public override TwoBoneIKInverseConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new TwoBoneIKInverseConstraintJob(); + + job.root = ReadOnlyTransformHandle.Bind(animator, data.root); + job.mid = ReadOnlyTransformHandle.Bind(animator, data.mid); + job.tip = ReadOnlyTransformHandle.Bind(animator, data.tip); + job.target = ReadWriteTransformHandle.Bind(animator, data.target); + + if (data.hint != null) + job.hint = ReadWriteTransformHandle.Bind(animator, data.hint); + + job.targetOffset = AffineTransform.identity; + if (data.maintainTargetPositionOffset) + job.targetOffset.translation = -(data.tip.position - data.target.position); + if (data.maintainTargetRotationOffset) + job.targetOffset.rotation = Quaternion.Inverse(data.tip.rotation) * data.target.rotation; + + job.targetPositionWeight = FloatProperty.Bind(animator, component, data.targetPositionWeightFloatProperty); + job.targetRotationWeight = FloatProperty.Bind(animator, component, data.targetRotationWeightFloatProperty); + job.hintWeight = FloatProperty.Bind(animator, component, data.hintWeightFloatProperty); + + return job; + } + + /// + public override void Destroy(TwoBoneIKInverseConstraintJob job) + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs.meta new file mode 100644 index 00000000..21080772 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/AnimationJobs/TwoBoneIKInverseConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97b56b4e0dccdff47b19d995fcc12954 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints.meta new file mode 100644 index 00000000..d93fda17 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 63607c45d964e47a18e3197ffb465217 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs new file mode 100644 index 00000000..d795330e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs @@ -0,0 +1,24 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// MultiAim inverse constraint. + /// + /// + [InverseRigConstraint(typeof(MultiAimConstraint))] + public class MultiAimInverseConstraint : OverrideRigConstraint< + MultiAimConstraint, + MultiAimInverseConstraintJob, + MultiAimConstraintData, + MultiAimInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public MultiAimInverseConstraint(MultiAimConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs.meta new file mode 100644 index 00000000..e11e31a7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiAimInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55179714ce28f4fad97dbe8831d7b7b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs new file mode 100644 index 00000000..512abf00 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// MultiParent inverse constraint. + /// + /// + [InverseRigConstraint(typeof(MultiParentConstraint))] + public class MultiParentInverseConstraint : OverrideRigConstraint< + MultiParentConstraint, + MultiParentInverseConstraintJob, + MultiParentConstraintData, + MultiParentInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public MultiParentInverseConstraint(MultiParentConstraint baseConstraint) : base(baseConstraint) { } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs.meta new file mode 100644 index 00000000..06aa20c7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiParentInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0bc68cc00b8ce547b1cc869a707c8b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs new file mode 100644 index 00000000..ac25e6f6 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs @@ -0,0 +1,24 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// MultiPosition inverse constraint. + /// + /// + [InverseRigConstraint(typeof(MultiPositionConstraint))] + public class MultiPositionInverseConstraint : OverrideRigConstraint< + MultiPositionConstraint, + MultiPositionInverseConstraintJob, + MultiPositionConstraintData, + MultiPositionInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public MultiPositionInverseConstraint(MultiPositionConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs.meta new file mode 100644 index 00000000..3c93a918 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiPositionInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f565b137dff19490c925f69f8fbfb243 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs new file mode 100644 index 00000000..1f3157bd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs @@ -0,0 +1,23 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// MultiReferential inverse constraint. + /// + [InverseRigConstraint(typeof(MultiReferentialConstraint))] + public class MultiReferentialInverseConstraint : OverrideRigConstraint< + MultiReferentialConstraint, + MultiReferentialInverseConstraintJob, + MultiReferentialConstraintData, + MultiReferentialInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public MultiReferentialInverseConstraint(MultiReferentialConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs.meta new file mode 100644 index 00000000..a645ef5c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiReferentialInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 246edcdada8bcb14cb045a083c910774 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs new file mode 100644 index 00000000..2cb82afa --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs @@ -0,0 +1,24 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// MultiRotation inverse constraint. + /// + /// + [InverseRigConstraint(typeof(MultiRotationConstraint))] + public class MultiRotationInverseConstraint : OverrideRigConstraint< + MultiRotationConstraint, + MultiRotationInverseConstraintJob, + MultiRotationConstraintData, + MultiRotationInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public MultiRotationInverseConstraint(MultiRotationConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs.meta new file mode 100644 index 00000000..38437729 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/MultiRotationInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34672f6d93a624041a3e076be94601e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs new file mode 100644 index 00000000..c522050c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs @@ -0,0 +1,24 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// TwistChain inverse constraint. + /// + /// + [InverseRigConstraint(typeof(TwistChainConstraint))] + public class TwistChainInverseConstraint : OverrideRigConstraint< + TwistChainConstraint, + TwistChainInverseConstraintJob, + TwistChainConstraintData, + TwistChainInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public TwistChainInverseConstraint(TwistChainConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs.meta new file mode 100644 index 00000000..26ea90a1 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwistChainInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84300ca1d515442388c563e2508c8745 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs new file mode 100644 index 00000000..14a150de --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs @@ -0,0 +1,24 @@ +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// TwoBone IK inverse constraint. + /// + /// + [InverseRigConstraint(typeof(TwoBoneIKConstraint))] + public class TwoBoneIKInverseConstraint : OverrideRigConstraint< + TwoBoneIKConstraint, + TwoBoneIKInverseConstraintJob, + TwoBoneIKConstraintData, + TwoBoneIKInverseConstraintJobBinder + > + { + /// + /// Constructor. + /// + /// Base constraint to override. + public TwoBoneIKInverseConstraint(TwoBoneIKConstraint baseConstraint) : base(baseConstraint) {} + } +} + diff --git a/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs.meta b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs.meta new file mode 100644 index 00000000..5a973df1 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/InverseSolve/Constraints/TwoBoneIKInverseConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62e695bb417d14bdf9e0b26e15e8617c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shaders.meta b/Packages/com.unity.animation.rigging/Editor/Shaders.meta new file mode 100644 index 00000000..a2a9407f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6549b8b30f0b56746bf9d9a2f6061663 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader b/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader new file mode 100644 index 00000000..28223fc5 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader @@ -0,0 +1,67 @@ +Shader "Hidden/BoneHandles" +{ + Properties + { + _Color ("Color", Color) = (1,1,1,1) + } + SubShader + { + Tags { "Queue" = "Transparent" "RenderType"="Transparent" "ForceSupported" = "True" } + + Lighting Off + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Cull Back + Fog { Mode Off } + ZTest Always + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_instancing + #pragma multi_compile __ WIRE_ON + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f + { + float4 vertex : SV_POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + UNITY_INSTANCING_BUFFER_START(Props) + UNITY_DEFINE_INSTANCED_PROP(float4, _Color) + UNITY_INSTANCING_BUFFER_END(Props) + + v2f vert (appdata v) + { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_TRANSFER_INSTANCE_ID(v, o); + o.vertex = UnityObjectToClipPos(v.vertex); + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(i); + fixed4 col = UNITY_ACCESS_INSTANCED_PROP(Props, _Color); + + #ifdef WIRE_ON + col.a = 1.0f; + #endif + + return col; + } + ENDCG + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader.meta b/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader.meta new file mode 100644 index 00000000..71e18c37 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shaders/BoneHandles.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 55fcd8cae15234dbeba59eb9603e7779 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader b/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader new file mode 100644 index 00000000..5828a97a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader @@ -0,0 +1,66 @@ +Shader "Hidden/RigEffector" +{ + Properties + { + _Color ("Color", Color) = (1,1,1,1) + _Dither ("Dithering", float) = 0 + _HandleZTest ("_HandleZTest", Int) = 8 + } + + SubShader + { + Tags + { + "IgnoreProjector"="True" "RenderType"="Transparent" + } + Lighting Off + ZTest [_HandleZTest] + ZWrite Off + Cull Off + Blend SrcAlpha OneMinusSrcAlpha + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + + float4 _Color; + float _Dither; + + struct appdata + { + float4 vertex : POSITION; + }; + + struct v2f + { + float4 pos : SV_POSITION; + }; + + v2f vert (appdata v) + { + v2f o; + + // https://www.opengl.org/discussion_boards/showthread.php/166719-Clean-Wireframe-Over-Solid-Mesh + o.pos = float4(UnityObjectToViewPos(v.vertex.xyz), 1); + o.pos.xyz *= .99; + o.pos = mul(UNITY_MATRIX_P, o.pos); + + return o; + } + + half4 frag (v2f i) : COLOR + { + i.pos.xy = floor(i.pos.xy * 1) * .5; + float checker = -frac(i.pos.x + i.pos.y); + clip(lerp(1, checker, _Dither)); + + return _Color; + } + + ENDCG + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader.meta b/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader.meta new file mode 100644 index 00000000..dc7a189d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shaders/RigEffector.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 48843bad95d12480bb1ee7af0ef00b87 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes.meta b/Packages/com.unity.animation.rigging/Editor/Shapes.meta new file mode 100644 index 00000000..db94c9f9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d2ea9e88bb7d74ffd85330b997e5273c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset b/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset new file mode 100644 index 00000000..973a0eb5 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset @@ -0,0 +1,195 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!43 &4300000 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BallEffector + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 2304 + topology: 0 + baseVertex: 0 + firstVertex: 0 + vertexCount: 515 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + - serializedVersion: 2 + firstByte: 4608 + indexCount: 33 + topology: 4 + baseVertex: 0 + firstVertex: 515 + vertexCount: 32 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0, z: 0.5} + - serializedVersion: 2 + firstByte: 4674 + indexCount: 33 + topology: 4 + baseVertex: 0 + firstVertex: 547 + vertexCount: 32 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0} + - serializedVersion: 2 + firstByte: 4740 + indexCount: 33 + topology: 4 + baseVertex: 0 + firstVertex: 579 + vertexCount: 32 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0.5, z: 0.5} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 0 + m_KeepIndices: 0 + m_IndexFormat: 0 + m_IndexBuffer: b1008201b200ae00b100b200ad00b100ae00ad00b000b100b0006c00b100b1006c006d00b2006d000800cd006d006c00ae00b200af00af00b2006400b20083016400aa00ad00ae00ab00ae00af00aa00ae00ab003000b000ad002f003000ad002f00ad00aa0000002f00aa000000aa001a001a00aa00ab0030003100b0001a00ab001b0031006b00b000310011006b00b0006b006c001b00ab00ac00ab00af00ac001b00ac001c00c7006b001100c70011002e00ca006c006b00ca006b00c700cd006c00ca00ac00af006300af00640063001c00ac006200ac00630062001c0062000e00c600c7002e00c6002e002d00c900ca00c700c900c700c6000e006200b9000e00b9001f0062006300ba006200ba00b90063006400bb006300bb00ba00c500c6002d00c5002d002c001f00b900b6001f00b6001e0064006700bb00640084016700bb0067006600bb006600b800ba00bb00b800b80066006500b900ba00b700ba00b800b700b900b700b600b8006500b500b700b800b500b50065000f00b5000f002200b700b500b400b400b5002200b600b700b400b40022002100b600b400b300b300b40021001e00b600b300b300210020001e00b3001d001d00b30020001d00200002006a008b016d00c4008a016a00c100c4006a00c1006a00690069006a00cd006a008c01cd006900cd00cc00cc00cd00ca00cc00ca00c90068006900cc00cb00cc00c9006800cc00cb00be00c1006900be0069006800cb00c900c800c800c900c600c800c600c50010006800cb002800680010002800be0068001000cb002b002b00cb00c8002b00c8002a002a00c800c5002a00c50029002900c5002c0029002c0001002700be0028002700bd00be002600bd002700bd00c100be002600bc00bd000300bc00260003002300bc00bd00c000c100bc00c000bd00c000c400c1002300bf00bc00bc00bf00c00023002400bf00c000c300c400bf00c300c0002400c200bf00bf00c200c30024002500c200c3008801c400c40088018901c30087018801c2008701c300c2008601870125008601c200250085018601d60079000900d5007900d600d200d500d600d200d600d300d300d6007000d6008d017000d50078007900d100d500d200d4007800d500d100d400d500cf00d200d300ce00d100d200ce00d200cf004800d400d10047004800d1004700d100ce0004004700ce000400ce0032003200ce00cf0048004900d4003200cf00330049007700d400d400770078004900150077003300cf00d000cf00d300d0003300d0003400eb0077001500eb0015004600ee0078007700ee007700eb00d000d3006f00d30070006f003400d0006e00d0006f006e0034006e001200ea00eb004600ea004600450012006e00dd001200dd0037006e006f00de006e00de00dd006f007000df006f00df00de003700dd00da003700da003600dd00de00db00dd00db00da003600da00d7003600d70035003500d7003800350038000500d70039003800da00d800d700d700d8003900da00db00d800d8003a003900d800d9003a00db00d900d800d90013003a00de00dc00db00db00dc00d900de00df00dc00d90071001300dc007100d9003d0013007100df007200dc00dc00720071003d007100e600e600710072003c003d00e600df007300720070007300df0070008e0173003c00e600e3003b003c00e300e6007200e700e70072007300e300e600e7003b00e300e00007003b00e0000700e0003e00e7007300e800e80073008f01e300e700e400e000e300e400e400e700e8003e00e000e100e000e400e1003e00e1003f00e400e800e500e100e400e5003f00e100e200e100e500e2003f00e2004000e500e8007600e80090017600e50076007500e200e5007500e200750074004000e20074004000740014004100440006004100e9004400e900450044004200e9004100e900ea0045004200ec00e900ec00ea00e9004300ec004200ec00ed00ea00ed00eb00ea00ed00ee00eb004300ef00ec00ef00ed00ec009101ef00430091019201ef00ef00f000ed009201f000ef00f000ee00ed0092019301f000f000f100ee009301f100f000f1007800ee0093019401f100f1007900780094017900f1009401950179009b014a009c019b01fb004a009d01fb009b01fb004b004a009d01fe00fb009e01fe009d01fb00fc004b00fe00fc00fb00fc004c004b009e010101fe009a0101019e01fc00fd004c00fd0016004c00fe00ff00fc000101ff00fe00ff00fd00fc009a017a00010199017a009a01fd007d0016004f0016007d00ff000001fd0000017d00fd0001010201ff007a000201010102010001ff009901f4007a009801f40099014f007d000a014e004f000a017a007b000201f4007b007a0000017e007d000a017d007e000201030100017b000301020103017e0000019801f300f4009701f30098014e000a0107014d004e000701f400f7007b00f300f700f4007b007c000301f7007c007b0003017f007e007c007f0003010a017e000b0107010a010b010b017e007f009701f200f3009601f200970196015300f200f300f600f700f200f600f3005300f500f200f200f500f60053005400f500f700fa007c00f600fa00f7007c000a007f00fa000a007c000b017f000c010c017f000a005400f800f50054005500f800f500f900f600f600f900fa00f500f800f900fa0085000a00f9008500fa0055008300f800550017008300f8008400f900f90084008500f8008300840082000a0085000c010a00820008010b010c0107010b0108010f01830017000f0117005200120184008300120183000f0115018500840082008500150115018400120109010c01820008010c0109010401070108014d00070104019f014d0004019f010401a001040108010501a00104010501050108010901a0010501a101a10105010601050109010601a1010601a201090182008100060109018100810082001501a20106018000060181008000a2018000a301810015011401800081001401140115011201a30180001301800014011301a3011301a701140112011101130114011101110112010f01a70113011001130111011001a7011001a60111010f010e01100111010e010e010f0152000e0152005100a60110010d0110010e010d010d010e015100a6010d01a4010d0151005000a4010d015000a4015000a501b1015600b201b1011f015600b3011f01b1011f0157005600b30122011f01b4012201b3011f0120015700220120011f01200158005700b40125012201ad012501b401200121015800210118005800220123012001250123012201230121012001ad0186002501ac018600ad012101890018005b0018008900230124012101240189002101250126012301860026012501260124012301ac0118018600ab011801ac015b0089002e015a005b002e0186008700260118018700860024018a0089002e0189008a0026012701240187002701260127018a002401ab0117011801aa011701ab015a002e012b0159005a002b0118011b01870017011b0118018700880027011b018800870027018b008a0088008b0027012e018a002f012b012e012f012f018a008b00aa0116011701a8011601aa01a801a901160117011a011b0116011a011701a90119011601160119011a01a901ae0119011b011e0188001a011e011b0188000b008b001e010b0088002f018b00300130018b000b00ae011c011901ae01af011c0119011d011a011a011d011e0119011c011d011e0191000b001d0191001e01af018f001c01af01b0018f001c0190001d011d01900091001c018f0090008e000b00910030010b008e002c012f0130012b012f012c0133018f00b0013301b001be01360190008f0036018f0033013901910090008e00910039013901900036012d0130018e002c0130012d0128012b012c0159002b012801b50159002801b5012801b60128012c012901b6012801290129012c012d01b6012901b701b70129012a0129012d012a01b7012a01b8012d018e008d002a012d018d008d008e003901b8012a018c002a018d008c00b8018c00b9018d00390138018c008d003801380139013601b9018c0037018c0038013701b9013701c001380136013501370138013501350136013301c00137013401370135013401c0013401bf0135013301320134013501320132013301be013201be01bd01bf013401310134013201310131013201bd01bf013101ba013101bd01bb01ba013101bb01ba01bb01bc01c5019200c601c5013c019200c4013c01c5013c0193009200c4013b013c01c3013b01c4013c013f0193003b013f013c013f0194009300c3013a013b01c1013a01c301c101c2013a013b013e013f013a013e013b013f01420194003e0142013f0142010c009400c2013d013a013a013d013e01c201c7013d013e01410142013d0141013e0142019d000c0041019d004201e2010c009d00c70140013d013d0140014101c701c8014001e2019d005d01e101e2015d0141019c009d0040019c0041015d019d009c00c8019b00400140019b009c00c801c9019b00e1015d015c01e001e1015c015d019c005a015a019c009b005c015d015a0157019b00c9015a019b0057015701c901dc01e0015c015b01df01e0015b01df015b01de015c015a01590159015a0157015b015c01590156015701dc015901570156015601dc01db01de015b0158015b0159015801580159015601de015801dd0155015601db01580156015501dd01580155015501db01d901dd015501d801d8015501d901d801d901da01ca015c00cb01ca0143015c00cc014301ca0143015d005c00cc0146014301cd014601cc01430144015d0046014401430144015e005d00cd0149014601ce014901cd01ce01cf014901440145015e00450119005e00460147014401490147014601470145014401cf014a01490149014a014701cf01d0014a014501950019006100190095004701480145014a0148014701480195004501d0014b014a014a014b014801d001d1014b016100950052016000610052014801960095004b0196004801520195009600d10197004b014b0197009600d101d2019700600052014f015f0060004f015201960053015301960097004f015201530154019700d2015301970054015401d2019a005f004f014c01d3015f004c01d3014c01d4014f01530150015001530154014c014f015001510154019a0050015401510151019a009900d4014c014d014c0150014d014d0150015101d4014d01d5014e01510199004d0151014e01d5014d014e014e0199009800d5014e01d601d6014e019800d6019800d701ec01ed01ee01ec016701ed01f2016701ec016701ef01ed01f2016a016701f3016a01f20167016801ef016a01680167016801f001ef01f3016d016a01e8016d01f30168016901f0016901f101f0016a016b0168016d016b016a016b0169016801e8019e006d01e7019e00e8016901a100f101fb01f101a1006b016c0169016c01a10069016d016e016b019e006e016d016e016c016b01e70160019e00e6016001e701fb01a1007601fa01fb0176019e009f006e0160019f009e006c01a200a1007601a100a2006e016f016c019f006f016e016f01a2006c01e6015f016001e5015f01e601fa0176017301f501fa017301600163019f005f01630160019f00a0006f016301a0009f006f01a300a200a000a3006f017601a20077017301760177017701a200a300e5015e015f01e3015e01e501e301e4015e015f01620163015e0162015f01e40161015e015e0161016201e401e901610163016601a000620166016301a0000d00a30066010d00a0007701a30078017801a3000d00e90164016101e901ea0164016101650162016201650166016101640165016601a9000d006501a9006601ea01a7006401ea01eb01a7006401a80065016501a800a9006401a700a800a6000d00a90078010d00a6007401770178017301770174017b01a700eb017b01eb0100027e01a800a7007e01a7007b018101a900a800a600a90081018101a8007e0175017801a600740178017501700173017401f50173017001f401f5017001f4017001f601700174017101f60170017101710174017501f6017101f701f70171017201710175017201f7017201f8017501a600a50072017501a500a500a6008101f8017201a4007201a500a400f801a400f901a50081018001a400a5008001800181017e01f901a4007f01a40080017f01f9017f01020280017e017d017f0180017d017d017e017b0102027f017c017f017d017c0102027c0101027d017b017a017c017d017a017a017b0100027a010002ff0101027c0179017c017a01790179017a01ff0101027901fc017901ff01fd01fc017901fd01fc01fd01fe0103020402050206020702080209020a020b020c020d020e020f0210021102120213021402150216021702180219021a021b021c021d021e021f02200221022202030223022402250226022702280229022a022b022c022d022e022f0230023102320233023402350236023702380239023a023b023c023d023e023f02400241024202230243024402450246024702480249024a024b024c024d024e024f0250025102520253025402550256025702580259025a025b025c025d025e025f026002610262024302 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 611 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 12 + format: 0 + dimension: 3 + - stream: 0 + offset: 24 + format: 0 + dimension: 4 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 40 + format: 0 + dimension: 2 + - stream: 0 + offset: 48 + format: 0 + dimension: 2 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 34216 + _typelessdata: 145e933e145e93be145e93be3acd133f3acd13bf3bcd13bf0bd2343fce4ccbbab037353f000080bf9e62bf3efce9a23e263ab13e2e3ab13e145e933e145e93be145e933e3acd133f3acd13bf3bcd133fb33b35bfcd5fdbba02ce343f000080bf1ca51f3ffce9a23e263ab13ef262273f145e93be145e93be145e93be3acd13bf3acd13bf3bcd13bfac3b353f4543dbba09ce34bf000080bf20ecfd3dfce9a23ef662273f2e3ab13e145e93be145e93be145e933e3bcd13bf3acd13bf3acd133f06d234bfa95ecbbab63735bf000080bfe6985f3ffce9a23ef662273ff262273f3de2933eae67933e3de293bec6b9133f1bf4133fc6b913bf672f353fa0c8a9ba61da343f000080bf9e62bf3e00012f3f223a313fbf8b9d3e3de2933eae67933e3de2933ec6b9133f1bf4133fc6b9133ff3d334bf6063c3bacc35353f000080bf1ca51f3f00012f3f223a313f0000a0343de293beae67933e3de293bec6b913bf1bf4133fc6b913bff8d3343fba50c3bac73535bf000080bf20ecfd3d01012f3f0000803fbf8b9d3e3de293beae67933e3de2933ec6b913bf1bf4133fc6b9133f672f35bf05c6a9ba62da34bf000080bfe6985f3f00012f3f0000803f0000a03400000000000000bf0000000000000000000080bf00000000aab579be000000008c45783f000080bfe835bf3e0639b23c0c00003f0900003f33b3a3310000003f0ad7a3b1000000000000803f0000000060757b3f00000000a10640be000080bf4202c53e92c5783f1c9d583fc48b1d3e0000003f33b3a3b1000030b45cfe7f3f5244e6bb9fa06fbac19a753acb78543ba0ff7f3f000080bf6a56ff3eff3a003fc18b1d3e908b1d3eaeffaf337bd823b20000003f00000000d648debb7efe7f3f000080bf0b080db8abeb74b4000080bf029f3f3fff3a003ffbffff3e808b1d3e000000bf33b3a331000000005cfe7fbf0647e6bb86976fbac3ac753af73a58bb9dff7fbf000080bfcc927f3fff3a003ff68b1d3ef4ffff3e000070b4eb51b82a000000bf000000004c42eebb44fe7fbf0000803f4fb6d3b7100b4534000080bfa6dd7e3eff3a003f169d583fecffff3e006065b3cd7cb4becd7cb4be00000000f30435bff30435bf84ff7f3f4c83323b4d8332bb000080bfa6dd7e3ecce38d3e0c00003f2e3ab13ecd7cb4becd7cb4be00000000f30435bff40435bf000000006d3aa43a6d3aa4bae6ff7fbf000080bf008871bacce38d3ef662273f0900003f8fc27533cd7cb4becd7cb43e00000000f10435bff504353f84ff7fbf4283323b3f83323b000080bf029f3f3fcce38d3e0c00003ff262273fcd7cb43ecd7cb4be0ad72333f404353ff30435bf00000000d937a4badb37a4bae6ff7f3f000080bf6a56ff3ecce38d3e263ab13e0900003f0a17b53ea4a0b43e8fc2f5b36d0b353f7afe343f00000000309051bb309f513b54ff7f3f000080bf6a56ff3e1784393f223a313fc48b1d3e8fc27533a4a0b43e0a17b53e000000007afe343f6d0b353fd5ff7fbfd8fdd1bad1eed13a000080bf029f3f3f1784393f1c9d583f0000a0340a17b5bea4a0b43e000000006d0b35bf7afe343f00000000be93513bbea2513b54ff7fbf000080bfcc927f3f1784393f0000803fc48b1d3ec2f528b4a4a0b43e0a17b5be000000007afe343f6d0b35bfd5ff7f3f36fdd1ba2feed1ba000080bfa6dd7e3e1784393f1c9d583fbf8b9d3ecd04b53e999909aecd04b53e1003353f2ab913bc1103353fef0435bfcf913e38f804353f000080bf1ca51f3fff3a003fc08b1d3e000000000a17b53e85a467ba0a17b5bea402353fd37423bca40235bff904353f2eef3d38ee04353f000080bf9e62bf3eff3a003fc18b1d3ec08b9d3ecd04b5be9999092ecd04b53e110335bf2ab913bc1003353ff80435bf208f3e38ef0435bf000080bfe6985f3fff3a003ffbffff3e000000340a17b5be2ba267ba0a17b5bea50235bfb27423bca40235bfed04353f720f3e38f90435bf000080bf20ecfd3dff3a003ff58b1d3e263ab13e1ef5753e52289ebe52289ebe4309f63ec5bf1ebfc5bf1ebfb8c6453f508210bdfa4a223f000080bfce3fb53e8e109c3eabebc43e2e3ab13e99392e3e66a6a9be66a6a9be69a4ae3ec8292abfc7292abf1163663fc938da3cf7cfde3e000080bf400fa63eccc4943e079dd83e2e3ab13ef558b43d14a6b1be14a6b1be082e353ed22932bfd22932bf12fc783f7c227a3c15916d3e000080bfe7ab933e00b18f3e884eec3e2e3ab13e1ef575be52289ebe52289ebe5809f6becbbf1ebfb7bf1ebf5080493f5b5cdf3b9fe41dbf000080bfb03b133e8e109c3e248a1d3f2e3ab13e99392ebe66a6a9be66a6a9be71a4aebed5292abfb6292abf06c4623f44141a3cd68cedbe000080bfcc9c313eccc4943e68b1133f2e3ab13ef558b4bd14a6b1be14a6b1beeb2d35bed82932bfcf2932bf60c2783f2ab736bcfa8c71be000080bf7a63563e00b18f3e9cd8093f2e3ab13e52289ebe52289ebe1ef575bec5bf1ebfc5bf1ebf4309f6be67101e3fd16e01bc505d49bf000080bfd860d53d90109c3ef662273f8eebc43e66a6a9be66a6a9be99392ebec7292abfc8292abf69a4aebeff23ed3ee96209bc1ee062bf000080bfa49e983dcac4943ef662273f2e9dd83e14a6b1be14a6b1bef558b4bdd22932bfd22932bf082e35befe0a703eb340503c78d878bf000080bf80221e3d00b18f3ef662273fd24eec3e52289ebe52289ebe1ef5753eb7bf1ebfcbbf1ebf5809f63eaa2022bf5e050c3da4ec45bf000080bf4eaa643f90109c3ef662273f3d8a1d3f66a6a9be66a6a9be99392e3eb6292abfd5292abf71a4ae3ea34edfbe637dd0bca74666bf000080bf96426c3fccc4943ef662273f7eb1133f14a6b1be14a6b1bef558b43dcf2932bfd82932bfeb2d353e0b0e6fbe61f660bcc7e678bf000080bf4474753f00b18f3ef662273fc2d8093f1ef575be52289ebe52289e3e4309f6bec5bf1ebfc5bf1e3fb6c645bfa38210bdfe4a22bf000080bf7d875a3f8e109c3e248a1d3ff262273f99392ebe66a6a9be66a6a93e63a4aebec6292abfca292a3f136366bfd638da3ceccfdebe000080bf38ef523fccc4943e68b1133ff262273ff558b4bd14a6b1be14a6b13efe2d35bed02932bfd629323f11fc78bf16217a3c19916dbe000080bf8abd493f00b18f3e9cd8093ff262273f1ef5753e52289ebe52289e3e5809f63ecbbf1ebfb7bf1e3f528049bf4b5adf3b9ce41d3f000080bf83b6243f8e109c3eabebc43ef262273f99392e3e66a6a9be66a6a93e6aa4ae3ed3292abfb9292a3f07c462bfde141a3ccf8ced3e000080bfcc4e2c3fccc4943e079dd83ef262273ff558b43d14a6b1be14a6b13ee22d353ed42932bfd329323f60c278bf6cb636bcf58c713e000080bf7680353f00b18f3e884eec3ef262273f52289e3e52289ebe1ef5753ec5bf1e3fc5bf1ebf4309f63e61101ebf776c01bc545d493f000080bfb3931a3f8e109c3e263ab13e3d8a1d3f66a6a93e66a6a9be99392e3ec7292a3fc8292abf69a4ae3efc23edbe7d6209bc1fe0623f000080bf6bfb123fcac4943e263ab13e7eb1133f14a6b13e14a6b1bef558b43dd329323fd22932bf092e353e000b70be9840503c78d8783f000080bfbfc9093f00b18f3e263ab13ec2d8093f52289e3e52289ebe1ef575bec5bf1e3fc5bf1ebf4309f6be9a20223f7a050c3db1ec453f000080bf7085c93e8e109c3e263ab13e8eebc43e66a6a93e66a6a9be99392ebec7292a3fc8292abf69a4aebe8f4edf3efa7cd0bcac46663f000080bffeb5d83eccc4943e263ab13e2e9dd83e14a6b13e14a6b1bef558b4bdd229323fd32932bf092e35be100e6f3e7cf860bcc7e6783f000080bf5519eb3e00b18f3e263ab13ed24eec3eebb19e3ef6409e3e00b076be1fb21e3f6dd61e3ffff1f5be958f1a3f532a633c810b4c3f000080bf7085c93eb66d323f223a313f56da893eeb39aa3e85c3a93e5ccf2ebef1222a3fa5262a3f40cbaebe06cde53ec1c7263c4dc0643f000080bffeb5d83e9813363f223a313fc6516c3ec23db23eaec7b13e33f3b4bd6a30323f7a24323f451a35beb638753e7d6eec3b538b783f000080bf5519eb3e7e9d383f223a313fc0ee443eebb19e3ef6409e3e00b0763e1fb21e3f6dd61e3ffff1f53eead71abf51d646bc7dd64b3f000080bfb3931a3fb66d323f223a313f808b1d3deb39aa3e85c3a93e5ccf2e3eec222a3fa7262a3f47cbae3e41ffe6be9db0edbb0375643f000080bf6bfb123f9813363f223a313f208c9d3dc23db23eaec7b13e33f3b43d6730323f7f24323f481a353e94107abe07980fbbaa3f783f000080bfbfc9093f7e9d383f223a313f1052ec3d00b0763ef6409e3eebb19e3efff1f53e6dd61e3f1fb21e3fe4b64bbf0505363cc3021b3f000080bf83b6243fb66d323fe4123b3f0000a0345ccf2e3e85c3a93eeb39aa3e47cbae3ea7262a3fec222a3fe19564bf4bb40b3c217ae63e000080bfcc4e2c3f9813363f8ceb443f0000a03433f3b43daec7b13ec23db23e4d1a353e7e24323f6730323f6f5878bfe1eb793b9580783e000080bf7680353f7e9d383f52c44e3f0000a03400b076bef6409e3eebb19e3efff1f5be6dd61e3f1fb21e3fad294cbfb35873bc2e661abf000080bf7d875a3fb66d323f3427763f0000a0345ccf2ebe85c3a93eeb39aa3e47cbaebea7262a3fec222a3f36a064bf8e4812bc1b50e6be000080bf38ef523f9813363f824e6c3f0000a03433f3b4bdaec7b13ec23db23e491a35be7f24323f6730323f927378bfd903b8bbd5c376be000080bf8abd493f7e9d383fc475623f0000a034ebb19ebef6409e3e00b0763e1fb21ebf6dd61e3ffff1f53e968f1abf002a633c810b4cbf000080bf50aa643fb66d323f0000803f808b1d3deb39aabe85c3a93e5ccf2e3eec222abfa7262a3f47cbae3e09cde5be46c9263c4cc064bf000080bf96426c3f9813363f0000803f208c9d3dc23db2beaec7b13e33f3b43d683032bf7f24323f4d1a353eca3875be966dec3b528b78bf000080bf4474753f7e9d383f0000803f1052ec3debb19ebef6409e3e00b076be1fb21ebf6dd61e3ffff1f5bee8d71a3f53d746bc7ed64bbf000080bfd860d53db66d323f0000803f56da893eeb39aabe85c3a93e5ccf2ebeec222abfa7262a3f47cbaebe3fffe63e27b1edbb047564bf000080bfa49e983d9813363f0000803fc6516c3ec23db2beaec7b13e33f3b4bd683032bf7f24323f4d1a35be9b107a3ec3970fbbaa3f78bf000080bf80221e3d7e9d383f0000803fc0ee443e00b076bef6409e3eebb19ebefff1f5be6dd61e3f1fb21ebfe4b64b3f0405363cc3021bbf000080bfb23b133eb66d323f3427763fbf8b9d3e5ccf2ebe85c3a93eeb39aabe3acbaebea8262a3fee222abfe495643f90b30b3c167ae6be000080bfcc9c313e9813363f824e6c3fbf8b9d3e33f3b4bdaec7b13ec23db2be3e1a35be7f24323f683032bf6f58783f68e7793b918078be000080bf7a63563e7e9d383fc475623fbf8b9d3e00b0763ef6409e3eebb19ebefff1f53e6dd61e3f1fb21ebfae294c3f0e5973bc2d661a3f000080bfce3fb53eb66d323fe4123b3fbf8b9d3e5ccf2e3e85c3a93eeb39aabe40cbae3ea5262a3ff1222abf38a0643fc04712bc1350e63e000080bf400fa63e9813363f8ceb443fbf8b9d3e33f3b43daec7b13ec23db2be3e1a353e7b24323f6b3032bf9473783fc303b8bbc2c3763e000080bfe9ab933e7e9d383f52c44e3fbf8b9d3e52289e3e1ef575be52289e3ebcbf1e3f6d09f6bebcbf1e3f3bea34bf57bd893a9a1f353f000080bf1ca51f3f2865b23e388b1d3d0000000066a6a93e99392ebe66a6a93ec5292a3f7aa4aebec4292a3f37ff34bfc5abb239ae0a353f000080bf1ca51f3fbf29c93e778b9d3d0000000014a6b13ef558b4bd14a6b13e07ee313fbb613cbe07ee313fa50435bf08ad13384105353f000080bf1ca51f3f19d7e33e6151ec3d00000000ebb19e3e00f0753eebb19e3e15b71e3f4f36f63effb61e3ff32235bf662e9b3adee6343f000080bf1ca51f3f6b43273f5ada893e00000000eb39aa3e52f82d3eeb39aa3ee0242a3f0ccbae3ec1242a3f930b35bf7f85d03951fe343f000080bf1ca51f3f20e11b3f75516c3e00000000c23db23eb836b33dc23db23e3239323f3c48333e2839323f9e0535bf33f8ae384804353f000080bf1ca51f3f728a0e3f73ee443e00000000ebb19e3e00f0753eebb19ebe15b71e3f4f36f63effb61ebfd8e6343f2ecb9a3af922353f000080bf9e62bf3e6b43273f5ada893ec08b9d3eeb39aa3e52f82d3eeb39aabee0242a3f0ccbae3ec1242abf5cfe343ffd1bcb39880b353f000080bf9e62bf3e20e11b3f76516c3ec08b9d3ec23db23eb836b33dc23db2beab2a323fdf13353ea22a32bfac03353f47cf1e393a06353f000080bf9e62bf3e728a0e3f74ee443ec08b9d3e52289e3e1ef575be52289ebebcbf1e3f6d09f6bebcbf1ebf991f353f60ba893a3cea343f000080bf9e62bf3e2865b23e3c8b1d3dc08b9d3e66a6a93e99392ebe66a6a9bec5292a3f7aa4aebec4292abfad0a353fffa2b23938ff343f000080bf9e62bf3ebf29c93e798b9d3dc08b9d3e14a6b13ef558b4bd14a6b1be7bd6313f15243fbe7cd631bf7c06353f776f36396a03353f000080bf9e62bf3e19d7e33e6351ec3dc08b9d3e52289ebe1ef575be52289e3ec5bf1ebf4309f6bec5bf1e3fa71f35bf60018a3a2eea34bf000080bfe6985f3f2865b23e89ebc43e0000003466a6a9be99392ebe66a6a93ec7292abf69a4aebec8292a3faa0a35bf6235b2393bff34bf000080bfe6985f3fbf29c93eff9cd83e0000003414a6b1bef558b4bd14a6b13e04ee31bff7613cbe04ee313f3e0535bf2f150d38a90435bf000080bfe6985f3f19d7e33e874eec3e00000034ebb19ebe00f0753eebb19e3e05b71ebf6536f63e06b71e3fe1e634bf8bdc9a3aef2235bf000080bfe6985f3f6b43273f3a8a1d3f00000034eb39aabe52f82d3eeb39aa3ed0242abf16cbae3ed0242a3f55fe34bfb70bce39900b35bf000080bfe6985f3f20e11b3f6cb1133f00000034c23db2beb836b33dc23db23e2e3932bf1c48333e2e39323f430435bf24ebae38a30535bf000080bfe6985f3f728a0e3f96d8093f0000003452289ebe1ef575be52289ebec5bf1ebf4309f6bec5bf1ebf2eea343f6e048a3aa81f35bf000080bf20ecfd3d2865b23ec88b1d3d263ab13e66a6a9be99392ebe66a6a9bec8292abf69a4aebec7292abf3aff343f9b3fb239aa0a35bf000080bf20ecfd3dbf29c93e738b9d3d263ab13e14a6b1bef558b4bd14a6b1be79d631bf51243fbe79d631bf6e03353f01dc3439780635bf000080bf20ecfd3d19d7e33e7b51ec3d263ab13eebb19ebe00f0753eebb19ebe06b71ebf6536f63e05b71ebff022353f54dd9a3ae1e634bf000080bf20ecfd3d6b43273f47da893e263ab13eeb39aabe52f82d3eeb39aabed0242abf16cbae3ed0242abf8f0b353f9001ce3955fe34bf000080bf20ecfd3d20e11b3f77516c3e263ab13ec23db2beb836b33dc23db2bea82a32bfc313353ea82a32bf3906353f747b2039ad0335bf000080bf20ecfd3d728a0e3f66ee443e263ab13ec2a59bb24721d3bef6708fbe000000000d2954bf0f430fbf5fed7f3f67975a3cdedba1bc000080bfa6dd7e3e472e6f3e0c00003f8eebc43eaeef27b3aec7eabecd4c48be00000000ffd66bbf6e27c7be97ac7e3fb0f0213d59c5bfbd000080bfa6dd7e3e1786323e0c00003f2e9dd83e47294bb299f9f9be706dcebd00000000f5a77abfb52250bea2277a3f44e4303d7f0755be000080bfa6dd7e3e15fed03d0c00003fd24eec3ef6708fbe4721d3be0ad7a3320f430fbf0d2954bf000000005302d03c73758cbc3ce17fbf000080bf008871ba492e6f3e248a1d3f0900003fcd4c48beaec7eabe0ab7d1af7c27c7befcd66bbf000000008854a43da7c40abdf2067fbf000080bf008871baf980313e68b1133f0900003fc26dcebd99f9f9be00000000c32250bef5a77abf0000000069dd033ee1fddabcbac67dbf000080bf008871ba3592e33d9cd8093f0900003fcccc4c334721d3bef6708f3e000000000d2954bf0f430f3f75f57fbfc275243c978d733c000080bf029f3f3f472e6f3e0c00003f3d8a1d3f5208acb2aec7eabecd4c483e00000000ffd66bbf6e27c73e4c137fbfef49073dca35a03d000080bf029f3f3f1786323e0c00003f7eb1133f5280b7b299f9f9be706dce3d00000000f5a77abfb522503ea6b87ebf8237a63c752cc83d000080bf029f3f3fdd67d63d0c00003fc2d8093ff6708f3e4721d3be0ad723330f430f3f0d2954bf00000000bf98a8bcdcb063bccaeb7f3f000080bf6a56ff3e472e6f3eabebc43e0900003fcd4c483eaec7eabec21da7326e27c73effd66bbf000000001464d7bdd3e235bd8a537e3f000080bf6a56ff3e69a32d3e079dd83e0900003f706dce3d99f9f9be0ad723b2b022503ef5a77abf00000000b6431cbe98c101bd24df7c3f000080bf6a56ff3e15d2c73d884eec3e0900003fe1f28f3e8f62d33e5c8fc2b3fc340f3f8c32543f000000006bf1f5bb24fba53b50fd7f3f000080bf6a56ff3eec3d433fe4123b3fc48b1d3e7bfc483e5c0feb3eb81e85b3a9d4c63e72e86b3f00000000631d93bd68fcf73c96387f3f000080bf6a56ff3e46f5503f8ceb443fc48b1d3ea420cf3d854bfa3e703d0ab3e1474e3e7cc07a3f00000000223e0ebe1808ea3ca1697d3f000080bf6a56ff3e1bec663f52c44e3fc48b1d3ecccc4c338f62d33ee1f28f3e000000008c32543ffc340f3f50fd7fbf73fca5bb5af3f53b000080bf029f3f3fec3d433f1c9d583f808b1d3db81e05335c0feb3e7bfc483e0000000077e86b3f99d4c63e4c397fbf0e8bf7bc32da923d000080bf029f3f3f46f5503f1c9d583f208c9d3db81e8532854bfa3e5220cf3d000000007cc07a3fcf474e3e5e147cbfb6d90fbddcdc2e3e000080bffda73f3f9fe6663f1c9d583f1052ec3de1f28fbe8f62d33e00000000fc340fbf8c32543f000000001cf3f53b48fca53b50fd7fbf000080bfcc927f3fec3d433f3427763fc48b1d3e7bfc48be5c0feb3e000000009bd4c6be77e86b3f00000000b4a2943d958cfa3c6f347fbf000080bfcc927f3f46f5503f824e6c3fc48b1d3e5220cfbd854bfa3e00000000cc474ebe7ec07a3f00000000526b3b3e132e1a3db87d7bbf000080bfc6737f3f192b663fc475623fc48b1d3eb81e05b48f62d33ee1f28fbe000000008c32543ffd340fbf50fd7f3f77fda5bbd8f4f5bb000080bfa6dd7e3eec3d433f1c9d583f56da893eeb51b8b35c0feb3e7bfc48be0000000075e86b3f9bd4c6be97387f3fbdfbf7bc091d93bd000080bfa6dd7e3e46f5503f1c9d583fc6516c3ea4703db3854bfa3e5220cfbd000000007cc07a3fcf474ebe00077e3f6a78ccbc318df8bd000080bfa6dd7e3e1bec663f1c9d583fc0ee443e4721d33ef6708fbe0ad723b30e29543f0f430fbf00000000d3b5053add03463af9ff7f3f000080bf6a56ff3e0368a53e3c8b1d3d908b1d3eaec7ea3ecd4c48bea4a8a4b3fcd66b3f7d27c7be0000000063a108b87bcca1b80000803f000080bf6a56ff3efce2c03e788b9d3d908b1d3e99f9f93ec26dcebd0ad723b38b707a3f784354beabeab0b99bf8aa398963bab8ffff7f3f000080bf6a56ff3e8cb3df3e6251ec3d908b1d3e33dbd43e14aec72deb398e3ea7a3543f483f0cbc86880e3fdc890ebffcfbd935a6a5543f000080bf0bf7173fff3a003fc08b1d3e408d1d3d3383ec3e9999192e0aef433e864b6c3f090bf8bb1af2c43e8af3c4be82734537434d6c3f000080bfa015103fff3a003fc08b1d3e188c9d3d7b14fb3e0000002dc2c5c73df6fd7a3f316ee2bbfa6d493e396f49be76260cb77fff7a3f000080bf20f5073fff3a003fc08b1d3ed852ec3db8d6d33e47798f3e295c0fb43445543f56190f3f00000000b57207bad3eb483af9ff7f3f000080bf6a56ff3efcc12d3f5ada893e908b1d3ea370eb3e6626483e0ad723b4c3ee6b3fb0b6c63e00000000fd4e0638e7769fb80000803f000080bf6a56ff3e8004203f76516c3e908b1d3e7bb4fa3e5268cd3d7b142eb4d3cd7a3f86434d3ee1dc16bafaebc33950ea883af6ff7f3f000080bf6a56ff3e399c103f74ee443e908b1d3eb8d6d33e8fa467bae1f28fbe3c3c543f885e1cbc4b210fbfef220f3f8b2867b8ba3e543f000080bfbfbece3eff3a003fc18b1d3e3bda893ea370eb3e82a467ba7bfc48bee8e56b3fd3380cbc63d4c6be2cd6c63e29b398b821e86b3f000080bf9481de3eff3a003fc18b1d3ebc516c3e7bb4fa3ecea467ba5220cfbd1ed47a3fe55bfabb50a24cbe0baa4c3ea5a1583b50d57a3f000080bf94c2ee3eff3a003fc18b1d3eb8ee443e295c8f33f6708fbe4721d33e000000000f430fbf0d29543ff9ff7fbf770c463aa5bb053a000080bf029f3f3f0368a53e89ebc43e808b1d3e3d9aa233cd4c48beaec7ea3e000000008127c7befad66b3f000080bf66a1a1b80c7d08b8000080bf029f3f3ffce2c03eff9cd83e808b1d3e429eac33c26dcebd99f9f93e00000000189854be0e6c7a3f000080bf1b989eb752a386b6000080bf029f3f3f8cb3df3e874eec3e808b1d3eeb398ebe703d0a2e33dbd43e86880ebf483f0cbca7a3543fa6a554bfa203da35dc890ebf000080bfd6ea573fff3a003ffbffff3ef08b1d3d0aef43be7b140ead3383ec3e1af2c4be090bf8bb864b6c3f434d6cbfaa7545378af3c4be000080bf6c09503fff3a003ffbffff3e608c9d3dc2c5c7bd0ad71d2e7b14fb3eff6d49be076fe2bbf6fd7a3f7eff7abf86260cb73e6f49be000080bfece8473fff3a003ffbffff3e0853ec3d295c8f3347798f3eb8d6d33e0000000056190f3f3445543ff9ff7fbf6c00493a988007ba000080bf029f3f3ffcc12d3f3a8a1d3f808b1d3e0ad7a3336626483ea370eb3e00000000bbb6c63ec0ee6b3f000080bf772a9eb80c370538000080bf029f3f3f8004203f6ab1133f808b1d3e0ad7a333a468cd3d7bb4fa3e000000009cd54d3e5bc67a3f000080bf35995bb8b83e3437000080bf029f3f3f399c103f96d8093f808b1d3eeb398e3e703d0aae33dbd43e86880e3f483f0cbca7a3543fa6a554bf47904eb5dc890e3f000080bf2c53273fff3a003ffbffff3e52da893e0aef433e7b140e2d3383ec3e1af2c43e090bf8bb864b6c3f434d6cbf7e0abdb68bf3c43e000080bf96342f3fff3a003ffbffff3ea6516c3ec2c5c73d5c8f1eae7b14fb3e056e493efd6ee2bbf5fd7a3f7eff7abfd95fa635416f493e000080bf1655373fff3a003ffbffff3eb4ee443e4721d3bef6708fbe0ad723330e2954bf0f430fbf0000000058b405baab01463af9ff7fbf000080bfcc927f3f0368a53ecc8b1d3df4ffff3eaec7eabecd4c48be8f7a2533ffd66bbf6e27c7be0000000098d009389833a3b8000080bf000080bfcc927f3ffce2c03e748b9d3df4ffff3e99f9f9be706dcebd0ad723338d707abf674354becdddb0b974f7183aa5a593baf2ff7fbf000080bfcc927f3f8cb3df3e7c51ec3df4ffff3eb8d6d3be0ca267bae1f28fbe3c3c54bf5e5e1cbc4b210fbfef220f3f23115f38ba3e54bf000080bf9c7bc03dff3a003ff58b1d3eaeebc43ea370ebbec7a167ba7bfc48bee8e56bbfa7380cbc63d4c6be2bd6c63ef5869f3822e86bbf000080bf4870813dff3a003ff58b1d3e2e9dd83e7bb4fabecba167ba5220cfbd1fd47abfe65cfabb4aa24cbe08aa4c3ebc1659bb50d57abf000080bf90d8003dff3a003ff58b1d3ede4eec3eb8d6d3be47798f3e00000000344554bf56190f3f00000000b187073af30a493af9ff7fbf000080bf008871bafcc12d3f47da893ef4ffff3ea370ebbe6626483e00000000c1ee6bbfbab6c63e00000000dd1205b88aff9db8000080bf000080bf008871ba8004203f78516c3ef4ffff3e7bb4fabea468cd3d00000000d3cd7abf9d434d3e45d116ba28f5103a42a42fb9fdff7fbf000080bf008871ba399c103f67ee443ef4ffff3e33dbd4be1e85cbadeb398e3ea7a354bf483f0cbc86880e3fdc890ebfbf134eb5a6a554bf000080bff846673fff3a003ff68b1d3e358a1d3f3383ecbe999919ae0aef433e864b6cbf090bf8bb1af2c43e8bf3c4be210fbdb6434d6cbf000080bf62286f3fff3a003ff68b1d3e78b1133f7b14fbbe000000adc2c5c73df6fd7abfb46fe2bbfe6d493e3a6f49be659fae357fff7abf000080bfe248773fff3a003ff68b1d3ebbd8093f8fd2de32f6708fbe4721d3be000000000f430fbf0d2954bff9ff7f3ff302463a39b505ba000080bfa6dd7e3e0368a53eec123b3fecffff3ec22d10b2cd4c48beaec7eabe000000007d27c7befcd66bbf0000803f94c3a1b8de990838000080bfa6dd7e3efce2c03e9aeb443fecffff3ec22d1033c26dcebd99f9f9be00000000d5ee53be07757abff3ff7f3f71d19cba72b28439000080bfa6dd7e3e8cb3df3e5ac44e3fecffff3ee1f28f3eb0a367bab8d6d3be4b210f3f845e1cbc3c3c54bfba3e543fe6f55e38ef220f3f000080bf7e06b03eff3a003f169d583f7eebc43e7bfc483eefa367baa370ebbe60d4c63ec7380cbce8e56bbf22e86b3fe8879f3829d6c63e000080bfa943a03eff3a003f169d583f229dd83e5220cf3d54a367ba7bb4fabe4aa24c3efa5bfabb1fd47abf50d57a3f170b59bb08aa4c3e000080bfaa02903eff3a003f169d583fb64eec3e5c8f42b447798f3eb8d6d3be0000000057190f3f324554bff9ff7f3ffa01493aaa81073a000080bfa6dd7e3efcc12d3f4427763fecffff3eae4761b46626483ea370ebbe00000000c0b6c63ec0ee6bbf0000803ff4379eb8644205b8000080bfa6dd7e3e8004203f864e6c3fecffff3e1e856bb4a468cd3d7bb4fabe0000000095b14c3e4bd57abff8ff7f3f35a7733a9bd54639000080bfa6dd7e3e399c103fba75623fecffff3ee1f28fbe61a267bab8d6d3be4b210fbf695e1cbc3c3c54bfba3e543f934467b8ef220fbf000080bf50ae1d3eff3a003f169d583f388a1d3f7bfc48bea0a267baa370ebbe61d4c6beb1380cbce8e56bbf22e86b3f01b198b82bd6c6be000080bffa333d3eff3a003f169d583f7ab1133f5220cfbd14a367ba7bb4fabe55a24cbe775cfabb1ed47abf4fd57a3f48ad583b10aa4cbe000080bffab55d3eff3a003f169d583fc1d8093fae777e3e4709b5beae777ebef221fe3e895436bff221febef404353f1ecf9b33f204353f000080bf9e62bf3ece19913eabebc43e8eebc43e3313333ec23dc5becd0487be4fcfb23e1f4d46bfaafa06bf687d543fa018b0bb20c40e3f000080bf141daf3e1c68803e079dd83e8eebc43e47c1b83d52a8cfbec20d8dbe72e0383e569950bfc6040dbf398d773ffe0a373d096d803e000080bf4833993eeb96733e884eec3e8eebc43ecd04873ec23dc5be331333beaafa063f1f4d46bf4fcfb2bedd3c113f5d1e753c25c7523f000080bf28a8cf3e1c68803eabebc43e2e9dd83e855b3c3e14bed9be855b3cbef9e9bb3ebdd05abff9e9bbbee136403fcbf6203d00c9283f000080bf9e62bf3e2e5c5b3e079dd83e2e9dd83e1e8dc13df588e6be859b44bebd42413ece8267bfd200c4be8cf6693f4d749c3cfa96cf3e000080bfa2ada43e07153e3e884eec3e2e9dd83ec20d8d3e52a8cfbe47c1b8bdc7040d3f569950bf72e038bebf99623e4d2287bdc814793f000080bff491e53eeb96733eabebc43ed24eec3e859b443ef588e6be1e8dc1bdd300c43ece8267bfbd4241beb0e7e33e2a01cb3a443d653f000080bf9917da3e5779333e079dd83ed24eec3eb83eca3d0a07f5beb83ecabdedf74a3e91bc75bfeff74abee0ab403fbec89f3c627d283f000080bfd8c2be3e2bd5083e884eec3ed24eec3eae777ebe4709b5beae777ebef221febe895436bff221febef404353f9cdc4ab3f30435bf000080bf20ecfd3dce19913e248a1d3f8eebc43ecd0487bec23dc5be331333beaafa06bf1f4d46bf4fcfb2befc950b3fb72bde3bfb9656bf000080bff4d5bc3d1c68803e248a1d3f2e9dd83ec20d8dbe52a8cfbe47c1b8bdc6040dbf569950bf75e038be0d22873e59f50f3d35c276bf000080bf805d4a3ded96733e248a1d3fd24eec3e331333bec23dc5becd0487be4fcfb2be1f4d46bfaafa06bffc96563ffa2bdebbfb950bbf000080bf22811f3e1c68803e68b1133f8eebc43e855b3cbe14bed9be855b3cbef9e9bbbebdd05abff9e9bbbe7822343fecd9413bede535bf000080bf20ecfd3d60af563e68b1133f2e9dd83e859b44bef588e6be1e8dc1bdda00c4becd8267bfcf4241be7ffcd33ec171703c16ff68bf000080bf2c18933de5613e3e68b1133fd24eec3e47c1b8bd52a8cfbec20d8dbe75e038be569950bfc7040dbff5b3763f28550dbdff9487be000080bfbc544b3eed96733e9cd8093f8eebc43e1e8dc1bdf588e6be859b44bebf4241bece8267bfd200c4be2263693f32c286bcab39d2be000080bf0660343ee93e3d3e9cd8093f2e9dd83eb83ecabd0a07f5beb83ecabdf7f74abe91bc75bf04f84abeb48b2c3fe78b5a3c34153dbf000080bf9c35003e6795113e9cd8093fd24eec3eae777ebe4709b5beae777e3ef221febe895436bff221fe3ef00435bf5a0d87b4f60435bf000080bfe6985f3fce19913e248a1d3f3d8a1d3f331333bec23dc5becd04873e4fcfb2be1f4d46bfaafa063f638357bf4e1c493c89210abf000080bf2176573f1c68803e68b1133f3d8a1d3f47c1b8bd52a8cfbec20d8d3e75e038be569950bfc6040d3f376a78bf9fe9653da2a670be000080bf3c814c3fed96733e9cd8093f3d8a1d3fcd0487bec23dc5be3313333eaafa06bf1f4d46bf4fcfb23e71f108bf721089bc1a4058bf000080bfadbb673f1c68803e248a1d3f7eb1133f855b3cbe14bed9be855b3c3ef9e9bbbebdd05abff9e9bb3e7ec730bf9d6b65bcac2039bf000080bfe6985f3f60af563e68b1133f7eb1133f1e8dc1bdf588e6be859b443ebf4241bece8267bfd200c43e25bd68bf28705d3ca022d5be000080bf683e523fa7373a3e9cd8093f7eb1133fc20d8dbe52a8cfbe47c1b83dc7040dbf569950bf75e0383e8c6482be9c9b2bbd955377bf000080bf92b0723fed96733e248a1d3fc2d8093f859b44bef588e6be1e8dc13dda00c4becd8267bfcf42413e3c35d3be9e4d7dbc752b69bf000080bf66f36c3fe5613e3e68b1133fc2d8093fb83ecabd0a07f5beb83eca3df7f74abe91bc75bf04f84a3eceb035bf64368e3a675834bf000080bf1bc8613f85e00e3e9cd8093fc2d8093fae777e3e4709b5beae777e3ef221fe3e895436bff221fe3ef50435bf8dfd3db4f104353f000080bf1ca51f3fce19913eabebc43e3d8a1d3fcd04873ec23dc5be3313333eaafa063f1f4d46bf4fcfb23ef1660dbff1510fb98767553f000080bf5682173f1c68803eabebc43e7eb1133fc20d8d3e52a8cfbe47c1b83dc6040d3f569950bf75e0383e13488fbe2d42c03cb0b2753f000080bf708d0c3feb96733eabebc43ec2d8093f3313333ec23dc5becd04873e4fcfb23e1f4d46bfaafa063f886755bfb23c0f39f0660d3f000080bfdfc7273f1c68803e079dd83e3d8a1d3f855b3c3e14bed9be855b3c3ef9e9bb3ebdd05abff9e9bb3effb336bf7f47babb5950333f000080bf1ca51f3f2e5c5b3e079dd83e7eb1133f859b443ef588e6be1e8dc13dd200c43ece8267bfbd42413e07f4debe2299673bb973663f000080bf9d4a123fbb1b393e079dd83ec2d8093f47c1b83d52a8cfbec20d8d3e76e0383e569950bfc6040d3fa4cb75bffab0c8bcda908e3e000080bfc6bc323feb96733e884eec3e3d8a1d3f1e8dc13df588e6be859b443ebd42413ece8267bfd200c43e3df769bf8b8e9cbcc893cf3e000080bf9aff2c3fe5793b3e884eec3e7eb1133fb83eca3d0a07f5beb83eca3dedf74a3e91bc75bfeff74a3ebcdd3fbf4e5794bc946a293f000080bfe7751d3f6d47073e884eec3ec2d8093fae377f3e1e2db53eae377fbe0511fe3e5460363f0511febef304353fbbb25333f404353f000080bf9e62bf3eb8b0393fe4123b3f56da893e5c7f873e3d72c53ee1aa33beb0f1063f8f49463f31fab2be6728123f1c5e94bced1f523f000080bf28a8cf3e6ed53e3fe4123b3fc6516c3e148e8d3e99e9cf3e3d62b9bd51f90c3f25a2503f25cd38be101b793e46524d3d4ffa773f000080bff491e53ec223423fe4123b3fc0ee443ee1aa333e3d72c53e5c7f87be31fab23e8f49463fb0f106bfef1f523fc75d943c6428123f000080bf161daf3e6ed53e3f8ceb443f56da893e0aff3c3e0000da3e0aff3cbeecd4bb3ec6d95a3fecd4bbbef404353f4b0298b2f304353f000080bf9e62bf3e3b57453f8ceb443fc6516c3e3d42453ea4d0e63e9931c2bda5b6c33e3895673f870e41be9f5acd3ea626af3c62716a3f000080bf9b17da3ecc7f4f3f8ceb443fc0ee443e3d62b93d99e9cf3e148e8dbe1fcd383e27a2503f52f90cbf4ffa773f3a524dbd081b793e000080bf4833993ec223423f52c44e3f56da893e9931c23da4d0e63e3d4245be750e413e3a95673f9eb6c3be6b716a3fa327afbc745acd3e000080bfa2ada43ecc7f4f3f52c44e3fc6516c3e66eeca3d9959f53e66eecabd13f1493ea1d7753f22f149be6e9b353f809c77baf06d343f000080bf9e62bf3efee05e3f52c44e3fc0ee443eae377f3e1e2db53eae377f3e0511fe3e5460363f0511fe3ef30435bf0a120ab3f404353f000080bf1ca51f3fb8b0393fe4123b3f808b1d3de1aa333e3d72c53e5c7f873e31fab23e8f49463fb0f1063f8f0056bf3111783b117e0c3f000080bfdfc7273f6ed53e3f8ceb443f808b1d3d3d62b93d99e9cf3e148e8d3e25cd383e25a2503f51f90c3fd9a872bfdc2bafbbcb19a33e000080bfc6bc323fc223423f52c44e3f808b1d3d5c7f873e3d72c53ee1aa333eb0f1063f8f49463f31fab23e117e0cbf2a1178bb8f00563f000080bf5682173f6ed53e3fe4123b3f208c9d3d0aff3c3e0000da3e0aff3c3eecd4bb3ec6d95a3fecd4bb3ef30435bf6df671b2f304353f000080bf1ca51f3f3b57453f8ceb443f208c9d3d9931c23da4d0e63e3d42453e790e413e3a95673f9eb6c33ebe5c67bf3309f63baf1fdb3e000080bf9aff2c3fcc7f4f3f52c44e3f208c9d3d148e8d3e99e9cf3e3d62b93d52f90c3f27a2503f23cd383ece19a3bef72caf3bd9a8723f000080bf708d0c3fc223423fe4123b3f1052ec3d3d42453ea4d0e63e9931c23da6b6c33e3895673f850e413eb41fdbbe1409f6bbbd5c673f000080bf9c4a123fcc7f4f3f8ceb443f1052ec3d66eeca3d9959f53e66eeca3d16f1493ea1d7753f1ef1493ef71835bf38780339edf0343f000080bf1ca51f3ffee05e3f52c44e3f1052ec3dae377fbe1e2db53eae377f3e0511febe5460363f0511fe3ef20435bf9cbda633f40435bf000080bfe6985f3fb8b0393f3427763f808b1d3d5c7f87be3d72c53ee1aa333eb0f106bf8f49463f31fab23e692812bf595e94bcec1f52bf000080bfadbb673f6ed53e3f3427763f208c9d3d148e8dbe99e9cf3e3d62b93d52f90cbf27a2503f26cd383e171b79be31524d3d4efa77bf000080bf94b0723fc223423f3427763f1052ec3de1aa33be3d72c53e5c7f873e31fab2be8f49463fb0f1063fec1f52bf4c5e943c692812bf000080bf2176573f6ed53e3f824e6c3f808b1d3d0aff3cbe0000da3e0aff3c3eecd4bbbec6d95a3fecd4bb3ef30435bfa84a6fb2f30435bf000080bfe6985f3f3b57453f824e6c3f208c9d3d3d4245bea4d0e63e9931c23d9eb6c3be3a95673f790e413ed5acbabed41c233df4286ebf000080bf66f36c3fcc7f4f3f824e6c3f1052ec3d3d62b9bd99e9cf3e148e8d3e23cd38be27a2503f52f90c3f4ffa77bf40524dbd0c1b79be000080bf3c814c3fc223423fc475623f808b1d3d9931c2bda4d0e63e3d42453e780e41be3a95673f9eb6c33e958c6dbff11116bd4aeebdbe000080bf683e523fcc7f4f3fc475623f208c9d3d66eecabd9959f53e66eeca3d0ff149bea3d7753f0ff1493e717b32bffe61843bbc8437bf000080bfe6985f3fbb6b603fc475623f1052ec3dae377fbe1e2db53eae377fbe0511febe5460363f0511febef304353f052a86b2f30435bf000080bf20ecfd3db8b0393f3427763f56da893ee1aa33be3d72c53e5c7f87be22fab2be9349463fb1f106bf9700563f1814783b057e0cbf000080bf22811f3e6ed53e3f824e6c3f56da893eeb61b9bd99e9cf3e148e8dbe11cd38be27a2503f52f90cbfdfa8723ff827afbba519a3be000080bfba544b3ec223423fc475623f56da893e5c7f87be3d72c53ee1aa33beb0f106bf8f49463f31fab2be117e0c3f0e1178bb8f0056bf000080bff4d5bc3d6ed53e3f3427763fc6516c3e0aff3cbe0000da3e0aff3cbee6d4bbbec6d95a3ff4d4bbbefa04353f8ab31732ec0435bf000080bf20ecfd3d3b57453f824e6c3fc6516c3e9931c2bda4d0e63e3d4245be6f0e41be3895673fa8b6c3bebf5c673f9007f63ba91fdbbe000080bf0660343ecc7f4f3fc475623fc6516c3e148e8dbe99e9cf3e3d62b9bd52f90cbf27a2503f23cd38bed119a33e882daf3bd8a872bf000080bf805d4a3dc223423f3427763fc0ee443e3d4245bea4d0e63e9931c2bd9eb6c3be3a95673f790e41beb01fdb3e0b09f6bbbd5c67bf000080bf2c18933dcc7f4f3f824e6c3fc0ee443e66eecabd9959f53e66eecabd0ff149bea3d7753f0cf149bef204353f4594ab31f40435bf000080bf20ecfd3dfee05e3fc475623fc0ee443e4709b53eae777ebeae777ebe7c54363f2322febee721febecf79113fb262f2bbd6a3523f000080bf7074cd3eb6b1af3e3c8b1d3d3bda893ec23dc53ecd0487be331333be1e4d463fb4fa06bf37cfb2bec8dcd03e3ebeb1bb0cba693f000080bf8ca3dc3eb9c0aa3e3c8b1d3dbc516c3e52a8cf3ec20d8dbe47c1b8bd5699503fc6040dbf76e038be4b0a5c3e416612bbe6047a3f000080bf2584ed3eeaeba63e3c8b1d3db8ee443ec23dc53e851333becd0487be194d463f6acfb2bea9fa06bfaae90f3feaabe2ba0fb8533f000080bfa775ce3e929fc73e798b9d3d3bda893e14bed93e855b3cbe855b3cbec2d05a3f01eabbbed2e9bbbe0398c93eb71e13bb01526b3f000080bfe118de3e6badc43e798b9d3dbc516c3ef588e63e859b44be1e8dc1bdcd82673fda00c4becf4241be7baa503e5b79a7bad9a07a3f000080bfb87dee3e040fc23e788b9d3db8ee443e52a8cf3e47c1b8bdc20d8dbef23d503f7e6142be4dbf0cbfb9540f3f3e7e11ba1b1d543f000080bfa6c0ce3e1324e33e6351ec3d3bda893ef588e63e1e8dc1bd859b44be7922673fbbc249be5ba3c3be0d7bc73ed8423fba4ec56b3f000080bf4d84de3eadbee13e6351ec3dbc516c3e0a07f53eb83ecabdb83ecabd9087753fe9ef50be32e948be065a4d3e7e901a3aadcc7a3f000080bf8fc4ee3e845de03e6251ec3db8ee443e4709b53eae777ebeae777e3e7c54363f2322febee721fe3e27bc11bf2162ac3b0877523f000080bf319c183fb6b1af3e388b1d3d408d1d3dc23dc53e851333becd04873e194d463f6acfb2bea9fa063f05ea0fbfb85ee03ad2b7533f000080bf961b183f929fc73e778b9d3d408d1d3d52a8cf3e47c1b8bdc20d8d3e3d87503ff64640bef3800c3f090c0fbfc425e8372a4e543f000080bf17f6173f1324e33e6151ec3d408d1d3dc23dc53ecd0487be3313333e1e4d463fb6fa06bf36cfb23eead7d0be57e1b33b1cbb693f000080bfa304113fb9c0aa3e388b1d3d188c9d3d14bed93e855b3cbe855b3c3ec2d05a3f01eabbbed2e9bb3e0aa7c9be60e8fc3ad54e6b3f000080bff949103f6badc43e788b9d3d188c9d3df588e63e1e8dc1bd859b443ee35d673f2c0e49be55b8c23ef195c6bef9e731b6a8f56b3f000080bf4314103fadbee13e6251ec3d188c9d3d52a8cf3ec20d8dbe47c1b83d5699503fc4040dbf72e0383e002d5bbe4420683bcb107a3f000080bf5894083feaeba63e3c8b1d3dd852ec3df588e63e859b44be1e8dc13dcd82673fda00c4becd42413e91ab50be7b1fa63acba07a3f000080bf8f17083f040fc23e788b9d3dd852ec3d0a07f53eb83ecabdb83eca3d9e96753f444451be8b69473e1cb64bbe6fc0593516e27a3f000080bf23f4073f845de03e6251ec3dd852ec3deba1b53eae777e3eae377f3ed068363fea28fe3ec1e0fd3ec05311bff324f4bb11be523f000080bf329c183f249d283f5ada893e408d1d3d7be4c53eeb09873ee1aa333e3860463f9fe0063f31c9b23e2cd9d0bec27fa9bbf3ba693f000080bfa404113fa2152b3f5ada893e188c9d3d145ed03e1f158d3e3d62b93d11b5503fd9dc0c3fc3d2383e30ee5bbe2d930cbb75067a3f000080bf5894083f08002d3f5ada893ed852ec3d7be4c53e00d8323e5c7f873e2368463f15e1b23e0ccd063fc1b80fbffeaef0ba42d9533f000080bf961b183f36a61c3f75516c3e408d1d3db85eda3e0a273c3e0aff3c3ed2f15a3f67a8bb3e4791bb3e5d2ec9be1da012bb9a686b3f000080bff949103f481f1e3f75516c3e188c9d3deb31e73e3d72443e9931c23df59b673f4a8fc33ea72c413ed97850be27f1adba6da37a3f000080bf8f17083f7b6e1f3f75516c3ed852ec3d145ed03ea4a8b73d148e8d3e4efb503fdbb1373e3b8c0c3f2cdb0ebfd36e78b90e6f543f000080bf17f6173ff4e30e3f73ee443e408d1d3deb31e73eeb79c03d3d42453e6ad1673f8367413ea582c23ea50dc6be107f7eb949126c3f000080bf4314103fa8960f3f73ee443e188c9d3dccbcf53ef530c93d66eeca3df2f1753f754e4a3e4d8f473e018b4bbe959617b946e47a3f000080bf23f4073f3c47103f73ee443ed852ec3deba1b53eae777e3eae377fbed068363fea28fe3ec1e0fdbee6a1113fd6b7a13b5589523f000080bf6e74cd3e249d283f5ada893e3bda893e7be4c53e00d8323e5c7f87be2368463f12e1b23e0bcd06bfadbc0f3fb055d73aa0d6533f000080bfa775ce3e36a61c3f76516c3e3bda893e145ed03ea4a8b73d148e8dbe4fbd503f9dcf383ee1d00cbfb3250f3f713b043adb3c543f000080bfa6c0ce3ef4e30e3f74ee443e3bda893e7be4c53eeb09873ee1aa33be3b60463fa0e0063f23c9b2bea2cdd03e738cae3b78bd693f000080bf8ca3dc3ea2152b3f5ada893ebc516c3eb85eda3e0a273c3e0aff3cbed2f15a3f6fa8bb3e3f91bbbeb537c93eceb8053ba3666b3f000080bfe118de3e481f1e3f76516c3ebc516c3eeb31e73eeb79c03d3d4245be4aa2673fcd1d413ef174c3be9bfac63e88820e3a71e06b3f000080bf4d84de3ea8960f3f74ee443ebc516c3e145ed03e1f158d3eeb61b9bd11b5503fdadc0c3fb6d238be60115b3e793c623b53127a3f000080bf2584ed3e08002d3f5ada893eb8ee443eeb31e73e3d72443e9931c2bdf29b673f548fc33ea82c41becb75503eb6bab13a95a37a3f000080bfb87dee3e7b6e1f3f76516c3eb8ee443eccbcf53ef530c93d66eecabd09e9753f413d493e5a5149be4e404d3e22d2b73900ce7a3f000080bf8fc4ee3e3c47103f74ee443eb8ee443eae777e3eae777ebe4709b53e1922fe3e1922febe7054363fc2a352bf465ff2bbec79113f000080bf05ae263fb6b1af3e89ebc43e52da893e8513333ecd0487bec23dc53e40cfb23eb5fa06bf1c4d463f09ba69bf8bbcb1bbd6dcd03e000080bf93452e3fb9c0aa3e89ebc43ea6516c3ef5c0b83dc20d8dbe52a8cf3e24e0383ec6040dbf5a99503fed047abf986e12bbd3095c3e000080bfdeb5363feaeba63e89ebc43eb4ee443ecd04873e851333bec23dc53eb4fa063f4fcfb2be194d463f0ab853bf02b3e2bab1e90f3f000080bfa02e273f929fc73eff9cd83e52da893e855b3c3e855b3cbe14bed93ed6e9bb3ee4e9bbbec8d05a3f01526bbfa21d13bb0398c93e000080bf3d002f3f6badc43eff9cd83ea6516c3e1e8dc13d859b44bef588e63ea642413ed500c4bece82673fdba07abfa076a7ba51aa503e000080bfaa32373f040fc23eff9cd83eb4ee443ec20d8d3e47c1b8bd52a8cf3ef3800c3ff64640be3d87503fa64e54bfa565cbb8500b0f3f000080bf1f54273f1324e33e834eec3e52da893e859b443e1e8dc1bdf588e63e56b8c23e290e49bee35d673f11f66bbf08b5a3b8fe93c63e000080bff4352f3fadbee13e874eec3ea6516c3eb83eca3db83ecabd0a07f53e9069473e444451be9e96753f13e27abffd707a365fb64b3e000080bf1456373f845de03e874eec3eb4ee443eae777ebeae777ebe4709b53ef221febef221febe8954363ffd7652bf6d4eac3b37bc11bf000080bffd8f583fb6b1af3e89ebc43ef08b1d3dcd0487be331333bec23dc53eaafa06bf4fcfb2be1f4d463fd6b753bfcf77e03affe90fbf000080bf620f583f929fc73eff9cd83ef08b1d3dc20d8dbe47c1b8bd52a8cf3ef5800cbf344740be3b87503f2c4e54bfbc49f737060c0fbf000080bfe2e9573f1324e33e874eec3ef08b1d3d331333becd0487bec23dc53e42cfb2beabfa06bf224d463f21bb69bf52eeb33bd5d7d0be000080bf71f8503fb9c0aa3e89ebc43e608c9d3d855b3cbe855b3cbe14bed93ef0e9bbbe01eabbbebdd05a3fda4e6bbf6f86fd3aefa6c9be000080bfc73d503f6badc43eff9cd83e608c9d3d859b44be1e8dc1bdf588e63e63b8c2be480e49bede5d673fa5f56bbf65312ab60196c6be000080bf0e08503fadbee13e874eec3e608c9d3df5c0b8bdc20d8dbe52a8cf3e5ce038bec6040dbf5699503fcc107abf7f22683be12c5bbe000080bf2488483feaeba63e89ebc43e1053ec3d1e8dc1bd859b44bef588e63ec14241bee200c4becb82673fcba07abfe914a63a8fab50be000080bf5a0b483f040fc23eff9cd83e1053ec3db83ecabdb83ecabd0a07f53e8c6947be444451be9e96753f16e27abf5e8751351db64bbe000080bfeee7473f845de03e874eec3e1053ec3dae377fbeae777e3eeba1b53eb5e0fdbe1a29fe3ec368363f0bbe52bfad1cf4bbca5311bf000080bfff8f583f249d283f3a8a1d3ff08b1d3de1aa33beeb09873e7be4c53e18c9b2beabe0063f3760463ff6ba69bff57aa9bb1fd9d0be000080bf71f8503fa2152b3f3a8a1d3f608c9d3d3d62b9bd1f158d3e145ed03ec4d238bed9dc0c3f11b5503f75067abf25940cbb2eee5bbe000080bf2488483f08002d3f3a8a1d3f0853ec3d5c7f87be52d8323e7be4c53e0ccd06bf2be1b23e2068463f42d953bf7bb9f0bac1b80fbf000080bf620f583f36a61c3f6cb1133ff08b1d3d0aff3cbe0a273c3eb85eda3e1f91bbbe70a8bb3ed8f15a3f96686bbf514912bb732ec9be000080bfc73d503f481f1e3f6cb1133f608c9d3d9931c2bd3d72443eeb31e73eb72c41be518fc33ef29b673f6da37abf3d08aebada7850be000080bf5a0b483f7d6e1f3f6cb1133f0853ec3d148e8dbea4a8b73d145ed03e3b8c0cbf9fb1373e52fb503f116f54bf66db79b927db0ebf000080bfe2e9573ff4e30e3f96d8093ff08b1d3d3d4245beeb79c03deb31e73e9882c2be6667413e6fd1673f4a126cbf0aab7cb9a00dc6be000080bf0e08503fa8960f3f96d8093f608c9d3d66eecabdf530c93dccbcf53e628f47be804e4a3ef0f1753f46e47abff7b319b9fd8a4bbe000080bfeee7473f3c47103f96d8093f0853ec3dae377f3eae777e3eeba1b53eb5e0fd3e1a29fe3ec368363f618952bfc3cba13bd6a1113f000080bf05ae263f249d283f3a8a1d3f52da893e5c7f873e52d8323e7be4c53e0ccd063f2be1b23e2068463f9bd653bf0d3dd73ab3bc0f3f000080bfa02e273f36a61c3f6ab1133f52da893e148e8d3ea4a8b73d145ed03e3c8c0c3f9eb1373e51fb503f176f54bf82b37d391edb0e3f000080bf1f54273ff4e30e3f96d8093f52da893ee1aa333eeb09873e7be4c53e17c9b23eaae0063f3760463f73bd69bfd17eae3bb9cdd03e000080bf93452e3fa2152b3f3a8a1d3f9a516c3e0aff3c3e0a273c3eb85eda3e2191bb3e70a8bb3ed8f15a3f9c666bbff45d053bd737c93e000080bf3d002f3f481f1e3f6ab1133fa6516c3e3d42453eeb79c03deb31e73e9882c23e6667413e6fd1673fca116cbf27f310390410c63e000080bff4352f3fa8960f3f96d8093fa6516c3e3d62b93d1f158d3e145ed03ec8d2383ed8dc0c3f10b5503f51127abf5034623b8b115b3e000080bfdeb5363f08002d3f3a8a1d3fb4ee443e9931c23d3d72443eeb31e73ebe2c413e528fc33ef29b673f94a37abfc0c3b13adb75503e000080bfaa32373f7d6e1f3f6ab1133fb4ee443e66eeca3df530c93dccbcf53e698f473e824e4a3ef0f1753f26e47abf9a16cf387e8d4b3e000080bf1456373f3c47103f96d8093fb4ee443e4709b5beae777ebeae777e3e7c5436bfe721febe2322fe3ee17911bf6368f2bbc9a352bf000080bfcea1663fb6b1af3ecc8b1d3d358a1d3fc23dc5becd0487be8513333e194d46bfa9fa06bf6acfb23effdcd0bee2bdb1bb00ba69bf000080bf5c396e3fb9c0aa3ecc8b1d3d78b1133f52a8cfbec20d8dbe47c1b83d599950bfc7040dbf3ae0383e000a5cbe8f6712bbea047abf000080bfaaa9763feaeba63ecc8b1d3dbcd8093fc23dc5be331333becd04873e1e4d46bf36cfb2beb6fa063fb1e90fbf0ea8e2ba0ab853bf000080bf6c22673f929fc73e758b9d3d358a1d3f14bed9be855b3cbe855b3c3ec2d05abfd2e9bbbe01eabb3ef897c9becb6913bb03526bbf000080bf09f46e3f6badc43e758b9d3d78b1133ff588e6be859b44be1e8dc13dd38267bfc500c4bea242413e5faa50be225ca7badba07abf000080bf7426773f040fc23e748b9d3dbcd8093f52a8cfbe47c1b8bdc20d8d3e3b8750bf344740bef5800c3f560b0fbfb279c8b8a24e54bf000080bfeb47673f1324e33e7d51ec3d358a1d3ff588e6be1e8dc1bd859b443ede5d67bf480e49be63b8c23e0294c6be6d7fa7b810f66bbf000080bfbe296f3fadbee13e7d51ec3d78b1133f0a07f5beb83ecabdb83eca3d9f9675bf3a4451be7769473e66b64bbee1cecc3612e27abf000080bfe049773f845de03e7c51ec3dbbd8093f4709b5beae777ebeae777ebe895436bff221febef221febe39bc113f544cac3bfc7652bf000080bfd8a4c53db6b1af3ec88b1d3daeebc43ec23dc5be331333becd0487be1f4d46bf4fcfb2beaafa06bfffe90f3f8d78e03ad6b753bf000080bff89fc13d929fc73e738b9d3daeebc43e52a8cfbe47c1b8bdc20d8dbeef3d50bfb76142be4abf0cbf5b540f3f87931a3a5a1d54bf000080bffc73c03d1324e33e7b51ec3daeebc43ec23dc5becd0487be331333be1f4d46bfaafa06bf4fcfb2bee7d7d03e61edb33b1dbb69bf000080bf68e8883db9c0aa3ec88b1d3d2e9dd83e14bed9be855b3cbe855b3cbebdd05abff9e9bbbef9e9bbbef8a6c93e1784fd3ad84e6bbf000080bf1413833d6badc43e748b9d3d2e9dd83ef588e6be1e8dc1bd859b44be742267bfd6c249be66a3c3beed7ac73e272a413a54c56bbf000080bf5c65813dadbee13e7c51ec3d2e9dd83e52a8cfbec20d8dbe47c1b8bd569950bfc5040dbf72e038be022d5b3e771f683bcb107abf000080bf10cc0a3deaeba63ecc8b1d3dde4eec3ef588e6be859b44be1e8dc1bdce8267bfd200c4bebd4241be84ab503ee719a63acba07abf000080bf70ff023d040fc23e748b9d3dde4eec3e0a07f5beb83ecabdb83ecabd928775bfe0ef50be17e948beda244d3e48a5d23967cf7abf000080bfb8c8003d845de03e7c51ec3dde4eec3eeba1b5beae777e3eae377fbec36836bf1a29fe3eb5e0fdbec953113fbb1df4bb0bbe52bf000080bfdca4c53d249d283f47da893eaeebc43e7be4c5beeb09873ee1aa33be376046bfabe0063f18c9b2be21d9d03e457aa9bbf6ba69bf000080bf68e8883da2152b3f47da893e2e9dd83e145ed0be1f158d3e3d62b9bd11b550bfd9dc0c3fc5d238be2cee5b3e6e950cbb75067abf000080bf10cc0a3d08002d3f47da893ede4eec3e7be4c5be52d8323e5c7f87be206846bf2be1b23e0ccd06bfc1b80f3f4eb9f0ba42d953bf000080bff89fc13d36a61c3f77516c3eaeebc43eb85edabe0a273c3e0aff3cbed8f15abf70a8bb3e1f91bbbe742ec93e244812bb96686bbf000080bf1413833d481f1e3f77516c3e2e9dd83eeb31e7be3d72443e9931c2bdf29b67bf518fc33ebb2c41bedb78503e3f0baeba6da37abf000080bf70ff023d7d6e1f3f77516c3ede4eec3e145ed0bea4a8b73d148e8dbe53bd50bf64cf383ee1d00cbf2e260f3fc6c9eeb9883c54bf000080bffc73c03df4e30e3f66ee443eaeebc43eeb31e7beeb79c03d3d4245be4fa267bfb01d413ee374c3be49fac63e596b11ba82e06bbf000080bf5c65813da8960f3f66ee443e2e9dd83eccbcf5bef530c93d66eecabd07e975bf4c3d493e675149bea4fd4c3ec3aad7ba52d17abf000080bfb8c8003d3c47103f66ee443ede4eec3eeba1b5beae777e3eae377f3ec36836bf1a29fe3eb5e0fd3ed6a111bfc1cba13b618952bf000080bfcea1663f249d283f47da893e358a1d3f7be4c5be52d8323e5c7f873e206846bf2be1b23e0ccd063fb3bc0fbffe3ad73a9bd653bf000080bf6c22673f36a61c3f78516c3e358a1d3f145ed0bea4a8b73d148e8d3e51fb50bf9eb1373e3c8c0c3f1fdb0ebf36a37d39176f54bf000080bfeb47673ff4e30e3f67ee443e358a1d3f7be4c5beeb09873ee1aa333e376046bfaae0063f17c9b23eb6cdd0bee67fae3b74bd69bf000080bf5c396e3fa2152b3f47da893e78b1133fb85edabe0a273c3e0aff3c3ed8f15abf70a8bb3e2191bb3ed737c9bea05d053b9c666bbf000080bf09f46e3f481f1e3f78516c3e78b1133feb31e7beeb79c03d3d42453e6fd167bf6667413e9882c23e0310c6be5bff1039ca116cbf000080bfbe296f3fa8960f3f67ee443e78b1133f145ed0be1f158d3e3d62b93d10b550bfd8dc0c3fc6d2383e90115bbe6f31623b51127abf000080bfaaa9763f08002d3f47da893ebbd8093feb31e7be3d72443e9931c23df29b67bf518fc33eba2c413ed67550be3bc4b13a94a37abf000080bf7426773f7d6e1f3f78516c3ebbd8093fccbcf5bef530c93d66eeca3df0f175bf804e4a3e668f473e7b8d4bbeb6f7ce3826e47abf000080bfe049773f3c47103f67ee443ebbd8093fae777ebeae777ebe4709b5be2322febee721febe7c5436bfc9a3523f5e68f2bbe17911bf000080bfb0191b3eb6b1af3eec123b3f388a1d3f851333becd0487bec23dc5be6acfb2bea9fa06bf194d46bf00ba693ff4beb1bbfddcd0be000080bfea77393eb9c0aa3eec123b3f7ab1133f47c1b8bdc20d8dbe52a8cfbe39e038bec6040dbf599950bfea047a3f476912bbfa095cbe000080bf1c395b3eeaeba63eec123b3fc1d8093fcd0487be331333bec23dc5beb6fa06bf36cfb2be1e4d46bf0ab8533fa4a9e2bab1e90fbf000080bf201c1d3e929fc73e9aeb443f388a1d3f855b3cbe855b3cbe14bed9be01eabbbed2e9bbbec2d05abf03526b3fc96913bbf897c9be000080bf94623c3e6badc43e9aeb443f7ab1133f1e8dc1bd859b44bef588e6beaf4241becb00c4bed28267bfdba07a3f7b79a7ba55aa50be000080bf422c5d3e040fc23e9aeb443fc1d8093fc20d8dbe47c1b8bd52a8cfbe4abf0cbfb76142beef3d50bf191d543fbb2111babc540fbf000080bf20b21d3e1324e33e5ac44e3f388a1d3f859b44be1e8dc1bdf588e6be66a3c3bed6c249be742267bf4dc56b3ff9b73fba0f7bc7be000080bf6c393d3eadbee13e5ac44e3f7ab1133fb83ecabdb83ecabd0a07f5be30e948bee8ef50be908775bfadcc7a3ff58e1a3a045a4dbe000080bff0b95d3e845de03e5ac44e3fc1d8093fae777e3eae777ebe4709b5bee721fe3e2322febe7c5436bf0977523fa363ac3b26bc113f000080bfcc50b13eb6b1af3eec123b3f7eebc43ecd04873e851333bec23dc5bea9fa063f6acfb2be194d46bfd2b7533f7161e03a04ea0f3f000080bf974fb03e929fc73e9aeb443f7eebc43ec20d8d3e47c1b8bd52a8cfbe4dbf0c3f7e6142bef23d50bf571d543f261d1a3a60540f3f000080bf9604b03e1324e33e5ac44e3f7eebc43e3313333ecd0487bec23dc5be36cfb23eb6fa06bf1e4d46bf1bbb693fb9dfb33beed7d03e000080bfb021a23eb9c0aa3eec123b3f229dd83e855b3c3e855b3cbe14bed9bed2e9bb3e01eabbbec2d05abfd54e6b3f0aeafc3a09a7c93e000080bf5caca03e6badc43e9aeb443f229dd83e859b443e1e8dc1bdf588e6be59a3c33eb9c249be792267bf58c56b3fdf25413ade7ac73e000080bfef40a03eadbee13e5ac44e3f229dd83e47c1b83dc20d8dbe52a8cfbe75e0383ec7040dbf569950bfca107a3f541f683b062d5b3e000080bf1841913eeaeba63eec123b3fb64eec3e1e8dc13d859b44bef588e6becf42413eda00c4becd8267bfcba07a3f171ca63a96ab503e000080bf8447903e040fc23e9aeb443fb64eec3eb83eca3db83ecabd0a07f5be2ee9483ee5ef50be908775bf67cf7a3ffee7d339d1244d3e000080bfae00903e845de03e5ac44e3fb64eec3eae377f3eae777e3eeba1b5beb5e0fd3e1a29fe3ec36836bf0bbe523f771cf4bbca53113f000080bfcc50b13e249d283f4427763f7eebc43ee1aa333eeb09873e7be4c5be06c9b23eace0063f396046bffbba693f267ba9bb0bd9d03e000080bfb021a23ea2152b3f4427763f229dd83eeb61b93d1f158d3e145ed0beacd2383edadc0c3f11b550bf77067a3fd0990cbb04ee5b3e000080bf1841913e08002d3f4427763fb64eec3e5c7f873e52d8323e7be4c5be0ccd063f2be1b23e206846bf42d9533fe5baf0bac1b80f3f000080bf974fb03e36a61c3f864e6c3f7eebc43e0aff3c3e0a273c3eb85edabe1991bb3e7aa8bb3ed8f15abf97686b3f1e4712bb6f2ec93e000080bf5caca03e481f1e3f864e6c3f229dd83e9931c23d3d72443eeb31e7beab2c413e5a8fc33ef19b67bf6da37a3f2c09aebacd78503e000080bf8447903e7d6e1f3f864e6c3fb64eec3e148e8d3ea4a8b73d145ed0bee1d00c3f62cf383e53bd50bf883c543fdac8eeb92e260f3f000080bf9604b03ef4e30e3fba75623f7eebc43e3d42453eeb79c03deb31e7bee374c33eaf1d413e4fa267bf82e06b3f116c11ba49fac63e000080bfef40a03ea8960f3fba75623f229dd83e66eeca3df530c93dccbcf5be5f51493e4c3d493e07e975bf52d17a3f09abd7ba9cfd4c3e000080bfae00903e3c47103fba75623fb64eec3eae377fbeae777e3eeba1b5beb5e0fdbe1a29fe3ec36836bf6189523f10cca13bd6a111bf000080bfb0191b3e249d283f4427763f378a1d3f5c7f87be52d8323e7be4c5be0ccd06bf2be1b23e206846bf9bd6533fee3cd73ab3bc0fbf000080bf201c1d3e36a61c3f864e6c3f378a1d3f148e8dbea4a8b73d145ed0bee0d00cbf63cf383e54bd50bfda3c543fbac1033ab4250fbf000080bf20b21d3ef4e30e3fba75623f388a1d3fe1aa33beeb09873e7be4c5be06c9b2beace0063f396046bf78bd693f9080ae3ba2cdd0be000080bfea77393ea2152b3f4427763f7ab1133f0aff3cbe0a273c3eb85edabe1a91bbbe7aa8bb3ed8f15abf9d666b3fd45d053bd037c9be000080bf94623c3e481f1e3f864e6c3f7ab1133f3d4245beeb79c03deb31e7bee474c3beb01d413e4fa267bf74e06b3ff76d0e3a8efac6be000080bf6c393d3ea8960f3fba75623f7ab1133feb61b9bd1f158d3e145ed0beb7d238bedadc0c3f11b550bf53127a3f3a37623b6f115bbe000080bf1c395b3e08002d3f4427763fc1d8093f9931c2bd3d72443eeb31e7beba2c41be5d8fc33ef19b67bf94a37a3fe2bfb13adb7550be000080bf422c5d3e7d6e1f3f864e6c3fc1d8093f66eecabdf530c93dccbcf5be705149be4e3d493e07e975bf00ce7a3f373ab93941404dbe000080bff0b95d3e3c47103fba75623fc1d8093f706dce3d99f9f9be0ad723b2b022503ef5a77abf000000007dee9dbdfa2383bc6d347f3f000080bf6a56ff3e15d2c73d884eec3e0900003f00000000000000bf0000000000000000000080bf000000006f0b763f00000000ba648dbe0000803fe835bf3e0639b23c0c00003f0900003f00000000000000bf0000000000000000000080bf00000000fa6e2b3f000000005a1f3ebf000080bf6891f43d0639b23c0c00003f0900003fcd7cb4becd7cb4be00000000f30435bff40435bf000000006d3aa43a6d3aa4bae6ff7fbf000080bfcc927f3fcce38d3ef662273f0900003ff6708fbe4721d3be0ad7a3320f430fbf0d2954bf000000005302d03c73758cbc3ce17fbf000080bfcc927f3f492e6f3e248a1d3f0900003fcd4c48beaec7eabe0ab7d1af7c27c7befcd66bbf000000008854a43da7c40abdf2067fbf000080bfcc927f3ff980313e68b1133f0900003fc26dcebd99f9f9be00000000c32250bef5a77abf0000000069dd033ee1fddabcbac67dbf000080bfcc927f3fe1fcd33d9cd8093f0900003f00000000000000bf0000000000000000000080bf0000000078c0223e00000000f3be7cbf000080bf08b95f3f36f4a63c0c00003f0900003f00000000000000bf0000000000000000000080bf00000000260c7cbf00000000153f333e000080bf08b95f3f36f4a63c0c00003f0900003f00000000000000bf0000000000000000000080bf000000008c153fbf00000000655c2a3f000080bfbf561f3fa66a903c0c00003f0900003f706dce3d99f9f9be0ad723b2b022503ef5a77abf00000000c32b6dbe1df044bd0bbc783f000080bf6a56ff3e15d2c73d884eec3e0900003f33b3a3310000003f0ad7a3b1000000000000803f00000000f4a355be00000000d05d7a3f000080bf4202c53e92c5783f1c9d583fc48b1d3e33b3a3310000003f0ad7a3b1000000000000803f0000000075dc32bf00000000ee26373f000080bf3ea91e3f02d4793f1c9d583fc48b1d3e33b3a3310000003f0ad7a3b1000000000000803f000000002f2478bf0000000010c67b3e000080bf4205603fb81f793f1c9d583fc48b1d3e33b3a3310000003f0ad7a3b1000000000000803f000000002ad06d3e00000000280079bf000080bf4205603fb81f793f1c9d583fc48b1d3e0a17b5bea4a0b43e000000006d0b35bf7afe343f00000000be93513bbea2513b54ff7fbf000080bf008871ba1784393f0000803fc48b1d3ee1f28fbe8f62d33e00000000fc340fbf8c32543f000000001cf3f53b48fca53b50fd7fbf000080bf008871baec3d433f3427763fc48b1d3e7bfc48be5c0feb3e000000009bd4c6be77e86b3f00000000b4a2943d958cfa3c6f347fbf000080bf008871ba46f5503f824e6c3fc48b1d3e5220cfbd854bfa3e00000000cc474ebe7ec07a3f00000000526b3b3e132e1a3db87d7bbf000080bf008871ba1bec663fc475623fc48b1d3e33b3a3310000003f0ad7a3b1000000000000803f000000004c443b3f00000000668c2ebf000080bf7494043e92c5783f1c9d583fc48b1d3e145e933e145e93be145e93be3acd133f3acd13bf3bcd13bf0bd2343fce4ccbbab037353f000080bf9e62bf3efce9a23e00008032c08b9d3e52289e3e52289ebe1ef575bec5bf1e3fc5bf1ebf4309f6be9a20223f7a050c3db1ec453f000080bf7085c93e8e109c3e000080323bda893e66a6a93e66a6a9be99392ebec7292a3fc8292abf69a4aebe8f4edf3efa7cd0bcac46663f000080bffeb5d83eccc4943e00008032bc516c3e14a6b13e14a6b1bef558b4bdd229323fd32932bf092e35be100e6f3e7cf860bcc7e6783f000080bf5519eb3e00b18f3e00008032b8ee443ecd7cb43ecd7cb4be0ad72333f404353ff30435bf00000000d937a4badb37a4bae6ff7f3f000080bf6a56ff3ecce38d3e00008032908b1d3e52289e3e52289ebe1ef5753ec5bf1e3fc5bf1ebf4309f63e61101ebf776c01bc545d493f000080bfb3931a3f8e109c3e00000000408d1d3d145e933e145e93be145e933e3acd133f3acd13bf3bcd133fb33b35bfcd5fdbba02ce343f000080bf1ca51f3ffce9a23e000000000000000066a6a93e66a6a9be99392e3ec7292a3fc8292abf69a4ae3efc23edbe7d6209bc1fe0623f000080bf6bfb123fcac4943e00000000188c9d3d14a6b13e14a6b1bef558b43dd329323fd22932bf092e353e000b70be9840503c78d8783f000080bfbfc9093f00b18f3e00000000d852ec3d3de2933eae67933e3de2933ec6b9133f1bf4133fc6b9133ff3d334bf6063c3bacc35353f000080bf1ca51f3f00012f3fb98b9d3e00000000ebb19e3ef6409e3e00b0763e1fb21e3f6dd61e3ffff1f53eead71abf51d646bc7dd64b3f000080bfb3931a3fb66d323fb98b9d3e408d1d3deb39aa3e85c3a93e5ccf2e3eec222a3fa7262a3f47cbae3e41ffe6be9db0edbb0375643f000080bf6bfb123f9813363fb98b9d3e188c9d3dc23db23eaec7b13e33f3b43d6730323f7f24323f481a353e94107abe07980fbbaa3f783f000080bfbfc9093f7e9d383fb98b9d3ed852ec3d0a17b53ea4a0b43e8fc2f5b36d0b353f7afe343f00000000309051bb309f513b54ff7f3f000080bf6a56ff3e1784393fb98b9d3e908b1d3eebb19e3ef6409e3e00b076be1fb21e3f6dd61e3ffff1f5be958f1a3f532a633c810b4c3f000080bf7085c93eb66d323fb98b9d3e3bda893e3de2933eae67933e3de293bec6b9133f1bf4133fc6b913bf672f353fa0c8a9ba61da343f000080bf9e62bf3e00012f3fb98b9d3ec08b9d3eeb39aa3e85c3a93e5ccf2ebef1222a3fa5262a3f40cbaebe06cde53ec1c7263c4dc0643f000080bffeb5d83e9813363fb98b9d3ebc516c3ec23db23eaec7b13e33f3b4bd6a30323f7a24323f451a35beb638753e7d6eec3b538b783f000080bf5519eb3e7e9d383fb98b9d3eb8ee443e145e933e145e93be145e933e3acd133f3acd13bf3bcd133fb33b35bfcd5fdbba02ce343f000080bf1ca51f3ffce9a23e203ab13ebf8b9d3e52289e3e1ef575be52289e3ebcbf1e3f6d09f6bebcbf1e3f3bea34bf57bd893a9a1f353f000080bf1ca51f3f2865b23e89ebc43ebf8b9d3e1ef5753e52289ebe52289e3e5809f63ecbbf1ebfb7bf1e3f528049bf4b5adf3b9ce41d3f000080bf83b6243f8e109c3e203ab13e52da893e99392e3e66a6a9be66a6a93e6aa4ae3ed3292abfb9292a3f07c462bfde141a3ccf8ced3e000080bfcc4e2c3fccc4943e203ab13ea6516c3ef558b43d14a6b1be14a6b13ee22d353ed42932bfd329323f60c278bf6cb636bcf58c713e000080bf7680353f00b18f3e203ab13eb4ee443e8fc27533cd7cb4becd7cb43e00000000f10435bff504353f84ff7fbf4283323b3f83323b000080bf029f3f3fcce38d3e203ab13e808b1d3e66a6a93e99392ebe66a6a93ec5292a3f7aa4aebec4292a3f37ff34bfc5abb239ae0a353f000080bf1ca51f3fbf29c93eff9cd83ebf8b9d3e14a6b13ef558b4bd14a6b13e07ee313fbb613cbe07ee313fa50435bf08ad13384105353f000080bf1ca51f3f19d7e33e834eec3ebf8b9d3ecd04b53e999909aecd04b53e1003353f2ab913bc1103353fef0435bfcf913e38f804353f000080bf1ca51f3fff3a003ffbffff3ebf8b9d3e1ef575be52289ebe52289e3e4309f6bec5bf1ebfc5bf1e3fb6c645bfa38210bdfe4a22bf000080bf7d875a3f8e109c3e203ab13ef08b1d3d145e93be145e93be145e933e3bcd13bf3acd13bf3acd133f06d234bfa95ecbbab63735bf000080bfe6985f3ffce9a23e203ab13e0000003499392ebe66a6a9be66a6a93e63a4aebec6292abfca292a3f136366bfd638da3ceccfdebe000080bf38ef523fccc4943e203ab13e608c9d3df558b4bd14a6b1be14a6b13efe2d35bed02932bfd629323f11fc78bf16217a3c19916dbe000080bf8abd493f00b18f3e203ab13e1053ec3d3de293beae67933e3de2933ec6b913bf1bf4133fc6b9133f672f35bf05c6a9ba62da34bf000080bfe6985f3f00012f3fee62273f0000003400b076bef6409e3eebb19e3efff1f5be6dd61e3f1fb21e3fad294cbfb35873bc2e661abf000080bf7d875a3fb66d323fee62273ff08b1d3d5ccf2ebe85c3a93eeb39aa3e47cbaebea7262a3fec222a3f36a064bf8e4812bc1b50e6be000080bf38ef523f9813363fee62273f608c9d3d33f3b4bdaec7b13ec23db23e491a35be7f24323f6730323f927378bfd903b8bbd5c376be000080bf8abd493f7e9d383fee62273f0853ec3d8fc27533a4a0b43e0a17b53e000000007afe343f6d0b353fd5ff7fbfd8fdd1bad1eed13a000080bf029f3f3f1784393fee62273f808b1d3e00b0763ef6409e3eebb19e3efff1f53e6dd61e3f1fb21e3fe4b64bbf0505363cc3021b3f000080bf83b6243fb66d323fee62273f52da893eebb19e3e00f0753eebb19e3e15b71e3f4f36f63effb61e3ff32235bf662e9b3adee6343f000080bf1ca51f3f6b43273f3a8a1d3fbf8b9d3e3de2933eae67933e3de2933ec6b9133f1bf4133fc6b9133ff3d334bf6063c3bacc35353f000080bf1ca51f3f00012f3fee62273fbf8b9d3eeb39aa3e52f82d3eeb39aa3ee0242a3f0ccbae3ec1242a3f930b35bf7f85d03951fe343f000080bf1ca51f3f20e11b3f6ab1133fbf8b9d3ec23db23eb836b33dc23db23e3239323f3c48333e2839323f9e0535bf33f8ae384804353f000080bf1ca51f3f728a0e3f96d8093fbf8b9d3e5ccf2e3e85c3a93eeb39aa3e47cbae3ea7262a3fec222a3fe19564bf4bb40b3c217ae63e000080bfcc4e2c3f9813363fee62273f9a516c3e33f3b43daec7b13ec23db23e4d1a353e7e24323f6730323f6f5878bfe1eb793b9580783e000080bf7680353f7e9d383fee62273fb4ee443e145e93be145e93be145e933e3bcd13bf3acd13bf3acd133f06d234bfa95ecbbab63735bf000080bfe6985f3ffce9a23e00000033f262273f52289ebe1ef575be52289e3ec5bf1ebf4309f6bec5bf1e3fa71f35bf60018a3a2eea34bf000080bfe6985f3f2865b23ecc8b1d3df262273f52289ebe52289ebe1ef5753eb7bf1ebfcbbf1ebf5809f63eaa2022bf5e050c3da4ec45bf000080bf4eaa643f90109c3e00000033358a1d3f66a6a9be66a6a9be99392e3eb6292abfd5292abf71a4ae3ea34edfbe637dd0bca74666bf000080bf96426c3fccc4943e0000003378b1133f14a6b1be14a6b1bef558b43dcf2932bfd82932bfeb2d353e0b0e6fbe61f660bcc7e678bf000080bf4474753f00b18f3e00000033bcd8093fcd7cb4becd7cb4be00000000f30435bff40435bf000000006d3aa43a6d3aa4bae6ff7fbf000080bfcc927f3fcce38d3e00000033f4ffff3e66a6a9be99392ebe66a6a93ec7292abf69a4aebec8292a3faa0a35bf6235b2393bff34bf000080bfe6985f3fbf29c93e758b9d3df262273f14a6b1bef558b4bd14a6b13e04ee31bff7613cbe04ee313f3e0535bf2f150d38a90435bf000080bfe6985f3f19d7e33e7d51ec3df262273fcd04b5be9999092ecd04b53e110335bf2ab913bc1003353ff80435bf208f3e38ef0435bf000080bfe6985f3fff3a003ff68b1d3ef262273f52289ebe52289ebe1ef575bec5bf1ebfc5bf1ebf4309f6be67101e3fd16e01bc505d49bf000080bfd860d53d90109c3e00008032aeebc43e145e93be145e93be145e93be3acd13bf3acd13bf3bcd13bfac3b353f4543dbba09ce34bf000080bf20ecfd3dfce9a23e00008032263ab13e66a6a9be66a6a9be99392ebec7292abfc8292abf69a4aebeff23ed3ee96209bc1ee062bf000080bfa49e983dcac4943e000080322e9dd83e14a6b1be14a6b1bef558b4bdd22932bfd22932bf082e35befe0a703eb340503c78d878bf000080bf80221e3d00b18f3e00008032de4eec3ecd7cb4becd7cb4be00000000f30435bff40435bf000000006d3aa43a6d3aa4bae6ff7fbf000080bf008871bacce38d3e00000033f4ffff3e4721d3bef6708fbe0ad723330e2954bf0f430fbf0000000058b405baab01463af9ff7fbf000080bf008871ba0368a53ecc8b1d3df4ffff3eaec7eabecd4c48be8f7a2533ffd66bbf6e27c7be0000000098d009389833a3b8000080bf000080bf008871bafce2c03e748b9d3df4ffff3e99f9f9be706dcebd0ad723338d707abf674354becdddb0b974f7183aa5a593baf2ff7fbf000080bf008871ba8cb3df3e7c51ec3df4ffff3e000000bf33b3a331000000005cfe7fbf0647e6bb86976fbac3ac753af73a58bb9dff7fbf000080bf008871baff3a003ff68b1d3ef4ffff3e3de293beae67933e3de293bec6b913bf1bf4133fc6b913bff8d3343fba50c3bac73535bf000080bf20ecfd3d01012f3fbf8b9d3e263ab13eebb19ebef6409e3e00b076be1fb21ebf6dd61e3ffff1f5bee8d71a3f53d746bc7ed64bbf000080bfd860d53db66d323fbf8b9d3eaeebc43eeb39aabe85c3a93e5ccf2ebeec222abfa7262a3f47cbaebe3fffe63e27b1edbb047564bf000080bfa49e983d9813363fbf8b9d3e2e9dd83ec23db2beaec7b13e33f3b4bd683032bf7f24323f4d1a35be9b107a3ec3970fbbaa3f78bf000080bf80221e3d7e9d383fbf8b9d3ede4eec3e0a17b5bea4a0b43e000000006d0b35bf7afe343f00000000be93513bbea2513b54ff7fbf000080bf008871ba1784393fbf8b9d3ef4ffff3eebb19ebef6409e3e00b0763e1fb21ebf6dd61e3ffff1f53e968f1abf002a633c810b4cbf000080bf50aa643fb66d323fbf8b9d3e348a1d3febb19ebe00f0753eebb19e3e05b71ebf6536f63e06b71e3fe1e634bf8bdc9a3aef2235bf000080bfe6985f3f6b43273f47da893ef262273f3de293beae67933e3de2933ec6b913bf1bf4133fc6b9133f672f35bf05c6a9ba62da34bf000080bfe6985f3f00012f3fbf8b9d3ef262273feb39aabe52f82d3eeb39aa3ed0242abf16cbae3ed0242a3f55fe34bfb70bce39900b35bf000080bfe6985f3f20e11b3f78516c3ef262273fc23db2beb836b33dc23db23e2e3932bf1c48333e2e39323f430435bf24ebae38a30535bf000080bfe6985f3f728a0e3f67ee443ef262273feb39aabe85c3a93e5ccf2e3eec222abfa7262a3f47cbae3e09cde5be46c9263c4cc064bf000080bf96426c3f9813363fbf8b9d3e78b1133fc23db2beaec7b13e33f3b43d683032bf7f24323f4d1a353eca3875be966dec3b528b78bf000080bf4474753f7e9d383fbf8b9d3ebbd8093f0a17b5bea4a0b43e000000006d0b35bf7afe343f00000000be93513bbea2513b54ff7fbf000080bfcc927f3f1784393fbf8b9d3ef4ffff3eb8d6d3be47798f3e00000000344554bf56190f3f00000000b187073af30a493af9ff7fbf000080bfcc927f3ffcc12d3f47da893ef4ffff3ea370ebbe6626483e00000000c1ee6bbfbab6c63e00000000dd1205b88aff9db8000080bf000080bfcc927f3f8004203f78516c3ef4ffff3e7bb4fabea468cd3d00000000d3cd7abf9d434d3e45d116ba28f5103a42a42fb9fdff7fbf000080bfcc927f3f399c103f67ee443ef4ffff3e145e93be145e93be145e93be3acd13bf3acd13bf3bcd13bfac3b353f4543dbba09ce34bf000080bf20ecfd3dfce9a23e2a3a313ff162273f52289ebe1ef575be52289ebec5bf1ebf4309f6bec5bf1ebf2eea343f6e048a3aa81f35bf000080bf20ecfd3d2865b23eec123b3ff162273f1ef575be52289ebe52289ebe5809f6becbbf1ebfb7bf1ebf5080493f5b5cdf3b9fe41dbf000080bfb03b133e8e109c3e2a3a313f388a1d3f99392ebe66a6a9be66a6a9be71a4aebed5292abfb6292abf06c4623f44141a3cd68cedbe000080bfcc9c313eccc4943e2a3a313f7ab1133ff558b4bd14a6b1be14a6b1beeb2d35bed82932bfcf2932bf60c2783f2ab736bcfa8c71be000080bf7a63563e00b18f3e2a3a313fc1d8093f006065b3cd7cb4becd7cb4be00000000f30435bff30435bf84ff7f3f4c83323b4d8332bb000080bfa6dd7e3ecce38d3e2a3a313fecffff3e66a6a9be99392ebe66a6a9bec8292abf69a4aebec7292abf3aff343f9b3fb239aa0a35bf000080bf20ecfd3dbf29c93e9aeb443ff162273f14a6b1bef558b4bd14a6b1be79d631bf51243fbe79d631bf6e03353f01dc3439780635bf000080bf20ecfd3d19d7e33e5ac44e3ff162273f0a17b5be2ba267ba0a17b5bea50235bfb27423bca40235bfed04353f720f3e38f90435bf000080bf20ecfd3dff3a003f169d583ff162273f1ef5753e52289ebe52289ebe4309f63ec5bf1ebfc5bf1ebfb8c6453f508210bdfa4a223f000080bfce3fb53e8e109c3e2a3a313f7eebc43e52289e3e1ef575be52289ebebcbf1e3f6d09f6bebcbf1ebf991f353f60ba893a3cea343f000080bf9e62bf3e2865b23eec123b3f263ab13e145e933e145e93be145e93be3acd133f3acd13bf3bcd13bf0bd2343fce4ccbbab037353f000080bf9e62bf3efce9a23e2a3a313f263ab13e66a6a93e99392ebe66a6a9bec5292a3f7aa4aebec4292abfad0a353fffa2b23938ff343f000080bf9e62bf3ebf29c93e9aeb443f263ab13e14a6b13ef558b4bd14a6b1be7bd6313f15243fbe7cd631bf7c06353f776f36396a03353f000080bf9e62bf3e19d7e33e5ac44e3f263ab13e0a17b53e85a467ba0a17b5bea402353fd37423bca40235bff904353f2eef3d38ee04353f000080bf9e62bf3eff3a003f169d583f263ab13e99392e3e66a6a9be66a6a9be69a4ae3ec8292abfc7292abf1163663fc938da3cf7cfde3e000080bf400fa63eccc4943e2a3a313f229dd83ef558b43d14a6b1be14a6b1be082e353ed22932bfd22932bf12fc783f7c227a3c15916d3e000080bfe7ab933e00b18f3e2a3a313fb64eec3e3de2933eae67933e3de293bec6b9133f1bf4133fc6b913bf672f353fa0c8a9ba61da343f000080bf9e62bf3e00012f3f0300803f263ab13eebb19e3e00f0753eebb19ebe15b71e3f4f36f63effb61ebfd8e6343f2ecb9a3af922353f000080bf9e62bf3e6b43273f4427763f263ab13e00b0763ef6409e3eebb19ebefff1f53e6dd61e3f1fb21ebfae294c3f0e5973bc2d661a3f000080bfce3fb53eb66d323f0300803f7eebc43e5ccf2e3e85c3a93eeb39aabe40cbae3ea5262a3ff1222abf38a0643fc04712bc1350e63e000080bf400fa63e9813363f0300803f229dd83e33f3b43daec7b13ec23db2be3e1a353e7b24323f6b3032bf9473783fc303b8bbc2c3763e000080bfe9ab933e7e9d383f0300803fb64eec3ec2f528b4a4a0b43e0a17b5be000000007afe343f6d0b35bfd5ff7f3f36fdd1ba2feed1ba000080bfa6dd7e3e1784393f0300803fecffff3eeb39aa3e52f82d3eeb39aabee0242a3f0ccbae3ec1242abf5cfe343ffd1bcb39880b353f000080bf9e62bf3e20e11b3f864e6c3f263ab13ec23db23eb836b33dc23db2beab2a323fdf13353ea22a32bfac03353f47cf1e393a06353f000080bf9e62bf3e728a0e3fba75623f263ab13e00b076bef6409e3eebb19ebefff1f5be6dd61e3f1fb21ebfe4b64b3f0405363cc3021bbf000080bfb23b133eb66d323f0300803f378a1d3febb19ebe00f0753eebb19ebe06b71ebf6536f63e05b71ebff022353f54dd9a3ae1e634bf000080bf20ecfd3d6b43273f4427763ff162273f3de293beae67933e3de293bec6b913bf1bf4133fc6b913bff8d3343fba50c3bac73535bf000080bf20ecfd3d01012f3f0300803ff162273feb39aabe52f82d3eeb39aabed0242abf16cbae3ed0242abf8f0b353f9001ce3955fe34bf000080bf20ecfd3d20e11b3f864e6c3ff162273fc23db2beb836b33dc23db2bea82a32bfc313353ea82a32bf3906353f747b2039ad0335bf000080bf20ecfd3d728a0e3fba75623ff162273f5ccf2ebe85c3a93eeb39aabe3acbaebea8262a3fee222abfe495643f90b30b3c167ae6be000080bfcc9c313e9813363f0300803f7ab1133f33f3b4bdaec7b13ec23db2be3e1a35be7f24323f683032bf6f58783f68e7793b918078be000080bf7a63563e7e9d383f0300803fc1d8093f0000003f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fb3e00000000c2c5c73d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ec3e0000000016ef433e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43e00000000da398e3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b53e00000000f304b53e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9398e3e0000000032dbd43e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015ef433e000000005e83ec3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcc5c73d00000000bf14fb3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ebdbbb2000000000000003f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2c5c7bd00000000be14fb3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018ef43be000000005e83ec3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc398ebe0000000030dbd43e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b5be00000000f304b53e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032dbd4be00000000d9398e3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000006083ecbe0000000010ef433e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fbbe00000000c1c5c73d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf000000002ebd3bb30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fbbe00000000cdc5c7bd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ecbe0000000015ef43be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030dbd4be00000000db398ebe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f104b5be00000000f504b5be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6398ebe0000000034dbd4be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bef43be000000006183ecbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6c5c7bd00000000be14fbbe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002edecc3100000000000000bf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8c5c73d00000000be14fbbe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bef433e000000005d83ecbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd398e3e000000002fdbd4be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f704b53e00000000ef04b5be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43e00000000db398ebe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f83ec3e0000000015ef43be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fb3e00000000bcc5c7bd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fb3ec2c5c73d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ec3e16ef433e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43eda398e3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b53ef304b53e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9398e3e32dbd43e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015ef433e5e83ec3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcc5c73dbf14fb3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ebdbbb20000003f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2c5c7bdbe14fb3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018ef43be5e83ec3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc398ebe30dbd43e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b5bef304b53e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032dbd4bed9398e3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006083ecbe10ef433e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fbbec1c5c73d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf2ebd3bb3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fbbecdc5c7bd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ecbe15ef43be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030dbd4bedb398ebe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f104b5bef504b5be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6398ebe34dbd4be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bef43be6183ecbe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6c5c7bdbe14fbbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002edecc31000000bf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8c5c73dbe14fbbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bef433e5d83ecbe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd398e3e2fdbd4be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f704b53eef04b5be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43edb398ebe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f83ec3e15ef43be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fb3ebcc5c7bd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fb3ec2c5c73d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ec3e16ef433e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43eda398e3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b53ef304b53e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9398e3e32dbd43e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015ef433e5e83ec3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcc5c73dbf14fb3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ebdbbb20000003f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2c5c7bdbe14fb3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018ef43be5e83ec3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc398ebe30dbd43e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f304b5bef304b53e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032dbd4bed9398e3e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006083ecbe10ef433e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fbbec1c5c73d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf2ebd3bb3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be14fbbecdc5c7bd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e83ecbe15ef43be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030dbd4bedb398ebe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f104b5bef504b5be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6398ebe34dbd4be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bef43be6183ecbe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6c5c7bdbe14fbbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002edecc31000000bf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8c5c73dbe14fbbe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bef433e5d83ecbe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd398e3e2fdbd4be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f704b53eef04b5be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031dbd43edb398ebe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f83ec3e15ef43be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf14fb3ebcc5c7bd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 5.027176 + m_MeshMetrics[1]: 6.1736765 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset.meta b/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset.meta new file mode 100644 index 00000000..1cb05a47 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/BallEffector.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e050c2b16fe384bd994474655a4b4968 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset b/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset new file mode 100644 index 00000000..b39fcf80 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset @@ -0,0 +1,175 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!43 &4300000 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BoxEffector + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 36 + topology: 0 + baseVertex: 0 + firstVertex: 0 + vertexCount: 8 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + - serializedVersion: 2 + firstByte: 72 + indexCount: 24 + topology: 3 + baseVertex: 0 + firstVertex: 0 + vertexCount: 8 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 1 + m_KeepIndices: 1 + m_IndexFormat: 0 + m_IndexBuffer: 000002000100000003000200000001000500000005000400010002000600010006000500020003000700020007000600030000000400030004000700040005000600040006000700000001000100020002000300030000000400050005000600060007000700040000000400010005000200060003000700 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 8 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 96 + _typelessdata: 000000bf000000bf0000003f0000003f000000bf0000003f0000003f000000bf000000bf000000bf000000bf000000bf000000bf0000003f0000003f0000003f0000003f0000003f0000003f0000003f000000bf000000bf0000003f000000bf + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 1 + m_MeshMetrics[1]: 1 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset.meta b/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset.meta new file mode 100644 index 00000000..da668e8a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/BoxEffector.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 687b70eb9c49243639f9379f6965034f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset b/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset new file mode 100644 index 00000000..58368ec0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset @@ -0,0 +1,165 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!43 &4300000 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: CircleEffector + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 33 + topology: 4 + baseVertex: 0 + firstVertex: 0 + vertexCount: 32 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0, z: 0.5} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 1 + m_KeepIndices: 1 + m_IndexFormat: 0 + m_IndexBuffer: 00000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f000000 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 32 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 384 + _typelessdata: 0000003f0000000000000000be14fb3e00000000c2c5c73d5e83ec3e0000000016ef433e31dbd43e00000000da398e3ef304b53e00000000f304b53ed9398e3e0000000032dbd43e15ef433e000000005e83ec3ebcc5c73d00000000bf14fb3e2ebdbbb2000000000000003fc2c5c7bd00000000be14fb3e18ef43be000000005e83ec3edc398ebe0000000030dbd43ef304b5be00000000f304b53e32dbd4be00000000d9398e3e6083ecbe0000000010ef433ebf14fbbe00000000c1c5c73d000000bf000000002ebd3bb3be14fbbe00000000cdc5c7bd5e83ecbe0000000015ef43be30dbd4be00000000db398ebef104b5be00000000f504b5bed6398ebe0000000034dbd4be0bef43be000000006183ecbec6c5c7bd00000000be14fbbe2edecc3100000000000000bfc8c5c73d00000000be14fbbe1bef433e000000005d83ecbedd398e3e000000002fdbd4bef704b53e00000000ef04b5be31dbd43e00000000db398ebe5f83ec3e0000000015ef43bebf14fb3e00000000bcc5c7bd + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0, z: 0.5} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 1 + m_MeshMetrics[1]: 1 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset.meta b/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset.meta new file mode 100644 index 00000000..3ea9d63e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/CircleEffector.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b43e6ef97ee1b4e31be1a0187b9f4495 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset b/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset new file mode 100644 index 00000000..c8197d5d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset @@ -0,0 +1,165 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!43 &4300000 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: LocatorEffector + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 6 + topology: 3 + baseVertex: 0 + firstVertex: 0 + vertexCount: 6 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 1 + m_KeepIndices: 1 + m_IndexFormat: 0 + m_IndexBuffer: 000001000200030004000500 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 6 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 72 + _typelessdata: 000000bf00000000000000000000003f000000000000000000000000000000bf00000000000000000000003f000000000000000000000000000000bf00000000000000000000003f + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0.5, z: 0.5} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 1 + m_MeshMetrics[1]: 1 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset.meta b/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset.meta new file mode 100644 index 00000000..9d4dd381 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/LocatorEffector.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c6793350c150f456688e39a81f97364a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset b/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset new file mode 100644 index 00000000..9db280eb --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset @@ -0,0 +1,165 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!43 &4300000 +Mesh: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: SquareEffector + serializedVersion: 10 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 5 + topology: 4 + baseVertex: 0 + firstVertex: 0 + vertexCount: 4 + localAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0, z: 0.5} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_BonesAABB: [] + m_VariableBoneCountWeights: + m_Data: + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 1 + m_KeepIndices: 1 + m_IndexFormat: 0 + m_IndexBuffer: 00000100020003000000 + m_VertexData: + serializedVersion: 3 + m_VertexCount: 4 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 48 + _typelessdata: 000000bf00000000000000bf0000003f00000000000000bf0000003f000000000000003f000000bf000000000000003f + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0.5, y: 0, z: 0.5} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshMetrics[0]: 1 + m_MeshMetrics[1]: 1 + m_MeshOptimizationFlags: 1 + m_StreamData: + offset: 0 + size: 0 + path: diff --git a/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset.meta b/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset.meta new file mode 100644 index 00000000..6726b3dc --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Shapes/SquareEffector.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 707439f5f179a460987ecbb96364c8a9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef b/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef new file mode 100644 index 00000000..63bbdf3f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef @@ -0,0 +1,25 @@ +{ + "name": "Unity.Animation.Rigging.Editor", + "references": [ + "Unity.Animation.Rigging", + "Unity.Burst" + ], + "includePlatforms": [ + "Editor" + ], + "versionDefines": [ + { + "name": "Unity", + "expression": "2021.2.0a17", + "define": "SUPPORTS_SCENE_VIEW_OVERLAYS" + } + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef.meta b/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef.meta new file mode 100644 index 00000000..5167c57b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Unity.Animation.Rigging.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: af4a3ceb227b8f94cb8375f40e156398 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils.meta b/Packages/com.unity.animation.rigging/Editor/Utils.meta new file mode 100644 index 00000000..ae429128 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39a09c3077127a4429ea81c362640d19 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs new file mode 100644 index 00000000..cc8dc0cd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + internal static class AnimationRiggingContextMenus + { + [MenuItem("CONTEXT/Animator/Rig Setup", false, 611)] + static void RigSetup(MenuCommand command) + { + var animator = command.context as Animator; + + AnimationRiggingEditorUtils.RigSetup(animator.transform); + } + + [MenuItem("CONTEXT/Animator/Bone Renderer Setup", false, 612)] + static void BoneRendererSetup(MenuCommand command) + { + var animator = command.context as Animator; + + AnimationRiggingEditorUtils.BoneRendererSetup(animator.transform); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs.meta new file mode 100644 index 00000000..9addf5bf --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingContextMenus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91d51ce3535dd204d8a9271471f1583d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs new file mode 100644 index 00000000..d791552e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs @@ -0,0 +1,147 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + internal static class AnimationRiggingEditorUtils + { + public static void RigSetup(Transform transform) + { + var rigBuilder = transform.GetComponent(); + + if (rigBuilder == null) + rigBuilder = Undo.AddComponent(transform.gameObject); + else + Undo.RecordObject(rigBuilder, "Rig Builder Component Added."); + + var name = "Rig"; + var cnt = 1; + while (rigBuilder.transform.Find(string.Format("{0} {1}", name, cnt)) != null) + { + cnt++; + } + name = string.Format("{0} {1}", name, cnt); + var rigGameObject = new GameObject(name); + Undo.RegisterCreatedObjectUndo(rigGameObject, name); + rigGameObject.transform.SetParent(rigBuilder.transform); + + var rig = Undo.AddComponent(rigGameObject); + rigBuilder.layers.Add(new RigLayer(rig)); + + if (PrefabUtility.IsPartOfPrefabInstance(rigBuilder)) + EditorUtility.SetDirty(rigBuilder); + } + + public static void BoneRendererSetup(Transform transform) + { + var boneRenderer = transform.GetComponent(); + if (boneRenderer == null) + boneRenderer = Undo.AddComponent(transform.gameObject); + else + Undo.RecordObject(boneRenderer, "Bone renderer setup."); + + var animator = transform.GetComponent(); + var renderers = transform.GetComponentsInChildren(); + var bones = new List(); + if (animator != null && renderers != null && renderers.Length > 0) + { + for (int i = 0; i < renderers.Length; ++i) + { + var renderer = renderers[i]; + for (int j = 0; j < renderer.bones.Length; ++j) + { + var bone = renderer.bones[j]; + if (!bones.Contains(bone)) + { + bones.Add(bone); + + for (int k = 0; k < bone.childCount; k++) + { + if (!bones.Contains(bone.GetChild(k))) + bones.Add(bone.GetChild(k)); + } + } + } + } + } + else + { + bones.AddRange(transform.GetComponentsInChildren()); + } + + boneRenderer.transforms = bones.ToArray(); + + if (PrefabUtility.IsPartOfPrefabInstance(boneRenderer)) + EditorUtility.SetDirty(boneRenderer); + } + + public static void RestoreBindPose(Transform transform) + { + var animator = transform.GetComponentInParent(); + var root = (animator) ? animator.transform : transform; + var renderers = root.GetComponentsInChildren(); + + if (renderers.Length == 0) + { + Debug.LogError( + string.Format( + "Could not restore bind pose because no SkinnedMeshRenderers " + + "were found on {0} or any of its children.", root.name)); + return; + } + + Undo.RegisterFullObjectHierarchyUndo(root.gameObject, "Restore bind pose"); + + var bones = new Dictionary(); + foreach (var renderer in renderers) + { + for (int i = 0; i < renderer.bones.Length; ++i) + { + if (!bones.ContainsKey(renderer.bones[i])) + bones.Add(renderer.bones[i], renderer.sharedMesh.bindposes[i]); + } + } + + var transforms = transform.GetComponentsInChildren(); + var restoredPose = false; + foreach (var t in transforms) + { + if (!bones.ContainsKey(t)) + continue; + + // The root bone is the only bone in the skeleton + // hierarchy that does not have a parent bone. + var isRootBone = !bones.ContainsKey(t.parent); + + var matrix = bones[t]; + var wMatrix = matrix.inverse; + + if (!isRootBone) + { + if (t.parent) + matrix *= bones[t.parent].inverse; + matrix = matrix.inverse; + + t.localScale = new Vector3( + matrix.GetColumn(0).magnitude, + matrix.GetColumn(1).magnitude, + matrix.GetColumn(2).magnitude + ); + t.localPosition = matrix.MultiplyPoint(Vector3.zero); + } + t.rotation = wMatrix.rotation; + + restoredPose = true; + } + + if (!restoredPose) + { + Debug.LogWarning( + string.Format( + "No valid bindpose(s) have been found for the selected transform: {0}.", + transform.name)); + } + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs.meta new file mode 100644 index 00000000..e05043bc --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingEditorUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47c3d3ff34626ed49adac10f7ea74b25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs new file mode 100644 index 00000000..fd5f8fec --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs @@ -0,0 +1,90 @@ +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + internal static class AnimationRiggingMenu + { + static bool FilterSourceAndDestinationFromSelection(out Transform source, out Transform destination) + { + var selected = Selection.instanceIDs; + if (selected == null || selected.Length != 2) + { + source = destination = null; + return false; + } + + var srcGameObject = EditorUtility.InstanceIDToObject(selected[1]) as GameObject; + var dstGameObject = EditorUtility.InstanceIDToObject(selected[0]) as GameObject; + if (srcGameObject == null || dstGameObject == null) + { + source = destination = null; + return false; + } + + source = srcGameObject.transform; + destination = dstGameObject.transform; + + return true; + } + + [MenuItem("Animation Rigging/Align Transform", false, 0)] + static void PerformTransformAlign() + { + if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst)) + { + Undo.RecordObject(dst, "Align transform " + dst.name + " with " + src.name); + dst.SetPositionAndRotation(src.position, src.rotation); + } + } + + [MenuItem("Animation Rigging/Align Rotation", false, 0)] + static void PerformRotationAlign() + { + if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst)) + { + Undo.RecordObject(dst, "Align rotation of " + dst.name + " with " + src.name); + dst.rotation = src.rotation; + } + } + + [MenuItem("Animation Rigging/Align Position", false, 0)] + static void PerformPositionAlign() + { + if (FilterSourceAndDestinationFromSelection(out Transform src, out Transform dst)) + { + Undo.RecordObject(dst, "Align position of " + dst.name + " with " + src.name); + dst.position = src.position; + } + } + + [MenuItem("Animation Rigging/Restore Bind Pose", false, 11)] + static void RestoreBindPose() + { + var selection = Selection.activeTransform; + if (selection == null) + return; + + AnimationRiggingEditorUtils.RestoreBindPose(selection); + } + + [MenuItem("Animation Rigging/Rig Setup", false, 12)] + static void RigSetup() + { + var selection = Selection.activeTransform; + if (selection == null) + return; + + AnimationRiggingEditorUtils.RigSetup(selection); + } + + [MenuItem("Animation Rigging/Bone Renderer Setup", false, 13)] + static void BoneRendererSetup() + { + var selection = Selection.activeTransform; + if (selection == null) + return; + + AnimationRiggingEditorUtils.BoneRendererSetup(selection); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs.meta new file mode 100644 index 00000000..57d8895a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationRiggingMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 412e93db5b3ee8447bc607a8e21ca799 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs new file mode 100644 index 00000000..d35ad18b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs @@ -0,0 +1,85 @@ +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + [InitializeOnLoad] + static class AnimationWindowUtils + { + static AnimationWindow m_AnimationWindow = null; + + public static AnimationWindow animationWindow + { + get + { + if (m_AnimationWindow == null) + m_AnimationWindow = FindWindowOpen(); + + return m_AnimationWindow; + } + } + + public static AnimationClip activeAnimationClip + { + get + { + if (animationWindow != null) + return animationWindow.animationClip; + + return null; + } + set + { + if (animationWindow != null) + animationWindow.animationClip = value; + } + } + + public static void StartPreview() + { + if (animationWindow != null) + animationWindow.previewing = true; + } + + public static void StopPreview() + { + if (animationWindow != null) + animationWindow.previewing = false; + } + + public static bool isPreviewing + { + get + { + if (animationWindow != null) + return animationWindow.previewing; + + return false; + } + } + + // This does not check if there is an AnimationClip to play + public static bool canPreview + { + get + { + if (animationWindow != null) + return animationWindow.canPreview; + + return false; + } + } + + static AnimationWindow FindWindowOpen() + { + UnityEngine.Object[] objs = Resources.FindObjectsOfTypeAll(typeof(AnimationWindow)); + + foreach (UnityEngine.Object o in objs) + { + if (o.GetType() == typeof(AnimationWindow)) + return (AnimationWindow)o; + } + + return null; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs.meta new file mode 100644 index 00000000..0314ac04 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/AnimationWindowUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9235b286c8b543ab8660ef639271c0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs b/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs new file mode 100644 index 00000000..e551bf30 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs @@ -0,0 +1,879 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Animations.Rigging; +using UnityEngine.Playables; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// Utility class that groups bi-directional baking utilities. + /// + public static class BakeUtils + { + private const string kBakeToSkeletonUndoLabel = "Transfer motion to skeleton"; + private const string kBakeToConstraintUndoLabel = "Transfer motion to constraint"; + + interface IEvaluationGraph + { + void Evaluate(float time); + } + + class EvaluationGraph : IDisposable, IEvaluationGraph + { + SyncSceneToStreamLayer m_SyncSceneToStreamLayer; + List m_RigLayers; + PlayableGraph m_Graph; + + AnimationClip m_Clip; + bool m_ClipLoopTime; + + public EvaluationGraph(RigBuilder rigBuilder, AnimationClip clip, AnimationClip defaultPoseClip, IReadOnlyDictionary overrides, IRigConstraint lastConstraint = null) + { + m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer(); + + bool stopBuilding = false; + + var layers = rigBuilder.layers; + m_RigLayers = new List(layers.Count); + for (int i = 0; i < layers.Count; ++i) + { + if (stopBuilding == true) + break; + + if (layers[i].rig == null || !layers[i].active) + continue; + + IRigConstraint[] constraints = RigUtils.GetConstraints(layers[i].rig); + if (constraints == null || constraints.Length == 0) + continue; + + var newConstraints = new List(constraints.Length); + foreach (IRigConstraint constraint in constraints) + { + if (overrides.TryGetValue(constraint, out IRigConstraint newConstraint)) + { + if (newConstraint != null) + { + newConstraints.Add(newConstraint); + } + } + else + { + newConstraints.Add(constraint); + } + + if (constraint == lastConstraint) + { + stopBuilding = true; + break; + } + } + + m_RigLayers.Add(new OverrideRigLayer(layers[i].rig, newConstraints.ToArray())); + } + + m_Graph = PlayableGraph.Create("Evaluation-Graph"); + m_Graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); + + var animator = rigBuilder.GetComponent(); + + m_Clip = clip; + + var settings = AnimationUtility.GetAnimationClipSettings(m_Clip); + m_ClipLoopTime = settings.loopTime; + + // Override loop time in clip asset. + settings.loopTime = false; + AnimationUtility.SetAnimationClipSettings(m_Clip, settings); + + var defaultPosePlayable = AnimationClipPlayable.Create(m_Graph, defaultPoseClip); + var clipPlayable = AnimationClipPlayable.Create(m_Graph, m_Clip); + + defaultPosePlayable.SetApplyFootIK(false); + clipPlayable.SetApplyFootIK(false); + + AnimationLayerMixerPlayable mixer = AnimationLayerMixerPlayable.Create(m_Graph, 2); + mixer.ConnectInput(0, defaultPosePlayable, 0, 1.0f); + mixer.ConnectInput(1, clipPlayable, 0, 1.0f); + + Playable inputPlayable = mixer; + + var playableChains = RigBuilderUtils.BuildPlayables(animator, m_Graph, m_RigLayers, m_SyncSceneToStreamLayer); + foreach (var chain in playableChains) + { + if (!chain.IsValid()) + continue; + + chain.playables[0].AddInput(inputPlayable, 0, 1); + inputPlayable = chain.playables[chain.playables.Length - 1]; + } + + var output = AnimationPlayableOutput.Create(m_Graph, "bake-output", animator); + output.SetSourcePlayable(inputPlayable); + } + + public void Evaluate(float time) + { + if (!AnimationMode.InAnimationMode()) + return; + + m_SyncSceneToStreamLayer.Update(m_RigLayers); + + foreach (var layer in m_RigLayers) + { + if (layer.IsValid() && layer.active) + layer.Update(); + } + + AnimationMode.BeginSampling(); + AnimationMode.SamplePlayableGraph(m_Graph, 0, time); + AnimationMode.EndSampling(); + + m_SyncSceneToStreamLayer.LateUpdate(m_RigLayers); + + foreach (var layer in m_RigLayers) + { + if (layer.IsValid() && layer.active) + layer.LateUpdate(); + } + } + + public void Dispose() + { + m_Graph.Destroy(); + for (int i = 0; i < m_RigLayers.Count; ++i) + { + m_RigLayers[i].Reset(); + } + m_RigLayers.Clear(); + m_SyncSceneToStreamLayer.Reset(); + + // Restore loop time in clip asset. + var settings = AnimationUtility.GetAnimationClipSettings(m_Clip); + settings.loopTime = m_ClipLoopTime; + + AnimationUtility.SetAnimationClipSettings(m_Clip, settings); + } + } + + /// + /// Validates if the Editor and the provided RigBuilder are in a correct state to do motion transfer. + /// + /// The RigBuilder that will be used for motion transfer. + /// Returns true if both the editor and the provided RigBuilder are in a valid state for motion transfer. Returns false if the requirements are not met. + public static bool TransferMotionValidate(RigBuilder rigBuilder) + { + if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null) + return false; + + var selected = Selection.instanceIDs; + if (selected.Length != 1) + return false; + + var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject; + if (selectedGO != rigBuilder.gameObject) + return false; + + var animator = rigBuilder.GetComponent(); + if (animator.isHuman) + return false; + + return true; + } + + /// + /// Validates if the Editor and the provided Rig are in a correct state to do motion transfer. + /// + /// The Rig that will be used for motion transfer. + /// Returns true if both the editor and the provided Rig are in a valid state for motion transfer. Returns false if the requirements are not met. + public static bool TransferMotionValidate(Rig rig) + { + if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null) + return false; + + var selected = Selection.instanceIDs; + if (selected.Length != 1) + return false; + + var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject; + if (selectedGO != rig.gameObject) + return false; + + var rigBuilder = rig.GetComponentInParent(); + if (rigBuilder == null) + return false; + + var animator = rigBuilder.GetComponent(); + if (animator.isHuman) + return false; + + bool inRigBuilder = false; + var layers = rigBuilder.layers; + for (int i = 0; i < layers.Count; ++i) + { + if (layers[i].rig == rig && layers[i].active) + inRigBuilder = true; + } + + return inRigBuilder; + } + + /// + /// Validates if the Editor and the provided RigConstraint are in a correct state to do motion transfer. + /// + /// Type of RigConstraint that is to be validated. + /// The RigConstraint that will be used for motion transfer. + /// Returns true if both the editor and the provided RigConstraint are in a valid state for motion transfer. Returns false if the requirements are not met. + public static bool TransferMotionValidate(T constraint) + where T : MonoBehaviour, IRigConstraint + { + if (!AnimationWindowUtils.isPreviewing || AnimationWindowUtils.activeAnimationClip == null) + return false; + + var selected = Selection.instanceIDs; + if (selected.Length != 1) + return false; + + var selectedGO = EditorUtility.InstanceIDToObject(selected[0]) as GameObject; + if (selectedGO != constraint.gameObject) + return false; + + var rig = constraint.GetComponentInParent(); + if (rig == null) + return false; + + var rigBuilder = rig.GetComponentInParent(); + if (rigBuilder == null) + return false; + + var animator = rigBuilder.GetComponent(); + if (animator.isHuman) + return false; + + bool inRigBuilder = false; + var layers = rigBuilder.layers; + for (int i = 0; i < layers.Count; ++i) + { + if (layers[i].rig == rig && layers[i].active) + inRigBuilder = true; + } + + if (!inRigBuilder) + return false; + + return constraint.IsValid(); + } + + /// + /// Bakes motion from any RigConstraints in the RigBuilder to the skeleton. + /// + /// The RigBuilder whose RigConstraints are to be baked. + public static void TransferMotionToSkeleton(RigBuilder rigBuilder) + { + List layers = rigBuilder.layers; + List rigs = new List(layers.Count); + foreach(var layer in layers) + { + if (layer.rig != null && layer.active) + { + rigs.Add(layer.rig); + } + } + + TransferMotionToSkeleton(rigBuilder, rigs); + } + + /// + /// Bakes motion from any RigConstraints in the Rig to the skeleton. + /// + /// The Rig whose RigConstraints are to be baked. + public static void TransferMotionToSkeleton(Rig rig) + { + var rigBuilder = rig.GetComponentInParent(); + if (rigBuilder == null) + throw new InvalidOperationException("No rigbuilder was found in the hierarchy."); + + TransferMotionToSkeleton(rigBuilder, new Rig[]{rig}); + } + + private static void TransferMotionToSkeleton(RigBuilder rigBuilder, IEnumerable rigs) + { + var constraints = new List(); + foreach(var rig in rigs) + { + constraints.AddRange(RigUtils.GetConstraints(rig)); + } + + var clip = AnimationWindowUtils.activeAnimationClip; + + // Make sure we have a clip selected + if (clip == null) + { + throw new InvalidOperationException( + "There is no clip to work on." + + " The animation window must be open with an active clip!"); + } + + AnimationClip editableClip = clip; + if (!GetEditableClip(ref editableClip)) + return; + + AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder); + + Undo.RegisterCompleteObjectUndo(editableClip, kBakeToSkeletonUndoLabel); + + var animator = rigBuilder.GetComponent(); + if (editableClip != clip) + AddClipToAnimatorController(animator, editableClip); + + var bindingsToRemove = new HashSet(); + + foreach(IRigConstraint constraint in constraints) + { + var bakeParameters = FindBakeParameters(constraint); + if (bakeParameters == null || !bakeParameters.canBakeToSkeleton) + continue; + + // Flush out animation mode modifications + AnimationMode.BeginSampling(); + AnimationMode.EndSampling(); + + var bindings = bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint); + BakeToSkeleton(rigBuilder, constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToSkeletonCurveFilterOptions); + + bindingsToRemove.UnionWith(bakeParameters.GetSourceCurveBindings(rigBuilder, constraint)); + } + + // Remove weight curve & force constraint to be active + if (Preferences.forceConstraintWeightOnBake) + { + AnimationCurve zeroWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 0f); + + foreach(var rig in rigs) + { + AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, rig), zeroWeightCurve); + } + } + + if (Preferences.bakeToSkeletonAndRemoveCurves) + RemoveCurves(editableClip, bindingsToRemove); + } + + /// + /// Bakes motion from the RigConstraint to the skeleton. + /// + /// Type of RigConstraint that is to be baked. + /// The RigConstraint that will be baked to the skeleton. + public static void TransferMotionToSkeleton(T constraint) + where T : MonoBehaviour, IRigConstraint + { + var rigBuilder = constraint.GetComponentInParent(); + if (rigBuilder == null) + throw new InvalidOperationException("No rigbuilder was found in the hierarchy."); + + var bakeParameters = FindBakeParameters(constraint); + if (bakeParameters == null) + throw new InvalidOperationException(string.Format("Could not find BakeParameters class for constraint {0}.", constraint != null ? constraint.ToString() : "no-name")); + + if (!bakeParameters.canBakeToSkeleton) + throw new InvalidOperationException("Constraint disallows transfering motion to skeleton."); + + var bindings = bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint); + var clip = AnimationWindowUtils.activeAnimationClip; + + // Make sure we have a clip selected + if (clip == null) + { + throw new InvalidOperationException( + "There is no clip to work on." + + " The animation window must be open with an active clip!"); + } + + AnimationClip editableClip = clip; + if (!GetEditableClip(ref editableClip)) + return; + + AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder); + + Undo.RegisterCompleteObjectUndo(editableClip, kBakeToSkeletonUndoLabel); + + var animator = rigBuilder.GetComponent(); + if (editableClip != clip) + AddClipToAnimatorController(animator, editableClip); + + BakeToSkeleton(constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToSkeletonCurveFilterOptions); + + if (Preferences.forceConstraintWeightOnBake) + { + AnimationCurve zeroWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 0f); + AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, constraint), zeroWeightCurve); + } + + if (Preferences.bakeToSkeletonAndRemoveCurves) + RemoveCurves(editableClip, bakeParameters.GetSourceCurveBindings(rigBuilder, constraint)); + + } + + internal static void BakeToSkeleton(T constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable bindings, CurveFilterOptions filterOptions) + where T : MonoBehaviour, IRigConstraint + { + // Make sure we have a rigbuilder (which guarantees an animator). + var rigBuilder = constraint.GetComponentInParent(); + if (rigBuilder == null) + { + throw new InvalidOperationException( + "No rigbuilder was found in the hierarchy. " + + "A RigBuilder and Animator are required to construct valid bindings."); + } + + BakeToSkeleton(rigBuilder, constraint, clip, defaultPoseClip, bindings, filterOptions); + } + + private static void BakeToSkeleton(RigBuilder rigBuilder, IRigConstraint constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable bindings, CurveFilterOptions filterOptions) + { + // Make sure the base constraint is valid + if (constraint == null || !constraint.IsValid()) + { + throw new InvalidOperationException( + string.Format("The rig constraint {0} is not a valid constraint.", + constraint != null ? constraint.ToString() : "")); + } + + var overrides = new Dictionary(); + + using(var graph = new EvaluationGraph(rigBuilder, clip, defaultPoseClip, overrides, constraint)) + { + BakeCurvesToClip(clip, bindings, rigBuilder, graph, filterOptions); + } + } + + /// + /// Bakes motion from the skeleton to the constraints in the RigBuilder. + /// + /// The RigBuilder whose RigConstraints are to be baked. + public static void TransferMotionToConstraint(RigBuilder rigBuilder) + { + List layers = rigBuilder.layers; + List rigs = new List(layers.Count); + foreach(var layer in layers) + { + if (layer.rig != null && layer.active) + { + rigs.Add(layer.rig); + } + } + + TransferMotionToConstraint(rigBuilder, rigs); + } + + /// + /// Bakes motion from the skeleton to the constraints in the Rig. + /// + /// The Rig whose RigConstraints are to be baked. + public static void TransferMotionToConstraint(Rig rig) + { + var rigBuilder = rig.GetComponentInParent(); + if (rigBuilder == null) + throw new InvalidOperationException("No rigbuilder was found in the hierarchy."); + + TransferMotionToConstraint(rigBuilder, new Rig[]{rig}); + } + + private static void TransferMotionToConstraint(RigBuilder rigBuilder, IEnumerable rigs) + { + var constraints = new List(); + foreach(var rig in rigs) + { + constraints.AddRange(RigUtils.GetConstraints(rig)); + } + + var clip = AnimationWindowUtils.activeAnimationClip; + + // Make sure we have a clip selected + if (clip == null) + { + throw new InvalidOperationException( + "There is no clip to work on." + + " The animation window must be open with an active clip!"); + } + + AnimationClip editableClip = clip; + if (!GetEditableClip(ref editableClip)) + return; + + AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder); + + Undo.RegisterCompleteObjectUndo(editableClip, kBakeToConstraintUndoLabel); + + var animator = rigBuilder.GetComponent(); + if (editableClip != clip) + AddClipToAnimatorController(animator, editableClip); + + var bindingsToRemove = new HashSet(); + + // Remove weight curve & force constraint to be active + if (Preferences.forceConstraintWeightOnBake) + { + AnimationCurve oneWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 1f); + + foreach(var rig in rigs) + { + AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, rig), oneWeightCurve); + } + } + + foreach(IRigConstraint constraint in constraints) + { + var bakeParameters = FindBakeParameters(constraint); + if (bakeParameters == null || !bakeParameters.canBakeToConstraint) + continue; + + // Flush out animation mode modifications + AnimationMode.BeginSampling(); + AnimationMode.EndSampling(); + + var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint); + BakeToConstraint(rigBuilder, constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToConstraintCurveFilterOptions); + + bindingsToRemove.UnionWith(bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint)); + } + + if (Preferences.bakeToConstraintAndRemoveCurves) + RemoveCurves(editableClip, bindingsToRemove); + } + + /// + /// Bakes motion from the skeleton to the RigConstraint + /// + /// Type of RigConstraint that is to be baked. + /// The RigConstraint that will be baked to. + public static void TransferMotionToConstraint(T constraint) + where T : MonoBehaviour, IRigConstraint + { + var rigBuilder = constraint.GetComponentInParent(); + if (rigBuilder == null) + throw new InvalidOperationException("No rigbuilder was found in the hierarchy."); + + var bakeParameters = FindBakeParameters(constraint); + if (bakeParameters == null) + throw new InvalidOperationException(string.Format("Could not find BakeParameters class for constraint {0}.", constraint != null ? constraint.ToString() : "no-name")); + + if (!bakeParameters.canBakeToSkeleton) + throw new InvalidOperationException("Constraint disallows transfering motion to constraint."); + + var bindings = bakeParameters.GetSourceCurveBindings(rigBuilder, constraint); + var clip = AnimationWindowUtils.activeAnimationClip; + + // Make sure we have a clip selected + if (clip == null) + { + throw new InvalidOperationException( + "There is no clip to work on." + + " The animation window must be open with an active clip!"); + } + + AnimationClip editableClip = clip; + if (!GetEditableClip(ref editableClip)) + return; + + AnimationClip defaultPoseClip = CreateDefaultPose(rigBuilder); + + Undo.RegisterCompleteObjectUndo(editableClip, kBakeToConstraintUndoLabel); + + var animator = rigBuilder.GetComponent(); + if (editableClip != clip) + AddClipToAnimatorController(animator, editableClip); + + // Remove weight curve & force constraint to be active + if (Preferences.forceConstraintWeightOnBake) + { + AnimationCurve oneWeightCurve = AnimationCurve.Constant(0f, editableClip.length, 1f); + AnimationUtility.SetEditorCurve(editableClip, GetWeightCurveBinding(rigBuilder, constraint), oneWeightCurve); + } + + BakeToConstraint(constraint, editableClip, defaultPoseClip, bindings, Preferences.bakeToConstraintCurveFilterOptions); + + if (Preferences.bakeToConstraintAndRemoveCurves) + RemoveCurves(editableClip, bakeParameters.GetConstrainedCurveBindings(rigBuilder, constraint)); + } + + internal static void BakeToConstraint(T constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable bindings, CurveFilterOptions filterOptions) + where T : MonoBehaviour, IRigConstraint + { + // Make sure we have a rigbuilder (which guarantees an animator). + var rigBuilder = constraint.GetComponentInParent(); + if(rigBuilder == null) + { + throw new InvalidOperationException( + "No rigbuilder was found in the hierarchy. " + + "A RigBuilder and Animator are required to construct valid bindings."); + } + + BakeToConstraint(rigBuilder, constraint, clip, defaultPoseClip, bindings, filterOptions); + } + + private static void BakeToConstraint(RigBuilder rigBuilder, IRigConstraint constraint, AnimationClip clip, AnimationClip defaultPoseClip, IEnumerable bindings, CurveFilterOptions filterOptions) + { + // Make sure the base constraint is valid + if (constraint == null || !constraint.IsValid()) + { + throw new InvalidOperationException( + string.Format("The rig constraint {0} is not a valid constraint.", + constraint != null ? constraint.ToString() : "")); + } + + // Check if the constraint is inverse solvable + var inverseConstraint = FindInverseRigConstraint(constraint); + if (inverseConstraint == null) + { + throw new InvalidOperationException( + string.Format("No inverse rig constraint could be found for {0}.", + constraint.ToString())); + } + else if (!inverseConstraint.IsValid()) + { + throw new InvalidOperationException( + string.Format("The inverse rig constrain {1} for {0} is not a valid constraint.", + constraint.ToString(), + inverseConstraint.ToString())); + } + + var overrides = new Dictionary(); + overrides.Add(constraint, inverseConstraint); + + using(var graph = new EvaluationGraph(rigBuilder, clip, defaultPoseClip, overrides, constraint)) + { + BakeCurvesToClip(clip, bindings, rigBuilder, graph, filterOptions); + } + } + + private static AnimationClip CreateDefaultPose(RigBuilder rigBuilder) + { + if(rigBuilder == null) + throw new ArgumentNullException("It is not possible to bake curves without an RigBuilder."); + + var defaultPoseClip = new AnimationClip() { name = "DefaultPose" }; + + if (!AnimationMode.InAnimationMode()) + return defaultPoseClip; + + var bindings = new List(); + + var gameObjects = new Queue(); + gameObjects.Enqueue(rigBuilder.gameObject); + + while (gameObjects.Count > 0) + { + var gameObject = gameObjects.Dequeue(); + + EditorCurveBinding[] allBindings = AnimationUtility.GetAnimatableBindings(gameObject, rigBuilder.gameObject); + foreach (var binding in allBindings) + { + if (binding.isPPtrCurve) + continue; + + if (binding.type == typeof(GameObject)) + continue; + + UnityEngine.Object target = gameObject.GetComponent(binding.type); + if (!AnimationMode.IsPropertyAnimated(target, binding.propertyName)) + continue; + + bindings.Add(binding); + } + + // Iterate over all child GOs + for (int i = 0; i < gameObject.transform.childCount; i++) + { + Transform childTransform = gameObject.transform.GetChild(i); + gameObjects.Enqueue(childTransform.gameObject); + } + } + + // Flush out animation mode modifications + AnimationMode.BeginSampling(); + AnimationMode.EndSampling(); + + foreach(var binding in bindings) + { + float floatValue; + AnimationUtility.GetFloatValue(rigBuilder.gameObject, binding, out floatValue); + + var key = new Keyframe(0f, floatValue); + var curve = new AnimationCurve(new Keyframe[] {key}); + defaultPoseClip.SetCurve(binding.path, binding.type, binding.propertyName, curve); + } + + return defaultPoseClip; + } + + private static void BakeCurvesToClip(AnimationClip clip, IEnumerable bindings, RigBuilder rigBuilder, IEvaluationGraph graph, CurveFilterOptions filterOptions) + { + if(rigBuilder == null) + throw new ArgumentNullException("It is not possible to bake curves without an RigBuilder."); + + if (clip == null) + throw new ArgumentNullException("It is not possible to bake curves to a clip that is null."); + + if (!AnimationMode.InAnimationMode()) + throw new ArgumentException("AnimationMode must be active during bake operation."); + + var animator = rigBuilder.GetComponent(); + + var recorder = new GameObjectRecorder(animator.gameObject); + foreach (var binding in bindings) + recorder.Bind(binding); + + var frameCount = (int)(clip.length * clip.frameRate); + float dt = 1f / clip.frameRate; + float time = 0f; + + graph?.Evaluate(0f); + recorder.TakeSnapshot(0f); + + for (int frame = 1; frame <= frameCount; ++frame) + { + time = frame / clip.frameRate; + graph?.Evaluate(time); + recorder.TakeSnapshot(dt); + } + + var tempClip = new AnimationClip(); + recorder.SaveToClip(tempClip, clip.frameRate, filterOptions); + CopyCurvesToClip(tempClip, clip); + } + + private static void RemoveCurves(AnimationClip clip, IEnumerable bindings) + { + if (clip == null) + throw new ArgumentNullException("The destination animation clip cannot be null."); + + var rotationBinding = new EditorCurveBinding(); + foreach(var binding in bindings) + { + // Remove the correct editor curve binding for a rotation curves + if (EditorCurveBindingUtils.RemapRotationBinding(clip, binding, ref rotationBinding)) + AnimationUtility.SetEditorCurve(clip, rotationBinding, null); + else + AnimationUtility.SetEditorCurve(clip, binding, null); + } + } + + private static void CopyCurvesToClip(AnimationClip fromClip, AnimationClip toClip) + { + var rotationBinding = new EditorCurveBinding(); + var bindings = AnimationUtility.GetCurveBindings(fromClip); + foreach(var binding in bindings) + { + var curve = AnimationUtility.GetEditorCurve(fromClip, binding); + + if (EditorCurveBindingUtils.RemapRotationBinding(toClip, binding, ref rotationBinding)) + AnimationUtility.SetEditorCurve(toClip, rotationBinding, curve); + else + AnimationUtility.SetEditorCurve(toClip, binding, curve); + } + } + + private static IRigConstraint FindInverseRigConstraint(IRigConstraint constraint) + { + if (constraint == null) + return null; + + var inverseConstraintTypes = TypeCache.GetTypesWithAttribute(); + foreach (var inverseConstraintType in inverseConstraintTypes) + { + var attribute = inverseConstraintType.GetCustomAttribute(); + + if (attribute.baseConstraint == constraint.GetType()) + return (IRigConstraint)Activator.CreateInstance(inverseConstraintType, new object[] { constraint }); + } + + return null; + } + + internal static IBakeParameters FindBakeParameters(IRigConstraint constraint) + { + var constraintType = constraint.GetType(); + var bakeParametersTypes = TypeCache.GetTypesWithAttribute(); + foreach (var bakeParametersType in bakeParametersTypes) + { + if (!typeof(IBakeParameters).IsAssignableFrom(bakeParametersType)) + continue; + + var attribute = bakeParametersType.GetCustomAttribute(); + if (attribute.constraintType == constraintType) + return (IBakeParameters)Activator.CreateInstance(bakeParametersType); + } + + return null; + } + + private static EditorCurveBinding GetWeightCurveBinding(RigBuilder rigBuilder, Rig rig) + { + var path = AnimationUtility.CalculateTransformPath(rig.transform, rigBuilder.transform); + var binding = EditorCurveBinding.FloatCurve(path, typeof(Rig), ConstraintProperties.s_Weight); + return binding; + } + + private static EditorCurveBinding GetWeightCurveBinding(RigBuilder rigBuilder, T constraint) + where T : MonoBehaviour, IRigConstraint + { + var path = AnimationUtility.CalculateTransformPath(constraint.transform, rigBuilder.transform); + var binding = EditorCurveBinding.FloatCurve(path, typeof(T), ConstraintProperties.s_Weight); + return binding; + } + + private static bool GetEditableClip(ref AnimationClip clip) + { + if (clip == null) + return false; + + if ((clip.hideFlags & HideFlags.NotEditable) != 0) + { + var path = EditorUtility.SaveFilePanelInProject( + "Save new clip", + clip.name + "(Clone)", + "anim", + string.Format("Create an editable clone of the readonly clip {0}.", clip.name)); + + if (path == "") + return false; + + clip = UnityEngine.Object.Instantiate(clip); + AssetDatabase.CreateAsset(clip, path); + } + + return true; + } + + private static void AddClipToAnimatorController(Animator animator, AnimationClip clip) + { + RuntimeAnimatorController runtimeController = animator.runtimeAnimatorController; + + AnimatorController effectiveController = runtimeController as AnimatorController; + if (effectiveController == null) + { + AnimatorOverrideController overrideController = runtimeController as AnimatorOverrideController; + if (overrideController != null) + { + effectiveController = overrideController.runtimeAnimatorController as AnimatorController; + } + } + + if (effectiveController != null) + { + string title = "Add clip to controller"; + string message = String.Format("Do you want to add clip '{0}' to controller '{1}'?", clip.name, effectiveController.name); + if (EditorUtility.DisplayDialog(title, message, "yes", "no", DialogOptOutDecisionType.ForThisSession, "com.unity.animation.rigging-add-clip-to-controller")) + { + effectiveController.AddMotion(clip); + AnimationWindowUtils.activeAnimationClip = clip; + AnimationWindowUtils.StartPreview(); + } + } + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs.meta new file mode 100644 index 00000000..b83ea330 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BakeUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0dc49fe6569e4a94196eaacaa0de329c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs new file mode 100644 index 00000000..3f36e332 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs @@ -0,0 +1,82 @@ +using UnityEngine.Animations.Rigging; +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + [CustomEditor(typeof(BoneRenderer))] + [CanEditMultipleObjects] + class BoneRendererInspector : Editor + { + static readonly GUIContent k_BoneSizeLabel = new GUIContent("Bone Size"); + static readonly GUIContent k_BoneColorLabel = new GUIContent("Color"); + static readonly GUIContent k_BoneShapeLabel = new GUIContent("Shape"); + static readonly GUIContent k_TripodSizeLabel = new GUIContent("Tripod Size"); + + SerializedProperty m_DrawBones; + SerializedProperty m_BoneShape; + SerializedProperty m_BoneSize; + SerializedProperty m_BoneColor; + + SerializedProperty m_DrawTripods; + SerializedProperty m_TripodSize; + + SerializedProperty m_Transforms; + + public void OnEnable() + { + m_DrawBones = serializedObject.FindProperty("drawBones"); + m_BoneSize = serializedObject.FindProperty("boneSize"); + m_BoneShape = serializedObject.FindProperty("boneShape"); + m_BoneColor = serializedObject.FindProperty("boneColor"); + + m_DrawTripods = serializedObject.FindProperty("drawTripods"); + m_TripodSize = serializedObject.FindProperty("tripodSize"); + + m_Transforms = serializedObject.FindProperty("m_Transforms"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_DrawBones, k_BoneSizeLabel); + using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) + EditorGUILayout.PropertyField(m_BoneSize, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_BoneShape, k_BoneShapeLabel); + EditorGUILayout.PropertyField(m_BoneColor, k_BoneColorLabel); + EditorGUI.indentLevel--; + } + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PropertyField(m_DrawTripods, k_TripodSizeLabel); + using (new EditorGUI.DisabledScope(!m_DrawTripods.boolValue)) + EditorGUILayout.PropertyField(m_TripodSize, GUIContent.none); + EditorGUILayout.EndHorizontal(); + + bool isDragPerformed = Event.current.type == EventType.DragPerform; + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_Transforms, true); + bool boneRendererDirty = EditorGUI.EndChangeCheck(); + boneRendererDirty |= Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed"; + boneRendererDirty |= Event.current.type == EventType.Used && isDragPerformed; + + serializedObject.ApplyModifiedProperties(); + + if (boneRendererDirty) + { + for (int i = 0; i < targets.Length; i++) + { + var boneRenderer = targets[i] as BoneRenderer; + boneRenderer.ExtractBones(); + } + } + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs.meta new file mode 100644 index 00000000..79bfd7bb --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56b70e56bbf6f4b739c75015365a6dc1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs new file mode 100644 index 00000000..2ef6371d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs @@ -0,0 +1,498 @@ +using System.Collections.Generic; +using UnityEditor.Experimental.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + using BoneShape = BoneRenderer.BoneShape; + + [InitializeOnLoad] + static class BoneRendererUtils + { + private class BatchRenderer + { + const int kMaxDrawMeshInstanceCount = 1023; + + public enum SubMeshType + { + BoneFaces, + BoneWire, + Count + } + + public Mesh mesh; + public Material material; + + private List m_Matrices = new List(); + private List m_Colors = new List(); + private List m_Highlights = new List(); + + public void AddInstance(Matrix4x4 matrix, Color color, Color highlight) + { + m_Matrices.Add(matrix); + m_Colors.Add(color); + m_Highlights.Add(highlight); + } + + public void Clear() + { + m_Matrices.Clear(); + m_Colors.Clear(); + m_Highlights.Clear(); + } + + private static int RenderChunkCount(int totalCount) + { + return Mathf.CeilToInt((totalCount / (float)kMaxDrawMeshInstanceCount)); + } + + private static T[] GetRenderChunk(List array, int chunkIndex) + { + int rangeCount = (chunkIndex < (RenderChunkCount(array.Count) - 1)) ? + kMaxDrawMeshInstanceCount : array.Count - (chunkIndex * kMaxDrawMeshInstanceCount); + + return array.GetRange(chunkIndex * kMaxDrawMeshInstanceCount, rangeCount).ToArray(); + } + + public void Render() + { + if (m_Matrices.Count == 0 || m_Colors.Count == 0 || m_Highlights.Count == 0) + return; + + int count = System.Math.Min(m_Matrices.Count, System.Math.Min(m_Colors.Count, m_Highlights.Count)); + + Material mat = material; + mat.SetPass(0); + + MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock(); + CommandBuffer cb = new CommandBuffer(); + + Matrix4x4[] matrices = null; + + int chunkCount = RenderChunkCount(count); + for (int i = 0; i < chunkCount; ++i) + { + cb.Clear(); + matrices = GetRenderChunk(m_Matrices, i); + propertyBlock.SetVectorArray("_Color", GetRenderChunk(m_Colors, i)); + + material.DisableKeyword("WIRE_ON"); + cb.DrawMeshInstanced(mesh, (int)SubMeshType.BoneFaces, material, 0, matrices, matrices.Length, propertyBlock); + Graphics.ExecuteCommandBuffer(cb); + + cb.Clear(); + propertyBlock.SetVectorArray("_Color", GetRenderChunk(m_Highlights, i)); + + material.EnableKeyword("WIRE_ON"); + cb.DrawMeshInstanced(mesh, (int)SubMeshType.BoneWire, material, 0, matrices, matrices.Length, propertyBlock); + Graphics.ExecuteCommandBuffer(cb); + } + } + } + + static List s_BoneRendererComponents = new List(); + + private static BatchRenderer s_PyramidMeshRenderer; + private static BatchRenderer s_BoxMeshRenderer; + + private static Material s_Material; + + private const float k_Epsilon = 1e-5f; + + private const float k_BoneBaseSize = 2f; + private const float k_BoneTipSize = 0.5f; + + private static int s_ButtonHash = "BoneHandle".GetHashCode(); + + private static int s_VisibleLayersCache = 0; + + static BoneRendererUtils() + { + BoneRenderer.onAddBoneRenderer += OnAddBoneRenderer; + BoneRenderer.onRemoveBoneRenderer += OnRemoveBoneRenderer; + SceneVisibilityManager.visibilityChanged += OnVisibilityChanged; + EditorApplication.hierarchyChanged += OnHierarchyChanged; + + SceneView.duringSceneGui += DrawSkeletons; + + s_VisibleLayersCache = Tools.visibleLayers; + } + + private static Material material + { + get + { + if (!s_Material) + { + Shader shader = (Shader)EditorGUIUtility.LoadRequired("BoneHandles.shader"); + s_Material = new Material(shader); + s_Material.hideFlags = HideFlags.DontSaveInEditor; + s_Material.enableInstancing = true; + } + + return s_Material; + } + } + + private static BatchRenderer pyramidMeshRenderer + { + get + { + if (s_PyramidMeshRenderer == null) + { + var mesh = new Mesh(); + mesh.name = "BoneRendererPyramidMesh"; + mesh.subMeshCount = (int)BatchRenderer.SubMeshType.Count; + mesh.hideFlags = HideFlags.DontSave; + + // Bone vertices + Vector3[] vertices = new Vector3[] + { + new Vector3(0.0f, 1.0f, 0.0f), + new Vector3(0.0f, 0.0f, -1.0f), + new Vector3(-0.9f, 0.0f, 0.5f), + new Vector3(0.9f, 0.0f, 0.5f), + }; + + mesh.vertices = vertices; + + // Build indices for different sub meshes + int[] boneFaceIndices = new int[] + { + 0, 2, 1, + 0, 1, 3, + 0, 3, 2, + 1, 2, 3 + }; + mesh.SetIndices(boneFaceIndices, MeshTopology.Triangles, (int)BatchRenderer.SubMeshType.BoneFaces); + + int[] boneWireIndices = new int[] + { + 0, 1, 0, 2, 0, 3, 1, 2, 2, 3, 3, 1 + }; + mesh.SetIndices(boneWireIndices, MeshTopology.Lines, (int)BatchRenderer.SubMeshType.BoneWire); + + s_PyramidMeshRenderer = new BatchRenderer() + { + mesh = mesh, + material = material + }; + } + + return s_PyramidMeshRenderer; + } + } + + private static BatchRenderer boxMeshRenderer + { + get + { + if (s_BoxMeshRenderer == null) + { + var mesh = new Mesh(); + mesh.name = "BoneRendererBoxMesh"; + mesh.subMeshCount = (int)BatchRenderer.SubMeshType.Count; + mesh.hideFlags = HideFlags.DontSave; + + // Bone vertices + Vector3[] vertices = new Vector3[] + { + new Vector3(-0.5f, 0.0f, 0.5f), + new Vector3(0.5f, 0.0f, 0.5f), + new Vector3(0.5f, 0.0f, -0.5f), + new Vector3(-0.5f, 0.0f, -0.5f), + new Vector3(-0.5f, 1.0f, 0.5f), + new Vector3(0.5f, 1.0f, 0.5f), + new Vector3(0.5f, 1.0f, -0.5f), + new Vector3(-0.5f, 1.0f, -0.5f) + }; + + mesh.vertices = vertices; + + // Build indices for different sub meshes + int[] boneFaceIndices = new int[] + { + 0, 2, 1, + 0, 3, 2, + + 0, 1, 5, + 0, 5, 4, + + 1, 2, 6, + 1, 6, 5, + + 2, 3, 7, + 2, 7, 6, + + 3, 0, 4, + 3, 4, 7, + + 4, 5, 6, + 4, 6, 7 + }; + mesh.SetIndices(boneFaceIndices, MeshTopology.Triangles, (int)BatchRenderer.SubMeshType.BoneFaces); + + int[] boneWireIndices = new int[] + { + 0, 1, 1, 2, 2, 3, 3, 0, + 4, 5, 5, 6, 6, 7, 7, 4, + 0, 4, 1, 5, 2, 6, 3, 7 + }; + mesh.SetIndices(boneWireIndices, MeshTopology.Lines, (int)BatchRenderer.SubMeshType.BoneWire); + + s_BoxMeshRenderer = new BatchRenderer() + { + mesh = mesh, + material = material + }; + + } + + return s_BoxMeshRenderer; + } + } + + private static Matrix4x4 ComputeBoneMatrix(Vector3 start, Vector3 end, float length, float size) + { + Vector3 direction = (end - start) / length; + Vector3 tangent = Vector3.Cross(direction, Vector3.up); + if (Vector3.SqrMagnitude(tangent) < 0.1f) + tangent = Vector3.Cross(direction, Vector3.right); + tangent.Normalize(); + Vector3 bitangent = Vector3.Cross(direction, tangent); + + float scale = length * k_BoneBaseSize * size; + + return new Matrix4x4( + new Vector4(tangent.x * scale, tangent.y * scale, tangent.z * scale , 0f), + new Vector4(direction.x * length, direction.y * length, direction.z * length, 0f), + new Vector4(bitangent.x * scale, bitangent.y * scale, bitangent.z * scale , 0f), + new Vector4(start.x, start.y, start.z, 1f)); + } + + static void DrawSkeletons(SceneView sceneview) + { + if (Tools.visibleLayers != s_VisibleLayersCache) + { + OnVisibilityChanged(); + s_VisibleLayersCache = Tools.visibleLayers; + } + + var gizmoColor = Gizmos.color; + + pyramidMeshRenderer.Clear(); + boxMeshRenderer.Clear(); + + for (var i = 0; i < s_BoneRendererComponents.Count; i++) + { + var boneRenderer = s_BoneRendererComponents[i]; + + if (boneRenderer.bones == null) + continue; + + PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null) + { + StageHandle stageHandle = prefabStage.stageHandle; + if (stageHandle.IsValid() && !stageHandle.Contains(boneRenderer.gameObject)) + continue; + } + + if (boneRenderer.drawBones) + { + var size = boneRenderer.boneSize * 0.025f; + var shape = boneRenderer.boneShape; + var color = boneRenderer.boneColor; + var nubColor = new Color(color.r, color.g, color.b, color.a); + var selectionColor = Color.white; + + for (var j = 0; j < boneRenderer.bones.Length; j++) + { + var bone = boneRenderer.bones[j]; + if (bone.first == null || bone.second == null) + continue; + + DoBoneRender(bone.first, bone.second, shape, color, size); + } + + for (var k = 0; k < boneRenderer.tips.Length; k++) + { + var tip = boneRenderer.tips[k]; + if (tip == null) + continue; + + DoBoneRender(tip, null, shape, color, size); + } + } + + if (boneRenderer.drawTripods) + { + var size = boneRenderer.tripodSize * 0.025f; + for (var j = 0; j < boneRenderer.transforms.Length; j++) + { + var tripodSize = 1f; + var transform = boneRenderer.transforms[j]; + if (transform == null) + continue; + + var position = transform.position; + var xAxis = position + transform.rotation * Vector3.right * size * tripodSize; + var yAxis = position + transform.rotation * Vector3.up * size * tripodSize; + var zAxis = position + transform.rotation * Vector3.forward * size * tripodSize; + + Handles.color = Color.red; + Handles.DrawLine(position, xAxis); + Handles.color = Color.green; + Handles.DrawLine(position, yAxis); + Handles.color = Color.blue; + Handles.DrawLine(position, zAxis); + } + } + } + + pyramidMeshRenderer.Render(); + boxMeshRenderer.Render(); + + Gizmos.color = gizmoColor; + } + + + private static void DoBoneRender(Transform transform, Transform childTransform, BoneShape shape, Color color, float size) + { + Vector3 start = transform.position; + Vector3 end = childTransform != null ? childTransform.position : start; + + GameObject boneGO = transform.gameObject; + + float length = (end - start).magnitude; + bool tipBone = (length < k_Epsilon); + + int id = GUIUtility.GetControlID(s_ButtonHash, FocusType.Passive); + Event evt = Event.current; + + switch (evt.GetTypeForControl(id)) + { + case EventType.Layout: + { + HandleUtility.AddControl(id, tipBone ? HandleUtility.DistanceToCircle(start, k_BoneTipSize * size * 0.5f) : HandleUtility.DistanceToLine(start, end)); + break; + } + case EventType.MouseMove: + if (id == HandleUtility.nearestControl) + HandleUtility.Repaint(); + break; + case EventType.MouseDown: + { + if (evt.alt) + break; + + if (HandleUtility.nearestControl == id && evt.button == 0) + { + if (!SceneVisibilityManager.instance.IsPickingDisabled(boneGO, false)) + { + GUIUtility.hotControl = id; // Grab mouse focus + EditorHelper.HandleClickSelection(boneGO, evt); + evt.Use(); + } + } + break; + } + case EventType.MouseDrag: + { + if (!evt.alt && GUIUtility.hotControl == id) + { + if (!SceneVisibilityManager.instance.IsPickingDisabled(boneGO, false)) + { + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new UnityEngine.Object[] {transform}; + DragAndDrop.StartDrag(ObjectNames.GetDragAndDropTitle(transform)); + + GUIUtility.hotControl = 0; + + evt.Use(); + } + } + break; + } + case EventType.MouseUp: + { + if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2)) + { + GUIUtility.hotControl = 0; + evt.Use(); + } + break; + } + case EventType.Repaint: + { + Color highlight = color; + + bool hoveringBone = GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id; + hoveringBone = hoveringBone && !SceneVisibilityManager.instance.IsPickingDisabled(transform.gameObject, false); + + if (hoveringBone) + { + highlight = Handles.preselectionColor; + } + else if (Selection.Contains(boneGO) || Selection.activeObject == boneGO) + { + highlight = Handles.selectedColor; + } + + if (tipBone) + { + Handles.color = highlight; + Handles.SphereHandleCap(0, start, Quaternion.identity, k_BoneTipSize * size, EventType.Repaint); + } + else if (shape == BoneShape.Line) + { + Handles.color = highlight; + Handles.DrawLine(start, end); + } + else + { + if (shape == BoneShape.Pyramid) + pyramidMeshRenderer.AddInstance(ComputeBoneMatrix(start, end, length, size), color, highlight); + else // if (shape == BoneShape.Box) + boxMeshRenderer.AddInstance(ComputeBoneMatrix(start, end, length, size), color, highlight); + } + + } + break; + } + } + + public static void OnAddBoneRenderer(BoneRenderer obj) + { + s_BoneRendererComponents.Add(obj); + } + + public static void OnRemoveBoneRenderer(BoneRenderer obj) + { + s_BoneRendererComponents.Remove(obj); + } + + public static void OnVisibilityChanged() + { + foreach(var boneRenderer in s_BoneRendererComponents) + { + boneRenderer.Invalidate(); + } + + SceneView.RepaintAll(); + } + + public static void OnHierarchyChanged() + { + foreach(var boneRenderer in s_BoneRendererComponents) + { + boneRenderer.Invalidate(); + } + + SceneView.RepaintAll(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs.meta new file mode 100644 index 00000000..c4884ae8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/BoneRendererUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47d08c0b946e946b5bd8fc603eeed5b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs b/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs new file mode 100644 index 00000000..5f3c7f28 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs @@ -0,0 +1,85 @@ +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + static class CommonContent + { + public static readonly GUIContent constrainedAxesPosition = EditorGUIUtility.TrTextContent( + "Constrained Axes", + "Specifies the axes to which the constraint can apply translation." + ); + + public static readonly GUIContent constrainedAxesRotation = EditorGUIUtility.TrTextContent( + "Constrained Axes", + "Specifies the axes to which the constraint can apply rotation." + ); + + public static readonly GUIContent constrainedObject = EditorGUIUtility.TrTextContent( + "Constrained Object", + "The GameObject affected by the Source Objects." + ); + + public static readonly GUIContent maintainOffset = EditorGUIUtility.TrTextContent( + "Maintain Offset", + "Specifies whether to maintain the initial offset between the Constrained Object and the Source Objects" + ); + + public static readonly GUIContent maintainPositionOffset = EditorGUIUtility.TrTextContent( + "Maintain Offset", + "Specifies whether to maintain the initial position offset between the Constrained Object and the Source Objects." + ); + + public static readonly GUIContent maintainRotationOffset = EditorGUIUtility.TrTextContent( + "Maintain Offset", + "Specifies whether to maintain the initial rotation offset between the Constrained Object and the Source Objects." + ); + + public static readonly GUIContent maintainIKTargetOffset = EditorGUIUtility.TrTextContent( + "Maintain Target Offset", + "Specifies whether to maintain the initial offset between the Tip and the Target." + ); + + public static readonly GUIContent offsetPosition = EditorGUIUtility.TrTextContent( + "Offset", + "Specifies an additional local space translation offset to apply to the Constrained Object, after it has been translated toward its target." + ); + + public static readonly GUIContent offsetRotation = EditorGUIUtility.TrTextContent( + "Offset", + "Specifies an additional local space rotation offset to apply to the Constrained Object, after it has been rotated toward its target." + ); + + public static readonly GUIContent settings = EditorGUIUtility.TrTextContent( + "Settings" + ); + + public static readonly GUIContent sourceObjects = EditorGUIUtility.TrTextContent( + "Source Objects", + "The list of GameObjects that influence the Constrained Object's position and orientation, and the amount of weight they contribute to the final pose. " + + "The constraint applies linearly interpolated, weighted translation and rotation toward each target. " + + "The order of Source Objects does not affect the result." + ); + + public static readonly GUIContent sourceObjectsWeightedPosition = EditorGUIUtility.TrTextContent( + "Source Objects", + "The list of GameObjects that influence the Constrained Object's position, and the amount of weight they contribute to the final pose. " + + "The constraint calculates translation toward each target to produce a weighted sum. " + + "The order of Source Objects does not affect the result." + ); + + public static readonly GUIContent sourceObjectsWeightedRotation = EditorGUIUtility.TrTextContent( + "Source Objects", + "The list of GameObjects that influence the Constrained Object's orientation, and the amount of weight they contribute to the final pose. " + + "The constraint calculates rotation toward each target to produce a weighted sum. " + + "The order of Source Objects does not affect the result." + ); + + public static readonly GUIContent weight = EditorGUIUtility.TrTextContent( + "Weight", + "The overall weight of the constraint. " + + "If set to 0, the constraint has no influence on the Constrained Object. " + + "When set to 1, it applies full influence with the current settings. " + + "Intermediate values are interpolated linearly." + ); + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs.meta new file mode 100644 index 00000000..5b4bc2ab --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/CommonContent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5f0a8cdd0db7492b962f3f63b841f506 +timeCreated: 1607533731 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs b/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs new file mode 100644 index 00000000..c2f1af95 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// Utility class that provides an easy way of retrieving EditorCurveBindings for common data types. + /// + public static class EditorCurveBindingUtils + { + /// + /// Collects EditorCurveBindings for a Vector3 on a MonoBehavior. + /// + /// The Type of the MonoBehavior the Vector3 is found on. + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Vector3. + /// The MonoBehavior on which the Vector3 is found. + /// Name of the Vector3 variable we are constructing a binding for. + /// List to which the bindings for the Vector3 will be appended. + public static void CollectVector3Bindings(Transform root, T component, string propertyName, List bindings) + where T : MonoBehaviour + { + if (root == null || component == null || propertyName == "" || bindings == null) + throw new ArgumentNullException("Arguments cannot be null."); + + var path = AnimationUtility.CalculateTransformPath(component.transform, root); + + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".x")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".y")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(T), propertyName + ".z")); + } + + /// + /// Collects translation, rotation and scale bindings for a Transform component. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Transform. + /// The transform whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectTRSBindings(Transform root, Transform transform, List bindings) + { + CollectPositionBindings(root, transform, bindings); + CollectRotationBindings(root, transform, bindings); + CollectScaleBindings(root, transform, bindings); + } + + /// + /// Collects translation, rotation bindings for a Transform component. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Transform. + /// The transform whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectTRBindings(Transform root, Transform transform, List bindings) + { + CollectPositionBindings(root, transform, bindings); + CollectRotationBindings(root, transform, bindings); + } + + /// + /// Collects translation bindings for a Transform component. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Transform. + /// The transform whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectPositionBindings(Transform root, Transform transform, List bindings) + { + if (root == null || transform == null || bindings == null) + throw new ArgumentNullException("Arguments cannot be null."); + + var path = AnimationUtility.CalculateTransformPath(transform, root); + + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.x")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.y")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalPosition.z")); + } + + /// + /// Collects rotation bindings for a Transform component. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Transform. + /// The transform whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectRotationBindings(Transform root, Transform transform, List bindings) + { + if (root == null || transform == null || bindings == null) + throw new ArgumentNullException("Arguments cannot be null."); + + var path = AnimationUtility.CalculateTransformPath(transform, root); + + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.x")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.y")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "localEulerAnglesRaw.z")); + } + + /// + /// Collects scale bindings for a Transform component. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the Transform. + /// The transform whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectScaleBindings(Transform root, Transform transform, List bindings) + { + if (root == null || transform == null || bindings == null) + throw new ArgumentNullException("Arguments cannot be null."); + + var path = AnimationUtility.CalculateTransformPath(transform, root); + + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.x")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.y")); + bindings.Add(EditorCurveBinding.FloatCurve(path, typeof(Transform), "m_LocalScale.z")); + } + + /// + /// Collects the binding for a single float property on a MonoBehavior. + /// + /// The root to which the bindings are relative. Generally the root has the Animator which animates the float property. + /// The component on which the property is found. + /// The name of the float property whose bindings are collected. + /// List to which the bindings for the Transform will be appended. + public static void CollectPropertyBindings(Transform root, MonoBehaviour component, string propertyName, List bindings) + { + if (root == null || component == null || bindings == null) + throw new ArgumentNullException("Arguments cannot be null."); + + var path = AnimationUtility.CalculateTransformPath(component.transform, root); + + bindings.Add(EditorCurveBinding.FloatCurve(path, component.GetType(), propertyName)); + } + + internal static bool RemapRotationBinding(AnimationClip clip, EditorCurveBinding binding, ref EditorCurveBinding rotationBinding) + { + if (!binding.propertyName.StartsWith("localEulerAngles")) + return false; + + string suffix = binding.propertyName.Split('.')[1]; + + rotationBinding = binding; + + // Euler Angles + rotationBinding.propertyName = "localEulerAnglesRaw." + suffix; + if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null) + return true; + + // Euler Angles (Quaternion) interpolation + rotationBinding.propertyName = "localEulerAnglesBaked." + suffix; + if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null) + return true; + + // Quaternion interpolation + rotationBinding.propertyName = "localEulerAngles." + suffix; + if (AnimationUtility.GetEditorCurve(clip, rotationBinding) != null) + return true; + + return false; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs.meta new file mode 100644 index 00000000..48fe50fe --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/EditorCurveBindingUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3827b1eea1bd6048965bb412d1daabc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs b/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs new file mode 100644 index 00000000..67f4b99f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs @@ -0,0 +1,221 @@ +using System; +using UnityEngine; +using UnityEngine.Animations.Rigging; +using UnityEditorInternal; + +namespace UnityEditor.Animations.Rigging +{ + /// + /// Helper class to manage ReorderableList appearance in the editor. + /// + public static class ReorderableListHelper + { + const int k_NoHeaderHeight = 2; + const int k_ElementHeightPadding = 2; + + /// + /// Creates a ReorderableList using a SerializedProperty array as source. + /// + /// SerializedObject owning the SerializedProperty. + /// SerializedProperty array. + /// Toggles whether an object is draggable in the list. True when an object is draggable, false otherwise. + /// Displays the ReorderableList header. + /// Returns a new ReorderableList. + public static ReorderableList Create(SerializedObject obj, SerializedProperty property, bool draggable = true, bool displayHeader = false) + { + var list = new ReorderableList(obj, property, draggable, displayHeader, true, true); + + list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => + { + var element = list.serializedProperty.GetArrayElementAtIndex(index); + + var offset = k_ElementHeightPadding * 0.5f; + rect.y += offset; + rect.height = EditorGUIUtility.singleLineHeight; + + EditorGUI.PropertyField(rect, element, GUIContent.none); + }; + + list.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding; + + if (!displayHeader) + list.headerHeight = k_NoHeaderHeight; + + return list; + } + } + + /// + /// Helper class to manage WeightedTransform and WeightedTransformArray appearance in the editor. + /// + [Obsolete("This is now handled automatically in the inspector for WeightedTransformArray.", false)] + public static class WeightedTransformHelper + { + const int k_NoHeaderHeight = 2; + const int k_ElementHeightPadding = 2; + + /// + /// Creates a ReorderableList using a WeightedTransformArray as source. + /// + /// The serialized property of the WeightedTransformArray. + /// The source WeightedTransformArray. + /// Range attribute given for weights in WeightedTransform. No boundaries are set if null. + /// Toggles whether a WeightedTransform is draggable in the list. True when WeightedTransform is draggable, false otherwise. + /// Displays the ReorderableList header. + /// Returns a new ReorderableList for a WeightedTransformArray. + public static ReorderableList CreateReorderableList(SerializedProperty property, ref WeightedTransformArray array, RangeAttribute range = null, bool draggable = true, bool displayHeader = false) + { + var reorderableList = new ReorderableList(array, typeof(WeightedTransform), draggable, displayHeader, true, true); + + reorderableList.drawElementBackgroundCallback = (Rect rect, int index, bool isActive, bool isFocused) => + { + // Didn't find a way to register a callback before we repaint the reorderable list to toggle draggable flag. + // Ideally, we'd need a callback canReorderElementCallback that would enable/disable draggable handle. + reorderableList.draggable = !AnimationMode.InAnimationMode() && !Application.isPlaying; + ReorderableList.defaultBehaviours.DrawElementBackground(rect, index, isActive, isFocused, reorderableList.draggable); + }; + + reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => + { + var element = property.FindPropertyRelative("m_Item" + index); + + var offset = k_ElementHeightPadding * 0.5f; + rect.y += offset; + rect.height = EditorGUIUtility.singleLineHeight; + + EditorGUI.BeginChangeCheck(); + + WeightedTransformOnGUI(rect, element, range); + + if (EditorGUI.EndChangeCheck()) + { + var transformProperty = element.FindPropertyRelative("transform"); + var weightProperty = element.FindPropertyRelative("weight"); + + reorderableList.list[index] = new WeightedTransform(transformProperty.objectReferenceValue as Transform, weightProperty.floatValue);; + } + }; + + reorderableList.onCanAddCallback = (ReorderableList list) => + { + return list.list.Count < WeightedTransformArray.k_MaxLength && !AnimationMode.InAnimationMode() && !Application.isPlaying; + }; + + reorderableList.onCanRemoveCallback = (ReorderableList list) => + { + return !AnimationMode.InAnimationMode() && !Application.isPlaying; + }; + + reorderableList.onAddCallback = (ReorderableList list) => + { + list.list.Add(WeightedTransform.Default(1f)); + }; + + reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding; + + if (!displayHeader) + reorderableList.headerHeight = k_NoHeaderHeight; + + return reorderableList; + } + + /// + /// Display a single WeightedTransform in the editor. + /// + /// Rectangle on the screen to use for the WeightedTransform. + /// Serialized property + /// Range attribute given for weights in WeightedTransform. No boundaries are set if null. + public static void WeightedTransformOnGUI(Rect rect, SerializedProperty property, RangeAttribute range = null) => + WeightedTransformDrawer.DoGUI(rect, property, range?.min ?? float.NaN, range?.max ?? float.NaN); + } + + internal static class MaintainOffsetHelper + { + static readonly string[] k_MaintainOffsetTypeLables = { "None", "Position and Rotation", "Position", "Rotation"}; + static readonly int[] k_BitsToIndex = new int[] {0, 2, 3, 1}; + static readonly int[] k_IndexToBits = new int[] {0, 3, 1, 2}; + + public static void DoDropdown(GUIContent label, SerializedProperty maintainPosition, SerializedProperty maintainRotation) + { + int currIndex = k_BitsToIndex[System.Convert.ToInt32(maintainPosition.boolValue) | (System.Convert.ToInt32(maintainRotation.boolValue) << 1)]; + int newIndex = EditorGUILayout.Popup(label, currIndex, k_MaintainOffsetTypeLables); + if (newIndex == currIndex) + return; + + var bits = k_IndexToBits[newIndex]; + maintainPosition.boolValue = (bits & 0x1) != 0; + maintainRotation.boolValue = (bits & 0x2) != 0; + } + } + + internal static class EditorHelper + { + private const string EditorFolder = "Packages/com.unity.animation.rigging/Editor/"; + private const string ShadersFolder = EditorFolder + "Shaders/"; + private const string ShapesFolder = EditorFolder + "Shapes/"; + + public static Shader LoadShader(string filename) + { + return AssetDatabase.LoadAssetAtPath(ShadersFolder + filename); + } + + public static Mesh LoadShape(string filename) + { + return AssetDatabase.LoadAssetAtPath(ShapesFolder + filename); + } + + public static T GetClosestComponent(Transform transform, Transform root = null) + { + if (transform == null) + return default(T); + + var top = (root != null) ? root : transform.root; + + while (true) + { + bool gotComponent = transform.TryGetComponent(out T component); + if (gotComponent) return component; + if (transform == top) break; + transform = transform.parent; + } + + return default(T); + } + + public static void HandleClickSelection(GameObject gameObject, Event evt) + { + if (evt.shift || EditorGUI.actionKey) + { + UnityEngine.Object[] existingSelection = Selection.objects; + + // For shift, we check if EXACTLY the active GO is hovered by mouse and then subtract. Otherwise additive. + // For control/cmd, we check if ANY of the selected GO is hovered by mouse and then subtract. Otherwise additive. + // Control/cmd takes priority over shift. + bool subtractFromSelection = EditorGUI.actionKey ? Selection.Contains(gameObject) : Selection.activeGameObject == gameObject; + if (subtractFromSelection) + { + // subtract from selection + var newSelection = new UnityEngine.Object[existingSelection.Length - 1]; + + int index = Array.IndexOf(existingSelection, gameObject); + + System.Array.Copy(existingSelection, newSelection, index); + System.Array.Copy(existingSelection, index + 1, newSelection, index, newSelection.Length - index); + + Selection.objects = newSelection; + } + else + { + // add to selection + var newSelection = new UnityEngine.Object[existingSelection.Length + 1]; + System.Array.Copy(existingSelection, newSelection, existingSelection.Length); + newSelection[existingSelection.Length] = gameObject; + + Selection.objects = newSelection; + } + } + else + Selection.activeObject = gameObject; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs.meta new file mode 100644 index 00000000..9bc26dc2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/EditorHelpers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5683d572ae4e82746901186dcc3f26cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs b/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs new file mode 100644 index 00000000..769c0ba0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomPropertyDrawer(typeof(ExpandChildrenAttribute))] + class ExpandChildrenDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + property.isExpanded = true; + return EditorGUI.GetPropertyHeight(property) + - EditorGUIUtility.standardVerticalSpacing + - EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + var endProperty = property.GetEndProperty(); + var childProperty = property.Copy(); + childProperty.NextVisible(true); + while (!SerializedProperty.EqualContents(childProperty, endProperty)) + { + position.height = EditorGUI.GetPropertyHeight(childProperty); + OnChildPropertyGUI(position, childProperty); + position.y += position.height + EditorGUIUtility.standardVerticalSpacing; + childProperty.NextVisible(false); + } + } + + protected virtual void OnChildPropertyGUI(Rect position, SerializedProperty childProperty) + { + EditorGUI.PropertyField(position, childProperty, true); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs.meta new file mode 100644 index 00000000..62df6eb5 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/ExpandChildrenDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc65bad2b0f4a43c4a49ffb6bfb2437a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs b/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs new file mode 100644 index 00000000..3e789afc --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs @@ -0,0 +1,43 @@ +namespace UnityEditor.Animations.Rigging +{ + /// + /// Class to easily store a foldout state for a custom editor. + /// + class FoldoutState + { + bool m_Initialized; + bool m_Value; + string m_Name; + + public bool value + { + get + { + if (!m_Initialized) + { + m_Value = EditorPrefs.GetBool(m_Name, m_Value); + m_Initialized = true; + } + return m_Value; + } + set + { + if (m_Value != value) + EditorPrefs.SetBool(m_Name, m_Value = value); + } + } + + FoldoutState() {} + + public static FoldoutState Create(string name, bool value) => + new FoldoutState + { + m_Name = $"{typeof(T)}.{name}", + m_Value = value + }; + + public static FoldoutState ForSettings() => Create("Settings", false); + + public static FoldoutState ForSourceObjects() => Create("SourceObjects", true); + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs.meta new file mode 100644 index 00000000..a70906bf --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/FoldoutState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dfb779bb474841c58c97b2660149692e +timeCreated: 1608320504 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs b/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs new file mode 100644 index 00000000..e5bd714d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs @@ -0,0 +1,271 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor.Animations.Rigging +{ + internal static class Preferences + { + static readonly string k_Prefix = "com.unity.animation.rigging"; + + static readonly string k_BakeToConstraintPrefix = k_Prefix + ".bakeToConstraint"; + static readonly string k_BakeToSkeletonPrefix = k_Prefix + ".bakeToSkeleton"; + + static readonly string k_UnrollRotation = ".unrollRotation"; + static readonly string k_KeyReduceSuffix = ".keyReduceEnable"; + static readonly string k_KeyReducePositionErrorSuffix = ".keyReducePositionError"; + static readonly string k_KeyReduceRotationErrorSuffix = ".keyReduceRotationError"; + static readonly string k_KeyReduceScaleErrorSuffix = ".keyReduceScaleError"; + static readonly string k_KeyReduceFloatErrorSuffix = ".keyReduceFloatError"; + + static readonly string k_RemoveCurvesSuffix = ".removeCurves"; + static readonly string k_ForceWeightSuffix = ".forceWeight"; + + static CurveFilterOptions m_BakeToConstraintCurveFilterOptions; + static CurveFilterOptions m_BakeToSkeletonCurveFilterOptions; + + static bool m_BakeToConstraintAndRemoveCurves; + static bool m_BakeToSkeletonAndRemoveCurves; + + static bool m_ForceConstraintWeightOnBake; + + static Preferences() + { + m_BakeToConstraintCurveFilterOptions = new CurveFilterOptions() + { + unrollRotation = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_UnrollRotation, true), + keyframeReduction = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_KeyReduceSuffix, true), + positionError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReducePositionErrorSuffix, 0.5f), + rotationError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceRotationErrorSuffix, 0.5f), + scaleError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceScaleErrorSuffix, 0.5f), + floatError = EditorPrefs.GetFloat(k_BakeToConstraintPrefix + k_KeyReduceFloatErrorSuffix, 0.5f) + }; + + m_BakeToSkeletonCurveFilterOptions = new CurveFilterOptions() + { + unrollRotation = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_UnrollRotation, true), + keyframeReduction = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_KeyReduceSuffix, true), + positionError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReducePositionErrorSuffix, 0.5f), + rotationError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceRotationErrorSuffix, 0.5f), + scaleError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceScaleErrorSuffix, 0.5f), + floatError = EditorPrefs.GetFloat(k_BakeToSkeletonPrefix + k_KeyReduceFloatErrorSuffix, 0.5f) + }; + + m_BakeToConstraintAndRemoveCurves = EditorPrefs.GetBool(k_BakeToConstraintPrefix + k_RemoveCurvesSuffix, false); + m_BakeToSkeletonAndRemoveCurves = EditorPrefs.GetBool(k_BakeToSkeletonPrefix + k_RemoveCurvesSuffix, false); + + m_ForceConstraintWeightOnBake = EditorPrefs.GetBool(k_Prefix + k_ForceWeightSuffix, true); + } + + public static void SetDefaultValues() + { + var defaultOptions = new CurveFilterOptions() + { + unrollRotation = true, + keyframeReduction = true, + positionError = .5f, + rotationError = .5f, + scaleError = .5f, + floatError = .5f + }; + + bakeToConstraintCurveFilterOptions = defaultOptions; + bakeToSkeletonCurveFilterOptions = defaultOptions; + + bakeToConstraintAndRemoveCurves = false; + bakeToSkeletonAndRemoveCurves = false; + + forceConstraintWeightOnBake = true; + } + + public static CurveFilterOptions bakeToConstraintCurveFilterOptions + { + get => m_BakeToConstraintCurveFilterOptions; + set + { + m_BakeToConstraintCurveFilterOptions = value; + + EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_UnrollRotation, m_BakeToConstraintCurveFilterOptions.unrollRotation); + EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_KeyReduceSuffix, m_BakeToConstraintCurveFilterOptions.keyframeReduction); + EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReducePositionErrorSuffix, m_BakeToConstraintCurveFilterOptions.positionError); + EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceRotationErrorSuffix, m_BakeToConstraintCurveFilterOptions.rotationError); + EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceScaleErrorSuffix, m_BakeToConstraintCurveFilterOptions.scaleError); + EditorPrefs.SetFloat(k_BakeToConstraintPrefix + k_KeyReduceFloatErrorSuffix, m_BakeToConstraintCurveFilterOptions.floatError); + } + } + + public static bool bakeToConstraintAndRemoveCurves + { + get => m_BakeToConstraintAndRemoveCurves; + set + { + m_BakeToConstraintAndRemoveCurves = value; + EditorPrefs.SetBool(k_BakeToConstraintPrefix + k_RemoveCurvesSuffix, value); + } + } + + public static CurveFilterOptions bakeToSkeletonCurveFilterOptions + { + get => m_BakeToSkeletonCurveFilterOptions; + set + { + m_BakeToSkeletonCurveFilterOptions = value; + + EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_UnrollRotation, m_BakeToSkeletonCurveFilterOptions.unrollRotation); + EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_KeyReduceSuffix, m_BakeToSkeletonCurveFilterOptions.keyframeReduction); + EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReducePositionErrorSuffix, m_BakeToSkeletonCurveFilterOptions.positionError); + EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceRotationErrorSuffix, m_BakeToSkeletonCurveFilterOptions.rotationError); + EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceScaleErrorSuffix, m_BakeToSkeletonCurveFilterOptions.scaleError); + EditorPrefs.SetFloat(k_BakeToSkeletonPrefix + k_KeyReduceFloatErrorSuffix, m_BakeToSkeletonCurveFilterOptions.floatError); + } + } + + public static bool bakeToSkeletonAndRemoveCurves + { + get => m_BakeToSkeletonAndRemoveCurves; + set + { + m_BakeToSkeletonAndRemoveCurves = value; + EditorPrefs.SetBool(k_BakeToSkeletonPrefix + k_RemoveCurvesSuffix, value); + } + } + + public static bool forceConstraintWeightOnBake + { + get => m_ForceConstraintWeightOnBake; + set + { + m_ForceConstraintWeightOnBake = value; + EditorPrefs.SetBool(k_Prefix + k_ForceWeightSuffix, value); + } + } + } + + class PreferencesProvider : SettingsProvider + { + private class Styles + { + public static readonly int marginLeft = 10; + public static readonly int marginTop = 10; + public static readonly int majorSpacing = 10; + public static readonly int minorSpacing = 5; + public static readonly int resetButtonWidth = 120; + + public static readonly GUIContent forceWeightsLabel = EditorGUIUtility.TrTextContent("Force Weights On Bake", "Remove weight curves and set constraints weights to zero or one after baking operation."); + public static readonly GUIContent resetPreferencesButton = EditorGUIUtility.TrTextContent("Use Defaults", "Reset all the Animation Rigging preferenecs back to default settings."); + + public static readonly GUIContent bakeToConstraintLabel = EditorGUIUtility.TrTextContent("Transfer Motion To Constraint"); + public static readonly GUIContent bakeToSkeletonLabel = EditorGUIUtility.TrTextContent("Transfer Motion To Skeleton"); + + public static readonly GUIContent unrollRotationLabel = EditorGUIUtility.TrTextContent("Unroll Rotation", "Unroll rotation will adjust rotation to avoid discontinuity in between keyframes generated by baking operations."); + public static readonly GUIContent keyReduceEnableLabel = EditorGUIUtility.TrTextContent("Apply keyframe reduction", "Keyframe Reduction will remove unecessary keys in animation curves generated by baking operations."); + public static readonly GUIContent keyReducePositionErrorLabel = EditorGUIUtility.TrTextContent("Position Error", "Tolerance used in keyframe reduction for position values (percentage value between 0 and 100)."); + public static readonly GUIContent keyReduceRotationErrorLabel = EditorGUIUtility.TrTextContent("Rotation Error", "Tolerance used in keyframe reduction for rotation values (percentage value between 0 and 100)."); + public static readonly GUIContent keyReduceScaleErrorLabel = EditorGUIUtility.TrTextContent("Scale Error", "Tolerance used in keyframe reduction for scale values (percentage value between 0 and 100)."); + public static readonly GUIContent keyReduceFloatErrorLabel = EditorGUIUtility.TrTextContent("Float Error", "Tolerance used in keyframe reduction for float values (percentage value between 0 and 100)."); + + public static readonly GUIContent removeCurvesLabel = EditorGUIUtility.TrTextContent("Remove Curves", "Original curves are removed after baking operation."); + } + + public PreferencesProvider(string path, SettingsScope scopes, IEnumerable keywords = null) + : base(path, scopes, keywords) + { + } + + public override void OnActivate(string searchContext, VisualElement rootElement) + { + } + + public override void OnGUI(string searchContext) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(Styles.marginLeft); + GUILayout.BeginVertical(); + GUILayout.Space(Styles.marginTop); + + // Force weights + EditorGUI.BeginChangeCheck(); + bool newValue = EditorGUILayout.Toggle(Styles.forceWeightsLabel, Preferences.forceConstraintWeightOnBake); + if (EditorGUI.EndChangeCheck()) + Preferences.forceConstraintWeightOnBake = newValue; + + GUILayout.Space(Styles.majorSpacing); + + // Transfer to constraint + EditorGUILayout.LabelField(Styles.bakeToConstraintLabel, EditorStyles.boldLabel); + + // - Remove curves + EditorGUI.BeginChangeCheck(); + newValue = EditorGUILayout.Toggle(Styles.removeCurvesLabel, Preferences.bakeToConstraintAndRemoveCurves); + if (EditorGUI.EndChangeCheck()) + Preferences.bakeToConstraintAndRemoveCurves = newValue; + + // - Keyframe reduction + EditorGUI.BeginChangeCheck(); + + var curveFilterOptions = Preferences.bakeToConstraintCurveFilterOptions; + curveFilterOptions.unrollRotation = EditorGUILayout.Toggle(Styles.unrollRotationLabel, curveFilterOptions.unrollRotation); + curveFilterOptions.keyframeReduction = EditorGUILayout.Toggle(Styles.keyReduceEnableLabel, curveFilterOptions.keyframeReduction); + using (new EditorGUI.DisabledScope(!curveFilterOptions.keyframeReduction)) + { + EditorGUI.indentLevel++; + curveFilterOptions.positionError = EditorGUILayout.Slider(Styles.keyReducePositionErrorLabel, curveFilterOptions.positionError, 0f, 100f); + curveFilterOptions.rotationError = EditorGUILayout.Slider(Styles.keyReduceRotationErrorLabel, curveFilterOptions.rotationError, 0f, 100f); + curveFilterOptions.scaleError = EditorGUILayout.Slider(Styles.keyReduceScaleErrorLabel, curveFilterOptions.scaleError, 0f, 100f); + curveFilterOptions.floatError = EditorGUILayout.Slider(Styles.keyReduceFloatErrorLabel, curveFilterOptions.floatError, 0f, 100f); + EditorGUI.indentLevel--; + } + + if (EditorGUI.EndChangeCheck()) + Preferences.bakeToConstraintCurveFilterOptions = curveFilterOptions; + + GUILayout.Space(Styles.majorSpacing); + + // Transfer to Skeleton + EditorGUILayout.LabelField(Styles.bakeToSkeletonLabel, EditorStyles.boldLabel); + + // - Remove curves + EditorGUI.BeginChangeCheck(); + newValue = EditorGUILayout.Toggle(Styles.removeCurvesLabel, Preferences.bakeToSkeletonAndRemoveCurves); + if (EditorGUI.EndChangeCheck()) + Preferences.bakeToSkeletonAndRemoveCurves = newValue; + + // - Keyframe reduction + EditorGUI.BeginChangeCheck(); + + curveFilterOptions = Preferences.bakeToSkeletonCurveFilterOptions; + curveFilterOptions.unrollRotation = EditorGUILayout.Toggle(Styles.unrollRotationLabel, curveFilterOptions.unrollRotation); + curveFilterOptions.keyframeReduction = EditorGUILayout.Toggle(Styles.keyReduceEnableLabel, curveFilterOptions.keyframeReduction); + using (new EditorGUI.DisabledScope(!curveFilterOptions.keyframeReduction)) + { + EditorGUI.indentLevel++; + curveFilterOptions.positionError = EditorGUILayout.Slider(Styles.keyReducePositionErrorLabel, curveFilterOptions.positionError, 0f, 100f); + curveFilterOptions.rotationError = EditorGUILayout.Slider(Styles.keyReduceRotationErrorLabel, curveFilterOptions.rotationError, 0f, 100f); + curveFilterOptions.scaleError = EditorGUILayout.Slider(Styles.keyReduceScaleErrorLabel, curveFilterOptions.scaleError, 0f, 100f); + curveFilterOptions.floatError = EditorGUILayout.Slider(Styles.keyReduceFloatErrorLabel, curveFilterOptions.floatError, 0f, 100f); + EditorGUI.indentLevel--; + } + + if (EditorGUI.EndChangeCheck()) + Preferences.bakeToSkeletonCurveFilterOptions = curveFilterOptions; + + GUILayout.Space(Styles.majorSpacing); + + // Reset to defaults + if (GUILayout.Button(Styles.resetPreferencesButton, GUILayout.Width(Styles.resetButtonWidth))) + Preferences.SetDefaultValues(); + + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + } + + [SettingsProvider] + public static SettingsProvider CreateAnimationRiggingProjectSettingProvider() + { + return new PreferencesProvider( + "Preferences/Animation Rigging", + SettingsScope.User, + GetSearchKeywordsFromGUIContentProperties()); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs.meta new file mode 100644 index 00000000..ac7e23ba --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/Preferences.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd9b01ca0e39c426cb60419730fb3b29 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs b/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs new file mode 100644 index 00000000..6adc4b0d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs @@ -0,0 +1,202 @@ +using UnityEngine; +using System; +using System.Collections.Generic; +using Object = UnityEngine.Object; + +namespace UnityEditor.Animations.Rigging +{ + #if !SUPPORTS_SCENE_VIEW_OVERLAYS + static class SceneViewOverlay + { + public delegate void WindowFunction(Object target, SceneView sceneView); + + static SceneView s_SceneView; + static List s_Windows; + + static SceneViewOverlay() + { + s_Windows = new List(); + } + + public static void Begin(SceneView sceneView) + { + s_SceneView = sceneView; + + if (Event.current.type == EventType.Layout) + s_Windows.Clear(); + } + + static class Styles + { + public static readonly GUIStyle sceneViewOverlayTransparentBackground = "SceneViewOverlayTransparentBackground"; + public static readonly GUIStyle title = new GUIStyle(GUI.skin.window); + + public static readonly float windowPadding = 9f; + public static readonly float minWidth = 210f; + + static Styles() + { + title.padding.top = title.padding.bottom; + } + } + + public static void End() + { + s_Windows.Sort(); + + if (s_Windows.Count > 0) + { + var windowOverlayRect = new Rect(0, 0f, s_SceneView.position.width, s_SceneView.position.height); + GUILayout.Window("SceneViewOverlay".GetHashCode(), windowOverlayRect, WindowTrampoline, "", Styles.sceneViewOverlayTransparentBackground); + } + + s_SceneView = null; + } + + static void WindowTrampoline(int id) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.BeginVertical(); + GUILayout.FlexibleSpace(); + EditorGUILayout.BeginVertical(GUILayout.MinWidth(Styles.minWidth)); + + var paddingOffset = -Styles.windowPadding; + + bool showSceneViewWindows = SceneView.lastActiveSceneView == s_SceneView; + + foreach (OverlayWindow win in s_Windows) + { + if (!showSceneViewWindows && win.editorWindow != s_SceneView) + continue; + + GUILayout.Space(Styles.windowPadding + paddingOffset); + paddingOffset = 0f; + ResetGUIState(); + if (win.canCollapse) + { + GUILayout.BeginVertical(Styles.title); + + win.expanded = EditorGUILayout.Foldout(win.expanded, win.title, true); + + if (win.expanded) + win.sceneViewFunc(win.target, s_SceneView); + GUILayout.EndVertical(); + } + else + { + GUILayout.BeginVertical(win.title, GUI.skin.window); + win.sceneViewFunc(win.target, s_SceneView); + GUILayout.EndVertical(); + } + } + + GUILayout.EndVertical(); + + var inputEaterRect = GUILayoutUtility.GetLastRect(); + EatMouseInput(inputEaterRect); + + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + } + + static void EatMouseInput(Rect position) + { + var id = GUIUtility.GetControlID("SceneViewOverlay".GetHashCode(), FocusType.Passive, position); + switch (Event.current.GetTypeForControl(id)) + { + case EventType.MouseDown: + if (position.Contains(Event.current.mousePosition)) + { + GUIUtility.hotControl = id; + Event.current.Use(); + } + break; + case EventType.MouseUp: + if (GUIUtility.hotControl == id) + { + GUIUtility.hotControl = 0; + Event.current.Use(); + } + break; + case EventType.MouseDrag: + if (GUIUtility.hotControl == id) + Event.current.Use(); + break; + case EventType.ScrollWheel: + if (position.Contains(Event.current.mousePosition)) + Event.current.Use(); + break; + } + } + + static void ResetGUIState() + { + GUI.skin = null; + GUI.backgroundColor = GUI.contentColor = Color.white; + GUI.color = Color.white; + GUI.enabled = true; + GUI.changed = false; + EditorGUI.indentLevel = 0; + EditorGUIUtility.fieldWidth = 0; + EditorGUIUtility.labelWidth = 0; + EditorGUIUtility.hierarchyMode = false; + EditorGUIUtility.wideMode = false; + } + + // pass window parameter to render in sceneviews that are not the active view. + public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order) + { + Window(title, sceneViewFunc, order, null); + } + + // pass window parameter to render in sceneviews that are not the active view. + public static void Window(GUIContent title, WindowFunction sceneViewFunc, int order, Object target, EditorWindow window = null) + { + if (Event.current.type != EventType.Layout) + return; + + var newWindow = new OverlayWindow(title, sceneViewFunc, order, target) + { + secondaryOrder = s_Windows.Count, + canCollapse = false + }; + + + s_Windows.Add(newWindow); + } + } + + internal class OverlayWindow : IComparable + { + public OverlayWindow(GUIContent title, SceneViewOverlay.WindowFunction guiFunction, int primaryOrder, Object target) + { + this.title = title; + this.sceneViewFunc = guiFunction; + this.primaryOrder = primaryOrder; + this.target = target; + this.canCollapse = true; + this.expanded = true; + } + + public SceneViewOverlay.WindowFunction sceneViewFunc { get; } + public int primaryOrder { get; } + public int secondaryOrder { get; set; } + public Object target { get; } + public EditorWindow editorWindow { get; set; } + + public bool canCollapse { get; set; } + public bool expanded { get; set; } + + public GUIContent title { get; } + + public int CompareTo(OverlayWindow other) + { + var result = other.primaryOrder.CompareTo(primaryOrder); + if (result == 0) + result = other.secondaryOrder.CompareTo(secondaryOrder); + return result; + } + } + #endif +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs.meta new file mode 100644 index 00000000..be9e1d93 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/SceneViewOverlay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12e21a601544b4644bd45d51de0e8095 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs b/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs new file mode 100644 index 00000000..23ba7b10 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs @@ -0,0 +1,61 @@ +using UnityEngine; +using UnityEngine.Animations.Rigging; + +namespace UnityEditor.Animations.Rigging +{ + [CustomPropertyDrawer(typeof(Vector3Bool))] + class Vector3BoolDrawer : PropertyDrawer + { + private const int k_Offset = 16; + private const int k_ToggleWidth = 50; + private static readonly GUIContent k_XLabel = new GUIContent("X"); + private static readonly GUIContent k_YLabel = new GUIContent("Y"); + private static readonly GUIContent k_ZLabel = new GUIContent("Z"); + + private SerializedProperty m_X; + private SerializedProperty m_Y; + private SerializedProperty m_Z; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(rect, label, property); + + m_X = property.FindPropertyRelative("x"); + m_Y = property.FindPropertyRelative("y"); + m_Z = property.FindPropertyRelative("z"); + + rect = EditorGUI.PrefixLabel(rect, label); + + int indentLvl = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + + rect.x -= 1; + var xRect = new Rect (rect.xMin, rect.yMin, k_ToggleWidth, rect.height); + var yRect = new Rect (xRect.xMax, rect.yMin, k_ToggleWidth, rect.height); + var zRect = new Rect (yRect.xMax, rect.yMin, k_ToggleWidth, rect.height); + + EditorGUI.BeginChangeCheck(); + DrawToggleField(xRect, m_X, k_XLabel); + DrawToggleField(yRect, m_Y, k_YLabel); + DrawToggleField(zRect, m_Z, k_ZLabel); + if(EditorGUI.EndChangeCheck()) + property.serializedObject.ApplyModifiedProperties(); + + EditorGUI.indentLevel = indentLvl; + EditorGUI.EndProperty(); + } + + void DrawToggleField(Rect rect, SerializedProperty property, GUIContent label) + { + EditorGUI.LabelField(rect, label); + rect.x += k_Offset; + rect.width -= k_Offset; + EditorGUI.PropertyField(rect, property, GUIContent.none); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs.meta b/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs.meta new file mode 100644 index 00000000..0bc0787e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Editor/Utils/Vector3BoolDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a7b477fca018024b969f39a241674b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/LICENSE.md b/Packages/com.unity.animation.rigging/LICENSE.md new file mode 100644 index 00000000..57f10f85 --- /dev/null +++ b/Packages/com.unity.animation.rigging/LICENSE.md @@ -0,0 +1,5 @@ +Animation Rigging copyright © 2020 Unity Technologies ApS + +Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). + +Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. diff --git a/Packages/com.unity.animation.rigging/LICENSE.md.meta b/Packages/com.unity.animation.rigging/LICENSE.md.meta new file mode 100644 index 00000000..7a2e53c7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4b3b7999e2ee0ad499076dd62f2cc86a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/README.md b/Packages/com.unity.animation.rigging/README.md new file mode 100644 index 00000000..518ff8a9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/README.md @@ -0,0 +1,5 @@ +# Animation Rigging + +The Animation Rigging package exposes a set of predefined constraints and tools to manually build a control rig hierarchy. At runtime, rig constraints are converted to a list of IAnimationJobs which are appended to the Animator playable evaluation. + +Detailed information on how best to use this package can be found [**here**](./Documentation~/index.md). diff --git a/Packages/com.unity.animation.rigging/README.md.meta b/Packages/com.unity.animation.rigging/README.md.meta new file mode 100644 index 00000000..6ababcdd --- /dev/null +++ b/Packages/com.unity.animation.rigging/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e0a6f6b2f74cefa40ad1265babe4844c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime.meta b/Packages/com.unity.animation.rigging/Runtime.meta new file mode 100644 index 00000000..1e97f3e9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d6f24432d25fea43a11f3ca98bfd420 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs.meta new file mode 100644 index 00000000..0e944a83 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2409134bc1887e14bb5735db9784b1cb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs new file mode 100644 index 00000000..7edd896f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs @@ -0,0 +1,226 @@ +using System.Collections.Generic; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// CacheIndex is used in AnimationJobCache to recover the index to the cached data. + /// + public struct CacheIndex + { + internal int idx; + } + + /// + /// AnimationJobCache can be used in animation jobs to store values that + /// can be updated through the AnimationJobCache during the Update loop without + /// rebuilding the job. + /// + public struct AnimationJobCache : System.IDisposable + { + NativeArray m_Data; + + internal AnimationJobCache(float[] data) + { + m_Data = new NativeArray(data, Allocator.Persistent); + } + + /// + /// Dispose of the AnimationJobCache memory. + /// + public void Dispose() + { + m_Data.Dispose(); + } + + /// + /// Gets raw float data at specified index. + /// + /// CacheIndex value. + /// Offset to the CacheIndex. + /// The raw float data. + public float GetRaw(CacheIndex index, int offset = 0) + { + return m_Data[index.idx + offset]; + } + + /// + /// Sets raw float data at specified index. + /// + /// Raw float data. + /// CacheIndex value. + /// Offset to the CacheIndex. + public void SetRaw(float val, CacheIndex index, int offset = 0) + { + m_Data[index.idx + offset] = val; + } + + /// + /// Gets value at specified index. + /// + /// CacheIndex value. + /// Offset to the CacheIndex. + /// The value type. + /// + unsafe public T Get(CacheIndex index, int offset = 0) where T : unmanaged + { + int size = UnsafeUtility.SizeOf(); + int stride = size / UnsafeUtility.SizeOf(); + + T val = default(T); + UnsafeUtility.MemCpy(&val, (float*)m_Data.GetUnsafeReadOnlyPtr() + index.idx + offset * stride, size); + return val; + } + + /// + /// Sets value at specified index. + /// + /// Value. + /// CacheIndex value. + /// Offset to the CacheIndex. + /// + unsafe public void Set(T val, CacheIndex index, int offset = 0) where T : unmanaged + { + int size = UnsafeUtility.SizeOf(); + int stride = size / UnsafeUtility.SizeOf(); + + UnsafeUtility.MemCpy((float*)m_Data.GetUnsafePtr() + index.idx + offset * stride, &val, size); + } + + /// + /// Sets an array of values at specified index. + /// + /// Array of values. + /// CacheIndex value. + /// Offset to the CacheIndex. + /// + unsafe public void SetArray(T[] v, CacheIndex index, int offset = 0) where T : unmanaged + { + int size = UnsafeUtility.SizeOf(); + int stride = size / UnsafeUtility.SizeOf(); + + fixed (void* ptr = v) + { + UnsafeUtility.MemCpy((float*)m_Data.GetUnsafePtr() + index.idx + offset * stride, ptr, size * v.Length); + } + } + } + + /// + /// AnimationJobCacheBuilder can be used to create a new AnimationJobCache object. + /// + public class AnimationJobCacheBuilder + { + List m_Data; + + /// + /// Constructor. + /// + public AnimationJobCacheBuilder() + { + m_Data = new List(); + } + + /// + /// Adds a float value to the AnimationJobCache. + /// + /// Float value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(float v) + { + m_Data.Add(v); + return new CacheIndex { idx = m_Data.Count - 1 }; + } + + /// + /// Adds a Vector2 value to the AnimationJobCache. + /// + /// Vector2 value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(Vector2 v) + { + m_Data.Add(v.x); + m_Data.Add(v.y); + return new CacheIndex { idx = m_Data.Count - 2 }; + } + + /// + /// Adds a Vector3 value to the AnimationJobCache. + /// + /// Vector3 value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(Vector3 v) + { + m_Data.Add(v.x); + m_Data.Add(v.y); + m_Data.Add(v.z); + return new CacheIndex { idx = m_Data.Count - 3 }; + } + + /// + /// Adds a Vector4 value to the AnimationJobCache. + /// + /// Vector4 value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(Vector4 v) + { + m_Data.Add(v.x); + m_Data.Add(v.y); + m_Data.Add(v.z); + m_Data.Add(v.w); + return new CacheIndex { idx = m_Data.Count - 4 }; + } + + /// + /// Adds a Quaternion value to the AnimationJobCache. + /// + /// Quaternion value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(Quaternion v) + { + return Add(new Vector4(v.x, v.y, v.z, v.w)); + } + + /// + /// Adds a AffineTransform value to the AnimationJobCache. + /// + /// AffineTransform value. + /// CacheIndex that refers to the new value. + public CacheIndex Add(AffineTransform tx) + { + Add(tx.translation); + Add(tx.rotation); + return new CacheIndex { idx = m_Data.Count - 7 }; + } + + /// + /// Allocates uninitialized chunk of specified size in the AnimationJobCacheBuilder. + /// + /// Size of chunk to allocate. + /// CacheIndex that refers to the allocated chunk. + public CacheIndex AllocateChunk(int size) + { + m_Data.AddRange(new float[size]); + return new CacheIndex { idx = m_Data.Count - size }; + } + + /// + /// Sets value in AnimationJobCacheBuilder at specified index. + /// + /// CacheIndex value. + /// Offset to the CacheIndex. + /// float data. + public void SetValue(CacheIndex index, int offset, float value) + { + if (index.idx + offset < m_Data.Count) + m_Data[index.idx + offset] = value; + } + + /// + /// Creates a new AnimationJobCache. + /// + /// AnimationJobCache object with newly set values. + public AnimationJobCache Build() => new AnimationJobCache(m_Data.ToArray()); + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs.meta new file mode 100644 index 00000000..45890a81 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/AnimationJobCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81d27177925c7894da0eb85baee3d530 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs new file mode 100644 index 00000000..e203a6e7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs @@ -0,0 +1,156 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The Blend constraint job. + /// + [Unity.Burst.BurstCompile] + public struct BlendConstraintJob : IWeightedAnimationJob + { + private const int k_BlendTranslationMask = 1 << 0; + private const int k_BlendRotationMask = 1 << 1; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for sourceA Transform. + public ReadOnlyTransformHandle sourceA; + /// The Transform handle for sourceB Transform. + public ReadOnlyTransformHandle sourceB; + /// TR offset to apply to sourceA if maintainOffset is enabled. + public AffineTransform sourceAOffset; + /// TR offset to apply to sourceB if maintainOffset is enabled. + public AffineTransform sourceBOffset; + + /// Toggles whether to blend position in the job. + public BoolProperty blendPosition; + /// Toggles whether to blend rotation in the job. + public BoolProperty blendRotation; + /// + /// Specifies the weight with which to blend position. + /// A weight of zero will result in the position of sourceA, while a weight of one will result in the position of sourceB. + /// + public FloatProperty positionWeight; + /// + /// Specifies the weight with which to blend rotation. + /// A weight of zero will result in the rotation of sourceA, while a weight of one will result in the rotation of sourceB. + /// + public FloatProperty rotationWeight; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + if (blendPosition.Get(stream)) + { + Vector3 posBlend = Vector3.Lerp( + sourceA.GetPosition(stream) + sourceAOffset.translation, + sourceB.GetPosition(stream) + sourceBOffset.translation, + positionWeight.Get(stream) + ); + driven.SetPosition(stream, Vector3.Lerp(driven.GetPosition(stream), posBlend, w)); + } + else + driven.SetLocalPosition(stream, driven.GetLocalPosition(stream)); + + if (blendRotation.Get(stream)) + { + Quaternion rotBlend = Quaternion.Lerp( + sourceA.GetRotation(stream) * sourceAOffset.rotation, + sourceB.GetRotation(stream) * sourceBOffset.rotation, + rotationWeight.Get(stream) + ); + driven.SetRotation(stream, Quaternion.Lerp(driven.GetRotation(stream), rotBlend, w)); + } + else + driven.SetLocalRotation(stream, driven.GetLocalRotation(stream)); + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + } + + /// + /// This interface defines the data mapping for the Blend constraint. + /// + public interface IBlendConstraintData + { + /// The Transform affected by the two source Transforms. + Transform constrainedObject { get; } + /// First Transform in blend. + Transform sourceObjectA { get; } + /// Second Transform in blend. + Transform sourceObjectB { get; } + + /// This is used to maintain the current position offset from the constrained GameObject to the source GameObjects. + bool maintainPositionOffsets { get; } + /// This is used to maintain the current rotation offset from the constrained GameObject to the source GameObjects. + bool maintainRotationOffsets { get; } + + /// The path to the blend position property in the constraint component. + string blendPositionBoolProperty { get; } + /// The path to the blend rotation property in the constraint component. + string blendRotationBoolProperty { get; } + /// The path to the position weight property in the constraint component. + string positionWeightFloatProperty { get; } + /// The path to the rotation weight property in the constraint component. + string rotationWeightFloatProperty { get; } + } + + /// + /// The Blend constraint job binder. + /// + /// The constraint data type + public class BlendConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IBlendConstraintData + { + /// + public override BlendConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new BlendConstraintJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.sourceA = ReadOnlyTransformHandle.Bind(animator, data.sourceObjectA); + job.sourceB = ReadOnlyTransformHandle.Bind(animator, data.sourceObjectB); + + job.sourceAOffset = job.sourceBOffset = AffineTransform.identity; + if (data.maintainPositionOffsets) + { + var drivenPos = data.constrainedObject.position; + job.sourceAOffset.translation = drivenPos - data.sourceObjectA.position; + job.sourceBOffset.translation = drivenPos - data.sourceObjectB.position; + } + + if (data.maintainRotationOffsets) + { + var drivenRot = data.constrainedObject.rotation; + job.sourceAOffset.rotation = Quaternion.Inverse(data.sourceObjectA.rotation) * drivenRot; + job.sourceBOffset.rotation = Quaternion.Inverse(data.sourceObjectB.rotation) * drivenRot; + } + + job.blendPosition = BoolProperty.Bind(animator, component, data.blendPositionBoolProperty); + job.blendRotation = BoolProperty.Bind(animator, component, data.blendRotationBoolProperty); + job.positionWeight = FloatProperty.Bind(animator, component, data.positionWeightFloatProperty); + job.rotationWeight = FloatProperty.Bind(animator, component, data.rotationWeightFloatProperty); + + return job; + } + + /// + public override void Destroy(BlendConstraintJob job) + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs.meta new file mode 100644 index 00000000..f7406c81 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/BlendConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6aab915008afb774bbdc597691ed5d5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs new file mode 100644 index 00000000..d0542810 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs @@ -0,0 +1,188 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The ChainIK constraint job. + /// + [Unity.Burst.BurstCompile] + public struct ChainIKConstraintJob : IWeightedAnimationJob + { + /// An array of Transform handles that represents the Transform chain. + public NativeArray chain; + /// The Transform handle for the target Transform. + public ReadOnlyTransformHandle target; + + /// The offset applied to the target transform if maintainTargetPositionOffset or maintainTargetRotationOffset is enabled. + public AffineTransform targetOffset; + + /// An array of length in between Transforms in the chain. + public NativeArray linkLengths; + + /// An array of positions for Transforms in the chain. + public NativeArray linkPositions; + + /// The weight for which ChainIK target has an effect on chain (up to tip Transform). This is a value in between 0 and 1. + public FloatProperty chainRotationWeight; + /// The weight for which ChainIK target has and effect on tip Transform. This is a value in between 0 and 1. + public FloatProperty tipRotationWeight; + + /// CacheIndex to ChainIK tolerance value. + /// + public CacheIndex toleranceIdx; + /// CacheIndex to ChainIK maxIterations value. + /// + public CacheIndex maxIterationsIdx; + /// Cache for static properties in the job. + public AnimationJobCache cache; + + /// The maximum distance the Transform chain can reach. + public float maxReach; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + for (int i = 0; i < chain.Length; ++i) + { + var handle = chain[i]; + linkPositions[i] = handle.GetPosition(stream); + chain[i] = handle; + } + + int tipIndex = chain.Length - 1; + if (AnimationRuntimeUtils.SolveFABRIK(ref linkPositions, ref linkLengths, target.GetPosition(stream) + targetOffset.translation, + cache.GetRaw(toleranceIdx), maxReach, (int)cache.GetRaw(maxIterationsIdx))) + { + var chainRWeight = chainRotationWeight.Get(stream) * w; + for (int i = 0; i < tipIndex; ++i) + { + var prevDir = chain[i + 1].GetPosition(stream) - chain[i].GetPosition(stream); + var newDir = linkPositions[i + 1] - linkPositions[i]; + var rot = chain[i].GetRotation(stream); + chain[i].SetRotation(stream, Quaternion.Lerp(rot, QuaternionExt.FromToRotation(prevDir, newDir) * rot, chainRWeight)); + } + } + + chain[tipIndex].SetRotation( + stream, + Quaternion.Lerp( + chain[tipIndex].GetRotation(stream), + target.GetRotation(stream) * targetOffset.rotation, + tipRotationWeight.Get(stream) * w + ) + ); + } + else + { + for (int i = 0; i < chain.Length; ++i) + AnimationRuntimeUtils.PassThrough(stream, chain[i]); + } + } + } + + /// + /// This interface defines the data mapping for the ChainIK constraint. + /// + public interface IChainIKConstraintData + { + /// The root Transform of the ChainIK hierarchy. + Transform root { get; } + /// The tip Transform of the ChainIK hierarchy. The tip needs to be a descendant/child of the root Transform. + Transform tip { get; } + /// The ChainIK target Transform. + Transform target { get; } + + /// The maximum number of iterations allowed for the ChainIK algorithm to converge to a solution. + int maxIterations { get; } + /// + /// The allowed distance between the tip and target Transform positions. + /// When the distance is smaller than the tolerance, the algorithm has converged on a solution and will stop. + /// + float tolerance { get; } + /// This is used to maintain the current position offset from the tip Transform to target Transform. + bool maintainTargetPositionOffset { get; } + /// This is used to maintain the current rotation offset from the tip Transform to target Transform. + bool maintainTargetRotationOffset { get; } + + /// The path to the chain rotation weight property in the constraint component. + string chainRotationWeightFloatProperty { get; } + /// The path to the tip rotation weight property in the constraint component. + string tipRotationWeightFloatProperty { get; } + } + + /// + /// The ChainIK constraint job binder. + /// + /// The constraint data type + public class ChainIKConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IChainIKConstraintData + { + /// + public override ChainIKConstraintJob Create(Animator animator, ref T data, Component component) + { + Transform[] chain = ConstraintsUtils.ExtractChain(data.root, data.tip); + + var job = new ChainIKConstraintJob(); + job.chain = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.linkLengths = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.linkPositions = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.maxReach = 0f; + + int tipIndex = chain.Length - 1; + for (int i = 0; i < chain.Length; ++i) + { + job.chain[i] = ReadWriteTransformHandle.Bind(animator, chain[i]); + job.linkLengths[i] = (i != tipIndex) ? Vector3.Distance(chain[i].position, chain[i + 1].position) : 0f; + job.maxReach += job.linkLengths[i]; + } + + job.target = ReadOnlyTransformHandle.Bind(animator, data.target); + job.targetOffset = AffineTransform.identity; + if (data.maintainTargetPositionOffset) + job.targetOffset.translation = data.tip.position - data.target.position; + if (data.maintainTargetRotationOffset) + job.targetOffset.rotation = Quaternion.Inverse(data.target.rotation) * data.tip.rotation; + + job.chainRotationWeight = FloatProperty.Bind(animator, component, data.chainRotationWeightFloatProperty); + job.tipRotationWeight = FloatProperty.Bind(animator, component, data.tipRotationWeightFloatProperty); + + var cacheBuilder = new AnimationJobCacheBuilder(); + job.maxIterationsIdx = cacheBuilder.Add(data.maxIterations); + job.toleranceIdx = cacheBuilder.Add(data.tolerance); + job.cache = cacheBuilder.Build(); + + return job; + } + + /// + public override void Destroy(ChainIKConstraintJob job) + { + job.chain.Dispose(); + job.linkLengths.Dispose(); + job.linkPositions.Dispose(); + job.cache.Dispose(); + } + + /// + public override void Update(ChainIKConstraintJob job, ref T data) + { + job.cache.SetRaw(data.maxIterations, job.maxIterationsIdx); + job.cache.SetRaw(data.tolerance, job.toleranceIdx); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs.meta new file mode 100644 index 00000000..e5349ae9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/ChainIKConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43ef7c2783a6019459e739a74712bf50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs new file mode 100644 index 00000000..5ab6631e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs @@ -0,0 +1,160 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The DampedTransform job. + /// + [Unity.Burst.BurstCompile] + public struct DampedTransformJob : IWeightedAnimationJob + { + const float k_FixedDt = 0.01667f; // 60Hz simulation step + const float k_DampFactor = 40f; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the source object Transform. + public ReadOnlyTransformHandle source; + + /// Initial TR offset from source to constrained object. + public AffineTransform localBindTx; + + /// Aim axis used to adjust constrained object rotation if maintainAim is enabled. + public Vector3 aimBindAxis; + + /// Previous frame driven Transform TR components. + public AffineTransform prevDrivenTx; + + /// + /// Defines how much of constrained object position follows source object position. + /// constrained position will closely follow source object when set to 0, and will + /// not move when set to 1. + /// + public FloatProperty dampPosition; + /// + /// Defines how much of constrained object rotation follows source object rotation. + /// constrained rotation will closely follow source object when set to 0, and will + /// not move when set to 1. + /// + public FloatProperty dampRotation; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + float streamDt = Mathf.Abs(stream.deltaTime); + driven.GetGlobalTR(stream, out Vector3 drivenPos, out Quaternion drivenRot); + + if (w > 0f && streamDt > 0f) + { + source.GetGlobalTR(stream, out Vector3 sourcePos, out Quaternion sourceRot); + var sourceTx = new AffineTransform(sourcePos, sourceRot); + var targetTx = sourceTx * localBindTx; + targetTx.translation = Vector3.Lerp(drivenPos, targetTx.translation, w); + targetTx.rotation = Quaternion.Lerp(drivenRot, targetTx.rotation, w); + + var dampPosW = AnimationRuntimeUtils.Square(1f - dampPosition.Get(stream)); + var dampRotW = AnimationRuntimeUtils.Square(1f - dampRotation.Get(stream)); + bool doAimAjustements = Vector3.Dot(aimBindAxis, aimBindAxis) > 0f; + + while (streamDt > 0f) + { + float factoredDt = k_DampFactor * Mathf.Min(k_FixedDt, streamDt); + + prevDrivenTx.translation += + (targetTx.translation - prevDrivenTx.translation) * dampPosW * factoredDt; + + prevDrivenTx.rotation *= Quaternion.Lerp( + Quaternion.identity, + Quaternion.Inverse(prevDrivenTx.rotation) * targetTx.rotation, + dampRotW * factoredDt + ); + + if (doAimAjustements) + { + var fromDir = prevDrivenTx.rotation * aimBindAxis; + var toDir = sourceTx.translation - prevDrivenTx.translation; + prevDrivenTx.rotation = + Quaternion.AngleAxis(Vector3.Angle(fromDir, toDir), Vector3.Cross(fromDir, toDir).normalized) * prevDrivenTx.rotation; + } + + streamDt -= k_FixedDt; + } + + driven.SetGlobalTR(stream, prevDrivenTx.translation, prevDrivenTx.rotation); + } + else + { + prevDrivenTx.Set(drivenPos, drivenRot); + AnimationRuntimeUtils.PassThrough(stream, driven); + } + } + } + + /// + /// This interface defines the data mapping for DampedTransform. + /// + public interface IDampedTransformData + { + /// The Transform affected by the constraint Source Transform. + Transform constrainedObject { get; } + /// The source Transform. + Transform sourceObject { get; } + + /// Toggles whether damping will enforces aim. + bool maintainAim { get; } + + /// The path to the damp position weight property in the constraint component. + string dampPositionFloatProperty { get; } + /// The path to the damp rotation weight property in the constraint component. + string dampRotationFloatProperty { get; } + } + + /// + /// The DampedTransform job binder. + /// + /// The constraint data type + public class DampedTransformJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IDampedTransformData + { + /// + public override DampedTransformJob Create(Animator animator, ref T data, Component component) + { + var job = new DampedTransformJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.source = ReadOnlyTransformHandle.Bind(animator, data.sourceObject); + + var drivenTx = new AffineTransform(data.constrainedObject.position, data.constrainedObject.rotation); + var sourceTx = new AffineTransform(data.sourceObject.position, data.sourceObject.rotation); + + job.localBindTx = sourceTx.InverseMul(drivenTx); + job.prevDrivenTx = drivenTx; + + job.dampPosition = FloatProperty.Bind(animator, component, data.dampPositionFloatProperty); + job.dampRotation = FloatProperty.Bind(animator, component, data.dampRotationFloatProperty); + + if (data.maintainAim && AnimationRuntimeUtils.SqrDistance(data.constrainedObject.position, data.sourceObject.position) > 0f) + job.aimBindAxis = Quaternion.Inverse(data.constrainedObject.rotation) * (sourceTx.translation - drivenTx.translation).normalized; + else + job.aimBindAxis = Vector3.zero; + + return job; + } + + /// + public override void Destroy(DampedTransformJob job) + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs.meta new file mode 100644 index 00000000..c151a07e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/DampedTransformJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88f002c8d5442ea4ea02ed982c62e8f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs new file mode 100644 index 00000000..233889f8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs @@ -0,0 +1,511 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Interface for animatable property handles used to read and write + /// values in the AnimationStream. + /// + /// The animatable value type + public interface IAnimatableProperty + { + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The property value. + T Get(AnimationStream stream); + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new property value. + void Set(AnimationStream stream, T value); + } + + /// + /// Boolean property handle used to read and write values in the AnimationStream. + /// + public struct BoolProperty : IAnimatableProperty + { + /// The PropertyStreamHandle used in the AnimationStream. + public PropertyStreamHandle value; + + /// + /// Creates a BoolProperty handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a BoolProperty handle that represents the new binding. + public static BoolProperty Bind(Animator animator, Component component, string name) + { + return new BoolProperty() + { + value = animator.BindStreamProperty(component.transform, component.GetType(), name) + }; + } + + /// + /// Creates a BoolProperty handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a BoolProperty handle that represents the new binding. + public static BoolProperty BindCustom(Animator animator, string property) + { + return new BoolProperty + { + value = animator.BindCustomStreamProperty(property, CustomStreamPropertyType.Bool) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The boolean property value. + public bool Get(AnimationStream stream) => value.GetBool(stream); + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new boolean property value. + public void Set(AnimationStream stream, bool v) => value.SetBool(stream, v); + } + + /// + /// Integer property handle used to read and write values in the AnimationStream. + /// + public struct IntProperty : IAnimatableProperty + { + /// The PropertyStreamHandle used in the AnimationStream. + public PropertyStreamHandle value; + + /// + /// Creates a IntProperty handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a IntProperty handle that represents the new binding. + public static IntProperty Bind(Animator animator, Component component, string name) + { + return new IntProperty() + { + value = animator.BindStreamProperty(component.transform, component.GetType(), name) + }; + } + + /// + /// Creates a IntProperty handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a IntProperty handle that represents the new binding. + public static IntProperty BindCustom(Animator animator, string property) + { + return new IntProperty + { + value = animator.BindCustomStreamProperty(property, CustomStreamPropertyType.Int) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The integer property value. + public int Get(AnimationStream stream) => value.GetInt(stream); + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new integer property value. + public void Set(AnimationStream stream, int v) => value.SetInt(stream, v); + } + + /// + /// Float property handle used to read and write values in the AnimationStream. + /// + public struct FloatProperty : IAnimatableProperty + { + /// The PropertyStreamHandle used in the AnimationStream. + public PropertyStreamHandle value; + + /// + /// Creates a FloatProperty handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a FloatProperty handle that represents the new binding. + public static FloatProperty Bind(Animator animator, Component component, string name) + { + return new FloatProperty() + { + value = animator.BindStreamProperty(component.transform, component.GetType(), name) + }; + } + + /// + /// Creates a FloatProperty handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a FloatProperty handle that represents the new binding. + public static FloatProperty BindCustom(Animator animator, string property) + { + return new FloatProperty + { + value = animator.BindCustomStreamProperty(property, CustomStreamPropertyType.Float) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The float property value. + public float Get(AnimationStream stream) => value.GetFloat(stream); + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new float property value. + public void Set(AnimationStream stream, float v) => value.SetFloat(stream, v); + } + + /// + /// Vector2 property handle used to read and write values in the AnimationStream. + /// + public struct Vector2Property : IAnimatableProperty + { + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle x; + /// The PropertyStreamHandle used for the Y component in the AnimationStream. + public PropertyStreamHandle y; + + /// + /// Creates a Vector2Property handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a Vector2Property handle that represents the new binding. + public static Vector2Property Bind(Animator animator, Component component, string name) + { + var type = component.GetType(); + return new Vector2Property + { + x = animator.BindStreamProperty(component.transform, type, name + ".x"), + y = animator.BindStreamProperty(component.transform, type, name + ".y") + }; + } + + /// + /// Creates a Vector2Property handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a Vector2Property handle that represents the new binding. + public static Vector2Property BindCustom(Animator animator, string name) + { + return new Vector2Property + { + x = animator.BindCustomStreamProperty(name + ".x", CustomStreamPropertyType.Float), + y = animator.BindCustomStreamProperty(name + ".y", CustomStreamPropertyType.Float) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The Vector2 property value. + public Vector2 Get(AnimationStream stream) => + new Vector2(x.GetFloat(stream), y.GetFloat(stream)); + + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new Vector2 property value. + public void Set(AnimationStream stream, Vector2 value) + { + x.SetFloat(stream, value.x); + y.SetFloat(stream, value.y); + } + } + + /// + /// Vector3 property handle used to read and write values in the AnimationStream. + /// + public struct Vector3Property : IAnimatableProperty + { + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle x; + /// The PropertyStreamHandle used for the Y component in the AnimationStream. + public PropertyStreamHandle y; + /// The PropertyStreamHandle used for the Z component in the AnimationStream. + public PropertyStreamHandle z; + + /// + /// Creates a Vector3Property handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a Vector3Property handle that represents the new binding. + public static Vector3Property Bind(Animator animator, Component component, string name) + { + var type = component.GetType(); + return new Vector3Property + { + x = animator.BindStreamProperty(component.transform, type, name + ".x"), + y = animator.BindStreamProperty(component.transform, type, name + ".y"), + z = animator.BindStreamProperty(component.transform, type, name + ".z") + }; + } + + /// + /// Creates a Vector3Property handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a Vector3Property handle that represents the new binding. + public static Vector3Property BindCustom(Animator animator, string name) + { + return new Vector3Property + { + x = animator.BindCustomStreamProperty(name + ".x", CustomStreamPropertyType.Float), + y = animator.BindCustomStreamProperty(name + ".y", CustomStreamPropertyType.Float), + z = animator.BindCustomStreamProperty(name + ".z", CustomStreamPropertyType.Float) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The Vector3 property value. + public Vector3 Get(AnimationStream stream) => + new Vector3(x.GetFloat(stream), y.GetFloat(stream), z.GetFloat(stream)); + + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new Vector3 property value. + public void Set(AnimationStream stream, Vector3 value) + { + x.SetFloat(stream, value.x); + y.SetFloat(stream, value.y); + z.SetFloat(stream, value.z); + } + } + + /// + /// Vector3Int property handle used to read and write values in the AnimationStream. + /// + public struct Vector3IntProperty : IAnimatableProperty + { + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle x; + /// The PropertyStreamHandle used for the Y component in the AnimationStream. + public PropertyStreamHandle y; + /// The PropertyStreamHandle used for the Z component in the AnimationStream. + public PropertyStreamHandle z; + + /// + /// Creates a Vector3IntProperty handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a Vector3IntProperty handle that represents the new binding. + public static Vector3IntProperty Bind(Animator animator, Component component, string name) + { + var type = component.GetType(); + return new Vector3IntProperty + { + x = animator.BindStreamProperty(component.transform, type, name + ".x"), + y = animator.BindStreamProperty(component.transform, type, name + ".y"), + z = animator.BindStreamProperty(component.transform, type, name + ".z") + }; + } + + /// + /// Creates a Vector3IntProperty handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a Vector3IntProperty handle that represents the new binding. + public static Vector3IntProperty BindCustom(Animator animator, string name) + { + return new Vector3IntProperty + { + x = animator.BindCustomStreamProperty(name + ".x", CustomStreamPropertyType.Int), + y = animator.BindCustomStreamProperty(name + ".y", CustomStreamPropertyType.Int), + z = animator.BindCustomStreamProperty(name + ".z", CustomStreamPropertyType.Int) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The Vector3Int property value. + public Vector3Int Get(AnimationStream stream) => + new Vector3Int(x.GetInt(stream), y.GetInt(stream), z.GetInt(stream)); + + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new Vector3Int property value. + public void Set(AnimationStream stream, Vector3Int value) + { + x.SetInt(stream, value.x); + y.SetInt(stream, value.y); + z.SetInt(stream, value.z); + } + } + + /// + /// Vector3Bool property handle used to read and write values in the AnimationStream. + /// + public struct Vector3BoolProperty : IAnimatableProperty + { + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle x; + /// The PropertyStreamHandle used for the Y component in the AnimationStream. + public PropertyStreamHandle y; + /// The PropertyStreamHandle used for the Z component in the AnimationStream. + public PropertyStreamHandle z; + + /// + /// Creates a Vector3BoolProperty handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a Vector3BoolProperty handle that represents the new binding. + public static Vector3BoolProperty Bind(Animator animator, Component component, string name) + { + var type = component.GetType(); + return new Vector3BoolProperty + { + x = animator.BindStreamProperty(component.transform, type, name + ".x"), + y = animator.BindStreamProperty(component.transform, type, name + ".y"), + z = animator.BindStreamProperty(component.transform, type, name + ".z") + }; + } + + /// + /// Creates a Vector3BoolProperty handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a Vector3BoolProperty handle that represents the new binding. + public static Vector3BoolProperty BindCustom(Animator animator, string name) + { + return new Vector3BoolProperty + { + x = animator.BindCustomStreamProperty(name + ".x", CustomStreamPropertyType.Bool), + y = animator.BindCustomStreamProperty(name + ".y", CustomStreamPropertyType.Bool), + z = animator.BindCustomStreamProperty(name + ".z", CustomStreamPropertyType.Bool) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The Vector3Bool property value. + public Vector3Bool Get(AnimationStream stream) => + new Vector3Bool(x.GetBool(stream), y.GetBool(stream), z.GetBool(stream)); + + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new Vector3Bool property value. + public void Set(AnimationStream stream, Vector3Bool value) + { + x.SetBool(stream, value.x); + y.SetBool(stream, value.y); + z.SetBool(stream, value.z); + } + } + + /// + /// Vector4 property handle used to read and write values in the AnimationStream. + /// + public struct Vector4Property : IAnimatableProperty + { + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle x; + /// The PropertyStreamHandle used for the Y component in the AnimationStream. + public PropertyStreamHandle y; + /// The PropertyStreamHandle used for the Z component in the AnimationStream. + public PropertyStreamHandle z; + /// The PropertyStreamHandle used for the X component in the AnimationStream. + public PropertyStreamHandle w; + + /// + /// Creates a Vector4Property handle representing a property binding on a Component. + /// + /// The Animator on which to bind the new handle. + /// The Component owning the parameter. + /// The property name + /// Returns a Vector4Property handle that represents the new binding. + public static Vector4Property Bind(Animator animator, Component component, string name) + { + var type = component.GetType(); + return new Vector4Property + { + x = animator.BindStreamProperty(component.transform, type, name + ".x"), + y = animator.BindStreamProperty(component.transform, type, name + ".y"), + z = animator.BindStreamProperty(component.transform, type, name + ".z"), + w = animator.BindStreamProperty(component.transform, type, name + ".w") + }; + } + + /// + /// Creates a Vector4Property handle for a custom property in the AnimationStream to pass extra data to downstream animation jobs in the graph. + /// + /// The Animator on which to bind the new handle. + /// The name of the property. + /// Returns a Vector4Property handle that represents the new binding. + public static Vector4Property BindCustom(Animator animator, string name) + { + return new Vector4Property + { + x = animator.BindCustomStreamProperty(name + ".x", CustomStreamPropertyType.Float), + y = animator.BindCustomStreamProperty(name + ".y", CustomStreamPropertyType.Float), + z = animator.BindCustomStreamProperty(name + ".z", CustomStreamPropertyType.Float), + w = animator.BindCustomStreamProperty(name + ".w", CustomStreamPropertyType.Float) + }; + } + + /// + /// Gets the property value from a stream. + /// + /// The AnimationStream that holds the animated values. + /// The Vector4 property value. + public Vector4 Get(AnimationStream stream) => + new Vector4(x.GetFloat(stream), y.GetFloat(stream), z.GetFloat(stream), w.GetFloat(stream)); + + /// + /// Sets the property value into a stream. + /// + /// The AnimationStream that holds the animated values. + /// The new Vector4 property value. + public void Set(AnimationStream stream, Vector4 value) + { + x.SetFloat(stream, value.x); + y.SetFloat(stream, value.y); + z.SetFloat(stream, value.z); + w.SetFloat(stream, value.w); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs.meta new file mode 100644 index 00000000..2c1048fe --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimatableProperty.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae72ed6fe0e30e142b831b12b8cd9b3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs new file mode 100644 index 00000000..9e75cf9b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs @@ -0,0 +1,111 @@ +using UnityEngine.Playables; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// This is the base interface for all animation job binders. + /// + public interface IAnimationJobBinder + { + /// + /// Creates the animation job. + /// + /// The animated hierarchy Animator component. + /// The constraint data. + /// The constraint component. + /// Returns a new job interface. + IAnimationJob Create(Animator animator, IAnimationJobData data, Component component = null); + /// + /// Destroys the animation job. + /// + /// The animation job to destroy. + void Destroy(IAnimationJob job); + /// + /// Updates the animation job. + /// + /// The animation job to update. + /// The constraint data. + void Update(IAnimationJob job, IAnimationJobData data); + + /// + /// Late-Updates the animation job. + /// + /// The animation job to late-update. + /// The constraint data. + void LateUpdate(IAnimationJob job, IAnimationJobData data); + + /// + /// Creates an AnimationScriptPlayable with the specified animation job. + /// + /// The PlayableGraph that will own the AnimationScriptPlayable. + /// The animation job to use in the AnimationScriptPlayable. + /// Returns a new AnimationScriptPlayable. + AnimationScriptPlayable CreatePlayable(PlayableGraph graph, IAnimationJob job); + } + + /// + /// The is the base class for all animation job binders. + /// + /// The constraint job. + /// The constraint data. + public abstract class AnimationJobBinder : IAnimationJobBinder + where TJob : struct, IAnimationJob + where TData : struct, IAnimationJobData + { + /// + /// Creates the animation job. + /// + /// The animated hierarchy Animator component. + /// The constraint data. + /// The constraint component. + /// Returns a new job interface. + public abstract TJob Create(Animator animator, ref TData data, Component component); + /// + /// Destroys the animation job. + /// + /// The animation job to destroy. + public abstract void Destroy(TJob job); + /// + /// Updates the animation job. + /// + /// The animation job to update. + /// The constraint data. + public virtual void Update(TJob job, ref TData data) {} + + public virtual void LateUpdate(TJob job, ref TData data) { } + + /// + IAnimationJob IAnimationJobBinder.Create(Animator animator, IAnimationJobData data, Component component) + { + Debug.Assert(data is TData); + TData tData = (TData)data; + return Create(animator, ref tData, component); + } + /// + void IAnimationJobBinder.Destroy(IAnimationJob job) + { + Debug.Assert(job is TJob); + Destroy((TJob)job); + } + /// + void IAnimationJobBinder.Update(IAnimationJob job, IAnimationJobData data) + { + Debug.Assert(data is TData && job is TJob); + TData tData = (TData)data; + Update((TJob)job, ref tData); + } + /// + void IAnimationJobBinder.LateUpdate(IAnimationJob job, IAnimationJobData data) + { + Debug.Assert(data is TData && job is TJob); + TData tData = (TData) data; + LateUpdate((TJob) job, ref tData); + } + /// + AnimationScriptPlayable IAnimationJobBinder.CreatePlayable(PlayableGraph graph, IAnimationJob job) + { + Debug.Assert(job is TJob); + return AnimationScriptPlayable.Create(graph, (TJob)job); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs.meta new file mode 100644 index 00000000..f99aedef --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobBinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bef79d42046e7414a8a36ee9d28430ee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs new file mode 100644 index 00000000..7cae500a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs @@ -0,0 +1,18 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// This interface is used to represent all constraint data structs. + /// + public interface IAnimationJobData + { + /// + /// Retrieves the data valid state. + /// + /// Returns true if data can be successfully used in a constraint. Returns false otherwise. + bool IsValid(); + /// + /// Resets values to defaults. + /// + void SetDefaultValues(); + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs.meta new file mode 100644 index 00000000..b5549aea --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IAnimationJobData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef3cacef35acee14290c1dae6963fed8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs new file mode 100644 index 00000000..55842d15 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs @@ -0,0 +1,11 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// This interface represents an animation job with a weight value. + /// + public interface IWeightedAnimationJob : IAnimationJob + { + /// The main weight given to the constraint. This is a value in between 0 and 1. + FloatProperty jobWeight { get; set; } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs.meta new file mode 100644 index 00000000..45a7c954 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/IWeightedAnimationJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6b0b0d5c8df2df4aaeca18077358f75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs new file mode 100644 index 00000000..4c1dcb8f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs @@ -0,0 +1,355 @@ +using System; +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiAim constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiAimConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// + /// Specifies how the world up vector used by the Multi-Aim constraint is defined. + /// + public enum WorldUpType + { + /// Neither defines nor uses a world up vector. + None, + /// Uses and defines the world up vector as the Unity Scene up vector (the Y axis). + SceneUp, + /// Uses and defines the world up vector as a vector from the constrained object, in the direction of the up object. + ObjectUp, + /// Uses and defines the world up vector as relative to the local space of the object. + ObjectRotationUp, + /// Uses and defines the world up vector as a vector specified by the user. + Vector + }; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-rotation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// Local aim axis of the constrained object Transform. + public Vector3 aimAxis; + /// Local up axis of the constrained object Transform. + public Vector3 upAxis; + + /// + /// Specifies which mode to use to keep the upward direction of the constrained Object. + /// + public WorldUpType worldUpType; + /// + /// A static vector in world coordinates that is the general upward direction. This is used when World Up Type is set to WorldUpType.Vector. + /// + public Vector3 worldUpAxis; + /// + /// The Transform handle for the world up object. This is used when World Up Type is set to WorldUpType.ObjectUp or WorldUpType.ObjectRotationUp. + /// + public ReadOnlyTransformHandle worldUpObject; + + /// Axes mask. Rotation will apply on the local axis for a value of 1.0, and will be kept as is for a value of 0.0. + public Vector3 axesMask; + + /// Minimum rotation value. + public FloatProperty minLimit; + /// Maximum rotation value. + public FloatProperty maxLimit; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AnimationStreamHandleUtility.ReadFloats(stream, sourceWeights, weightBuffer); + + float sumWeights = AnimationRuntimeUtils.Sum(weightBuffer); + if (sumWeights < k_Epsilon) + { + AnimationRuntimeUtils.PassThrough(stream, driven); + return; + } + + var weightScale = sumWeights > 1f ? 1f / sumWeights : 1f; + + var drivenParentInvRot = Quaternion.Inverse(drivenParent.GetRotation(stream)); + var drivenParentInvWorldMatrix = Matrix4x4.Inverse(drivenParent.GetLocalToWorldMatrix(stream)); + + var accumWeights = 0f; + var accumDeltaRot = QuaternionExt.zero; + + var drivenWPos = driven.GetPosition(stream); + var drivenLRot = driven.GetLocalRotation(stream); + + var worldUpVector = ComputeWorldUpVector(stream); + var upVector = AnimationRuntimeUtils.Select(Vector3.zero, upAxis, axesMask); + + var hasMasks = Vector3.Dot(axesMask, axesMask) < 3f; + var hasUpAxisCorrection = worldUpType != WorldUpType.None && Vector3.Dot(upVector, upVector) > k_Epsilon; + + var minMaxAngles = new Vector2(minLimit.Get(stream), maxLimit.Get(stream)); + + for (int i = 0; i < sourceTransforms.Length; ++i) + { + var normalizedWeight = weightBuffer[i] * weightScale; + if (normalizedWeight < k_Epsilon) + continue; + + var sourceTransform = sourceTransforms[i]; + + var fromDir = drivenLRot * aimAxis; + var toDir = drivenParentInvWorldMatrix.MultiplyVector(sourceTransform.GetPosition(stream) - drivenWPos); + + if (toDir.sqrMagnitude < k_Epsilon) + continue; + + var crossDir = Vector3.Cross(fromDir, toDir).normalized; + if (hasMasks) + { + crossDir = AnimationRuntimeUtils.Select(Vector3.zero, crossDir, axesMask).normalized; + if (Vector3.Dot(crossDir, crossDir) > k_Epsilon) + { + fromDir = AnimationRuntimeUtils.ProjectOnPlane(fromDir, crossDir); + toDir = AnimationRuntimeUtils.ProjectOnPlane(toDir, crossDir); + } + else + { + toDir = fromDir; + } + } + + var rotToSource = Quaternion.AngleAxis( + Mathf.Clamp(Vector3.Angle(fromDir, toDir), minMaxAngles.x, minMaxAngles.y), + crossDir + ); + + if (hasUpAxisCorrection) + { + var wupProject = Vector3.Cross(Vector3.Cross(drivenParentInvRot * worldUpVector, toDir).normalized, toDir).normalized; + var rupProject = Vector3.Cross(Vector3.Cross(rotToSource * drivenLRot * upVector, toDir).normalized, toDir).normalized; + + rotToSource = QuaternionExt.FromToRotation(rupProject, wupProject) * rotToSource; + } + + accumDeltaRot = QuaternionExt.Add( + accumDeltaRot, + QuaternionExt.Scale(sourceOffsets[i] * rotToSource, normalizedWeight) + ); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + accumWeights += normalizedWeight; + } + + accumDeltaRot = QuaternionExt.NormalizeSafe(accumDeltaRot); + if (accumWeights < 1f) + accumDeltaRot = Quaternion.Lerp(Quaternion.identity, accumDeltaRot, accumWeights); + + var newRot = accumDeltaRot * drivenLRot; + if (hasMasks) + newRot = Quaternion.Euler(AnimationRuntimeUtils.Select(drivenLRot.eulerAngles, newRot.eulerAngles, axesMask)); + + var offset = drivenOffset.Get(stream); + if (Vector3.Dot(offset, offset) > 0f) + newRot *= Quaternion.Euler(offset); + + driven.SetLocalRotation(stream, Quaternion.Lerp(drivenLRot, newRot, w)); + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + + Vector3 ComputeWorldUpVector(AnimationStream stream) + { + var result = Vector3.up; + switch (worldUpType) + { + case WorldUpType.None: + result = Vector3.zero; + break; + case WorldUpType.SceneUp: + // the scene Up vector and the World Up vector are the same thing + break; + case WorldUpType.ObjectUp: + { + // the target's Up vector points to the up object + var referencePos = Vector3.zero; + if (worldUpObject.IsValid(stream)) + referencePos = worldUpObject.GetPosition(stream); + var targetPos = driven.GetPosition(stream); + + result = (referencePos - targetPos).normalized; + break; + } + case WorldUpType.ObjectRotationUp: + { + var upRotation = Quaternion.identity; + if (worldUpObject.IsValid(stream)) + upRotation = worldUpObject.GetRotation(stream); + + // if no object is specified, the up vector is defined relative to the scene world space + result = upRotation * worldUpAxis; + break; + } + case WorldUpType.Vector: + result = worldUpAxis; + break; + default: + break; + } + + return result; + } + } + + /// + /// This interface defines the data mapping for the MultiAim constraint. + /// + public interface IMultiAimConstraintData + { + /// The Transform affected by the constraint Source Transforms. + Transform constrainedObject { get; } + /// + /// The list of Transforms that influence the constrained Transform orientation. + /// Each source has a weight from 0 to 1. + /// + WeightedTransformArray sourceObjects { get; } + /// + /// This is used to maintain the current rotation offset from the constrained GameObject to the source GameObjects. + /// + bool maintainOffset { get; } + + /// Specifies the local aim axis of the constrained Transform to use in order to orient itself to the Source Transforms. + Vector3 aimAxis { get; } + /// Specified the local up axis of the constrained Transform to use in order to orient itself to the Source Transforms. + Vector3 upAxis { get; } + + + /// + /// Specifies which mode to use to keep the upward direction of the constrained Object. + /// + /// + int worldUpType { get; } + /// + /// A static vector in world coordinates that is the general upward direction. This is used when World Up Type is set to WorldUpType.Vector. + /// + Vector3 worldUpAxis { get; } + /// + /// The Transform used to calculate the upward direction. This is used when World Up Type is set to WorldUpType.ObjectUp or WorldUpType.ObjectRotationUp. + /// + Transform worldUpObject { get; } + + /// Toggles whether the constrained Transform will rotate along the X axis. + bool constrainedXAxis { get; } + /// Toggles whether the constrained Transform will rotate along the Y axis. + bool constrainedYAxis { get; } + /// Toggles whether the constrained Transform will rotate along the Z axis. + bool constrainedZAxis { get; } + + /// The path to the offset property in the constraint component. + string offsetVector3Property { get; } + /// The path to the minimum limit property in the constraint component. + string minLimitFloatProperty { get; } + /// The path to the maximum limit property in the constraint component. + string maxLimitFloatProperty { get; } + /// The path to the source objects property in the constraint component. + string sourceObjectsProperty { get; } + } + + /// + /// The MultiAim constraint job binder. + /// + /// The constraint data type + public class MultiAimConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiAimConstraintData + { + /// + public override MultiAimConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiAimConstraintJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.aimAxis = data.aimAxis; + job.upAxis = data.upAxis; + + job.worldUpType = (MultiAimConstraintJob.WorldUpType)data.worldUpType; + job.worldUpAxis = data.worldUpAxis; + + if (data.worldUpObject != null) + job.worldUpObject = ReadOnlyTransformHandle.Bind(animator, data.worldUpObject); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadOnlyTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int i = 0; i < sourceObjects.Count; ++i) + { + if (data.maintainOffset) + { + var constrainedAim = data.constrainedObject.rotation * data.aimAxis; + job.sourceOffsets[i] = QuaternionExt.FromToRotation( + sourceObjects[i].transform.position - data.constrainedObject.position, + constrainedAim + ); + } + else + job.sourceOffsets[i] = Quaternion.identity; + } + + job.minLimit = FloatProperty.Bind(animator, component, data.minLimitFloatProperty); + job.maxLimit = FloatProperty.Bind(animator, component, data.maxLimitFloatProperty); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + + job.axesMask = new Vector3( + System.Convert.ToSingle(data.constrainedXAxis), + System.Convert.ToSingle(data.constrainedYAxis), + System.Convert.ToSingle(data.constrainedZAxis) + ); + + return job; + } + + /// + public override void Destroy(MultiAimConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs.meta new file mode 100644 index 00000000..db30a6ed --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiAimConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b3fcaeaf10650b4ba5972b838169673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs new file mode 100644 index 00000000..47ca6e75 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs @@ -0,0 +1,222 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiParent constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiParentConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// Position axes mask. Position will apply on the local axis for a value of 1.0, and will be kept as is for a value of 0.0. + public Vector3 positionAxesMask; + /// Rotation axes mask. Rotation will apply on the local axis for a value of 1.0, and will be kept as is for a value of 0.0. + public Vector3 rotationAxesMask; + + /// The main weight given to the constraint. This is a value in between 0 and 1. + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AnimationStreamHandleUtility.ReadFloats(stream, sourceWeights, weightBuffer); + + float sumWeights = AnimationRuntimeUtils.Sum(weightBuffer); + if (sumWeights < k_Epsilon) + { + AnimationRuntimeUtils.PassThrough(stream, driven); + return; + } + + float weightScale = sumWeights > 1f ? 1f / sumWeights : 1f; + + float accumWeights = 0f; + var accumTx = new AffineTransform(Vector3.zero, QuaternionExt.zero); + for (int i = 0; i < sourceTransforms.Length; ++i) + { + ReadOnlyTransformHandle sourceTransform = sourceTransforms[i]; + var normalizedWeight = weightBuffer[i] * weightScale; + if (normalizedWeight < k_Epsilon) + continue; + + sourceTransform.GetGlobalTR(stream, out Vector3 srcWPos, out Quaternion srcWRot); + var sourceTx = new AffineTransform(srcWPos, srcWRot); + + sourceTx *= sourceOffsets[i]; + + accumTx.translation += sourceTx.translation * normalizedWeight; + accumTx.rotation = QuaternionExt.Add(accumTx.rotation, QuaternionExt.Scale(sourceTx.rotation, normalizedWeight)); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + accumWeights += normalizedWeight; + } + + driven.GetGlobalTR(stream, out Vector3 currentWPos, out Quaternion currentWRot); + var drivenTx = new AffineTransform(currentWPos, currentWRot); + + accumTx.rotation = QuaternionExt.NormalizeSafe(accumTx.rotation); + if (accumWeights < 1f) + { + accumTx.translation += currentWPos * (1f - accumWeights); + accumTx.rotation = Quaternion.Lerp(currentWRot, accumTx.rotation, accumWeights); + } + + var parentTx = AffineTransform.identity; + + // Convert accumTx and drivenTx to local space + if (drivenParent.IsValid(stream)) + { + drivenParent.GetGlobalTR(stream, out Vector3 parentWPos, out Quaternion parentWRot); + parentTx = new AffineTransform(parentWPos, parentWRot); + accumTx = parentTx.InverseMul(accumTx); + drivenTx = parentTx.InverseMul(drivenTx); + } + + if (Vector3.Dot(positionAxesMask, positionAxesMask) < 3f) + accumTx.translation = AnimationRuntimeUtils.Lerp(drivenTx.translation, accumTx.translation, positionAxesMask); + if (Vector3.Dot(rotationAxesMask, rotationAxesMask) < 3f) + accumTx.rotation = Quaternion.Euler(AnimationRuntimeUtils.Lerp(drivenTx.rotation.eulerAngles, accumTx.rotation.eulerAngles, rotationAxesMask)); + + // Convert accumTx back to world space + accumTx = parentTx * accumTx; + + driven.SetGlobalTR( + stream, + Vector3.Lerp(currentWPos, accumTx.translation, w), + Quaternion.Lerp(currentWRot, accumTx.rotation, w) + ); + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + } + + /// + /// This interface defines the data mapping for the MultiParent constraint. + /// + public interface IMultiParentConstraintData + { + /// The Transform affected by the constraint Source Transforms. + Transform constrainedObject { get; } + /// + /// The list of Transforms that influence the constrained Transform rotation. + /// Each source has a weight from 0 to 1. + /// + WeightedTransformArray sourceObjects { get; } + /// This is used to maintain the current position offset from the constrained GameObject to the source GameObjects. + bool maintainPositionOffset { get; } + /// This is used to maintain the current rotation offset from the constrained GameObject to the source GameObjects. + bool maintainRotationOffset { get; } + + /// Toggles whether the constrained transform will translate along the X axis. + bool constrainedPositionXAxis { get; } + /// Toggles whether the constrained transform will translate along the Y axis. + bool constrainedPositionYAxis { get; } + /// Toggles whether the constrained transform will translate along the Z axis. + bool constrainedPositionZAxis { get; } + /// Toggles whether the constrained transform will rotate along the X axis. + bool constrainedRotationXAxis { get; } + /// Toggles whether the constrained transform will rotate along the Y axis. + bool constrainedRotationYAxis { get; } + /// Toggles whether the constrained transform will rotate along the Z axis. + bool constrainedRotationZAxis { get; } + + /// The path to the source objects property in the constraint component. + string sourceObjectsProperty { get; } + } + + /// + /// The MultiParent constraint job binder. + /// + /// The constraint data type + public class MultiParentConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiParentConstraintData + { + /// + public override MultiParentConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiParentConstraintJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadOnlyTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + var drivenTx = new AffineTransform(data.constrainedObject.position, data.constrainedObject.rotation); + for (int i = 0; i < sourceObjects.Count; ++i) + { + var sourceTransform = sourceObjects[i].transform; + + var srcTx = new AffineTransform(sourceTransform.position, sourceTransform.rotation); + var srcOffset = AffineTransform.identity; + var tmp = srcTx.InverseMul(drivenTx); + + if (data.maintainPositionOffset) + srcOffset.translation = tmp.translation; + if (data.maintainRotationOffset) + srcOffset.rotation = tmp.rotation; + + job.sourceOffsets[i] = srcOffset; + } + + job.positionAxesMask = new Vector3( + System.Convert.ToSingle(data.constrainedPositionXAxis), + System.Convert.ToSingle(data.constrainedPositionYAxis), + System.Convert.ToSingle(data.constrainedPositionZAxis) + ); + job.rotationAxesMask = new Vector3( + System.Convert.ToSingle(data.constrainedRotationXAxis), + System.Convert.ToSingle(data.constrainedRotationYAxis), + System.Convert.ToSingle(data.constrainedRotationZAxis) + ); + + return job; + } + + /// + public override void Destroy(MultiParentConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs.meta new file mode 100644 index 00000000..a70ade4f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiParentConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ad0821068505234982932fc083e74bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs new file mode 100644 index 00000000..b0af3678 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs @@ -0,0 +1,180 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiPosition constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiPositionConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-translation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// Axes mask. Rotation will apply on the local axis for a value of 1.0, and will be kept as is for a value of 0.0. + public Vector3 axesMask; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AnimationStreamHandleUtility.ReadFloats(stream, sourceWeights, weightBuffer); + + float sumWeights = AnimationRuntimeUtils.Sum(weightBuffer); + if (sumWeights < k_Epsilon) + { + AnimationRuntimeUtils.PassThrough(stream, driven); + return; + } + + float weightScale = sumWeights > 1f ? 1f / sumWeights : 1f; + + var currentWPos = driven.GetPosition(stream); + var drivenPos = currentWPos; + + Vector3 accumPos = currentWPos; + for (int i = 0; i < sourceTransforms.Length; ++i) + { + var normalizedWeight = weightBuffer[i] * weightScale; + if (normalizedWeight < k_Epsilon) + continue; + + ReadOnlyTransformHandle sourceTransform = sourceTransforms[i]; + accumPos += (sourceTransform.GetPosition(stream) + sourceOffsets[i] - currentWPos) * normalizedWeight; + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + } + + var parentTx = AffineTransform.identity; + + // Convert accumPos and drivenPos to local space + if (drivenParent.IsValid(stream)) + { + drivenParent.GetGlobalTR(stream, out Vector3 parentWPos, out Quaternion parentWRot); + parentTx = new AffineTransform(parentWPos, parentWRot); + accumPos = parentTx.InverseTransform(accumPos); + drivenPos = parentTx.InverseTransform(drivenPos); + } + + if (Vector3.Dot(axesMask, axesMask) < 3f) + accumPos = AnimationRuntimeUtils.Lerp(drivenPos, accumPos, axesMask); + + // Convert accumPos back to world space + accumPos = parentTx * (accumPos + drivenOffset.Get(stream)); + + driven.SetPosition(stream, Vector3.Lerp(currentWPos, accumPos, w)); + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + } + + /// + /// This interface defines the data mapping for the MultiPosition constraint. + /// + public interface IMultiPositionConstraintData + { + /// The Transform affected by the constraint Source Transforms. + Transform constrainedObject { get; } + /// + /// The list of Transforms that influence the constrained Transform position. + /// Each source has a weight from 0 to 1. + /// + WeightedTransformArray sourceObjects { get; } + /// This is used to maintain the current position offset from the constrained GameObject to the source GameObjects. + bool maintainOffset { get; } + + /// The path to the offset property in the constraint component. + string offsetVector3Property { get; } + /// The path to the source objects property in the constraint component. + string sourceObjectsProperty { get; } + + /// Toggles whether the constrained transform will translate along the X axis. + bool constrainedXAxis { get; } + /// Toggles whether the constrained transform will translate along the Y axis. + bool constrainedYAxis { get; } + /// Toggles whether the constrained transform will translate along the Z axis. + bool constrainedZAxis { get; } + } + + /// + /// The MultiPosition constraint job binder. + /// + /// The constraint data type + public class MultiPositionConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiPositionConstraintData + { + /// + public override MultiPositionConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiPositionConstraintJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadOnlyTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + Vector3 drivenPos = data.constrainedObject.position; + for (int i = 0; i < sourceObjects.Count; ++i) + { + job.sourceOffsets[i] = data.maintainOffset ? (drivenPos - sourceObjects[i].transform.position) : Vector3.zero; + } + + job.axesMask = new Vector3( + System.Convert.ToSingle(data.constrainedXAxis), + System.Convert.ToSingle(data.constrainedYAxis), + System.Convert.ToSingle(data.constrainedZAxis) + ); + + return job; + } + + /// + public override void Destroy(MultiPositionConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs.meta new file mode 100644 index 00000000..6d9a1ca8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiPositionConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 819c022146d742641b3cc4ec97fdae03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs new file mode 100644 index 00000000..6bf5a634 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs @@ -0,0 +1,141 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiReferential constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiReferentialConstraintJob : IWeightedAnimationJob + { + /// The driver index. + public IntProperty driver; + /// The list of Transforms that are affected by the specified driver. + public NativeArray sources; + /// Cache of AffineTransform representing the source objects initial positions. + public NativeArray sourceBindTx; + /// List of AffineTransform to apply to driven source objects. + public NativeArray offsetTx; + + private int m_PrevDriverIdx; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + var driverIdx = driver.Get(stream); + if (driverIdx != m_PrevDriverIdx) + UpdateOffsets(driverIdx); + + sources[driverIdx].GetGlobalTR(stream, out Vector3 driverWPos, out Quaternion driverWRot); + var driverTx = new AffineTransform(driverWPos, driverWRot); + + int offset = 0; + for (int i = 0; i < sources.Length; ++i) + { + if (i == driverIdx) + continue; + + var tx = driverTx * offsetTx[offset]; + + var src = sources[i]; + src.GetGlobalTR(stream, out Vector3 srcWPos, out Quaternion srcWRot); + src.SetGlobalTR(stream, Vector3.Lerp(srcWPos, tx.translation, w), Quaternion.Lerp(srcWRot, tx.rotation, w)); + offset++; + + sources[i] = src; + } + + AnimationRuntimeUtils.PassThrough(stream, sources[driverIdx]); + } + else + { + for (int i = 0; i < sources.Length; ++i) + AnimationRuntimeUtils.PassThrough(stream, sources[i]); + } + } + + internal void UpdateOffsets(int driver) + { + driver = Mathf.Clamp(driver, 0, sources.Length - 1); + + int offset = 0; + var invDriverTx = sourceBindTx[driver].Inverse(); + for (int i = 0; i < sourceBindTx.Length; ++i) + { + if (i == driver) + continue; + + offsetTx[offset] = invDriverTx * sourceBindTx[i]; + offset++; + } + + m_PrevDriverIdx = driver; + } + } + + /// + /// This interface defines the data mapping for the MultiReferential constraint. + /// + public interface IMultiReferentialConstraintData + { + /// The driver index. This is a value in between 0 and the number of sourceObjects. + int driverValue { get; } + /// The path to the driver property in the constraint component. + string driverIntProperty { get; } + /// The list of Transforms that can act as drivers for the constrained Transform. + Transform[] sourceObjects { get; } + } + + /// + /// The MultiReferential constraint job binder. + /// + /// The constraint data type + public class MultiReferentialConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiReferentialConstraintData + { + /// + public override MultiReferentialConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiReferentialConstraintJob(); + + var sources = data.sourceObjects; + job.driver = IntProperty.Bind(animator, component, data.driverIntProperty); + job.sources = new NativeArray(sources.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.sourceBindTx = new NativeArray(sources.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.offsetTx = new NativeArray(sources.Length - 1, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int i = 0; i < sources.Length; ++i) + { + job.sources[i] = ReadWriteTransformHandle.Bind(animator, sources[i].transform); + job.sourceBindTx[i] = new AffineTransform(sources[i].position, sources[i].rotation); + } + + job.UpdateOffsets(data.driverValue); + + return job; + } + + /// + public override void Destroy(MultiReferentialConstraintJob job) + { + job.sources.Dispose(); + job.sourceBindTx.Dispose(); + job.offsetTx.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs.meta new file mode 100644 index 00000000..134ef516 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d4d5179cf211d24d9f152ce8fd9c880 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs new file mode 100644 index 00000000..e9d8a47a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs @@ -0,0 +1,180 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiRotation constraint job. + /// + [Unity.Burst.BurstCompile] + public struct MultiRotationConstraintJob : IWeightedAnimationJob + { + const float k_Epsilon = 1e-5f; + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the constrained object parent Transform. + public ReadOnlyTransformHandle drivenParent; + /// The post-rotation offset applied to the constrained object. + public Vector3Property drivenOffset; + + /// List of Transform handles for the source objects. + public NativeArray sourceTransforms; + /// List of weights for the source objects. + public NativeArray sourceWeights; + /// List of offsets to apply to source rotations if maintainOffset is enabled. + public NativeArray sourceOffsets; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// Axes mask. Rotation will apply on the local axis for a value of 1.0, and will be kept as is for a value of 0.0. + public Vector3 axesMask; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AnimationStreamHandleUtility.ReadFloats(stream, sourceWeights, weightBuffer); + + float sumWeights = AnimationRuntimeUtils.Sum(weightBuffer); + if (sumWeights < k_Epsilon) + { + AnimationRuntimeUtils.PassThrough(stream, driven); + return; + } + + float weightScale = sumWeights > 1f ? 1f / sumWeights : 1f; + + float accumWeights = 0f; + Quaternion accumRot = QuaternionExt.zero; + for (int i = 0; i < sourceTransforms.Length; ++i) + { + var normalizedWeight = weightBuffer[i] * weightScale; + if (normalizedWeight < k_Epsilon) + continue; + + ReadOnlyTransformHandle sourceTransform = sourceTransforms[i]; + accumRot = QuaternionExt.Add(accumRot, QuaternionExt.Scale(sourceTransform.GetRotation(stream) * sourceOffsets[i], normalizedWeight)); + + // Required to update handles with binding info. + sourceTransforms[i] = sourceTransform; + accumWeights += normalizedWeight; + } + + accumRot = QuaternionExt.NormalizeSafe(accumRot); + if (accumWeights < 1f) + accumRot = Quaternion.Lerp(driven.GetRotation(stream), accumRot, accumWeights); + + // Convert accumRot to local space + if (drivenParent.IsValid(stream)) + accumRot = Quaternion.Inverse(drivenParent.GetRotation(stream)) * accumRot; + + Quaternion currentLRot = driven.GetLocalRotation(stream); + if (Vector3.Dot(axesMask, axesMask) < 3f) + accumRot = Quaternion.Euler(AnimationRuntimeUtils.Lerp(currentLRot.eulerAngles, accumRot.eulerAngles, axesMask)); + + var offset = drivenOffset.Get(stream); + if (Vector3.Dot(offset, offset) > 0f) + accumRot *= Quaternion.Euler(offset); + + driven.SetLocalRotation(stream, Quaternion.Lerp(currentLRot, accumRot, w)); + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + } + + /// + /// This interface defines the data mapping for the MultiRotation constraint. + /// + public interface IMultiRotationConstraintData + { + /// The Transform affected by the constraint Source Transforms. + Transform constrainedObject { get; } + + /// + /// The list of Transforms that influence the constrained Transform rotation. + /// Each source has a weight from 0 to 1. + /// + WeightedTransformArray sourceObjects { get; } + /// This is used to maintain the current rotation offset from the constrained GameObject to the source GameObjects. + bool maintainOffset { get; } + + /// The path to the offset property in the constraint component. + string offsetVector3Property { get; } + /// The path to the source objects property in the constraint component. + string sourceObjectsProperty { get; } + + /// Toggles whether the constrained transform will rotate along the X axis. + bool constrainedXAxis { get; } + /// Toggles whether the constrained transform will rotate along the Y axis. + bool constrainedYAxis { get; } + /// Toggles whether the constrained transform will rotate along the Z axis. + bool constrainedZAxis { get; } + } + + /// + /// The MultiRotation constraint job binder. + /// + /// The constraint data type + public class MultiRotationConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IMultiRotationConstraintData + { + /// + public override MultiRotationConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new MultiRotationConstraintJob(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + job.drivenParent = ReadOnlyTransformHandle.Bind(animator, data.constrainedObject.parent); + job.drivenOffset = Vector3Property.Bind(animator, component, data.offsetVector3Property); + + WeightedTransformArray sourceObjects = data.sourceObjects; + + WeightedTransformArrayBinder.BindReadOnlyTransforms(animator, component, sourceObjects, out job.sourceTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, sourceObjects, data.sourceObjectsProperty, out job.sourceWeights); + + job.sourceOffsets = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.weightBuffer = new NativeArray(sourceObjects.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + Quaternion drivenRot = data.constrainedObject.rotation; + for (int i = 0; i < sourceObjects.Count; ++i) + { + job.sourceOffsets[i] = data.maintainOffset ? + (Quaternion.Inverse(sourceObjects[i].transform.rotation) * drivenRot) : Quaternion.identity; + } + + job.axesMask = new Vector3( + System.Convert.ToSingle(data.constrainedXAxis), + System.Convert.ToSingle(data.constrainedYAxis), + System.Convert.ToSingle(data.constrainedZAxis) + ); + + return job; + } + + /// + public override void Destroy(MultiRotationConstraintJob job) + { + job.sourceTransforms.Dispose(); + job.sourceWeights.Dispose(); + job.sourceOffsets.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs.meta new file mode 100644 index 00000000..d5affcc4 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/MultiRotationConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e65922aac9b8f54c99855336aea7f75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs new file mode 100644 index 00000000..40ed4097 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs @@ -0,0 +1,241 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The OverrideTransform job. + /// + [Unity.Burst.BurstCompile] + public struct OverrideTransformJob : IWeightedAnimationJob + { + /// + /// The override space controls how the override source Transform + /// is copied unto constrained Transform. + /// + public enum Space + { + /// Copy override world TR components into world TR components of the constrained Transform. + World = 0, + /// Copy override local TR components into local TR components of the constrained Transform. + Local = 1, + /// Add override local TR components to local TR components of the constrained Transform. + Pivot = 2 + } + + /// The Transform handle for the constrained object Transform. + public ReadWriteTransformHandle driven; + /// The Transform handle for the override source object Transform. + public ReadOnlyTransformHandle source; + /// Cached inverse local TR used in space switching calculations. + public AffineTransform sourceInvLocalBindTx; + + /// Cached source to world coordinates quaternion. Used when Space is set to World. + public Quaternion sourceToWorldRot; + /// Cached source to local coordinates quaternion. Used when Space is set to Local. + public Quaternion sourceToLocalRot; + /// Cached source to pivot coordinates quaternion. Used when Space is set to Pivot. + public Quaternion sourceToPivotRot; + + /// CacheIndex to the override space. + /// + public CacheIndex spaceIdx; + /// CacheIndex to source to space quaternion used in current space. + /// + public CacheIndex sourceToCurrSpaceRotIdx; + + /// The override position. + public Vector3Property position; + /// The override rotation. + public Vector3Property rotation; + /// The weight for which override position has an effect on the constrained Transform. This is a value in between 0 and 1. + public FloatProperty positionWeight; + /// The weight for which override rotation has an effect on the constrained Transform. This is a value in between 0 and 1. + public FloatProperty rotationWeight; + + /// Cache for static properties in the job. + public AnimationJobCache cache; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AffineTransform overrideTx; + if (source.IsValid(stream)) + { + source.GetLocalTRS(stream, out Vector3 srcLPos, out Quaternion srcLRot, out _); + var sourceLocalTx = new AffineTransform(srcLPos, srcLRot); + var sourceToSpaceRot = cache.Get(sourceToCurrSpaceRotIdx); + overrideTx = Quaternion.Inverse(sourceToSpaceRot) * (sourceInvLocalBindTx * sourceLocalTx) * sourceToSpaceRot; + } + else + overrideTx = new AffineTransform(position.Get(stream), Quaternion.Euler(rotation.Get(stream))); + + Space overrideSpace = (Space)cache.GetRaw(spaceIdx); + var posW = positionWeight.Get(stream) * w; + var rotW = rotationWeight.Get(stream) * w; + switch (overrideSpace) + { + case Space.World: + { + driven.GetGlobalTR(stream, out Vector3 drivenWPos, out Quaternion drivenWRot); + driven.SetGlobalTR( + stream, + Vector3.Lerp(drivenWPos, overrideTx.translation, posW), + Quaternion.Lerp(drivenWRot, overrideTx.rotation, rotW) + ); + } + break; + case Space.Local: + { + driven.GetLocalTRS(stream, out Vector3 drivenLPos, out Quaternion drivenLRot, out Vector3 drivenLScale); + driven.SetLocalTRS( + stream, + Vector3.Lerp(drivenLPos, overrideTx.translation, posW), + Quaternion.Lerp(drivenLRot, overrideTx.rotation, rotW), + drivenLScale + ); + } + break; + case Space.Pivot: + { + driven.GetLocalTRS(stream, out Vector3 drivenLPos, out Quaternion drivenLRot, out Vector3 drivenLScale); + var drivenLocalTx = new AffineTransform(drivenLPos, drivenLRot); + overrideTx = drivenLocalTx * overrideTx; + + driven.SetLocalTRS( + stream, + Vector3.Lerp(drivenLocalTx.translation, overrideTx.translation, posW), + Quaternion.Lerp(drivenLocalTx.rotation, overrideTx.rotation, rotW), + drivenLScale + ); + } + break; + default: + break; + } + } + else + AnimationRuntimeUtils.PassThrough(stream, driven); + } + + internal void UpdateSpace(int space) + { + if ((int)cache.GetRaw(spaceIdx) == space) + return; + + cache.SetRaw(space, spaceIdx); + + Space currSpace = (Space)space; + if (currSpace == Space.Pivot) + cache.Set(sourceToPivotRot, sourceToCurrSpaceRotIdx); + else if (currSpace == Space.Local) + cache.Set(sourceToLocalRot, sourceToCurrSpaceRotIdx); + else + cache.Set(sourceToWorldRot, sourceToCurrSpaceRotIdx); + } + } + + /// + /// This interface defines the data mapping for OverrideTransform. + /// + public interface IOverrideTransformData + { + /// The Transform affected by the constraint source Transform. + Transform constrainedObject { get; } + /// The override source Transform. + Transform sourceObject { get; } + /// The override space. + int space { get; } + + /// The path to the position weight property in the constraint component. + string positionWeightFloatProperty { get; } + /// The path to the rotation weight property in the constraint component. + string rotationWeightFloatProperty { get; } + /// The path to the override position property in the constraint component. + string positionVector3Property { get; } + /// The path to the override rotation property in the constraint component. + string rotationVector3Property { get; } + } + + /// + /// The OverrideTransform job binder. + /// + /// The constraint data type + public class OverrideTransformJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IOverrideTransformData + { + /// + public override OverrideTransformJob Create(Animator animator, ref T data, Component component) + { + var job = new OverrideTransformJob(); + var cacheBuilder = new AnimationJobCacheBuilder(); + + job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); + + if (data.sourceObject != null) + { + // Cache source to possible space rotation offsets (world, local and pivot) + // at bind time so we can switch dynamically between them at runtime. + + job.source = ReadOnlyTransformHandle.Bind(animator, data.sourceObject); + var sourceLocalTx = new AffineTransform(data.sourceObject.localPosition, data.sourceObject.localRotation); + job.sourceInvLocalBindTx = sourceLocalTx.Inverse(); + + var sourceWorldTx = new AffineTransform(data.sourceObject.position, data.sourceObject.rotation); + var drivenWorldTx = new AffineTransform(data.constrainedObject.position, data.constrainedObject.rotation); + job.sourceToWorldRot = sourceWorldTx.Inverse().rotation; + job.sourceToPivotRot = sourceWorldTx.InverseMul(drivenWorldTx).rotation; + + var drivenParent = data.constrainedObject.parent; + if (drivenParent != null) + { + var drivenParentWorldTx = new AffineTransform(drivenParent.position, drivenParent.rotation); + job.sourceToLocalRot = sourceWorldTx.InverseMul(drivenParentWorldTx).rotation; + } + else + job.sourceToLocalRot = job.sourceToPivotRot; + } + + job.spaceIdx = cacheBuilder.Add(data.space); + if (data.space == (int)OverrideTransformJob.Space.Pivot) + job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToPivotRot); + else if (data.space == (int)OverrideTransformJob.Space.Local) + job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToLocalRot); + else + job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToWorldRot); + + job.position = Vector3Property.Bind(animator, component, data.positionVector3Property); + job.rotation = Vector3Property.Bind(animator, component, data.rotationVector3Property); + job.positionWeight = FloatProperty.Bind(animator, component, data.positionWeightFloatProperty); + job.rotationWeight = FloatProperty.Bind(animator, component, data.rotationWeightFloatProperty); + + job.cache = cacheBuilder.Build(); + + return job; + } + + /// + public override void Destroy(OverrideTransformJob job) + { + job.cache.Dispose(); + } + + /// + public override void Update(OverrideTransformJob job, ref T data) + { + job.UpdateSpace(data.space); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs.meta new file mode 100644 index 00000000..6b429d26 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/OverrideTransformJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adadb93c5c0fa494797401ddcc0c1922 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs new file mode 100644 index 00000000..d9190edf --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs @@ -0,0 +1,250 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + using TransformSyncer = RigSyncSceneToStreamJob.TransformSyncer; + using PropertySyncer = RigSyncSceneToStreamJob.PropertySyncer; + + [Unity.Burst.BurstCompile] + internal struct RigSyncSceneToStreamJob : IAnimationJob + { + public struct TransformSyncer : System.IDisposable + { + public NativeArray sceneHandles; + public NativeArray streamHandles; + + public static TransformSyncer Create(int size) + { + return new TransformSyncer() { + sceneHandles = new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), + streamHandles = new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory) + }; + } + + public void Dispose() + { + if (sceneHandles.IsCreated) + sceneHandles.Dispose(); + + if (streamHandles.IsCreated) + streamHandles.Dispose(); + } + + public void BindAt(int index, Animator animator, Transform transform) + { + sceneHandles[index] = animator.BindSceneTransform(transform); + streamHandles[index] = animator.BindStreamTransform(transform); + } + + public void Sync(ref AnimationStream stream) + { + for (int i = 0, count = sceneHandles.Length; i < count; ++i) + { + var sceneHandle = sceneHandles[i]; + if (!sceneHandle.IsValid(stream)) + continue; + + var streamHandle = streamHandles[i]; + sceneHandle.GetLocalTRS(stream, out Vector3 scenePos, out Quaternion sceneRot, out Vector3 sceneScale); + streamHandle.SetLocalTRS(stream, scenePos, sceneRot, sceneScale, true); + + streamHandles[i] = streamHandle; + sceneHandles[i] = sceneHandle; + } + } + } + + internal struct PropertySyncer : System.IDisposable + { + public NativeArray sceneHandles; + public NativeArray streamHandles; + public NativeArray buffer; + + public static PropertySyncer Create(int size) + { + return new PropertySyncer() { + sceneHandles = new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), + streamHandles = new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), + buffer = new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory) + }; + } + + public void Dispose() + { + if (sceneHandles.IsCreated) + sceneHandles.Dispose(); + + if (streamHandles.IsCreated) + streamHandles.Dispose(); + + if (buffer.IsCreated) + buffer.Dispose(); + } + + public void BindAt(int index, Animator animator, Component component, string property) + { + sceneHandles[index] = animator.BindSceneProperty(component.transform, component.GetType(), property); + streamHandles[index] = animator.BindStreamProperty(component.transform, component.GetType(), property); + } + + public void Sync(ref AnimationStream stream) + { + AnimationSceneHandleUtility.ReadFloats(stream, sceneHandles, buffer); + AnimationStreamHandleUtility.WriteFloats(stream, streamHandles, buffer, true); + } + + public NativeArray StreamValues(ref AnimationStream stream) + { + AnimationStreamHandleUtility.ReadFloats(stream, streamHandles, buffer); + return buffer; + } + } + + public TransformSyncer transformSyncer; + public PropertySyncer propertySyncer; + public PropertySyncer rigWeightSyncer; + public PropertySyncer constraintWeightSyncer; + + public NativeArray rigStates; + public NativeArray rigConstraintEndIdx; + + public NativeArray modulatedConstraintWeights; + + public void ProcessRootMotion(AnimationStream stream) { } + + public void ProcessAnimation(AnimationStream stream) + { + transformSyncer.Sync(ref stream); + propertySyncer.Sync(ref stream); + rigWeightSyncer.Sync(ref stream); + constraintWeightSyncer.Sync(ref stream); + + var currRigWeights = rigWeightSyncer.StreamValues(ref stream); + var currConstraintWeights = constraintWeightSyncer.StreamValues(ref stream); + + int rigIndex = 0; + for (int i = 0, count = currConstraintWeights.Length; i < count; ++i) + { + if (i >= rigConstraintEndIdx[rigIndex]) + rigIndex++; + + currConstraintWeights[i] *= (currRigWeights[rigIndex] * rigStates[rigIndex]); + } + + AnimationStreamHandleUtility.WriteFloats(stream, modulatedConstraintWeights, currConstraintWeights, false); + } + } + + internal struct SyncableProperties + { + public RigProperties rig; + public ConstraintProperties[] constraints; + } + + internal interface IRigSyncSceneToStreamData + { + Transform[] syncableTransforms { get; } + SyncableProperties[] syncableProperties { get; } + + bool[] rigStates { get; } + } + + internal class RigSyncSceneToStreamJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, IRigSyncSceneToStreamData + { + internal static string[] s_PropertyElementNames = new string[] {".x", ".y", ".z", ".w"}; + + public override RigSyncSceneToStreamJob Create(Animator animator, ref T data, Component component) + { + var job = new RigSyncSceneToStreamJob(); + + var transforms = data.syncableTransforms; + if (transforms != null) + { + job.transformSyncer = TransformSyncer.Create(transforms.Length); + for (int i = 0; i < transforms.Length; ++i) + job.transformSyncer.BindAt(i, animator, transforms[i]); + } + + var properties = data.syncableProperties; + if (properties != null) + { + int rigCount = properties.Length, constraintCount = 0, propertyCount = 0; + for (int i = 0; i < properties.Length; ++i) + { + constraintCount += properties[i].constraints.Length; + for (int j = 0; j < properties[i].constraints.Length; ++j) + for (int k = 0; k < properties[i].constraints[j].properties.Length; ++k) + propertyCount += properties[i].constraints[j].properties[k].descriptor.size; + } + + job.propertySyncer = PropertySyncer.Create(propertyCount); + job.rigWeightSyncer = PropertySyncer.Create(rigCount); + job.constraintWeightSyncer = PropertySyncer.Create(constraintCount); + job.rigStates = new NativeArray(rigCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.rigConstraintEndIdx = new NativeArray(rigCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.modulatedConstraintWeights = new NativeArray(constraintCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + int constraintIdx = 0, propertyIdx = 0; + for (int i = 0; i < properties.Length; ++i) + { + job.rigWeightSyncer.BindAt(i, animator, properties[i].rig.component, RigProperties.s_Weight); + job.rigStates[i] = data.rigStates[i] ? 1f : 0f; + + var constraints = properties[i].constraints; + for (int j = 0; j < constraints.Length; ++j) + { + ref var constraint = ref constraints[j]; + + job.constraintWeightSyncer.BindAt(constraintIdx, animator, constraint.component, ConstraintProperties.s_Weight); + job.modulatedConstraintWeights[constraintIdx++] = animator.BindCustomStreamProperty( + ConstraintsUtils.ConstructCustomPropertyName(constraint.component, ConstraintProperties.s_Weight), + CustomStreamPropertyType.Float + ); + + for (int k = 0; k < constraint.properties.Length; ++k) + { + ref var property = ref constraint.properties[k]; + if (property.descriptor.size == 1) + job.propertySyncer.BindAt(propertyIdx++, animator, constraint.component, property.name); + else + { + Debug.Assert(property.descriptor.size <= 4); + for (int l = 0; l < property.descriptor.size; ++l) + job.propertySyncer.BindAt(propertyIdx++, animator, constraint.component, property.name + s_PropertyElementNames[l]); + } + } + } + + job.rigConstraintEndIdx[i] = constraintIdx; + } + } + + return job; + } + + public override void Destroy(RigSyncSceneToStreamJob job) + { + job.transformSyncer.Dispose(); + job.propertySyncer.Dispose(); + job.rigWeightSyncer.Dispose(); + job.constraintWeightSyncer.Dispose(); + + if (job.rigStates.IsCreated) + job.rigStates.Dispose(); + + if (job.rigConstraintEndIdx.IsCreated) + job.rigConstraintEndIdx.Dispose(); + + if (job.modulatedConstraintWeights.IsCreated) + job.modulatedConstraintWeights.Dispose(); + } + + public override void Update(RigSyncSceneToStreamJob job, ref T data) + { + int count = Mathf.Min(job.rigStates.Length, data.rigStates.Length); + for (int i = 0; i < count; ++i) + job.rigStates[i] = data.rigStates[i] ? 1f : 0f; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs.meta new file mode 100644 index 00000000..fca6d282 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/RigSyncSceneToStreamJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e244834d2385d54382ec1664e76601c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs new file mode 100644 index 00000000..1c7c62d8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs @@ -0,0 +1,312 @@ +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// Read/write handle on a Transform component used in Animation C# Jobs. + /// + public struct ReadWriteTransformHandle + { + TransformStreamHandle m_Handle; + + /// + /// Gets the position of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + public Vector3 GetLocalPosition(AnimationStream stream) => m_Handle.GetLocalPosition(stream); + /// + /// Gets the rotation of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform relative to the parent. + public Quaternion GetLocalRotation(AnimationStream stream) => m_Handle.GetLocalRotation(stream); + /// + /// Gets the scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The scale of the transform relative to the parent. + public Vector3 GetLocalScale(AnimationStream stream) => m_Handle.GetLocalScale(stream); + /// + /// Gets the position, rotation and scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + /// The rotation of the transform relative to the parent. + /// The scale of the transform relative to the parent. + public void GetLocalTRS(AnimationStream stream, out Vector3 position, out Quaternion rotation, out Vector3 scale) => + m_Handle.GetLocalTRS(stream, out position, out rotation, out scale); + + /// + /// Sets the position of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + public void SetLocalPosition(AnimationStream stream, Vector3 position) => m_Handle.SetLocalPosition(stream, position); + /// + /// Sets the rotation of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform relative to the parent. + public void SetLocalRotation(AnimationStream stream, Quaternion rotation) => m_Handle.SetLocalRotation(stream, rotation); + /// + /// Sets the scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The scale of the transform relative to the parent. + public void SetLocalScale(AnimationStream stream, Vector3 scale) => m_Handle.SetLocalScale(stream, scale); + /// + /// Sets the position, rotation and scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + /// The rotation of the transform relative to the parent. + /// The scale of the transform relative to the parent. + /// Set to true to write the specified parameters if the matching stream parameters have not already been modified. + public void SetLocalTRS(AnimationStream stream, Vector3 position, Quaternion rotation, Vector3 scale, bool useMask = false) => + m_Handle.SetLocalTRS(stream, position, rotation, scale, useMask); + + /// + /// Gets the position of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + public Vector3 GetPosition(AnimationStream stream) => m_Handle.GetPosition(stream); + /// + /// Gets the rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform in world space. + public Quaternion GetRotation(AnimationStream stream) => m_Handle.GetRotation(stream); + /// + /// Gets the position and scaled rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + /// The rotation of the transform in world space. + public void GetGlobalTR(AnimationStream stream, out Vector3 position, out Quaternion rotation) => + m_Handle.GetGlobalTR(stream, out position, out rotation); + + /// + /// Sets the position of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + public void SetPosition(AnimationStream stream, Vector3 position) => m_Handle.SetPosition(stream, position); + /// + /// Sets the rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform in world space. + public void SetRotation(AnimationStream stream, Quaternion rotation) => m_Handle.SetRotation(stream, rotation); + /// + /// Sets the position and rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + /// The rotation of the transform in world space. + /// Set to true to write the specified parameters if the matching stream parameters have not already been modified. + public void SetGlobalTR(AnimationStream stream, Vector3 position, Quaternion rotation, bool useMask = false) => + m_Handle.SetGlobalTR(stream, position, rotation, useMask); + + /// + /// Returns whether this handle is resolved. + /// A ReadWriteTransformHandle is resolved if it is valid, if it has the same bindings version than the one in the stream, and if it is bound to the transform in the stream. + /// A ReadWriteTransformHandle can become unresolved if the animator bindings have changed or if the transform had been destroyed. + /// + /// + /// + /// The AnimationStream that holds the animated values. + /// Returns true if the handle is resolved, false otherwise. + public bool IsResolved(AnimationStream stream) => m_Handle.IsResolved(stream); + /// + /// Returns whether this is a valid handle. + /// A ReadWriteTransformHandle may be invalid if, for example, you didn't use the correct function to create it. + /// + /// + /// The AnimationStream that holds the animated values. + /// Returns whether this is a valid handle. + public bool IsValid(AnimationStream stream) => m_Handle.IsValid(stream); + /// + /// Bind this handle with an animated values from the AnimationStream. + /// Handles are lazily resolved as they're accessed, but in order to prevent unwanted CPU spikes, this method allows to resolve handles in a deterministic way. + /// + /// + /// The AnimationStream that holds the animated values. + public void Resolve(AnimationStream stream) => m_Handle.Resolve(stream); + + /// + /// Create a ReadWriteTransformHandle representing the new binding between the Animator and a Transform already bound to the Animator. + /// + /// The Animator on which to bind the new handle. + /// The Transform to bind. + /// Returns the ReadWriteTransformHandle that represents the new binding. + /// Thrown if transform is null. + /// Thrown if transform is not a child in the Animator hierarchy. + public static ReadWriteTransformHandle Bind(Animator animator, Transform transform) + { + ReadWriteTransformHandle handle = new ReadWriteTransformHandle(); + if (transform == null) + throw new ArgumentNullException(nameof(transform)); + + if (!transform.IsChildOf(animator.avatarRoot)) + throw new InvalidOperationException($"Transform '{transform.name}' is not a child of the Animator hierarchy, and cannot be written to."); + + handle.m_Handle = animator.BindStreamTransform(transform); + return handle; + } + } + + /// + /// Read-only handle on a Transform component used in Animation C# Jobs. + /// + public struct ReadOnlyTransformHandle + { + TransformStreamHandle m_StreamHandle; + TransformSceneHandle m_SceneHandle; + byte m_InStream; + + /// + /// Gets the position of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + public Vector3 GetLocalPosition(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetLocalPosition(stream) : m_SceneHandle.GetLocalPosition(stream); + + /// + /// Gets the rotation of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform relative to the parent. + public Quaternion GetLocalRotation(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetLocalRotation(stream) : m_SceneHandle.GetLocalRotation(stream); + + /// + /// Gets the scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The scale of the transform relative to the parent. + public Vector3 GetLocalScale(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetLocalScale(stream) : m_SceneHandle.GetLocalScale(stream); + + /// + /// Gets the position, rotation and scale of the transform relative to the parent. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform relative to the parent. + /// The rotation of the transform relative to the parent. + /// The scale of the transform relative to the parent. + public void GetLocalTRS(AnimationStream stream, out Vector3 position, out Quaternion rotation, out Vector3 scale) + { + if (m_InStream == 1) + m_StreamHandle.GetLocalTRS(stream, out position, out rotation, out scale); + else + m_SceneHandle.GetLocalTRS(stream, out position, out rotation, out scale); + } + + /// + /// Gets the matrix of the transform in local space. + /// + /// The AnimationStream that holds the animated values. + /// The matrix of the transform in local space. + public Matrix4x4 GetLocalToParentMatrix(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetLocalToParentMatrix(stream) : m_SceneHandle.GetLocalToParentMatrix(stream); + + + /// + /// Gets the position of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + public Vector3 GetPosition(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetPosition(stream) : m_SceneHandle.GetPosition(stream); + + /// + /// Gets the rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The rotation of the transform in world space. + public Quaternion GetRotation(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetRotation(stream) : m_SceneHandle.GetRotation(stream); + + /// + /// Gets the position and scaled rotation of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The position of the transform in world space. + /// The rotation of the transform in world space. + public void GetGlobalTR(AnimationStream stream, out Vector3 position, out Quaternion rotation) + { + if (m_InStream == 1) + m_StreamHandle.GetGlobalTR(stream, out position, out rotation); + else + m_SceneHandle.GetGlobalTR(stream, out position, out rotation); + } + + /// + /// Gets the matrix of the transform in world space. + /// + /// The AnimationStream that holds the animated values. + /// The matrix of the transform in world space. + public Matrix4x4 GetLocalToWorldMatrix(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.GetLocalToWorldMatrix(stream) : m_SceneHandle.GetLocalToWorldMatrix(stream); + + + /// + /// Returns whether this handle is resolved. + /// A ReadOnlyTransformHandle is resolved if it is valid, if it has the same bindings version than the one in the stream, and if it is bound to the transform in the stream. + /// A ReadOnlyTransformHandle can become unresolved if the animator bindings have changed or if the transform had been destroyed. + /// + /// + /// + /// The AnimationStream that holds the animated values. + /// Returns true if the handle is resolved, false otherwise. + public bool IsResolved(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.IsResolved(stream) : true; + + /// + /// Returns whether this is a valid handle. + /// A ReadOnlyTransformHandle may be invalid if, for example, you didn't use the correct function to create it. + /// + /// + /// The AnimationStream that holds the animated values. + /// Returns whether this is a valid handle. + public bool IsValid(AnimationStream stream) => + m_InStream == 1 ? m_StreamHandle.IsValid(stream) : m_SceneHandle.IsValid(stream); + + /// + /// Bind this handle with an animated values from the AnimationStream. + /// Handles are lazily resolved as they're accessed, but in order to prevent unwanted CPU spikes, this method allows to resolve handles in a deterministic way. + /// + /// + /// The AnimationStream that holds the animated values. + public void Resolve(AnimationStream stream) + { + if (m_InStream == 1) + m_StreamHandle.Resolve(stream); + } + + /// + /// Create a ReadOnlyTransformHandle representing the new binding between the Animator and a Transform already bound to the Animator. + /// + /// The Animator on which to bind the new handle. + /// The Transform to bind. + /// Returns the ReadOnlyTransformHandle that represents the new binding. + /// Thrown if transform is null. + public static ReadOnlyTransformHandle Bind(Animator animator, Transform transform) + { + ReadOnlyTransformHandle handle = new ReadOnlyTransformHandle(); + if (transform == null) + throw new ArgumentNullException(nameof(transform)); + + handle.m_InStream = (byte)(transform.IsChildOf(animator.avatarRoot) ? 1 : 0); + if (handle.m_InStream == 1) + handle.m_StreamHandle = animator.BindStreamTransform(transform); + else + handle.m_SceneHandle = animator.BindSceneTransform(transform); + + return handle; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs.meta new file mode 100644 index 00000000..c4c577aa --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TransformHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85da06385921c524c9e558da8da655c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs new file mode 100644 index 00000000..5e2a2ce3 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs @@ -0,0 +1,155 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwistChain constraint job. + /// + [Unity.Burst.BurstCompile] + public struct TwistChainConstraintJob : IWeightedAnimationJob + { + /// The Transform handle for the root target Transform. + public ReadWriteTransformHandle rootTarget; + /// The Transform handle for the tip target Transform. + public ReadWriteTransformHandle tipTarget; + + /// An array of Transform handles that represents the Transform chain. + public NativeArray chain; + + /// An array of interpolant values used to reevaluate the weights. + public NativeArray steps; + /// An array of weight values used to adjust how twist is distributed along the chain. + public NativeArray weights; + /// An array of rotation offsets to maintain the chain initial shape. + public NativeArray rotations; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + // Retrieve root and tip rotation. + Quaternion rootRotation = rootTarget.GetRotation(stream); + Quaternion tipRotation = tipTarget.GetRotation(stream); + + // Interpolate rotation on chain. + chain[0].SetRotation(stream, Quaternion.Lerp(chain[0].GetRotation(stream), rootRotation, w)); + for (int i = 1; i < chain.Length - 1; ++i) + { + chain[i].SetRotation(stream, Quaternion.Lerp(chain[i].GetRotation(stream), rotations[i] * Quaternion.Lerp(rootRotation, tipRotation, weights[i]), w)); + } + chain[chain.Length - 1].SetRotation(stream, Quaternion.Lerp(chain[chain.Length - 1].GetRotation(stream), tipRotation, w)); + +#if UNITY_EDITOR + // Update position of tip handle for easier visualization. + rootTarget.SetPosition(stream, chain[0].GetPosition(stream)); + tipTarget.SetPosition(stream, chain[chain.Length - 1].GetPosition(stream)); +#endif + } + else + { + for (int i = 0; i < chain.Length; ++i) + AnimationRuntimeUtils.PassThrough(stream, chain[i]); + } + } + } + + /// + /// This interface defines the data mapping for the TwistChain constraint. + /// + public interface ITwistChainConstraintData + { + /// The root Transform of the TwistChain hierarchy. + Transform root { get; } + /// The tip Transform of the TwistChain hierarchy. + Transform tip { get; } + + /// The TwistChain root target Transform. + Transform rootTarget { get; } + /// The TwistChain tip target Transform. + Transform tipTarget { get; } + + /// Curve with weight values used to adjust how twist is distributed along the chain. + AnimationCurve curve { get; } + } + + /// + /// The TwistChain constraint job binder. + /// + /// The constraint data type + public class TwistChainConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, ITwistChainConstraintData + { + /// + public override TwistChainConstraintJob Create(Animator animator, ref T data, Component component) + { + // Retrieve chain in-between root and tip transforms. + Transform[] chain = ConstraintsUtils.ExtractChain(data.root, data.tip); + + // Extract steps from chain. + float[] steps = ConstraintsUtils.ExtractSteps(chain); + + // Build Job. + var job = new TwistChainConstraintJob(); + job.chain = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.steps = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.weights = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.rotations = new NativeArray(chain.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + job.rootTarget = ReadWriteTransformHandle.Bind(animator, data.rootTarget); + job.tipTarget = ReadWriteTransformHandle.Bind(animator, data.tipTarget); + + // Set values in NativeArray. + for (int i = 0; i < chain.Length; ++i) + { + job.chain[i] = ReadWriteTransformHandle.Bind(animator, chain[i]); + job.steps[i] = steps[i]; + job.weights[i] = Mathf.Clamp01(data.curve.Evaluate(steps[i])); + } + + job.rotations[0] = Quaternion.identity; + job.rotations[chain.Length - 1] = Quaternion.identity; + for (int i = 1; i < chain.Length - 1; ++i) + { + job.rotations[i] = Quaternion.Inverse(Quaternion.Lerp(chain[0].rotation, chain[chain.Length - 1].rotation, job.weights[i])) * chain[i].rotation; + } + + + return job; + } + + /// + public override void Destroy(TwistChainConstraintJob job) + { + job.chain.Dispose(); + job.weights.Dispose(); + job.steps.Dispose(); + job.rotations.Dispose(); + } + +#if UNITY_EDITOR + /// + public override void Update(TwistChainConstraintJob job, ref T data) + { + // Update weights based on curve. + for (int i = 0; i < job.steps.Length; ++i) + { + job.weights[i] = Mathf.Clamp01(data.curve.Evaluate(job.steps[i])); + } + } +#endif + } +} + diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs.meta new file mode 100644 index 00000000..63dfc7b1 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistChainConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e6ac5cbead2d48f8b8e5e3911f53ced +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs new file mode 100644 index 00000000..c680095a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs @@ -0,0 +1,141 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwistCorrection job. + /// + [Unity.Burst.BurstCompile] + public struct TwistCorrectionJob : IWeightedAnimationJob + { + /// The Transform handle for the source object Transform. + public ReadOnlyTransformHandle source; + /// Cached inverse local rotation for source Transform. + public Quaternion sourceInverseBindRotation; + /// The local twist axis + public Vector3 axisMask; + + /// List of Transform handles for the twist nodes. + public NativeArray twistTransforms; + /// List of weights for the twist nodes. + public NativeArray twistWeights; + /// List of cached local rotation for twist nodes. + public NativeArray twistBindRotations; + + /// Buffer used to store weights during job execution. + public NativeArray weightBuffer; + + /// + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + if (twistTransforms.Length == 0) + return; + + AnimationStreamHandleUtility.ReadFloats(stream, twistWeights, weightBuffer); + + Quaternion twistRot = TwistRotation(axisMask, sourceInverseBindRotation * source.GetLocalRotation(stream)); + Quaternion invTwistRot = Quaternion.Inverse(twistRot); + for (int i = 0; i < twistTransforms.Length; ++i) + { + ReadWriteTransformHandle twistTransform = twistTransforms[i]; + + float twistWeight = Mathf.Clamp(weightBuffer[i], -1f, 1f); + Quaternion rot = Quaternion.Lerp(Quaternion.identity, Mathf.Sign(twistWeight) < 0f ? invTwistRot : twistRot, Mathf.Abs(twistWeight)); + twistTransform.SetLocalRotation(stream, Quaternion.Lerp(twistBindRotations[i], rot, w)); + + // Required to update handles with binding info. + twistTransforms[i] = twistTransform; + } + } + else + { + for (int i = 0; i < twistTransforms.Length; ++i) + AnimationRuntimeUtils.PassThrough(stream, twistTransforms[i]); + } + } + + static Quaternion TwistRotation(Vector3 axis, Quaternion rot) + { + return new Quaternion(axis.x * rot.x, axis.y * rot.y, axis.z * rot.z, rot.w); + } + } + + /// + /// This interface defines the data mapping for TwistCorrection. + /// + public interface ITwistCorrectionData + { + /// The source Transform that influences the twist nodes. + Transform source { get; } + /// + /// The list of Transforms on which to apply twist corrections. + /// Each twist node has a weight that ranges from -1 to 1 to control + /// how closely a twist node follows source rotation (from 0 to 1), + /// or opposite rotation (from -1 to 0). + /// + WeightedTransformArray twistNodes { get; } + /// The local twist axis of the source Transform on which to evaluate twist rotation. + Vector3 twistAxis { get; } + /// The path to the twist nodes property in the constraint component. + string twistNodesProperty { get; } + } + + /// + /// The TwistCorrection job binder. + /// + /// The constraint data type + public class TwistCorrectionJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, ITwistCorrectionData + { + /// + public override TwistCorrectionJob Create(Animator animator, ref T data, Component component) + { + var job = new TwistCorrectionJob(); + + job.source = ReadOnlyTransformHandle.Bind(animator, data.source); + job.sourceInverseBindRotation = Quaternion.Inverse(data.source.localRotation); + job.axisMask = data.twistAxis; + + WeightedTransformArray twistNodes = data.twistNodes; + + WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, twistNodes, out job.twistTransforms); + WeightedTransformArrayBinder.BindWeights(animator, component, twistNodes, data.twistNodesProperty, out job.twistWeights); + + job.weightBuffer = new NativeArray(twistNodes.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + job.twistBindRotations = new NativeArray(twistNodes.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int i = 0; i < twistNodes.Count; ++i) + { + var sourceTransform = twistNodes[i].transform; + job.twistBindRotations[i] = sourceTransform.localRotation; + } + + return job; + } + + /// + public override void Destroy(TwistCorrectionJob job) + { + job.twistTransforms.Dispose(); + job.twistWeights.Dispose(); + job.twistBindRotations.Dispose(); + job.weightBuffer.Dispose(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs.meta new file mode 100644 index 00000000..13e2a788 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwistCorrectionJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d160fc2718684f4c823027eda103445 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs new file mode 100644 index 00000000..cc07846a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs @@ -0,0 +1,142 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwoBoneIK constraint job. + /// + [Unity.Burst.BurstCompile] + public struct TwoBoneIKConstraintJob : IWeightedAnimationJob + { + /// The transform handle for the root transform. + public ReadWriteTransformHandle root; + /// The transform handle for the mid transform. + public ReadWriteTransformHandle mid; + /// The transform handle for the tip transform. + public ReadWriteTransformHandle tip; + + /// The transform handle for the hint transform. + public ReadOnlyTransformHandle hint; + /// The transform handle for the target transform. + public ReadOnlyTransformHandle target; + + /// The offset applied to the target transform if maintainTargetPositionOffset or maintainTargetRotationOffset is enabled. + public AffineTransform targetOffset; + + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty targetPositionWeight; + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty targetRotationWeight; + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + public FloatProperty hintWeight; + + /// The main weight given to the constraint. This is a value in between 0 and 1. + public FloatProperty jobWeight { get; set; } + + /// + /// Defines what to do when processing the root motion. + /// + /// The animation stream to work on. + public void ProcessRootMotion(AnimationStream stream) { } + + /// + /// Defines what to do when processing the animation. + /// + /// The animation stream to work on. + public void ProcessAnimation(AnimationStream stream) + { + float w = jobWeight.Get(stream); + if (w > 0f) + { + AnimationRuntimeUtils.SolveTwoBoneIK( + stream, root, mid, tip, target, hint, + targetPositionWeight.Get(stream) * w, + targetRotationWeight.Get(stream) * w, + hintWeight.Get(stream) * w, + targetOffset + ); + } + else + { + AnimationRuntimeUtils.PassThrough(stream, root); + AnimationRuntimeUtils.PassThrough(stream, mid); + AnimationRuntimeUtils.PassThrough(stream, tip); + } + } + } + + /// + /// This interface defines the data mapping for the TwoBoneIK constraint. + /// + public interface ITwoBoneIKConstraintData + { + /// The root transform of the two bones hierarchy. + Transform root { get; } + /// The mid transform of the two bones hierarchy. + Transform mid { get; } + /// The tip transform of the two bones hierarchy. + Transform tip { get; } + /// The IK target transform. + Transform target { get; } + /// The IK hint transform. + Transform hint { get; } + + /// This is used to maintain the offset of the tip position to the target position. + bool maintainTargetPositionOffset { get; } + /// This is used to maintain the offset of the tip rotation to the target rotation. + bool maintainTargetRotationOffset { get; } + + /// The path to the target position weight property in the constraint component. + string targetPositionWeightFloatProperty { get; } + /// The path to the target rotation weight property in the constraint component. + string targetRotationWeightFloatProperty { get; } + /// The path to the hint weight property in the constraint component. + string hintWeightFloatProperty { get; } + } + + /// + /// The TwoBoneIK constraint job binder. + /// + /// The constraint data type + public class TwoBoneIKConstraintJobBinder : AnimationJobBinder + where T : struct, IAnimationJobData, ITwoBoneIKConstraintData + { + /// + /// Creates the animation job. + /// + /// The animated hierarchy Animator component. + /// The constraint data. + /// The constraint component. + /// Returns a new job interface. + public override TwoBoneIKConstraintJob Create(Animator animator, ref T data, Component component) + { + var job = new TwoBoneIKConstraintJob(); + + job.root = ReadWriteTransformHandle.Bind(animator, data.root); + job.mid = ReadWriteTransformHandle.Bind(animator, data.mid); + job.tip = ReadWriteTransformHandle.Bind(animator, data.tip); + job.target = ReadOnlyTransformHandle.Bind(animator, data.target); + + if (data.hint != null) + job.hint = ReadOnlyTransformHandle.Bind(animator, data.hint); + + job.targetOffset = AffineTransform.identity; + if (data.maintainTargetPositionOffset) + job.targetOffset.translation = data.tip.position - data.target.position; + if (data.maintainTargetRotationOffset) + job.targetOffset.rotation = Quaternion.Inverse(data.target.rotation) * data.tip.rotation; + + job.targetPositionWeight = FloatProperty.Bind(animator, component, data.targetPositionWeightFloatProperty); + job.targetRotationWeight = FloatProperty.Bind(animator, component, data.targetRotationWeightFloatProperty); + job.hintWeight = FloatProperty.Bind(animator, component, data.hintWeightFloatProperty); + + return job; + } + + /// + /// Destroys the animation job. + /// + /// The animation job to destroy. + public override void Destroy(TwoBoneIKConstraintJob job) + { + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs.meta new file mode 100644 index 00000000..1822ab9b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/TwoBoneIKConstraintJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9c984ab44d4265e41be0681eb0a999b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs new file mode 100644 index 00000000..e9e0c9ed --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs @@ -0,0 +1,62 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// This class is used to create Animation C# jobs handles for WeightedTransformArray. + /// + public class WeightedTransformArrayBinder + { + /// + /// Creates an array of ReadOnlyTransformHandles representing the new bindings between the Animator and the Transforms in a WeightedTransformArray. + /// + /// The Animator on which to bind the new handle. + /// The component owning the WeightedTransformArray property. + /// The WeightedTransformArray property. + /// The resulting array of ReadOnlyTransformHandles. + public static void BindReadOnlyTransforms(Animator animator, Component component, WeightedTransformArray weightedTransformArray, out NativeArray transforms) + { + transforms = new NativeArray(weightedTransformArray.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int index = 0; index < weightedTransformArray.Count; ++index) + { + transforms[index] = ReadOnlyTransformHandle.Bind(animator, weightedTransformArray[index].transform); + } + } + + /// + /// Creates an array of ReadWriteTransformHandles representing the new bindings between the Animator and the Transforms in a WeightedTransformArray. + /// + /// The Animator on which to bind the new handle. + /// The component owning the WeightedTransformArray property. + /// The WeightedTransformArray property. + /// The resulting array of ReadWriteTransformHandles. + public static void BindReadWriteTransforms(Animator animator, Component component, WeightedTransformArray weightedTransformArray, out NativeArray transforms) + { + transforms = new NativeArray(weightedTransformArray.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int index = 0; index < weightedTransformArray.Count; ++index) + { + transforms[index] = ReadWriteTransformHandle.Bind(animator, weightedTransformArray[index].transform); + } + } + + /// + /// Creates an array of PropertyStreamHandle representing the new bindings between the Animator and the weights in a WeightedTransformArray. + /// + /// The Animator on which to bind the new handle. + /// The component owning the WeightedTransformArray property. + /// The WeightedTransformArray property. + /// + /// + public static void BindWeights(Animator animator, Component component, WeightedTransformArray weightedTransformArray, string name, out NativeArray weights) + { + weights = new NativeArray(weightedTransformArray.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + for (int index = 0; index < weightedTransformArray.Count; ++index) + { + weights[index] = animator.BindStreamProperty(component.transform, component.GetType(), name + ".m_Item" + index + ".weight"); + } + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs.meta new file mode 100644 index 00000000..c0ce8d91 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationJobs/WeightedTransformArrayBinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26fe63952ce3041dfa37f9c05626256d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig.meta new file mode 100644 index 00000000..011fe498 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e51642a8e5961844a4f89eccdc5eccf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints.meta new file mode 100644 index 00000000..62b87e89 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 57fb05f5d7fd88b41b5eca2077a707f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs new file mode 100644 index 00000000..a3e36bfd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs @@ -0,0 +1,92 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The Blend constraint data. + /// + [System.Serializable] + public struct BlendConstraintData : IAnimationJobData, IBlendConstraintData + { + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField] Transform m_SourceA; + [SyncSceneToStream, SerializeField] Transform m_SourceB; + [SyncSceneToStream, SerializeField] bool m_BlendPosition; + [SyncSceneToStream, SerializeField] bool m_BlendRotation; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_PositionWeight; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_RotationWeight; + + [NotKeyable, SerializeField] bool m_MaintainPositionOffsets; + [NotKeyable, SerializeField] bool m_MaintainRotationOffsets; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + /// + public Transform sourceObjectA { get => m_SourceA; set => m_SourceA = value; } + /// + public Transform sourceObjectB { get => m_SourceB; set => m_SourceB = value; } + /// Toggles whether position is blended in the constraint. + public bool blendPosition { get => m_BlendPosition; set => m_BlendPosition = value; } + /// Toggles whether rotation is blended in the constraint. + public bool blendRotation { get => m_BlendRotation; set => m_BlendRotation = value; } + /// + /// Specifies the weight with which to blend position. + /// A weight of zero will result in the position of sourceObjectA, while a weight of one will result in the position of sourceObjectB. + /// + public float positionWeight { get => m_PositionWeight; set => m_PositionWeight = Mathf.Clamp01(value); } + /// + /// Specifies the weight with which to blend rotation. + /// A weight of zero will result in the rotation of sourceObjectA, while a weight of one will result in the rotation of sourceObjectB. + /// + public float rotationWeight { get => m_RotationWeight; set => m_RotationWeight = Mathf.Clamp01(value); } + /// + public bool maintainPositionOffsets { get => m_MaintainPositionOffsets; set => m_MaintainPositionOffsets = value; } + /// + public bool maintainRotationOffsets { get => m_MaintainRotationOffsets; set => m_MaintainRotationOffsets = value; } + + /// + string IBlendConstraintData.blendPositionBoolProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_BlendPosition)); + /// + string IBlendConstraintData.blendRotationBoolProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_BlendRotation)); + /// + string IBlendConstraintData.positionWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_PositionWeight)); + /// + string IBlendConstraintData.rotationWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_RotationWeight)); + + /// + bool IAnimationJobData.IsValid() => !(m_ConstrainedObject == null || m_SourceA == null || m_SourceB == null); + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_SourceA = null; + m_SourceB = null; + m_BlendPosition = true; + m_BlendRotation = true; + m_PositionWeight = 0.5f; + m_RotationWeight = 0.5f; + m_MaintainPositionOffsets = false; + m_MaintainRotationOffsets = false; + } + } + + /// + /// Blend constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Blend Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/BlendConstraint.html")] + public class BlendConstraint : RigConstraint< + BlendConstraintJob, + BlendConstraintData, + BlendConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.positionWeight = Mathf.Clamp01(m_Data.positionWeight); + m_Data.rotationWeight = Mathf.Clamp01(m_Data.rotationWeight); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs.meta new file mode 100644 index 00000000..b4dabac1 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/BlendConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a2cc48f812a19f40a5c644fbc9b4d9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs new file mode 100644 index 00000000..4861304c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs @@ -0,0 +1,107 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The ChainIK constraint data. + /// + [System.Serializable] + public struct ChainIKConstraintData : IAnimationJobData, IChainIKConstraintData + { + internal const int k_MinIterations = 1; + internal const int k_MaxIterations = 50; + internal const float k_MinTolerance = 0f; + internal const float k_MaxTolerance = 0.01f; + + [SerializeField] Transform m_Root; + [SerializeField] Transform m_Tip; + + [SyncSceneToStream, SerializeField] Transform m_Target; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_ChainRotationWeight; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_TipRotationWeight; + + [NotKeyable, SerializeField, Range(k_MinIterations, k_MaxIterations)] int m_MaxIterations; + [NotKeyable, SerializeField, Range(k_MinTolerance, k_MaxTolerance)] float m_Tolerance; + [NotKeyable, SerializeField] bool m_MaintainTargetPositionOffset; + [NotKeyable, SerializeField] bool m_MaintainTargetRotationOffset; + + /// + public Transform root { get => m_Root; set => m_Root = value; } + /// + public Transform tip { get => m_Tip; set => m_Tip = value; } + /// + public Transform target { get => m_Target; set => m_Target = value; } + /// The weight for which ChainIK target has an effect on chain (up to tip Transform). This is a value in between 0 and 1. + public float chainRotationWeight { get => m_ChainRotationWeight; set => m_ChainRotationWeight = Mathf.Clamp01(value); } + /// The weight for which ChainIK target has and effect on tip Transform. This is a value in between 0 and 1. + public float tipRotationWeight { get => m_TipRotationWeight; set => m_TipRotationWeight = Mathf.Clamp01(value); } + /// + public int maxIterations { get => m_MaxIterations; set => m_MaxIterations = Mathf.Clamp(value, k_MinIterations, k_MaxIterations); } + /// + public float tolerance { get => m_Tolerance; set => m_Tolerance = Mathf.Clamp(value, k_MinTolerance, k_MaxTolerance); } + /// + public bool maintainTargetPositionOffset { get => m_MaintainTargetPositionOffset; set => m_MaintainTargetPositionOffset = value; } + /// + public bool maintainTargetRotationOffset { get => m_MaintainTargetRotationOffset; set => m_MaintainTargetRotationOffset = value; } + + /// + string IChainIKConstraintData.chainRotationWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_ChainRotationWeight)); + /// + string IChainIKConstraintData.tipRotationWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_TipRotationWeight)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_Root == null || m_Tip == null || m_Target == null) + return false; + + int count = 1; + Transform tmp = m_Tip; + while (tmp != null && tmp != m_Root) + { + tmp = tmp.parent; + ++count; + } + + return (tmp == m_Root && count > 2); + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_Root = null; + m_Tip = null; + m_Target = null; + m_ChainRotationWeight = 1f; + m_TipRotationWeight = 1f; + m_MaxIterations = 15; + m_Tolerance = 0.0001f; + m_MaintainTargetPositionOffset = false; + m_MaintainTargetRotationOffset = false; + } + } + + /// + /// ChainIK constraint + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Chain IK Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/ChainIKConstraint.html")] + public class ChainIKConstraint : RigConstraint< + ChainIKConstraintJob, + ChainIKConstraintData, + ChainIKConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.chainRotationWeight = Mathf.Clamp01(m_Data.chainRotationWeight); + m_Data.tipRotationWeight = Mathf.Clamp01(m_Data.tipRotationWeight); + m_Data.maxIterations = Mathf.Clamp( + m_Data.maxIterations, ChainIKConstraintData.k_MinIterations, ChainIKConstraintData.k_MaxIterations + ); + m_Data.tolerance = Mathf.Clamp( + m_Data.tolerance, ChainIKConstraintData.k_MinTolerance, ChainIKConstraintData.k_MaxTolerance + ); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs.meta new file mode 100644 index 00000000..0ef79dd8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/ChainIKConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 796b75e345bd64d47a31edd757bd2670 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs new file mode 100644 index 00000000..b64a249c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs @@ -0,0 +1,74 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The DampedTransform constraint data. + /// + [System.Serializable] + public struct DampedTransformData : IAnimationJobData, IDampedTransformData + { + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField] Transform m_Source; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_DampPosition; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_DampRotation; + + [NotKeyable, SerializeField] bool m_MaintainAim; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + /// + public Transform sourceObject { get => m_Source; set => m_Source = value; } + /// + /// Damp position weight. Defines how much of constrained object position follows source object position. + /// Constrained position will closely follow source object when set to 0, and will + /// not move when set to 1. + /// + public float dampPosition { get => m_DampPosition; set => m_DampPosition = Mathf.Clamp01(value); } + /// + /// Damp rotation weight. Defines how much of constrained object rotation follows source object rotation. + /// Constrained rotation will closely follow source object when set to 0, and will + /// not move when set to 1. + /// + public float dampRotation { get => m_DampRotation; set => m_DampRotation = Mathf.Clamp01(value); } + /// + public bool maintainAim { get => m_MaintainAim; set => m_MaintainAim = value; } + + /// + string IDampedTransformData.dampPositionFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_DampPosition)); + /// + string IDampedTransformData.dampRotationFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_DampRotation)); + + /// + bool IAnimationJobData.IsValid() => !(m_ConstrainedObject == null || m_Source == null); + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_Source = null; + m_DampPosition = 0.5f; + m_DampRotation = 0.5f; + m_MaintainAim = true; + } + } + + /// + /// DampedTransform constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Damped Transform")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/DampedTransform.html")] + public class DampedTransform : RigConstraint< + DampedTransformJob, + DampedTransformData, + DampedTransformJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.dampPosition = Mathf.Clamp01(m_Data.dampPosition); + m_Data.dampRotation = Mathf.Clamp01(m_Data.dampRotation); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs.meta new file mode 100644 index 00000000..d193f06c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/DampedTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b37cffab30b939469bac076fdd82c59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs new file mode 100644 index 00000000..b48fc3de --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs @@ -0,0 +1,214 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiAim constraint data. + /// + [System.Serializable] + public struct MultiAimConstraintData : IAnimationJobData, IMultiAimConstraintData + { + /// + /// Axis type for MultiAimConstraint. + /// + public enum Axis + { + /// Positive X Axis (1, 0, 0) + X, + /// Negative X Axis (-1, 0, 0) + X_NEG, + /// Positive Y Axis (0, 1, 0) + Y, + /// Negative Y Axis (0, -1, 0) + Y_NEG, + /// Positive Z Axis (0, 0, 1) + Z, + /// Negative Z Axis (0, 0, -1) + Z_NEG + } + + /// + /// Specifies how the world up vector used by the Multi-Aim constraint is defined. + /// + public enum WorldUpType + { + /// Neither defines nor uses a world up vector. + None, + /// Uses and defines the world up vector as the Unity Scene up vector (the Y axis). + SceneUp, + /// Uses and defines the world up vector as a vector from the constrained object, in the direction of the up object. + ObjectUp, + /// Uses and defines the world up vector as relative to the local space of the object. + ObjectRotationUp, + /// Uses and defines the world up vector as a vector specified by the user. + Vector + }; + + internal const float k_MinAngularLimit = -180f; + internal const float k_MaxAngularLimit = 180f; + + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField, WeightRange(0f, 1f)] WeightedTransformArray m_SourceObjects; + [SyncSceneToStream, SerializeField] Vector3 m_Offset; + [SyncSceneToStream, SerializeField, Range(k_MinAngularLimit, k_MaxAngularLimit)] float m_MinLimit; + [SyncSceneToStream, SerializeField, Range(k_MinAngularLimit, k_MaxAngularLimit)] float m_MaxLimit; + + [NotKeyable, SerializeField] Axis m_AimAxis; + [NotKeyable, SerializeField] Axis m_UpAxis; + + [NotKeyable, SerializeField] WorldUpType m_WorldUpType; + [SyncSceneToStream, SerializeField] Transform m_WorldUpObject; + [NotKeyable, SerializeField] Axis m_WorldUpAxis; + + [NotKeyable, SerializeField] bool m_MaintainOffset; + [NotKeyable, SerializeField] Vector3Bool m_ConstrainedAxes; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + + /// + public WeightedTransformArray sourceObjects + { + get => m_SourceObjects; + set => m_SourceObjects = value; + } + + /// + public bool maintainOffset { get => m_MaintainOffset; set => m_MaintainOffset = value; } + /// + /// Post-Rotation offset applied to the constrained Transform. + /// + public Vector3 offset { get => m_Offset; set => m_Offset = value; } + + /// + /// Minimum and maximum value of the rotation permitted for the constraint. The values are in degrees. + /// + public Vector2 limits + { + get => new Vector2(m_MinLimit, m_MaxLimit); + + set + { + m_MinLimit = Mathf.Clamp(value.x, k_MinAngularLimit, k_MaxAngularLimit); + m_MaxLimit = Mathf.Clamp(value.y, k_MinAngularLimit, k_MaxAngularLimit); + } + } + + /// + public Axis aimAxis { get => m_AimAxis; set => m_AimAxis = value; } + /// + public Axis upAxis { get => m_UpAxis; set => m_UpAxis = value; } + + + /// + public WorldUpType worldUpType { get => m_WorldUpType; set => m_WorldUpType = value; } + /// + public Axis worldUpAxis { get => m_WorldUpAxis; set => m_WorldUpAxis = value; } + /// + public Transform worldUpObject { get => m_WorldUpObject; set => m_WorldUpObject = value; } + + /// + public bool constrainedXAxis { get => m_ConstrainedAxes.x; set => m_ConstrainedAxes.x = value; } + /// + public bool constrainedYAxis { get => m_ConstrainedAxes.y; set => m_ConstrainedAxes.y = value; } + /// + public bool constrainedZAxis { get => m_ConstrainedAxes.z; set => m_ConstrainedAxes.z = value; } + + + /// + Vector3 IMultiAimConstraintData.aimAxis => Convert(m_AimAxis); + /// + Vector3 IMultiAimConstraintData.upAxis => Convert(m_UpAxis); + /// + int IMultiAimConstraintData.worldUpType => (int) m_WorldUpType; + /// + Vector3 IMultiAimConstraintData.worldUpAxis => Convert(m_WorldUpAxis); + /// + string IMultiAimConstraintData.offsetVector3Property => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_Offset)); + /// + string IMultiAimConstraintData.minLimitFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_MinLimit)); + /// + string IMultiAimConstraintData.maxLimitFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_MaxLimit)); + /// + string IMultiAimConstraintData.sourceObjectsProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_SourceObjects)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_ConstrainedObject == null || m_SourceObjects.Count == 0) + return false; + + foreach (var src in m_SourceObjects) + if (src.transform == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_UpAxis = Axis.Y; + m_AimAxis = Axis.Z; + m_WorldUpType = WorldUpType.None; + m_WorldUpAxis = Axis.Y; + m_WorldUpObject = null; + m_SourceObjects.Clear(); + m_MaintainOffset = false; + m_Offset = Vector3.zero; + m_ConstrainedAxes = new Vector3Bool(true); + m_MinLimit = -180f; + m_MaxLimit = 180f; + } + + static Vector3 Convert(Axis axis) + { + switch (axis) + { + case Axis.X: + return Vector3.right; + case Axis.X_NEG: + return Vector3.left; + case Axis.Y: + return Vector3.up; + case Axis.Y_NEG: + return Vector3.down; + case Axis.Z: + return Vector3.forward; + case Axis.Z_NEG: + return Vector3.back; + default: + return Vector3.up; + } + } + } + + /// + /// MultiAim constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Multi-Aim Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/MultiAimConstraint.html")] + public class MultiAimConstraint : RigConstraint< + MultiAimConstraintJob, + MultiAimConstraintData, + MultiAimConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + var weights = m_Data.sourceObjects; + WeightedTransformArray.OnValidate(ref weights); + m_Data.sourceObjects = weights; + var limits = m_Data.limits; + limits.x = Mathf.Clamp( + limits.x, MultiAimConstraintData.k_MinAngularLimit, MultiAimConstraintData.k_MaxAngularLimit + ); + limits.y = Mathf.Clamp( + limits.y, MultiAimConstraintData.k_MinAngularLimit, MultiAimConstraintData.k_MaxAngularLimit + ); + m_Data.limits = limits; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs.meta new file mode 100644 index 00000000..01222c58 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiAimConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3c430f382484144e925c097c2d33cfe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs new file mode 100644 index 00000000..4c591c1d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs @@ -0,0 +1,94 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiParent constraint data. + /// + [System.Serializable] + public struct MultiParentConstraintData : IAnimationJobData, IMultiParentConstraintData + { + [SerializeField] Transform m_ConstrainedObject; + + [SerializeField, SyncSceneToStream, WeightRange(0f, 1f)] WeightedTransformArray m_SourceObjects; + + [NotKeyable, SerializeField] Vector3Bool m_ConstrainedPositionAxes; + [NotKeyable, SerializeField] Vector3Bool m_ConstrainedRotationAxes; + [NotKeyable, SerializeField] bool m_MaintainPositionOffset; + [NotKeyable, SerializeField] bool m_MaintainRotationOffset; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + + /// + public WeightedTransformArray sourceObjects + { + get => m_SourceObjects; + set => m_SourceObjects = value; + } + + /// + public bool maintainPositionOffset { get => m_MaintainPositionOffset; set => m_MaintainPositionOffset = value; } + /// + public bool maintainRotationOffset { get => m_MaintainRotationOffset; set => m_MaintainRotationOffset = value; } + + /// + public bool constrainedPositionXAxis { get => m_ConstrainedPositionAxes.x; set => m_ConstrainedPositionAxes.x = value; } + /// + public bool constrainedPositionYAxis { get => m_ConstrainedPositionAxes.y; set => m_ConstrainedPositionAxes.y = value; } + /// + public bool constrainedPositionZAxis { get => m_ConstrainedPositionAxes.z; set => m_ConstrainedPositionAxes.z = value; } + /// + public bool constrainedRotationXAxis { get => m_ConstrainedRotationAxes.x; set => m_ConstrainedRotationAxes.x = value; } + /// + public bool constrainedRotationYAxis { get => m_ConstrainedRotationAxes.y; set => m_ConstrainedRotationAxes.y = value; } + /// + public bool constrainedRotationZAxis { get => m_ConstrainedRotationAxes.z; set => m_ConstrainedRotationAxes.z = value; } + + /// + string IMultiParentConstraintData.sourceObjectsProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_SourceObjects)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_ConstrainedObject == null || m_SourceObjects.Count == 0) + return false; + + foreach (var src in m_SourceObjects) + if (src.transform == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_ConstrainedPositionAxes = new Vector3Bool(true); + m_ConstrainedRotationAxes = new Vector3Bool(true); + m_SourceObjects.Clear(); + m_MaintainPositionOffset = false; + m_MaintainRotationOffset = false; + } + } + + /// + /// MultiParent constraint + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Multi-Parent Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/MultiParentConstraint.html")] + public class MultiParentConstraint : RigConstraint< + MultiParentConstraintJob, + MultiParentConstraintData, + MultiParentConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + var weights = m_Data.sourceObjects; + WeightedTransformArray.OnValidate(ref weights); + m_Data.sourceObjects = weights; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs.meta new file mode 100644 index 00000000..21682215 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiParentConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8935e03361a14ec4b8edc872f76bc822 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs new file mode 100644 index 00000000..866cd8dd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs @@ -0,0 +1,89 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiPosition constraint job. + /// + [System.Serializable] + public struct MultiPositionConstraintData : IAnimationJobData, IMultiPositionConstraintData + { + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField, WeightRange(0f, 1f)] WeightedTransformArray m_SourceObjects; + [SyncSceneToStream, SerializeField] Vector3 m_Offset; + + [NotKeyable, SerializeField] Vector3Bool m_ConstrainedAxes; + [NotKeyable, SerializeField] bool m_MaintainOffset; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + + /// + public WeightedTransformArray sourceObjects + { + get => m_SourceObjects; + set => m_SourceObjects = value; + } + + /// + public bool maintainOffset { get => m_MaintainOffset; set => m_MaintainOffset = value; } + + /// Post-Translation offset applied to the constrained Transform. + public Vector3 offset { get => m_Offset; set => m_Offset = value; } + + /// + public bool constrainedXAxis { get => m_ConstrainedAxes.x; set => m_ConstrainedAxes.x = value; } + /// + public bool constrainedYAxis { get => m_ConstrainedAxes.y; set => m_ConstrainedAxes.y = value; } + /// + public bool constrainedZAxis { get => m_ConstrainedAxes.z; set => m_ConstrainedAxes.z = value; } + + /// + string IMultiPositionConstraintData.offsetVector3Property => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_Offset)); + /// + string IMultiPositionConstraintData.sourceObjectsProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_SourceObjects)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_ConstrainedObject == null || m_SourceObjects.Count == 0) + return false; + + foreach (var src in m_SourceObjects) + if (src.transform == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_ConstrainedAxes = new Vector3Bool(true); + m_SourceObjects.Clear(); + m_MaintainOffset = false; + m_Offset = Vector3.zero; + } + } + + /// + /// MultiPosition constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Multi-Position Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/MultiPositionConstraint.html")] + public class MultiPositionConstraint : RigConstraint< + MultiPositionConstraintJob, + MultiPositionConstraintData, + MultiPositionConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + var weights = m_Data.sourceObjects; + WeightedTransformArray.OnValidate(ref weights); + m_Data.sourceObjects = weights; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs.meta new file mode 100644 index 00000000..de76f1c2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiPositionConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c10bdb03db33fb148b858a4a3b646cea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs new file mode 100644 index 00000000..83cabbec --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiReferential constraint data. + /// + [System.Serializable] + public struct MultiReferentialConstraintData : IAnimationJobData, IMultiReferentialConstraintData + { + [SyncSceneToStream, SerializeField] int m_Driver; + [SyncSceneToStream, SerializeField] List m_SourceObjects; + + /// The driver index. This is a value in between 0 and the number of sourceObjects. + public int driver + { + get => m_Driver; + set => m_Driver = Mathf.Clamp(value, 0, m_SourceObjects.Count - 1); + } + + /// The list of Transforms that are affected by the specified driver. + public List sourceObjects + { + get + { + if (m_SourceObjects == null) + m_SourceObjects = new List(); + + return m_SourceObjects; + } + + set + { + m_SourceObjects = value; + m_Driver = Mathf.Clamp(m_Driver, 0, m_SourceObjects.Count - 1); + } + } + + /// + Transform[] IMultiReferentialConstraintData.sourceObjects => m_SourceObjects.ToArray(); + /// + int IMultiReferentialConstraintData.driverValue => m_Driver; + /// + string IMultiReferentialConstraintData.driverIntProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_Driver)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_SourceObjects.Count < 2) + return false; + + foreach (var src in m_SourceObjects) + if (src == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_Driver = 0; + m_SourceObjects = new List(); + } + + /// + /// Updates the driver index to match the number of source objects. + /// + public void UpdateDriver() => + m_Driver = Mathf.Clamp(m_Driver, 0, m_SourceObjects != null ? m_SourceObjects.Count - 1 : 0); + } + + /// + /// MultiReferential constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Multi-Referential Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/MultiReferentialConstraint.html")] + public class MultiReferentialConstraint : RigConstraint< + MultiReferentialConstraintJob, + MultiReferentialConstraintData, + MultiReferentialConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.UpdateDriver(); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs.meta new file mode 100644 index 00000000..5f41a292 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiReferentialConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1577d1bce6e9ec47b4c95dd2d94493a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs new file mode 100644 index 00000000..22fb1022 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs @@ -0,0 +1,88 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The MultiRotation constraint job. + /// + [System.Serializable] + public struct MultiRotationConstraintData : IAnimationJobData, IMultiRotationConstraintData + { + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField, WeightRange(0f, 1f)] WeightedTransformArray m_SourceObjects; + [SyncSceneToStream, SerializeField] Vector3 m_Offset; + + [NotKeyable, SerializeField] Vector3Bool m_ConstrainedAxes; + [NotKeyable, SerializeField] bool m_MaintainOffset; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + + /// + public WeightedTransformArray sourceObjects + { + get => m_SourceObjects; + set => m_SourceObjects = value; + } + + /// + public bool maintainOffset { get => m_MaintainOffset; set => m_MaintainOffset = value; } + /// Post-Rotation offset applied to the constrained Transform. + public Vector3 offset { get => m_Offset; set => m_Offset = value; } + + /// + public bool constrainedXAxis { get => m_ConstrainedAxes.x; set => m_ConstrainedAxes.x = value; } + /// + public bool constrainedYAxis { get => m_ConstrainedAxes.y; set => m_ConstrainedAxes.y = value; } + /// + public bool constrainedZAxis { get => m_ConstrainedAxes.z; set => m_ConstrainedAxes.z = value; } + + /// + string IMultiRotationConstraintData.offsetVector3Property => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_Offset)); + /// + string IMultiRotationConstraintData.sourceObjectsProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_SourceObjects)); + + /// + bool IAnimationJobData.IsValid() + { + if (m_ConstrainedObject == null || m_SourceObjects.Count == 0) + return false; + + foreach (var src in m_SourceObjects) + if (src.transform == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_ConstrainedAxes = new Vector3Bool(true); + m_SourceObjects.Clear(); + m_MaintainOffset = false; + m_Offset = Vector3.zero; + } + } + + /// + /// MultiRotation constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Multi-Rotation Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/MultiRotationConstraint.html")] + public class MultiRotationConstraint : RigConstraint< + MultiRotationConstraintJob, + MultiRotationConstraintData, + MultiRotationConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + var weights = m_Data.sourceObjects; + WeightedTransformArray.OnValidate(ref weights); + m_Data.sourceObjects = weights; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs.meta new file mode 100644 index 00000000..fce44e0b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/MultiRotationConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdb90b913935e644baaa86c076d788e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs new file mode 100644 index 00000000..6d607795 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs @@ -0,0 +1,95 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The OverrideTransform constraint data. + /// + [System.Serializable] + public struct OverrideTransformData : IAnimationJobData, IOverrideTransformData + { + /// + /// The override space controls how the override source Transform + /// is copied unto constrained Transform. + /// + [System.Serializable] + public enum Space + { + /// Copy override world TR components into world TR components of the constrained Transform. + World = OverrideTransformJob.Space.World, + /// Copy override local TR components into local TR components of the constrained Transform. + Local = OverrideTransformJob.Space.Local, + /// Add override local TR components to local TR components of the constrained Transform. + Pivot = OverrideTransformJob.Space.Pivot + } + + [SerializeField] Transform m_ConstrainedObject; + + [SyncSceneToStream, SerializeField] Transform m_OverrideSource; + [SyncSceneToStream, SerializeField] Vector3 m_OverridePosition; + [SyncSceneToStream, SerializeField] Vector3 m_OverrideRotation; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_PositionWeight; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_RotationWeight; + + [NotKeyable, SerializeField] Space m_Space; + + /// + public Transform constrainedObject { get => m_ConstrainedObject; set => m_ConstrainedObject = value; } + /// + public Transform sourceObject { get => m_OverrideSource; set => m_OverrideSource = value; } + /// The override space. + public Space space { get => m_Space; set => m_Space = value; } + /// The override position. This is taken into account only if sourceObject is null. + public Vector3 position { get => m_OverridePosition; set => m_OverridePosition = value; } + /// The override rotation. This is taken into account only if sourceObject is null. + public Vector3 rotation { get => m_OverrideRotation; set => m_OverrideRotation = value; } + /// The weight for which override position has an effect on constrained Transform. This is a value in between 0 and 1. + public float positionWeight { get => m_PositionWeight; set => m_PositionWeight = Mathf.Clamp01(value); } + /// The weight for which override rotation has an effect on constrained Transform. This is a value in between 0 and 1. + public float rotationWeight { get => m_RotationWeight; set => m_RotationWeight = Mathf.Clamp01(value); } + + /// + int IOverrideTransformData.space => (int)m_Space; + /// + string IOverrideTransformData.positionWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_PositionWeight)); + /// + string IOverrideTransformData.rotationWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_RotationWeight)); + /// + string IOverrideTransformData.positionVector3Property => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_OverridePosition)); + /// + string IOverrideTransformData.rotationVector3Property => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_OverrideRotation)); + + /// + bool IAnimationJobData.IsValid() => m_ConstrainedObject != null; + + /// + void IAnimationJobData.SetDefaultValues() + { + m_ConstrainedObject = null; + m_OverrideSource = null; + m_OverridePosition = Vector3.zero; + m_OverrideRotation = Vector3.zero; + m_Space = Space.Pivot; + m_PositionWeight = 1f; + m_RotationWeight = 1f; + } + } + + /// + /// OverrideTransform constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Override Transform")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/OverrideTransform.html")] + public class OverrideTransform : RigConstraint< + OverrideTransformJob, + OverrideTransformData, + OverrideTransformJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.positionWeight = Mathf.Clamp01(m_Data.positionWeight); + m_Data.rotationWeight = Mathf.Clamp01(m_Data.rotationWeight); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs.meta new file mode 100644 index 00000000..8fe4f3e5 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/OverrideTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf219326d2292de48a0c671da18d402b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs new file mode 100644 index 00000000..e15bd27b --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs @@ -0,0 +1,53 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwistChain constraint data. + /// + [System.Serializable] + public struct TwistChainConstraintData : IAnimationJobData, ITwistChainConstraintData + { + [SerializeField] private Transform m_Root; + [SerializeField] private Transform m_Tip; + + [SyncSceneToStream, SerializeField] private Transform m_RootTarget; + [SyncSceneToStream, SerializeField] private Transform m_TipTarget; + + [SerializeField] private AnimationCurve m_Curve; + + /// + public Transform root { get => m_Root; set => m_Root = value; } + /// + public Transform tip { get => m_Tip; set => m_Tip = value; } + + /// + public Transform rootTarget { get => m_RootTarget; set => m_RootTarget = value; } + /// + public Transform tipTarget { get => m_TipTarget; set => m_TipTarget = value; } + + /// + public AnimationCurve curve { get => m_Curve; set => m_Curve = value; } + + /// + bool IAnimationJobData.IsValid() => !(root == null || tip == null || !tip.IsChildOf(root) || rootTarget == null || tipTarget == null || curve == null); + + /// + void IAnimationJobData.SetDefaultValues() + { + root = tip = rootTarget = tipTarget = null; + curve = AnimationCurve.Linear(0f, 0f, 1f, 1f); + } + } + + /// + /// TwistChain constraint + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Twist Chain Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/TwistChainConstraint.html")] + public class TwistChainConstraint : RigConstraint< + TwistChainConstraintJob, + TwistChainConstraintData, + TwistChainConstraintJobBinder + > + { + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs.meta new file mode 100644 index 00000000..9f7f8e75 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistChainConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afb1fbe61e5894c96b30693270a4db99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs new file mode 100644 index 00000000..fcf552e3 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs @@ -0,0 +1,101 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwistCorrection constraint data. + /// + [System.Serializable] + public struct TwistCorrectionData : IAnimationJobData, ITwistCorrectionData + { + /// + /// Axis type for TwistCorrection. + /// + public enum Axis + { + /// X Axis. + X, + /// Y Axis. + Y, + /// Z Axis. + Z + } + + [SyncSceneToStream, SerializeField] Transform m_Source; + + [NotKeyable, SerializeField] Axis m_TwistAxis; + [SyncSceneToStream, SerializeField, WeightRange(-1f, 1f)] WeightedTransformArray m_TwistNodes; + + /// The source Transform that influences the twist nodes. + public Transform sourceObject { get => m_Source; set => m_Source = value; } + + /// + public WeightedTransformArray twistNodes + { + get => m_TwistNodes; + set => m_TwistNodes = value; + } + + /// + public Axis twistAxis { get => m_TwistAxis; set => m_TwistAxis = value; } + + /// + Transform ITwistCorrectionData.source => m_Source; + /// + Vector3 ITwistCorrectionData.twistAxis => Convert(m_TwistAxis); + + /// + string ITwistCorrectionData.twistNodesProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_TwistNodes)); + + static Vector3 Convert(Axis axis) + { + if (axis == Axis.X) + return Vector3.right; + + if (axis == Axis.Y) + return Vector3.up; + + return Vector3.forward; + } + + /// + bool IAnimationJobData.IsValid() + { + if (m_Source == null) + return false; + + for (int i = 0; i < m_TwistNodes.Count; ++i) + if (m_TwistNodes[i].transform == null) + return false; + + return true; + } + + /// + void IAnimationJobData.SetDefaultValues() + { + m_Source = null; + m_TwistAxis = Axis.Z; + m_TwistNodes.Clear(); + } + } + + /// + /// TwistCorrection constraint. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Twist Correction")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/TwistCorrection.html")] + public class TwistCorrection : RigConstraint< + TwistCorrectionJob, + TwistCorrectionData, + TwistCorrectionJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + var weights = m_Data.twistNodes; + WeightedTransformArray.OnValidate(ref weights, -1f, 1f); + m_Data.twistNodes = weights; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs.meta new file mode 100644 index 00000000..0da7973e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwistCorrection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a4834896bd86034eb2aacc65cb0c593 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs new file mode 100644 index 00000000..c3923c20 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs @@ -0,0 +1,91 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// The TwoBoneIK constraint data. + /// + [System.Serializable] + public struct TwoBoneIKConstraintData : IAnimationJobData, ITwoBoneIKConstraintData + { + [SerializeField] Transform m_Root; + [SerializeField] Transform m_Mid; + [SerializeField] Transform m_Tip; + + [SyncSceneToStream, SerializeField] Transform m_Target; + [SyncSceneToStream, SerializeField] Transform m_Hint; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_TargetPositionWeight; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_TargetRotationWeight; + [SyncSceneToStream, SerializeField, Range(0f, 1f)] float m_HintWeight; + + [NotKeyable, SerializeField] bool m_MaintainTargetPositionOffset; + [NotKeyable, SerializeField] bool m_MaintainTargetRotationOffset; + + /// + public Transform root { get => m_Root; set => m_Root = value; } + /// + public Transform mid { get => m_Mid; set => m_Mid = value; } + /// + public Transform tip { get => m_Tip; set => m_Tip = value; } + /// + public Transform target { get => m_Target; set => m_Target = value; } + /// + public Transform hint { get => m_Hint; set => m_Hint = value; } + + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + public float targetPositionWeight { get => m_TargetPositionWeight; set => m_TargetPositionWeight = Mathf.Clamp01(value); } + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + public float targetRotationWeight { get => m_TargetRotationWeight; set => m_TargetRotationWeight = Mathf.Clamp01(value); } + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + public float hintWeight { get => m_HintWeight; set => m_HintWeight = Mathf.Clamp01(value); } + + /// + public bool maintainTargetPositionOffset { get => m_MaintainTargetPositionOffset; set => m_MaintainTargetPositionOffset = value; } + /// + public bool maintainTargetRotationOffset { get => m_MaintainTargetRotationOffset; set => m_MaintainTargetRotationOffset = value; } + + /// + string ITwoBoneIKConstraintData.targetPositionWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_TargetPositionWeight)); + /// + string ITwoBoneIKConstraintData.targetRotationWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_TargetRotationWeight)); + /// + string ITwoBoneIKConstraintData.hintWeightFloatProperty => ConstraintsUtils.ConstructConstraintDataPropertyName(nameof(m_HintWeight)); + + /// + bool IAnimationJobData.IsValid() => (m_Tip != null && m_Mid != null && m_Root != null && m_Target != null && m_Tip.IsChildOf(m_Mid) && m_Mid.IsChildOf(m_Root)); + + /// + void IAnimationJobData.SetDefaultValues() + { + m_Root = null; + m_Mid = null; + m_Tip = null; + m_Target = null; + m_Hint = null; + m_TargetPositionWeight = 1f; + m_TargetRotationWeight = 1f; + m_HintWeight = 1f; + m_MaintainTargetPositionOffset = false; + m_MaintainTargetRotationOffset = false; + } + } + + /// + /// TwoBoneIK constraint + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Two Bone IK Constraint")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/constraints/TwoBoneIKConstraint.html")] + public class TwoBoneIKConstraint : RigConstraint< + TwoBoneIKConstraintJob, + TwoBoneIKConstraintData, + TwoBoneIKConstraintJobBinder + > + { + /// + protected override void OnValidate() + { + base.OnValidate(); + m_Data.hintWeight = Mathf.Clamp01(m_Data.hintWeight); + m_Data.targetPositionWeight = Mathf.Clamp01(m_Data.targetPositionWeight); + m_Data.targetRotationWeight = Mathf.Clamp01(m_Data.targetRotationWeight); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs.meta new file mode 100644 index 00000000..62e1bc70 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Constraints/TwoBoneIKConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aeda7bfbf984f2a4da5ab4b8967b115d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs new file mode 100644 index 00000000..62bd6f2a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs @@ -0,0 +1,55 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// This interface is used to represent all constraints classes. + /// + public interface IRigConstraint + { + /// + /// Retrieves the constraint valid state. + /// + /// Returns true if constraint data can be successfully evaluated. Returns false otherwise. + bool IsValid(); + + /// + /// Creates the animation job for this constraint. + /// + /// The animated hierarchy Animator component. + /// Returns the newly instantiated job. + IAnimationJob CreateJob(Animator animator); + /// + /// Updates the specified job data. + /// + /// The job to update. + void UpdateJob(IAnimationJob job); + /// + /// Late Updates the specified job data. + /// + /// The job to late update. + void LateUpdateJob(IAnimationJob job); + /// + /// Frees the specified job memory. + /// + /// The job to destroy. + void DestroyJob(IAnimationJob job); + + /// + /// The data container for the constraint. + /// + IAnimationJobData data { get; } + /// + /// The job binder for the constraint. + /// + IAnimationJobBinder binder { get; } + + /// + /// The component for the constraint. + /// + Component component { get; } + + /// + /// The constraint weight. This is a value in between 0 and 1. + /// + float weight { get; set; } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs.meta new file mode 100644 index 00000000..503dcb18 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7fee10ab894af4417bc991ed3a2fedcb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs new file mode 100644 index 00000000..8280085c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs @@ -0,0 +1,46 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Interface for rig layers. + /// + public interface IRigLayer + { + /// The Rig associated to the IRigLayer + Rig rig { get; } + + /// The list of constraints associated with the IRigLayer. + IRigConstraint[] constraints { get; } + /// The list of jobs built from constraints associated with the IRigLayer. + IAnimationJob[] jobs { get; } + + /// The active state. True if the IRigLayer is active, false otherwise. + bool active { get; } + /// The IRigLayer name. + string name { get; } + + /// + /// Initializes the IRigLayer + /// + /// The Animator used to animate the IRigLayer constraints. + /// True if IRigLayer was initialized properly, false otherwise. + bool Initialize(Animator animator); + /// + /// Updates the IRigLayer jobs. + /// + void Update(); + /// + /// Late Updates the IRigLayer jobs. + /// + void LateUpdate(); + /// + /// Resets the IRigLayer. + /// + void Reset(); + + /// + /// Queries whether the IRigLayer is valid. + /// + /// True if IRigLayer is valid, false otherwise. + bool IsValid(); + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs.meta new file mode 100644 index 00000000..e97f96e8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/IRigLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74107573bb9a243fb90014afa5c417d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs new file mode 100644 index 00000000..512cbec8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs @@ -0,0 +1,106 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Use this class to define an override rig constraint on another rig constraint. + /// While the overriden constraint data remains the same, the job is overriden. + /// + /// The base constraint to override + /// The override job + /// The constraint data + /// The override constraint job binder + public class OverrideRigConstraint : IRigConstraint + where TConstraint : MonoBehaviour, IRigConstraint + where TJob : struct, IWeightedAnimationJob + where TData : struct, IAnimationJobData + where TBinder : AnimationJobBinder, new() + { + /// + /// The base constraint. + /// + [SerializeField] + protected TConstraint m_Constraint; + + static readonly TBinder s_Binder = new TBinder(); + + /// + /// Constructor + /// + /// The constraint to override. + public OverrideRigConstraint(TConstraint baseConstraint) + { + m_Constraint = baseConstraint; + } + + /// + /// Creates the animation job for this constraint. + /// + /// The animated hierarchy Animator component. + /// Returns the newly instantiated job. + public IAnimationJob CreateJob(Animator animator) + { + IAnimationJobBinder binder = (IAnimationJobBinder)s_Binder; + TJob job = (TJob)binder.Create(animator, m_Constraint.data, m_Constraint); + + // Bind constraint job weight property + job.jobWeight = FloatProperty.BindCustom( + animator, + ConstraintsUtils.ConstructCustomPropertyName(m_Constraint, ConstraintProperties.s_Weight) + ); + + return job; + } + + /// + /// Frees the specified job memory. + /// + /// + public void DestroyJob(IAnimationJob job) => s_Binder.Destroy((TJob)job); + + /// + /// Updates the specified job data. + /// + /// + public void UpdateJob(IAnimationJob job) + { + IAnimationJobBinder binder = (IAnimationJobBinder)s_Binder; + binder.Update(job, m_Constraint.data); + } + + /// + /// Late Updates the specified job data. + /// + /// + public void LateUpdateJob(IAnimationJob job) + { + IAnimationJobBinder binder = (IAnimationJobBinder) s_Binder; + binder.LateUpdate(job, m_Constraint.data); + } + + /// + /// Retrieves the constraint valid state. + /// + /// Returns true if constraint data can be successfully evaluated. Returns false otherwise. + public bool IsValid() + { + return m_Constraint.IsValid(); + } + + /// + /// The job binder for the constraint. + /// + IAnimationJobBinder IRigConstraint.binder => s_Binder; + /// + /// The data container for the constraint. + /// + IAnimationJobData IRigConstraint.data => m_Constraint.data; + /// + /// The component for the constraint. + /// + Component IRigConstraint.component => m_Constraint.component; + + /// + /// The constraint weight. This is a value between 0 and 1. + /// + public float weight { get => m_Constraint.weight; set => m_Constraint.weight = value; } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs.meta new file mode 100644 index 00000000..642f3d63 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08f245f2f5b21437e9f88d72a8cef6a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs new file mode 100644 index 00000000..367eb2e9 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs @@ -0,0 +1,129 @@ +using System; +using UnityEngine.Serialization; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The OverrideRigLayer is used to override constraints normally evaluated by + /// a specified Rig component. + /// + [Serializable] + public class OverrideRigLayer : IRigLayer + { + [SerializeField] [FormerlySerializedAs("rig")] private Rig m_Rig; + [SerializeField] [FormerlySerializedAs("active")] private bool m_Active = true; + + private IRigConstraint[] m_Constraints; + private IAnimationJob[] m_Jobs; + + /// The Rig associated to the OverrideRigLayer + public Rig rig { get => m_Rig; private set => m_Rig = value; } + /// The active state. True if the OverrideRigLayer is active, false otherwise. + public bool active { get => m_Active; set => m_Active = value; } + /// The OverrideRigLayer name. + public string name { get => (rig != null ? rig.gameObject.name : "no-name"); } + /// The list of constraints associated with the OverrideRigLayer. + public IRigConstraint[] constraints { get => isInitialized ? m_Constraints : null; } + /// The list of jobs built from constraints associated with the OverrideRigLayer. + public IAnimationJob[] jobs { get => isInitialized ? m_Jobs : null; } + /// Returns true if OverrideRigLayer was initialized or false otherwise. + /// + public bool isInitialized { get; private set; } + + /// + /// Constructor. + /// + /// The rig represented by this override rig layer. + /// The constraints that override those of the rig. + /// The active state of the override rig layer. + public OverrideRigLayer(Rig rig, IRigConstraint[] constraints, bool active = true) + { + this.rig = rig; + this.active = active; + + m_Constraints = constraints; + } + + /// + /// Initializes the OverrideRigLayer. This will create animation jobs using + /// the rig constraints provided to the OverrideRigLayer. + /// + /// The Animator used to animate the RigLayer constraints. + /// True if RigLayer was initialized properly, false otherwise. + public bool Initialize(Animator animator) + { + if (isInitialized) + return true; + + if (rig == null) + return false; + + if (m_Constraints == null || m_Constraints.Length == 0) + return false; + + m_Jobs = new IAnimationJob[m_Constraints.Length]; + for (int i = 0; i < m_Constraints.Length; ++i) + { + m_Jobs[i] = m_Constraints[i].CreateJob(animator); + } + + return isInitialized = true; + } + + /// + /// Updates the OverrideRigLayer jobs. This is called during the Update loop before + /// the Animator evaluates the PlayableGraph. + /// + public void Update() + { + if (!isInitialized) + return; + + for (int i = 0; i < m_Constraints.Length; ++i) + { + m_Constraints[i].UpdateJob(m_Jobs[i]); + } + } + + /// + /// Late Updates the OverrideRigLayer jobs. This is called during the LateUpdate loop before + /// the Animator evaluates the PlayableGraph. + /// + public void LateUpdate() + { + if (!isInitialized) + return; + + for (int i = 0; i < m_Constraints.Length; ++i) + { + m_Constraints[i].LateUpdateJob(m_Jobs[i]); + } + } + + /// + /// Resets the OverrideRigLayer. This will destroy the animation jobs and + /// free up memory. + /// + public void Reset() + { + if (!isInitialized) + return; + + for (int i = 0, count = m_Constraints.Length; i < count; ++i) + { + m_Constraints[i].DestroyJob(m_Jobs[i]); + } + + m_Constraints = null; + m_Jobs = null; + + isInitialized = false; + } + + /// + /// Queries whether the OverrideRigLayer is valid. + /// + /// True if OverrideRigLayer is valid, false otherwise. + public bool IsValid() => rig != null && isInitialized; + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs.meta new file mode 100644 index 00000000..5f905563 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/OverrideRigLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b4df322b421941b2a0ffa8b0a9847e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs new file mode 100644 index 00000000..932822d8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The Rig component is used to group constraints under its GameObject local hierarchy. + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Setup/Rig")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#rig-component")] + public class Rig : MonoBehaviour, IRigEffectorHolder + { + [SerializeField, Range(0f, 1f)] + private float m_Weight = 1f; + + /// The weight given to this rig and its associated constraints. This is a value in between 0 and 1. + public float weight { get => m_Weight; set => m_Weight = Mathf.Clamp01(value); } + + [SerializeField] private List m_Effectors = new List(); + +#if UNITY_EDITOR + /// + public IEnumerable effectors { get => m_Effectors; } + + /// + public void AddEffector(Transform transform, RigEffectorData.Style style) + { + var effector = new RigEffectorData(); + effector.Initialize(transform, style); + + m_Effectors.Add(effector); + } + + /// + public void RemoveEffector(Transform transform) + { + m_Effectors.RemoveAll((RigEffectorData data) => data.transform == transform); + } + + /// + public bool ContainsEffector(Transform transform) + { + return m_Effectors.Exists((RigEffectorData data) => data.transform == transform); + } +#endif + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs.meta new file mode 100644 index 00000000..1ad03523 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/Rig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70b342d8ce5c2fd48b8fa3147d48d1d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs new file mode 100644 index 00000000..04271c2d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs @@ -0,0 +1,374 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Playables; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// RigBuilder is the root component that holds the Rigs that create an Animation Rigging hierarchy. + /// Its purpose is to create the PlayableGraph that will be used in the associated Animator component to animate + /// a character with constraints. + /// + [RequireComponent(typeof(Animator))] + [DisallowMultipleComponent, ExecuteInEditMode, AddComponentMenu("Animation Rigging/Setup/Rig Builder")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#rig-builder-component")] + public class RigBuilder : MonoBehaviour, IAnimationWindowPreview, IRigEffectorHolder + { + [SerializeField] private List m_RigLayers; + + private IRigLayer[] m_RuntimeRigLayers; + private SyncSceneToStreamLayer m_SyncSceneToStreamLayer; + + [SerializeField] private List m_Effectors = new List(); + + private bool m_IsInPreview; + +#if UNITY_EDITOR + /// + public IEnumerable effectors { get => m_Effectors; } +#endif + + /// + /// Delegate function that covers a RigBuilder calling OnEnable. + /// + /// The RigBuilder component + public delegate void OnAddRigBuilderCallback(RigBuilder rigBuilder); + /// + /// Delegate function that covers a RigBuilder calling OnDisable. + /// + /// The RigBuilder component + public delegate void OnRemoveRigBuilderCallback(RigBuilder rigBuilder); + + /// + /// Notification callback that is sent whenever a RigBuilder calls OnEnable. + /// + public static OnAddRigBuilderCallback onAddRigBuilder; + /// + /// Notification callback that is sent whenever a RigBuilder calls OnDisable. + /// + public static OnRemoveRigBuilderCallback onRemoveRigBuilder; + + void OnEnable() + { + // Build runtime data. + if (Application.isPlaying) + Build(); + + onAddRigBuilder?.Invoke(this); + } + + void OnDisable() + { + // Clear runtime data. + if (Application.isPlaying) + Clear(); + + onRemoveRigBuilder?.Invoke(this); + } + + void OnDestroy() + { + Clear(); + } + + /// + /// Updates the RigBuilder layers and evaluates the PlayableGraph manually. + /// + /// The time in seconds by which to advance the RigBuilder PlayableGraph. + /// + /// Manually evaluate the RigBuilder in LateUpdate. + /// + /// + public void Evaluate(float deltaTime) + { + if (!graph.IsValid()) + return; + + UpdateLayers(); + + graph.Evaluate(deltaTime); + + LateUpdateLayers(); + } + + protected virtual void Update() + { + if (!graph.IsValid()) + return; + + UpdateLayers(); + } + + protected virtual void LateUpdate() + { + if (!graph.IsValid()) + return; + + LateUpdateLayers(); + } + + /// + /// Synchronizes rigs and constraints with scene values. + /// This must be called before evaluating the PlayableGraph. + /// + /// + /// + /// + /// + /// Synchronizing layers before evaluating a PlayableGraph created + /// outside the RigBuilder in LateUpdate. + /// + /// + public void UpdateLayers() + { + if (m_RuntimeRigLayers == null) + return; + + syncSceneToStreamLayer.Update(m_RuntimeRigLayers); + + for (int i = 0, count = m_RuntimeRigLayers.Length; i < count; ++i) + { + if (m_RuntimeRigLayers[i].IsValid() && m_RuntimeRigLayers[i].active) + m_RuntimeRigLayers[i].Update(); + } + } + + public void LateUpdateLayers() + { + if (m_RuntimeRigLayers == null) + return; + + syncSceneToStreamLayer.LateUpdate(m_RuntimeRigLayers); + + for (int i = 0, count = m_RuntimeRigLayers.Length; i < count; ++i) + { + if (m_RuntimeRigLayers[i].IsValid() && m_RuntimeRigLayers[i].active) + m_RuntimeRigLayers[i].LateUpdate(); + } + } + + /// + /// Builds the RigBuilder PlayableGraph. + /// + /// Returns true if the RigBuilder has created a valid PlayableGraph. Returns false otherwise. + public bool Build() + { + if (m_IsInPreview) + return false; + + Clear(); + + var animator = GetComponent(); + if (animator == null || layers.Count == 0) + return false; + + // Make a copy of the layers list. + m_RuntimeRigLayers = layers.ToArray(); + + graph = RigBuilderUtils.BuildPlayableGraph(animator, m_RuntimeRigLayers, syncSceneToStreamLayer); + + if (!graph.IsValid()) + return false; + + graph.Play(); + + return true; + } + + /// + /// Builds the RigBuilder playable nodes in an external PlayableGraph. + /// + /// Destination PlayableGraph. + /// Returns true if the RigBuilder has created Playable nodes. Returns false otherwise. + public bool Build(PlayableGraph graph) + { + if (m_IsInPreview) + return false; + + Clear(); + + var animator = GetComponent(); + if (animator == null || layers.Count == 0) + return false; + + // Make a copy of the layers list. + m_RuntimeRigLayers = layers.ToArray(); + + RigBuilderUtils.BuildPlayableGraph(graph, animator, m_RuntimeRigLayers, syncSceneToStreamLayer); + + return true; + } + + /// + /// Destroys the RigBuilder PlayableGraph and frees associated RigLayers memory. + /// + public void Clear() + { + if (m_IsInPreview) + return; + + if (graph.IsValid()) + graph.Destroy(); + + if (m_RuntimeRigLayers != null) + { + foreach (var layer in m_RuntimeRigLayers) + layer.Reset(); + + m_RuntimeRigLayers = null; + } + + syncSceneToStreamLayer.Reset(); + } + + // + // IAnimationWindowPreview methods implementation + // + + /// Notification callback when the animation previewer starts previewing an AnimationClip. + /// This is called by the Animation Window or the Timeline Editor. + public void StartPreview() + { + m_IsInPreview = true; + + if (!enabled) + return; + + // Make a copy of the layer list if it doesn't already exist. + if (m_RuntimeRigLayers == null) + m_RuntimeRigLayers = layers.ToArray(); + + var animator = GetComponent(); + if (animator != null) + { + foreach (var layer in m_RuntimeRigLayers) + { + layer.Initialize(animator); + } + } + } + + /// Notification callback when the animation previewer stops previewing an AnimationClip. + /// This is called by the Animation Window or the Timeline Editor. + public void StopPreview() + { + m_IsInPreview = false; + + if (!enabled) + return; + + if (Application.isPlaying) + return; + + Clear(); + } + + /// Notification callback when the animation previewer updates its PlayableGraph before sampling an AnimationClip. + /// This is called by the Animation Window or the Timeline Editor. + /// The animation previewer PlayableGraph + public void UpdatePreviewGraph(PlayableGraph graph) + { + if (!enabled) + return; + + if (!graph.IsValid() || m_RuntimeRigLayers == null) + return; + + syncSceneToStreamLayer.Update(m_RuntimeRigLayers); + + foreach (var layer in m_RuntimeRigLayers) + { + if (layer.IsValid() && layer.active) + layer.Update(); + } + } + + /// + /// Appends custom Playable nodes to the animation previewer PlayableGraph. + /// + /// The animation previewer PlayableGraph + /// The current root of the PlayableGraph + /// + public Playable BuildPreviewGraph(PlayableGraph graph, Playable inputPlayable) + { + if (!enabled) + return inputPlayable; + + if (m_RuntimeRigLayers == null) + StartPreview(); + + var animator = GetComponent(); + if (animator == null || m_RuntimeRigLayers == null || m_RuntimeRigLayers.Length == 0) + return inputPlayable; + + var playableChains = RigBuilderUtils.BuildPlayables(animator, graph, m_RuntimeRigLayers, syncSceneToStreamLayer); + foreach(var chain in playableChains) + { + if (chain.playables == null || chain.playables.Length == 0) + continue; + + chain.playables[0].AddInput(inputPlayable, 0, 1); + inputPlayable = chain.playables[chain.playables.Length - 1]; + } + + return inputPlayable; + } + +#if UNITY_EDITOR + /// + public void AddEffector(Transform transform, RigEffectorData.Style style) + { + var effector = new RigEffectorData(); + effector.Initialize(transform, style); + + m_Effectors.Add(effector); + } + + /// + public void RemoveEffector(Transform transform) + { + m_Effectors.RemoveAll((RigEffectorData data) => data.transform == transform); + } + + /// + public bool ContainsEffector(Transform transform) + { + return m_Effectors.Exists((RigEffectorData data) => data.transform == transform); + } +#endif + + /// + /// Returns a list of RigLayer associated to this RigBuilder. + /// + public List layers + { + get + { + if (m_RigLayers == null) + m_RigLayers = new List(); + + return m_RigLayers; + } + + set => m_RigLayers = value; + } + + private SyncSceneToStreamLayer syncSceneToStreamLayer + { + get + { + if (m_SyncSceneToStreamLayer == null) + m_SyncSceneToStreamLayer = new SyncSceneToStreamLayer(); + + return m_SyncSceneToStreamLayer; + } + + set => m_SyncSceneToStreamLayer = value; + } + + /// + /// Retrieves the PlayableGraph created by this RigBuilder. + /// + public PlayableGraph graph { get; private set; } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs.meta new file mode 100644 index 00000000..d913e337 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fff0960ef4ea6e04eac66b4a7fd2189d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs new file mode 100644 index 00000000..1e19a819 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Experimental.Animations; +using UnityEngine.Playables; + +namespace UnityEngine.Animations.Rigging +{ + internal static class RigBuilderUtils + { + public struct PlayableChain + { + public string name; + public Playable[] playables; + + public bool IsValid() => playables != null && playables.Length > 0; + } + + private static readonly ushort k_AnimationOutputPriority = 1000; + + public static Playable[] BuildRigPlayables(PlayableGraph graph, IRigLayer layer) + { + if (layer == null || layer.jobs == null || layer.jobs.Length == 0) + return null; + + var count = layer.jobs.Length; + var playables = new Playable[count]; + for (int i = 0; i < count; ++i) + { + var binder = layer.constraints[i].binder; + playables[i] = binder.CreatePlayable(graph, layer.jobs[i]); + } + + // Connect rig playables serially + for (int i = 1; i < count; ++i) + playables[i].AddInput(playables[i - 1], 0, 1); + + return playables; + } + + public static IEnumerable BuildPlayables(Animator animator, PlayableGraph graph, IList layers, SyncSceneToStreamLayer syncSceneToStreamLayer) + { + var playableChains = new PlayableChain[layers.Count + 1]; + + // Create all rig layers + int index = 1; + foreach (var layer in layers) + { + var chain = new PlayableChain(); + chain.name = layer.name; + + if (layer.Initialize(animator)) + chain.playables = BuildRigPlayables(graph, layer); + + playableChains[index++] = chain; + } + + // Create sync to stream job with all rig references + if (syncSceneToStreamLayer.Initialize(animator, layers) && syncSceneToStreamLayer.IsValid()) + { + var chain = new PlayableChain(); + + chain.name = "syncSceneToStream"; + chain.playables = new Playable[1] {RigUtils.syncSceneToStreamBinder.CreatePlayable(graph, syncSceneToStreamLayer.job)}; + + playableChains[0] = chain; + } + + return playableChains; + } + + public static PlayableGraph BuildPlayableGraph(Animator animator, IList layers, SyncSceneToStreamLayer syncSceneToStreamLayer) + { + string graphName = animator.gameObject.transform.name + "_Rigs"; + PlayableGraph graph = PlayableGraph.Create(graphName); + graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime); + + BuildPlayableGraph(graph, animator, layers, syncSceneToStreamLayer); + + return graph; + } + + public static void BuildPlayableGraph(PlayableGraph graph, Animator animator, IList layers, SyncSceneToStreamLayer syncSceneToStreamLayer) + { + IEnumerable playableChains = BuildPlayables(animator, graph, layers, syncSceneToStreamLayer); + + foreach(var chain in playableChains) + { + if (!chain.IsValid()) + continue; + + AnimationPlayableOutput output = AnimationPlayableOutput.Create(graph, String.Format("{0}-Output", chain.name), animator); + output.SetAnimationStreamSource(AnimationStreamSource.PreviousInputs); + output.SetSortingOrder(k_AnimationOutputPriority); + + // Connect last rig playable to output + output.SetSourcePlayable(chain.playables[chain.playables.Length - 1]); + } + } + } +} + diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs.meta new file mode 100644 index 00000000..a4257ef0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigBuilderUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0b922d7bee7fe4a43810ba0b90911a1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs new file mode 100644 index 00000000..bac4e4c3 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs @@ -0,0 +1,109 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// This is the base class for rig constraints. + /// Inherit from this class to implement custom constraints. + /// + /// The constraint job + /// The constraint data + /// The constraint job binder + public class RigConstraint : MonoBehaviour, IRigConstraint + where TJob : struct, IWeightedAnimationJob + where TData : struct, IAnimationJobData + where TBinder : AnimationJobBinder, new() + { + /// + /// The constraint weight parameter. + /// + [SerializeField, Range(0f, 1f)] + protected float m_Weight = 1f; + + /// + /// The constraint data. + /// + [SerializeField, ExpandChildren] + protected TData m_Data; + + static readonly TBinder s_Binder = new TBinder(); + + /// + /// Resets constraint data to default values. + /// + public void Reset() + { + m_Weight = 1f; + m_Data.SetDefaultValues(); + } + + /// + /// Retrieves the constraint valid state. + /// + /// Returns true if constraint data can be successfully evaluated. Returns false otherwise. + public bool IsValid() => m_Data.IsValid(); + + /// + /// This function is called when the script is loaded or a value is changed in the Inspector (Called in the editor only). + /// You can use this to ensure that when you modify data in an editor, that data stays within a certain range. + /// + protected virtual void OnValidate() => m_Weight = Mathf.Clamp01(m_Weight); + + /// + /// The data container for the constraint. + /// + public ref TData data => ref m_Data; + + /// + /// The constraint weight. This is a value in between 0 and 1. + /// + public float weight { get => m_Weight; set => m_Weight = Mathf.Clamp01(value); } + + /// + /// Creates the animation job for this constraint. + /// + /// The animated hierarchy Animator component + /// Returns the newly instantiated job. + public IAnimationJob CreateJob(Animator animator) + { + TJob job = s_Binder.Create(animator, ref m_Data, this); + + // Bind constraint job weight property + job.jobWeight = FloatProperty.BindCustom( + animator, + ConstraintsUtils.ConstructCustomPropertyName(this, ConstraintProperties.s_Weight) + ); + + return job; + } + + /// + /// Frees the specified job memory. + /// + /// The job to destroy. + public void DestroyJob(IAnimationJob job) => s_Binder.Destroy((TJob)job); + + /// + /// Updates the specified job data. + /// + /// The job to update. + public void UpdateJob(IAnimationJob job) => s_Binder.Update((TJob)job, ref m_Data); + + /// + /// Late Updates the specified job data. + /// + /// The job to late update. + public void LateUpdateJob(IAnimationJob job) => s_Binder.LateUpdate((TJob) job, ref m_Data); + + /// + /// The job binder for the constraint. + /// + IAnimationJobBinder IRigConstraint.binder => s_Binder; + /// + /// The data container for the constraint. + /// + IAnimationJobData IRigConstraint.data => m_Data; + /// + /// The component for the constraint. + /// + Component IRigConstraint.component => (Component)this; + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs.meta new file mode 100644 index 00000000..8ad95db2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96db938db259ddc4ba0c0105e23abf96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs new file mode 100644 index 00000000..a8cbefd0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs @@ -0,0 +1,117 @@ +using System; +using UnityEngine.Serialization; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The RigLayer is used by the RigBuilder to control in which order rigs will be + /// evaluated and whether they are active or not. + /// + [Serializable] + public class RigLayer : IRigLayer + { + [SerializeField][FormerlySerializedAs("rig")] private Rig m_Rig; + [SerializeField][FormerlySerializedAs("active")] private bool m_Active = true; + + private IRigConstraint[] m_Constraints; + private IAnimationJob[] m_Jobs; + + /// The Rig associated to the RigLayer + public Rig rig { get => m_Rig; private set => m_Rig = value; } + /// The active state. True if the RigLayer is active, false otherwise. + public bool active { get => m_Active; set => m_Active = value; } + /// The RigLayer name. + public string name { get => (rig != null ? rig.gameObject.name : "no-name"); } + /// The list of constraints associated with the RigLayer. + public IRigConstraint[] constraints { get => isInitialized ? m_Constraints : null; } + /// The list of jobs built from constraints associated with the RigLayer. + public IAnimationJob[] jobs { get => isInitialized ? m_Jobs : null; } + /// Returns true if RigLayer was initialized or false otherwise. + /// + public bool isInitialized { get; private set; } + + /// + /// Constructor. + /// + /// The rig represented by this rig layer. + /// The active state of the rig layer. + public RigLayer(Rig rig, bool active = true) + { + this.rig = rig; + this.active = active; + } + + /// + /// Initializes the RigLayer. This will retrieve the constraints associated with the Rig + /// and create the animation jobs required by the PlayableGraph. + /// + /// The Animator used to animate the RigLayer constraints. + /// True if RigLayer was initialized properly, false otherwise. + public bool Initialize(Animator animator) + { + if (isInitialized) + return true; + + if (rig != null) + { + m_Constraints = RigUtils.GetConstraints(rig); + if (m_Constraints == null || m_Constraints.Length == 0) + return false; + + m_Jobs = RigUtils.CreateAnimationJobs(animator, m_Constraints); + + return (isInitialized = true); + } + + return false; + } + + /// + /// Updates the RigLayer jobs. This is called during the Update loop before + /// the Animator evaluates the PlayableGraph. + /// + public void Update() + { + if (!isInitialized) + return; + + for (int i = 0, count = m_Constraints.Length; i < count; ++i) + m_Constraints[i].UpdateJob(m_Jobs[i]); + } + + /// + /// Late Updates the RigLayer jobs. This is called during the LateUpdate loop before + /// the Animator evaluates the PlayableGraph. + /// + public void LateUpdate() + { + if (!isInitialized) + return; + + for (int i = 0, count = m_Constraints.Length; i < count; ++i) + m_Constraints[i].LateUpdateJob(m_Jobs[i]); + } + + /// + /// Resets the RigLayer. This will destroy the animation jobs and + /// free up memory. + /// + public void Reset() + { + if (!isInitialized) + return; + + RigUtils.DestroyAnimationJobs(m_Constraints, m_Jobs); + m_Constraints = null; + m_Jobs = null; + + isInitialized = false; + } + + /// + /// Queries whether the RigLayer is valid. + /// + /// True if RigLayer is valid, false otherwise. + public bool IsValid() => rig != null && isInitialized; + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs.meta new file mode 100644 index 00000000..6bc45d83 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d7bdd63ca7404ca1bfb21319b7ddf34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs new file mode 100644 index 00000000..32539ddf --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs @@ -0,0 +1,11 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// + /// + [DisallowMultipleComponent, AddComponentMenu("Animation Rigging/Setup/Rig Transform")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#rig-transform")] + public class RigTransform : MonoBehaviour + { + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs.meta new file mode 100644 index 00000000..82700400 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48948dfe9d76b07488dacd46a7955fa1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs new file mode 100644 index 00000000..c5cb286a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace UnityEngine.Animations.Rigging +{ + static class RigUtils + { + internal static readonly Dictionary s_SupportedPropertyTypeToDescriptor = new Dictionary + { + { typeof(float) , new PropertyDescriptor{ size = 1, type = PropertyType.Float } }, + { typeof(int) , new PropertyDescriptor{ size = 1, type = PropertyType.Int } }, + { typeof(bool) , new PropertyDescriptor{ size = 1, type = PropertyType.Bool } }, + { typeof(Vector2) , new PropertyDescriptor{ size = 2, type = PropertyType.Float } }, + { typeof(Vector3) , new PropertyDescriptor{ size = 3, type = PropertyType.Float } }, + { typeof(Vector4) , new PropertyDescriptor{ size = 4, type = PropertyType.Float } }, + { typeof(Quaternion) , new PropertyDescriptor{ size = 4, type = PropertyType.Float } }, + { typeof(Vector3Int) , new PropertyDescriptor{ size = 3, type = PropertyType.Int } }, + { typeof(Vector3Bool), new PropertyDescriptor{ size = 3, type = PropertyType.Bool } } + }; + + public static IRigConstraint[] GetConstraints(Rig rig) + { + IRigConstraint[] constraints = rig.GetComponentsInChildren(); + if (constraints.Length == 0) + return null; + + List tmp = new List(constraints.Length); + foreach (var constraint in constraints) + { + if (constraint.IsValid()) + tmp.Add(constraint); + } + + return tmp.Count == 0 ? null : tmp.ToArray(); + } + + private static Transform[] GetSyncableRigTransforms(Animator animator) + { + RigTransform[] rigTransforms = animator.GetComponentsInChildren(); + if (rigTransforms.Length == 0) + return null; + + Transform[] transforms = new Transform[rigTransforms.Length]; + for (int i = 0; i < transforms.Length; ++i) + transforms[i] = rigTransforms[i].transform; + + return transforms; + } + + private static bool ExtractTransformType( + Animator animator, + FieldInfo field, + object data, + List syncableTransforms + ) + { + bool handled = true; + + Type fieldType = field.FieldType; + if (fieldType == typeof(Transform)) + { + var value = (Transform)field.GetValue(data); + if (value != null && value.IsChildOf(animator.avatarRoot)) + syncableTransforms.Add(value); + } + else if (fieldType == typeof(Transform[]) || fieldType == typeof(List)) + { + var list = (IEnumerable)field.GetValue(data); + foreach (var element in list) + if (element != null && element.IsChildOf(animator.avatarRoot)) + syncableTransforms.Add(element); + } + else + handled = false; + + return handled; + } + + private static bool ExtractPropertyType( + FieldInfo field, + object data, + List syncableProperties, + string namePrefix = "" + ) + { + if (!s_SupportedPropertyTypeToDescriptor.TryGetValue(field.FieldType, out PropertyDescriptor descriptor)) + return false; + + syncableProperties.Add( + new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(namePrefix + field.Name), descriptor = descriptor } + ); + + return true; + } + + private static bool ExtractWeightedTransforms( + Animator animator, + FieldInfo field, + object data, + List syncableTransforms, + List syncableProperties) + { + bool handled = true; + + Type fieldType = field.FieldType; + if (fieldType == typeof(WeightedTransform)) + { + var value = ((WeightedTransform)field.GetValue(data)).transform; + if (value != null && value.IsChildOf(animator.avatarRoot)) + syncableTransforms.Add(value); + + syncableProperties.Add( + new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(field.Name + ".weight"), descriptor = s_SupportedPropertyTypeToDescriptor[typeof(float)] } + ); + } + else if (fieldType == typeof(WeightedTransformArray)) + { + var list = (IEnumerable)field.GetValue(data); + int index = 0; + foreach (var element in list) + { + if (element.transform != null && element.transform.IsChildOf(animator.avatarRoot)) + syncableTransforms.Add(element.transform); + + syncableProperties.Add( + new Property { name = ConstraintsUtils.ConstructConstraintDataPropertyName(field.Name + ".m_Item" + index + ".weight"), descriptor = s_SupportedPropertyTypeToDescriptor[typeof(float)] } + ); + + ++index; + } + } + else + handled = false; + + return handled; + } + + private static bool ExtractNestedPropertyType( + Animator animator, + FieldInfo field, + object data, + List syncableTransforms, + List syncableProperties, + string namePrefix = "") + { + Type fieldType = field.FieldType; + var fieldData = field.GetValue(data); + var fieldName = namePrefix + field.Name + "."; + + // Only structs + if (!fieldType.IsValueType || fieldType.IsPrimitive) + return false; + + var fields = fieldType.GetFields( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + ).Where(info => info.GetCustomAttribute() != null); + + foreach (var childField in fields) + { + if (ExtractTransformType(animator, childField, fieldData, syncableTransforms)) + continue; + + if (ExtractPropertyType(childField, fieldData, syncableProperties, fieldName)) + continue; + + if (ExtractNestedPropertyType(animator, childField, fieldData, syncableTransforms, syncableProperties, fieldName)) + continue; + + throw new NotSupportedException("Field type [" + field.FieldType + "] is not a supported syncable property type."); + } + + return true; + } + + private static void ExtractAllSyncableData(Animator animator, IList layers, out List syncableTransforms, out List syncableProperties) + { + syncableTransforms = new List(); + syncableProperties = new List(layers.Count); + + Dictionary typeToSyncableFields = new Dictionary(); + foreach (var layer in layers) + { + if (!layer.IsValid()) + continue; + + var constraints = layer.constraints; + + List allConstraintProperties = new List(constraints.Length); + + foreach (var constraint in constraints) + { + var data = constraint.data; + var dataType = constraint.data.GetType(); + if (!typeToSyncableFields.TryGetValue(dataType, out FieldInfo[] syncableFields)) + { + FieldInfo[] allFields = dataType.GetFields( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + ); + + List filteredFields = new List(allFields.Length); + foreach (var field in allFields) + if (field.GetCustomAttribute() != null) + filteredFields.Add(field); + + syncableFields = filteredFields.ToArray(); + typeToSyncableFields[dataType] = syncableFields; + } + + List properties = new List(syncableFields.Length); + foreach (var field in syncableFields) + { + if (ExtractWeightedTransforms(animator, field, data, syncableTransforms, properties)) + continue; + if (ExtractTransformType(animator, field, data, syncableTransforms)) + continue; + if (ExtractPropertyType(field, data, properties)) + continue; + if (ExtractNestedPropertyType(animator, field, data, syncableTransforms, properties)) + continue; + + throw new NotSupportedException("Field type [" + field.FieldType + "] is not a supported syncable property type."); + } + + allConstraintProperties.Add( + new ConstraintProperties { + component = constraint.component, + properties = properties.ToArray() + } + ); + } + + syncableProperties.Add( + new SyncableProperties { + rig = new RigProperties { component = layer.rig as Component }, + constraints = allConstraintProperties.ToArray() + } + ); + } + + var extraTransforms = GetSyncableRigTransforms(animator); + if (extraTransforms != null) + syncableTransforms.AddRange(extraTransforms); + } + + public static IAnimationJob[] CreateAnimationJobs(Animator animator, IRigConstraint[] constraints) + { + if (constraints == null || constraints.Length == 0) + return null; + + IAnimationJob[] jobs = new IAnimationJob[constraints.Length]; + for (int i = 0; i < constraints.Length; ++i) + jobs[i] = constraints[i].CreateJob(animator); + + return jobs; + } + + public static void DestroyAnimationJobs(IRigConstraint[] constraints, IAnimationJob[] jobs) + { + if (jobs == null || jobs.Length != constraints.Length) + return; + + for (int i = 0; i < constraints.Length; ++i) + constraints[i].DestroyJob(jobs[i]); + } + + private struct RigSyncSceneToStreamData : IAnimationJobData, IRigSyncSceneToStreamData + { + public RigSyncSceneToStreamData(Transform[] transforms, SyncableProperties[] properties, int rigCount) + { + if (transforms != null && transforms.Length > 0) + { + var unique = UniqueTransformIndices(transforms); + if (unique.Length != transforms.Length) + { + syncableTransforms = new Transform[unique.Length]; + for (int i = 0; i < unique.Length; ++i) + syncableTransforms[i] = transforms[unique[i]]; + } + else + syncableTransforms = transforms; + } + else + syncableTransforms = null; + + syncableProperties = properties; + + rigStates = rigCount > 0 ? new bool[rigCount] : null; + + m_IsValid = !(((syncableTransforms == null || syncableTransforms.Length == 0) && + (syncableProperties == null || syncableProperties.Length == 0) && + rigStates == null)); + } + + static int[] UniqueTransformIndices(Transform[] transforms) + { + if (transforms == null || transforms.Length == 0) + return null; + + HashSet instanceIDs = new HashSet(); + List unique = new List(transforms.Length); + + for (int i = 0; i < transforms.Length; ++i) + if (instanceIDs.Add(transforms[i].GetInstanceID())) + unique.Add(i); + + return unique.ToArray(); + } + + public Transform[] syncableTransforms { get; private set; } + public SyncableProperties[] syncableProperties { get; private set; } + public bool[] rigStates { get; set; } + private readonly bool m_IsValid; + + bool IAnimationJobData.IsValid() => m_IsValid; + + void IAnimationJobData.SetDefaultValues() + { + syncableTransforms = null; + syncableProperties = null; + rigStates = null; + } + } + + internal static IAnimationJobData CreateSyncSceneToStreamData(Animator animator, IList layers) + { + ExtractAllSyncableData(animator, layers, out List syncableTransforms, out List syncableProperties); + return new RigSyncSceneToStreamData(syncableTransforms.ToArray(), syncableProperties.ToArray(), layers.Count); + } + + public static IAnimationJobBinder syncSceneToStreamBinder { get; } = new RigSyncSceneToStreamJobBinder(); + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs.meta new file mode 100644 index 00000000..5ac32b22 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/RigUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 063675370bb1d944995e46d040c62f28 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs new file mode 100644 index 00000000..fa3c7ada --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + class SyncSceneToStreamLayer + { + public bool Initialize(Animator animator, IList layers) + { + if (isInitialized) + return true; + + m_RigIndices = new List(layers.Count); + for (int i = 0; i < layers.Count; ++i) + { + if (!layers[i].IsValid()) + continue; + + m_RigIndices.Add(i); + } + + m_Data = RigUtils.CreateSyncSceneToStreamData(animator, layers); + if (!m_Data.IsValid()) + return false; + + job = RigUtils.syncSceneToStreamBinder.Create(animator, m_Data); + + return (isInitialized = true); + } + + public void Update(IList layers) + { + if (!isInitialized || !m_Data.IsValid()) + return; + + IRigSyncSceneToStreamData syncData = (IRigSyncSceneToStreamData)m_Data; + for (int i = 0, count = m_RigIndices.Count; i < count; ++i) + syncData.rigStates[i] = layers[m_RigIndices[i]].active; + + RigUtils.syncSceneToStreamBinder.Update(job, m_Data); + } + + public void LateUpdate(IList layers) + { + if (!isInitialized || !m_Data.IsValid()) + return; + + RigUtils.syncSceneToStreamBinder.LateUpdate(job, m_Data); + } + + public void Reset() + { + if (!isInitialized) + return; + + if (m_Data != null && m_Data.IsValid()) + { + RigUtils.syncSceneToStreamBinder.Destroy(job); + m_Data = null; + } + + isInitialized = false; + } + + public bool IsValid() => job != null && m_Data != null; + + public bool isInitialized { get; private set; } + + public IAnimationJob job; + + private IAnimationJobData m_Data; + private List m_RigIndices; + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs.meta new file mode 100644 index 00000000..688ddb04 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/SyncSceneToStreamLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e7adc2784a6054bccaf87e875f5e87af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs new file mode 100644 index 00000000..e9be0e83 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs @@ -0,0 +1,75 @@ +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// Provides an accessor to a Transform component reference. + /// + public interface ITransformProvider + { + /// + /// Reference to a Transform component. + /// + Transform transform { get; set; } + } + + /// + /// Provides an access to a weight value. + /// + public interface IWeightProvider + { + /// + /// Weight. This is a number in between 0 and 1. + /// + float weight { get; set; } + } + + /// + /// Tuple of a Reference to a Transform component and a weight number. + /// See also and . + /// + [Serializable] + public struct WeightedTransform : ITransformProvider, IWeightProvider, IEquatable + { + /// Reference to a Transform component. + public Transform transform; + /// Weight. This is a number be in between 0 and 1. + public float weight; + + /// + /// Constructor. + /// + /// Reference to a Transform component. + /// Weight. This is a number in between 0 and 1. + public WeightedTransform(Transform transform, float weight) + { + this.transform = transform; + this.weight = Mathf.Clamp01(weight); + } + + /// + /// Returns a WeightedTransform object with an null Transform component reference and the specified weight. + /// + /// Weight. This is a number in between 0 and 1. + /// Returns a new WeightedTransform + public static WeightedTransform Default(float weight) => new WeightedTransform(null, weight); + + /// + /// Compare two WeightedTransform objects for equality. + /// + /// A WeightedTransform object + /// Returns true if both WeightedTransform have the same values. False otherwise. + public bool Equals(WeightedTransform other) + { + if (transform == other.transform && weight == other.weight) + return true; + + return false; + } + + /// + Transform ITransformProvider.transform { get => transform; set => transform = value; } + /// + float IWeightProvider.weight { get => weight; set => weight = Mathf.Clamp01(value); } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs.meta new file mode 100644 index 00000000..e056d504 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab8a6a8cfed3ce44cb7f2b5b00713cf7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs new file mode 100644 index 00000000..6b21d60e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs @@ -0,0 +1,454 @@ +using System.Collections.Generic; +using System.Collections; +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// This struct defines a List of WeightedTransform. + /// WeightedTransformArray can be animated (as opposed to System.List and C# arrays) and is implemented + /// with a hard limit of eight WeightedTransform elements. + /// See also and . + /// + [Serializable] + public struct WeightedTransformArray : IList, IList + { + /// Maximum number of elements in WeightedTransformArray. + public static readonly int k_MaxLength = 8; + + [SerializeField, NotKeyable] private int m_Length; + + [SerializeField] private WeightedTransform m_Item0; + [SerializeField] private WeightedTransform m_Item1; + [SerializeField] private WeightedTransform m_Item2; + [SerializeField] private WeightedTransform m_Item3; + [SerializeField] private WeightedTransform m_Item4; + [SerializeField] private WeightedTransform m_Item5; + [SerializeField] private WeightedTransform m_Item6; + [SerializeField] private WeightedTransform m_Item7; + + /// + /// Constructor. + /// + /// Size of the array. This will clamped to be a number in between 0 and k_MaxLength. + /// + public WeightedTransformArray(int size) + { + m_Length = ClampSize(size); + m_Item0 = new WeightedTransform(); + m_Item1 = new WeightedTransform(); + m_Item2 = new WeightedTransform(); + m_Item3 = new WeightedTransform(); + m_Item4 = new WeightedTransform(); + m_Item5 = new WeightedTransform(); + m_Item6 = new WeightedTransform(); + m_Item7 = new WeightedTransform(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An IEnumerator object that can be used to iterate through the collection. + public IEnumerator GetEnumerator() + { + return new Enumerator(ref this); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An IEnumerator object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(ref this); + } + + /// + /// Adds an item to the WeightedTransformArray. + /// + /// The object to add to the WeightedTransformArray. + /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection. + int IList.Add(object value) + { + Add((WeightedTransform)value); + return m_Length - 1; + } + + /// + /// Adds an item to the WeightedTransformArray. + /// + /// The WeightedTransform to add to the WeightedTransformArray. + public void Add(WeightedTransform value) + { + if (m_Length >= k_MaxLength) + throw new ArgumentException($"This array cannot have more than '{k_MaxLength}' items."); + + Set(m_Length, value); + + ++m_Length; + } + + /// + /// Removes all items from the WeightedTransformArray. + /// + public void Clear() + { + m_Length = 0; + } + + /// + /// Determines the index of a specific item in the WeightedTransformArray. + /// + /// The object to locate in the WeightedTransformArray. + /// The index of value if found in the list; otherwise, -1. + int IList.IndexOf(object value) => IndexOf((WeightedTransform)value); + + /// + /// Determines the index of a specific item in the WeightedTransformArray. + /// + /// The WeightedTransform to locate in the WeightedTransformArray. + /// The index of value if found in the list; otherwise, -1. + public int IndexOf(WeightedTransform value) + { + for (int i = 0; i < m_Length; ++i) + { + if (Get(i).Equals(value)) + return i; + } + + return -1; + } + + /// + /// Determines whether the WeightedTransformArray contains a specific value. + /// + /// The object to locate in the WeightedTransformArray. + /// true if the Object is found in the WeightedTransformArray; otherwise, false. + bool IList.Contains(object value) => Contains((WeightedTransform)value); + + /// + /// Determines whether the WeightedTransformArray contains a specific value. + /// + /// The WeightedTransform to locate in the WeightedTransformArray. + /// true if the Object is found in the WeightedTransformArray; otherwise, false. + public bool Contains(WeightedTransform value) + { + for (int i = 0; i < m_Length; ++i) + { + if (Get(i).Equals(value)) + return true; + } + + return false; + } + + /// + /// Copies the elements of the WeightedTransform to an Array, starting at a particular Array index. + /// + /// The one-dimensional Array that is the destination of the elements copied from WeightedTransform. The Array must have zero-based indexing. + /// The zero-based index in array at which copying begins. + void ICollection.CopyTo(Array array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("The array cannot be null."); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("The starting array index cannot be negative."); + if (Count > array.Length - arrayIndex + 1) + throw new ArgumentException("The destination array has fewer elements than the collection."); + + for (int i = 0; i < m_Length; i++) + { + array.SetValue(Get(i), i + arrayIndex); + } + } + + /// + /// Copies the elements of the WeightedTransform to an Array, starting at a particular Array index. + /// + /// The one-dimensional Array that is the destination of the elements copied from WeightedTransform. The Array must have zero-based indexing. + /// The zero-based index in array at which copying begins. + public void CopyTo(WeightedTransform[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("The array cannot be null."); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("The starting array index cannot be negative."); + if (Count > array.Length - arrayIndex + 1) + throw new ArgumentException("The destination array has fewer elements than the collection."); + + for (int i = 0; i < m_Length; i++) + { + array[i + arrayIndex] = Get(i); + } + } + + /// + /// Removes the first occurrence of a specific object from the WeightedTransformArray. + /// + /// The object to remove from the WeightedTransformArray. + void IList.Remove(object value) { Remove((WeightedTransform)value); } + + /// + /// Removes the first occurrence of a specific WeightedTransform from the WeightedTransformArray. + /// + /// The WeightedTransform to remove from the WeightedTransformArray. + /// True if value was removed from the array, false otherwise. + public bool Remove(WeightedTransform value) + { + for (int i = 0; i < m_Length; ++i) + { + if (Get(i).Equals(value)) + { + for (; i < m_Length - 1; ++i) + { + Set(i, Get(i + 1)); + } + + --m_Length; + return true; + } + } + + return false; + } + + /// + /// Removes the WeightedTransformArray item at the specified index. + /// + /// The zero-based index of the item to remove. + public void RemoveAt(int index) + { + CheckOutOfRangeIndex(index); + + for (int i = index; i < m_Length - 1; ++i) + { + Set(i, Get(i + 1)); + } + + --m_Length; + } + + /// + /// Inserts an item to the WeightedTransformArray at the specified index. + /// + /// The zero-based index at which value should be inserted. + /// The object to insert into the WeightedTransformArray. + void IList.Insert(int index, object value) => Insert(index, (WeightedTransform)value); + + /// + /// Inserts an item to the WeightedTransformArray at the specified index. + /// + /// The zero-based index at which value should be inserted. + /// The WeightedTransform to insert into the WeightedTransformArray. + public void Insert(int index, WeightedTransform value) + { + if (m_Length >= k_MaxLength) + throw new ArgumentException($"This array cannot have more than '{k_MaxLength}' items."); + + CheckOutOfRangeIndex(index); + + if (index >= m_Length) + { + Add(value); + return; + } + + for (int i = m_Length; i > index; --i) + { + Set(i, Get(i - 1)); + } + + Set(index, value); + ++m_Length; + } + + /// + /// Clamps specified value in between 0 and k_MaxLength. + /// + /// Size value. + /// Value in between 0 and k_MaxLength. + /// + private static int ClampSize(int size) + { + return Mathf.Clamp(size, 0, k_MaxLength); + } + + /// + /// Checks whether specified index value in within bounds. + /// + /// Index value. + /// Index value is not between 0 and k_MaxLength. + /// + private void CheckOutOfRangeIndex(int index) + { + if (index < 0 || index >= k_MaxLength) + throw new IndexOutOfRangeException($"Index {index} is out of range of '{m_Length}' Length."); + } + + /// + /// Retrieves a WeightedTransform at specified index. + /// + /// Index value. + /// The WeightedTransform value at specified index. + private WeightedTransform Get(int index) + { + CheckOutOfRangeIndex(index); + + switch(index) + { + case 0: return m_Item0; + case 1: return m_Item1; + case 2: return m_Item2; + case 3: return m_Item3; + case 4: return m_Item4; + case 5: return m_Item5; + case 6: return m_Item6; + case 7: return m_Item7; + } + + // Shouldn't happen. + return m_Item0; + } + + /// + /// Sets a WeightedTransform at specified index. + /// + /// Index value. + /// The WeightedTransform value to set. + private void Set(int index, WeightedTransform value) + { + CheckOutOfRangeIndex(index); + + switch(index) + { + case 0: m_Item0 = value; break; + case 1: m_Item1 = value; break; + case 2: m_Item2 = value; break; + case 3: m_Item3 = value; break; + case 4: m_Item4 = value; break; + case 5: m_Item5 = value; break; + case 6: m_Item6 = value; break; + case 7: m_Item7 = value; break; + } + } + + /// + /// Sets a weight on a WeightedTransform at specified index. + /// + /// Index value. + /// The weight value to set. + public void SetWeight(int index, float weight) + { + var weightedTransform = Get(index); + weightedTransform.weight = weight; + + Set(index, weightedTransform); + } + + /// + /// Retrieves a weight on a WeightedTransform at specified index. + /// + /// Index value. + /// The weight value at specified index. + public float GetWeight(int index) + { + return Get(index).weight; + } + + /// + /// Sets the Transform reference on a WeightedTransform at specified index. + /// + /// Index value. + /// The Transform reference to set. + public void SetTransform(int index, Transform transform) + { + var weightedTransform = Get(index); + weightedTransform.transform = transform; + + Set(index, weightedTransform); + } + + /// + /// Retrieves a Transform reference on a WeightedTransform at specified index. + /// + /// Index value. + /// The Transform reference at specified index. + public Transform GetTransform(int index) + { + return Get(index).transform; + } + + /// + /// Method to call from a provider's OnValidate() callback to ensure all weight values are within the valid range. + /// See also . + /// + /// WeightedTransformArray to adjust. + /// Minimum value to clamp array values with. + /// Maximum value to clamp array values with. + public static void OnValidate(ref WeightedTransformArray array, float min = 0f, float max = 1f) + { + for (var i = 0; i < k_MaxLength; ++i) + array.SetWeight(i, Mathf.Clamp(array.GetWeight(i), min, max)); + } + + /// + /// Gets or sets the element at the specified index. + /// + /// The zero-based index of the element to get or set. + object IList.this[int index] { get => (object)Get(index); set => Set(index, (WeightedTransform)value); } + + /// + /// Gets or sets the WeightedTransform at the specified index. + /// + /// The zero-based index of the WeightedTransform to get or set. + public WeightedTransform this[int index] { get => Get(index); set => Set(index, value); } + + /// The number of elements contained in the WeightedTransformArray. + public int Count { get => m_Length; } + + /// + /// Retrieves whether WeightedTransformArray is read-only. Always false. + /// + public bool IsReadOnly { get => false; } + + /// + /// Retrieves whether WeightedTransformArray has a fixed size. Always false. + /// + public bool IsFixedSize { get => false; } + + bool ICollection.IsSynchronized { get => true; } + + object ICollection.SyncRoot { get => null; } + + [System.Serializable] + private struct Enumerator : IEnumerator + { + private WeightedTransformArray m_Array; + private int m_Index; + + public Enumerator(ref WeightedTransformArray array) + { + m_Array = array; + m_Index = -1; + } + + public bool MoveNext() + { + m_Index++; + return (m_Index < m_Array.Count); + } + + public void Reset() + { + m_Index = -1; + } + + void IDisposable.Dispose() { } + + public WeightedTransform Current => m_Array.Get(m_Index); + + object IEnumerator.Current => Current; + } + } +} + diff --git a/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs.meta new file mode 100644 index 00000000..89e7e02a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AnimationRig/WeightedTransformArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80e58e006e9714edbbcbd43e30f6979d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs b/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs new file mode 100644 index 00000000..cd5db8f0 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Unity.Animation.Rigging.Editor")] +[assembly: InternalsVisibleTo("Unity.Animation.Rigging.Tests")] +[assembly: InternalsVisibleTo("Unity.Animation.Rigging.EditorTests")] diff --git a/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs.meta b/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs.meta new file mode 100644 index 00000000..ff445614 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9679337546d54a8b915dff03bf63dc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes.meta b/Packages/com.unity.animation.rigging/Runtime/Attributes.meta new file mode 100644 index 00000000..162764e7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 53534b516478036469d430efd7bbc497 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs b/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs new file mode 100644 index 00000000..6f0ed189 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs @@ -0,0 +1,4 @@ +namespace UnityEngine.Animations.Rigging +{ + sealed class ExpandChildrenAttribute : PropertyAttribute {} +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs.meta new file mode 100644 index 00000000..ea281492 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/ExpandChildrenAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8bce5d2428eb4d4aa4115d557baa49aa +timeCreated: 1607627626 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs b/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs new file mode 100644 index 00000000..96091e33 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs @@ -0,0 +1,70 @@ +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The [SyncSceneToStream] attribute can be used to ensure constraints properties are read from the scene + /// and written back in the AnimationStream if they were not previously animated. + /// Supported value types are: Float, Int, Bool, Vector2, Vector3, Vector4, Quaternion, Vector3Int, Vector3Bool, + /// Transform, Transform[], WeightedTransform and WeightedTransformArray. + /// + [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + public sealed class SyncSceneToStreamAttribute : Attribute { } + + internal enum PropertyType : byte { Bool, Int, Float }; + + internal struct PropertyDescriptor + { + public int size; + public PropertyType type; + } + + internal struct Property + { + public string name; + public PropertyDescriptor descriptor; + } + + internal struct RigProperties + { + public static string s_Weight = "m_Weight"; + public Component component; + } + + internal struct ConstraintProperties + { + public static string s_Weight = "m_Weight"; + public Component component; + public Property[] properties; + } + + /// + /// Utility functions for constraints (deprecated). + /// + [Obsolete("PropertyUtils is deprecated. Use ConstraintsUtils instead. (UnityUpgradable) -> ConstraintsUtils")] + public static class PropertyUtils + { + /// + /// Prepends RigConstraint data property to specified property name. + /// + /// Property name. + /// Return a complete property name. + [Obsolete("ConstructConstraintDataPropertyName is deprecated. Use ConstraintsUtils.ConstructConstraintDataPropertyName instead.")] + public static string ConstructConstraintDataPropertyName(string property) + { + return ConstraintsUtils.ConstructConstraintDataPropertyName(property); + } + + /// + /// Builds a unique property name for a custom property. + /// + /// Associated component. + /// Property name. + /// Returns a custom property name. + [Obsolete("ConstructCustomPropertyName is deprecated. Use ConstraintsUtils.ConstructCustomPropertyName instead.")] + public static string ConstructCustomPropertyName(Component component, string property) + { + return ConstraintsUtils.ConstructCustomPropertyName(component, property); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs.meta new file mode 100644 index 00000000..36102e48 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/SyncSceneToStreamAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6366b920d06ec1f4797e627265df1587 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs b/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs new file mode 100644 index 00000000..fc91e336 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs @@ -0,0 +1,34 @@ +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// By default, appears as a numeric input field in the Inspector. + /// Decorate or fields with this attribute to make it display using a slider with the specified range. + /// See also . + /// + [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + public sealed class WeightRangeAttribute : PropertyAttribute + { + /// + /// The smallest permissible value the weight may have. + /// + public readonly float min = 0f; + /// + /// The largest permissible value the weight may have. + /// + public readonly float max = 1f; + + /// + /// Constructs a new instance with the specified range. + /// A value of for either end of the range will permit any value to be entered. + /// + /// The smallest permissible value the weight may have. + /// The largest permissible value the weight may have. + public WeightRangeAttribute(float min, float max) + { + this.min = min; + this.max = max; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs.meta new file mode 100644 index 00000000..d895fdd7 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Attributes/WeightRangeAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5846e4298a804f4ab6b901fe69417ca7 +timeCreated: 1608217004 \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Runtime/Effectors.meta b/Packages/com.unity.animation.rigging/Runtime/Effectors.meta new file mode 100644 index 00000000..628a46fe --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Effectors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 292013508746644d4932640e9ed94af3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs b/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs new file mode 100644 index 00000000..a283eaae --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// This interface represents classes that hold and serialize effectors. + /// Effectors do nothing during runtime. + /// + public interface IRigEffectorHolder + { +#if UNITY_EDITOR + /// List of rig effectors associated with this IRigEffectorHolder. + IEnumerable effectors { get; } + + /// Adds a new effector to the IRigEffectorHolder. + /// The Transform represented by the effector. + /// The visual style of the effector. + void AddEffector(Transform transform, RigEffectorData.Style style); + /// Removes an effector from the IRigEffectorHolder. + /// The Transform from which to remove the effector. + void RemoveEffector(Transform transform); + /// Queries whether there is an effector for the specified Transform. + /// The Transform to query. + /// True if there is an effector for this transform. False otherwise. + bool ContainsEffector(Transform transform); +#endif + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs.meta new file mode 100644 index 00000000..945c325e --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Effectors/IRigEffectorHolder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79848f3b11e0243eaa4c599daee0d153 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs b/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs new file mode 100644 index 00000000..37358f10 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs @@ -0,0 +1,52 @@ +using System; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// This class holds the serialized data necessary to build + /// a rig effector. This is saved in either the RigBuilder, or the Rig component. + /// + [Serializable] + public class RigEffectorData + { + /// + /// The effector visual style. + /// + [Serializable] + public struct Style + { + /// The effector shape. This is represented by a Mesh. + public Mesh shape; + /// The effector main color. + public Color color; + /// The + public float size; + /// The position offset applied to the effector. + public Vector3 position; + /// The rotation offset applied to the effector. + public Vector3 rotation; + }; + + [SerializeField] private Transform m_Transform; + [SerializeField] private Style m_Style = new Style(); + [SerializeField] private bool m_Visible = true; + + /// The Transform represented by the effector. + public Transform transform { get => m_Transform; } + /// The visual style of the effector. + public Style style { get => m_Style; } + /// The visibility state of the effector. True if visible, false otherwise. + public bool visible { get => m_Visible; set => m_Visible = value; } + + /// + /// Initializes the effector with a Transform and a default style. + /// + /// The Transform represented by the effector. + /// The visual style of the effector. + public void Initialize(Transform transform, Style style) + { + m_Transform = transform; + m_Style = style; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs.meta new file mode 100644 index 00000000..16292cdb --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Effectors/RigEffectorData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 656f99ca017f841b1b1993eb44309b1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef b/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef new file mode 100644 index 00000000..8db728da --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef @@ -0,0 +1,12 @@ +{ + "name": "Unity.Animation.Rigging", + "references": ["Unity.Burst"], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef.meta b/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef.meta new file mode 100644 index 00000000..da32699f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Unity.Animation.Rigging.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7f7d1af65c2641843945d409d28f2e20 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils.meta b/Packages/com.unity.animation.rigging/Runtime/Utils.meta new file mode 100644 index 00000000..21fa94dd --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d12504fd35f9c844ea3366aaaa625093 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs new file mode 100644 index 00000000..fecd25ca --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs @@ -0,0 +1,114 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Rigid transform with only translation and rotation components. + /// + [System.Serializable] + public struct AffineTransform + { + /// Translation component of the AffineTransform. + public Vector3 translation; + /// Rotation component of the AffineTransform. + public Quaternion rotation; + + /// + /// Constructor. + /// + /// Translation component of the AffineTransform. + /// Rotation component of the AffineTransform. + public AffineTransform(Vector3 t, Quaternion r) + { + translation = t; + rotation = r; + } + + /// + /// Sets the translation and rotation in the AffineTransform. + /// + /// Translation component of the AffineTransform. + /// Rotation component of the AffineTransform. + public void Set(Vector3 t, Quaternion r) + { + translation = t; + rotation = r; + } + + /// + /// Transforms a Vector3 point by the AffineTransform. + /// + /// Vector3 point. + /// Transformed Vector3 point. + public Vector3 Transform(Vector3 p) => + rotation * p + translation; + + /// + /// Transforms a Vector3 point by the inverse of the AffineTransform. + /// + /// Vector3 point. + /// Transformed Vector3 point. + public Vector3 InverseTransform(Vector3 p) => + Quaternion.Inverse(rotation) * (p - translation); + + /// + /// Calculates the inverse of the AffineTransform. + /// + /// The inverse of the AffineTransform. + public AffineTransform Inverse() + { + var invR = Quaternion.Inverse(rotation); + return new AffineTransform(invR * -translation, invR); + } + + /// + /// Multiply a transform by the inverse of the AffineTransform. + /// + /// AffineTransform value. + /// Multiplied AffineTransform result. + public AffineTransform InverseMul(AffineTransform transform) + { + var invR = Quaternion.Inverse(rotation); + return new AffineTransform(invR * (transform.translation - translation), invR * transform.rotation); + } + + /// + /// Transforms a Vector3 point by the AffineTransform. + /// + /// AffineTransform value. + /// Vector3 point. + /// Transformed Vector3 point. + public static Vector3 operator *(AffineTransform lhs, Vector3 rhs) => + lhs.rotation * rhs + lhs.translation; + + /// + /// Multiplies two AffineTransform. + /// + /// First AffineTransform value. + /// Second AffineTransform value. + /// Multiplied AffineTransform result. + public static AffineTransform operator *(AffineTransform lhs, AffineTransform rhs) => + new AffineTransform(lhs.Transform(rhs.translation), lhs.rotation * rhs.rotation); + + /// + /// Rotates an AffineTransform. + /// + /// Quaternion rotation. + /// AffineTransform value. + /// Rotated AffineTransform result. + public static AffineTransform operator *(Quaternion lhs, AffineTransform rhs) => + new AffineTransform(lhs * rhs.translation, lhs * rhs.rotation); + + /// + /// Transforms a Quaternion value by the AffineTransform. + /// + /// AffineTransform value. + /// Quaternion rotation. + /// Transformed AffineTransform result. + public static AffineTransform operator *(AffineTransform lhs, Quaternion rhs) => + new AffineTransform(lhs.translation, lhs.rotation * rhs); + + /// + /// AffineTransform identity value. + /// + public static AffineTransform identity { get; } = new AffineTransform(Vector3.zero, Quaternion.identity); + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs.meta new file mode 100644 index 00000000..bab0719a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/AffineTransform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9baa6934219a6645a3f6830ee9960fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs new file mode 100644 index 00000000..6ea90f5a --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs @@ -0,0 +1,397 @@ +using Unity.Collections; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// Utility functions for runtime constraints. + /// + public static class AnimationRuntimeUtils + { + const float k_SqrEpsilon = 1e-8f; + + /// + /// Evaluates the Two-Bone IK algorithm. + /// + /// The animation stream to work on. + /// The transform handle for the root transform. + /// The transform handle for the mid transform. + /// The transform handle for the tip transform. + /// The transform handle for the target transform. + /// The transform handle for the hint transform. + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + /// The offset applied to the target transform. + public static void SolveTwoBoneIK( + AnimationStream stream, + ReadWriteTransformHandle root, + ReadWriteTransformHandle mid, + ReadWriteTransformHandle tip, + ReadOnlyTransformHandle target, + ReadOnlyTransformHandle hint, + float posWeight, + float rotWeight, + float hintWeight, + AffineTransform targetOffset + ) + { + Vector3 aPosition = root.GetPosition(stream); + Vector3 bPosition = mid.GetPosition(stream); + Vector3 cPosition = tip.GetPosition(stream); + target.GetGlobalTR(stream, out Vector3 targetPos, out Quaternion targetRot); + Vector3 tPosition = Vector3.Lerp(cPosition, targetPos + targetOffset.translation, posWeight); + Quaternion tRotation = Quaternion.Lerp(tip.GetRotation(stream), targetRot * targetOffset.rotation, rotWeight); + bool hasHint = hint.IsValid(stream) && hintWeight > 0f; + + Vector3 ab = bPosition - aPosition; + Vector3 bc = cPosition - bPosition; + Vector3 ac = cPosition - aPosition; + Vector3 at = tPosition - aPosition; + + float abLen = ab.magnitude; + float bcLen = bc.magnitude; + float acLen = ac.magnitude; + float atLen = at.magnitude; + + float oldAbcAngle = TriangleAngle(acLen, abLen, bcLen); + float newAbcAngle = TriangleAngle(atLen, abLen, bcLen); + + // Bend normal strategy is to take whatever has been provided in the animation + // stream to minimize configuration changes, however if this is collinear + // try computing a bend normal given the desired target position. + // If this also fails, try resolving axis using hint if provided. + Vector3 axis = Vector3.Cross(ab, bc); + if (axis.sqrMagnitude < k_SqrEpsilon) + { + axis = hasHint ? Vector3.Cross(hint.GetPosition(stream) - aPosition, bc) : Vector3.zero; + + if (axis.sqrMagnitude < k_SqrEpsilon) + axis = Vector3.Cross(at, bc); + + if (axis.sqrMagnitude < k_SqrEpsilon) + axis = Vector3.up; + } + axis = Vector3.Normalize(axis); + + float a = 0.5f * (oldAbcAngle - newAbcAngle); + float sin = Mathf.Sin(a); + float cos = Mathf.Cos(a); + Quaternion deltaR = new Quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos); + mid.SetRotation(stream, deltaR * mid.GetRotation(stream)); + + cPosition = tip.GetPosition(stream); + ac = cPosition - aPosition; + root.SetRotation(stream, QuaternionExt.FromToRotation(ac, at) * root.GetRotation(stream)); + + if (hasHint) + { + float acSqrMag = ac.sqrMagnitude; + if (acSqrMag > 0f) + { + bPosition = mid.GetPosition(stream); + cPosition = tip.GetPosition(stream); + ab = bPosition - aPosition; + ac = cPosition - aPosition; + + Vector3 acNorm = ac / Mathf.Sqrt(acSqrMag); + Vector3 ah = hint.GetPosition(stream) - aPosition; + Vector3 abProj = ab - acNorm * Vector3.Dot(ab, acNorm); + Vector3 ahProj = ah - acNorm * Vector3.Dot(ah, acNorm); + + float maxReach = abLen + bcLen; + if (abProj.sqrMagnitude > (maxReach * maxReach * 0.001f) && ahProj.sqrMagnitude > 0f) + { + Quaternion hintR = QuaternionExt.FromToRotation(abProj, ahProj); + hintR.x *= hintWeight; + hintR.y *= hintWeight; + hintR.z *= hintWeight; + hintR = QuaternionExt.NormalizeSafe(hintR); + root.SetRotation(stream, hintR * root.GetRotation(stream)); + } + } + } + + tip.SetRotation(stream, tRotation); + } + + /// + /// Sets the position for a hint and target given bone positions. + /// + /// The animation stream to work on. + /// The transform handle for the root transform. + /// The transform handle for the mid transform. + /// The transform handle for the tip transform. + /// The transform handle for the target transform. + /// The transform handle for the hint transform. + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + /// The offset applied to the target transform. + public static void InverseSolveTwoBoneIK( + AnimationStream stream, + ReadOnlyTransformHandle root, + ReadOnlyTransformHandle mid, + ReadOnlyTransformHandle tip, + ReadWriteTransformHandle target, + ReadWriteTransformHandle hint, + float posWeight, + float rotWeight, + float hintWeight, + AffineTransform targetOffset + ) + { + Vector3 rootPosition = root.GetPosition(stream); + Vector3 midPosition = mid.GetPosition(stream); + tip.GetGlobalTR(stream, out var tipPosition, out var tipRotation); + target.GetGlobalTR(stream, out var targetPosition, out var targetRotation); + bool isHintValid = hint.IsValid(stream); + Vector3 hintPosition = Vector3.zero; + if(isHintValid) + hintPosition = hint.GetPosition(stream); + + InverseSolveTwoBoneIK(rootPosition, midPosition, tipPosition, tipRotation, ref targetPosition, + ref targetRotation, ref hintPosition, isHintValid, posWeight, rotWeight, hintWeight, targetOffset); + + target.SetPosition(stream, targetPosition); + target.SetRotation(stream, targetRotation); + hint.SetPosition(stream, hintPosition); + } + + /// + /// Sets the position for a hint and target for given bone positions. + /// + /// The position of the root bone. + /// The position of the mid bone. + /// The position of the tip bone. + /// The rotation of the tip bone. + /// The position of the target. + /// The rotation of the target. + /// The position of the hint. + /// Whether the hint position should be set. + /// The weight for which target position has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which target rotation has an effect on IK calculations. This is a value in between 0 and 1. + /// The weight for which hint transform has an effect on IK calculations. This is a value in between 0 and 1. + /// The offset applied to the target transform. + public static void InverseSolveTwoBoneIK( + Vector3 rootPosition, + Vector3 midPosition, + Vector3 tipPosition, Quaternion tipRotation, + ref Vector3 targetPosition, ref Quaternion targetRotation, + ref Vector3 hintPosition, bool isHintValid, + float posWeight, + float rotWeight, + float hintWeight, + AffineTransform targetOffset + ) + { + targetPosition = (posWeight > 0f) ? tipPosition + targetOffset.translation : targetPosition; + targetRotation = (rotWeight > 0f) ? tipRotation * targetOffset.rotation : targetRotation; + + if (isHintValid) + { + var ac = tipPosition - rootPosition; + var ab = midPosition - rootPosition; + var bc = tipPosition - midPosition; + + float abLen = ab.magnitude; + float bcLen = bc.magnitude; + + var acSqrMag = Vector3.Dot(ac, ac); + var projectionPoint = rootPosition; + if (acSqrMag > k_SqrEpsilon) + projectionPoint += Vector3.Dot(ab / acSqrMag, ac) * ac; + var poleVectorDirection = midPosition - projectionPoint; + + var scale = abLen + bcLen; + hintPosition = (hintWeight > 0f) ? projectionPoint + (poleVectorDirection.normalized * scale) : hintPosition; + } + } + + static float TriangleAngle(float aLen, float aLen1, float aLen2) + { + float c = Mathf.Clamp((aLen1 * aLen1 + aLen2 * aLen2 - aLen * aLen) / (aLen1 * aLen2) / 2.0f, -1.0f, 1.0f); + return Mathf.Acos(c); + } + + /// + /// Evaluates the FABRIK ChainIK algorithm. + /// + /// Uninitialized buffer of positions. linkPositions and linkLengths must have the same size. + /// Array of distances in between positions. linkPositions and linkLenghts must have the same size. + /// Target position. + /// The maximum distance the resulting position and initial target are allowed to have in between them. + /// The maximum distance the Transform chain can reach. + /// The maximum number of iterations allowed for the ChainIK algorithm to converge to a solution. + /// Returns true if ChainIK calculations were successful. False otherwise. + /// + /// Implementation of unconstrained FABRIK solver : Forward and Backward Reaching Inverse Kinematic + /// Aristidou A, Lasenby J. FABRIK: a fast, iterative solver for the inverse kinematics problem. Graphical Models 2011; 73(5): 243–260. + /// + public static bool SolveFABRIK( + ref NativeArray linkPositions, + ref NativeArray linkLengths, + Vector3 target, + float tolerance, + float maxReach, + int maxIterations + ) + { + // If the target is unreachable + var rootToTargetDir = target - linkPositions[0]; + if (rootToTargetDir.sqrMagnitude > Square(maxReach)) + { + // Line up chain towards target + var dir = rootToTargetDir.normalized; + for (int i = 1; i < linkPositions.Length; ++i) + linkPositions[i] = linkPositions[i - 1] + dir * linkLengths[i - 1]; + + return true; + } + else + { + int tipIndex = linkPositions.Length - 1; + float sqrTolerance = Square(tolerance); + if (SqrDistance(linkPositions[tipIndex], target) > sqrTolerance) + { + var rootPos = linkPositions[0]; + int iteration = 0; + + do + { + // Forward reaching phase + // Set tip to target and propagate displacement to rest of chain + linkPositions[tipIndex] = target; + for (int i = tipIndex - 1; i > -1; --i) + linkPositions[i] = linkPositions[i + 1] + ((linkPositions[i] - linkPositions[i + 1]).normalized * linkLengths[i]); + + // Backward reaching phase + // Set root back at it's original position and propagate displacement to rest of chain + linkPositions[0] = rootPos; + for (int i = 1; i < linkPositions.Length; ++i) + linkPositions[i] = linkPositions[i - 1] + ((linkPositions[i] - linkPositions[i - 1]).normalized * linkLengths[i - 1]); + } + while ((SqrDistance(linkPositions[tipIndex], target) > sqrTolerance) && (++iteration < maxIterations)); + + return true; + } + } + + return false; + } + + /// + /// Returns the square length between two vectors. + /// + /// Vector3 value. + /// Vector3 value. + /// Square length between lhs and rhs. + public static float SqrDistance(Vector3 lhs, Vector3 rhs) + { + return (rhs - lhs).sqrMagnitude; + } + + /// + /// Returns the square value of a float. + /// + /// Float value. + /// Squared value. + public static float Square(float value) + { + return value * value; + } + + /// + /// Linearly interpolates between two vectors using a vector interpolant. + /// + /// Start Vector3 value. + /// End Vector3 value. + /// Interpolant Vector3 value. + /// Interpolated value. + public static Vector3 Lerp(Vector3 a, Vector3 b, Vector3 t) + { + return Vector3.Scale(a, Vector3.one - t) + Vector3.Scale(b, t); + } + + /// + /// Returns b if c is greater than zero, a otherwise. + /// + /// First float value. + /// Second float value. + /// Comparator float value. + /// Selected float value. + public static float Select(float a, float b, float c) + { + return (c > 0f) ? b : a; + } + + /// + /// Returns a componentwise selection between two vectors a and b based on a vector selection mask c. + /// Per component, the component from b is selected when c is greater than zero, otherwise the component from a is selected. + /// + /// First Vector3 value. + /// Second Vector3 value. + /// Comparator Vector3 value. + /// Selected Vector3 value. + public static Vector3 Select(Vector3 a, Vector3 b, Vector3 c) + { + return new Vector3(Select(a.x, b.x, c.x), Select(a.y, b.y, c.y), Select(a.z, b.z, c.z)); + } + + /// + /// Projects a vector onto a plane defined by a normal orthogonal to the plane. + /// + /// The location of the vector above the plane. + /// The direction from the vector towards the plane. + /// The location of the vector on the plane. + public static Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal) + { + float sqrMag = Vector3.Dot(planeNormal, planeNormal); + var dot = Vector3.Dot(vector, planeNormal); + return new Vector3(vector.x - planeNormal.x * dot / sqrMag, + vector.y - planeNormal.y * dot / sqrMag, + vector.z - planeNormal.z * dot / sqrMag); + } + + internal static float Sum(AnimationJobCache cache, CacheIndex index, int count) + { + if (count == 0) + return 0f; + + float sum = 0f; + for (int i = 0; i < count; ++i) + sum += cache.GetRaw(index, i); + + return sum; + } + + /// + /// Calculates the sum of all float elements in the array. + /// + /// An array of float elements. + /// Sum of all float elements. + public static float Sum(NativeArray floatBuffer) + { + if (floatBuffer.Length == 0) + return 0f; + + float sum = 0f; + for (int i = 0; i< floatBuffer.Length; ++i) + { + sum += floatBuffer[i]; + } + + return sum; + } + + /// + /// Copies translation, rotation and scale values from specified Transform handle to stream. + /// + /// The animation stream to work on. + /// The transform handle to copy. + public static void PassThrough(AnimationStream stream, ReadWriteTransformHandle handle) + { + handle.GetLocalTRS(stream, out Vector3 position, out Quaternion rotation, out Vector3 scale); + handle.SetLocalTRS(stream, position, rotation, scale); + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs.meta new file mode 100644 index 00000000..52be1611 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/AnimationRuntimeUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c57b76759571b24baf139b32a086d87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs new file mode 100644 index 00000000..42ec7d1f --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs @@ -0,0 +1,203 @@ +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// The BoneRenderer component is responsible for displaying pickable bones in the Scene View. + /// This component does nothing during runtime. + /// + [ExecuteInEditMode] + [AddComponentMenu("Animation Rigging/Setup/Bone Renderer")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/RiggingWorkflow.html#bone-renderer-component")] + public class BoneRenderer : MonoBehaviour + { + /// + /// Shape used by individual bones. + /// + public enum BoneShape + { + /// Bones are rendered with single lines. + Line, + + /// Bones are rendered with pyramid shapes. + Pyramid, + + /// Bones are rendered with box shapes. + Box + }; + + /// Shape of the bones. + public BoneShape boneShape = BoneShape.Pyramid; + + /// Toggles whether to render bone shapes or not. + public bool drawBones = true; + + /// Toggles whether to draw tripods on bones or not. + public bool drawTripods = false; + + /// Size of the bones. + [Range(0.01f, 5.0f)] public float boneSize = 1.0f; + + /// Size of the tripod axis. + [Range(0.01f, 5.0f)] public float tripodSize = 1.0f; + + /// Color of the bones. + public Color boneColor = new Color(0f, 0f, 1f, 0.5f); + + [SerializeField] private Transform[] m_Transforms; + + /// Transform references in the BoneRenderer hierarchy that are used to build bones. + public Transform[] transforms + { + get { return m_Transforms; } +#if UNITY_EDITOR + set + { + m_Transforms = value; + ExtractBones(); + } +#endif + } + +#if UNITY_EDITOR + /// + /// Bone described by two Transform references. + /// + public struct TransformPair + { + public Transform first; + public Transform second; + }; + + private TransformPair[] m_Bones; + private Transform[] m_Tips; + + /// Retrieves the bones isolated from the Transform references. + /// + public TransformPair[] bones + { + get => m_Bones; + } + + /// Retrieves the tip bones isolated from the Transform references. + /// + public Transform[] tips + { + get => m_Tips; + } + + /// + /// Delegate function that covers a BoneRenderer calling OnEnable. + /// + /// The BoneRenderer component + public delegate void OnAddBoneRendererCallback(BoneRenderer boneRenderer); + + /// + /// Delegate function that covers a BoneRenderer calling OnDisable. + /// + /// The BoneRenderer component + public delegate void OnRemoveBoneRendererCallback(BoneRenderer boneRenderer); + + /// + /// Notification callback that is sent whenever a BoneRenderer calls OnEnable. + /// + public static OnAddBoneRendererCallback onAddBoneRenderer; + + /// + /// Notification callback that is sent whenever a BoneRenderer calls OnDisable. + /// + public static OnRemoveBoneRendererCallback onRemoveBoneRenderer; + + void OnEnable() + { + ExtractBones(); + onAddBoneRenderer?.Invoke(this); + } + + void OnDisable() + { + onRemoveBoneRenderer?.Invoke(this); + } + + /// + /// Invalidate and Rebuild bones and tip bones from Transform references. + /// + public void Invalidate() + { + ExtractBones(); + } + + /// + /// Resets the BoneRenderer to default values. + /// + public void Reset() + { + ClearBones(); + } + + /// + /// Clears bones and tip bones. + /// + public void ClearBones() + { + m_Bones = null; + m_Tips = null; + } + + /// + /// Builds bones and tip bones from Transform references. + /// + public void ExtractBones() + { + if (m_Transforms == null || m_Transforms.Length == 0) + { + ClearBones(); + return; + } + + var transformsHashSet = new HashSet(m_Transforms); + + var bonesList = new List(m_Transforms.Length); + var tipsList = new List(m_Transforms.Length); + + for (int i = 0; i < m_Transforms.Length; ++i) + { + bool hasValidChildren = false; + + var transform = m_Transforms[i]; + if (transform == null) + continue; + + if (UnityEditor.SceneVisibilityManager.instance.IsHidden(transform.gameObject, false)) + continue; + + var mask = UnityEditor.Tools.visibleLayers; + if ((mask & (1 << transform.gameObject.layer)) == 0) + continue; + + if (transform.childCount > 0) + { + for (var k = 0; k < transform.childCount; ++k) + { + var childTransform = transform.GetChild(k); + + if (transformsHashSet.Contains(childTransform)) + { + bonesList.Add(new TransformPair() {first = transform, second = childTransform}); + hasValidChildren = true; + } + } + } + + if (!hasValidChildren) + { + tipsList.Add(transform); + } + } + + m_Bones = bonesList.ToArray(); + m_Tips = tipsList.ToArray(); + } +#endif // UNITY_EDITOR + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs.meta new file mode 100644 index 00000000..d27f188d --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/BoneRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2d8418b0b9634b1892b0268dd9c2743 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: ca41524ec86f0d84ab5675c9622cc1a2, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs new file mode 100644 index 00000000..51723af2 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; + +namespace UnityEngine.Animations.Rigging +{ + /// + /// Utility functions for constraints. + /// + public static class ConstraintsUtils + { + /// + /// Creates a list of Transforms all parented to one another in between two GameObjects. + /// + /// The root Transform. + /// The tip Transform. + /// + public static Transform[] ExtractChain(Transform root, Transform tip) + { + if (!tip.IsChildOf(root)) + return new Transform[0]{}; + + var chain = new List(); + + Transform tmp = tip; + while (tmp != root) + { + chain.Add(tmp); + tmp = tmp.parent; + } + chain.Add(root); + chain.Reverse(); + + return chain.ToArray(); + } + + /// + /// Calculates the distances in between every Transforms in the specified Transform chain. + /// + /// The Transform chain. + /// An array of distances. + public static float[] ExtractLengths(Transform[] chain) + { + float[] lengths = new float[chain.Length]; + lengths[0] = 0f; + + // Evaluate lengths as distance between each transform in the chain. + for (int i = 1; i < chain.Length; ++i) + { + lengths[i] = chain[i].localPosition.magnitude; + } + + return lengths; + } + + /// + /// Calculates the interpolant values for each Transform using distance as a measure + /// such that first Transform is at 0 and last Transform is at 1. + /// + /// The Transform chain. + /// An array of interpolants. + public static float[] ExtractSteps(Transform[] chain) + { + float[] lengths = ExtractLengths(chain); + + float totalLength = 0f; + Array.ForEach(lengths, (length) => totalLength += length); + + float[] steps = new float[lengths.Length]; + + // Evaluate weights and steps based on curve. + float cumulativeLength = 0.0f; + for (int i = 0; i < lengths.Length; ++i) + { + cumulativeLength += lengths[i]; + + float t = cumulativeLength / totalLength; + + steps[i] = t; + } + + return steps; + } + + /// + /// Prepends RigConstraint data property to specified property name. + /// + /// Property name. + /// Return a complete property name. + public static string ConstructConstraintDataPropertyName(string property) + { + return "m_Data." + property; + } + + /// + /// Builds a unique property name for a custom property. + /// + /// Associated component. + /// Property name. + /// Returns a custom property name. + public static string ConstructCustomPropertyName(Component component, string property) + { + return component.transform.GetInstanceID() + "/" + component.GetType() + "/" + property; + } + + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs.meta new file mode 100644 index 00000000..673ea846 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/ConstraintsUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff1d1a6b9dacf476fb3597334c297c7b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs new file mode 100644 index 00000000..124c620c --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs @@ -0,0 +1,77 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Supplementary functions for Quaternion. + /// + public static class QuaternionExt + { + const float k_FloatMin = 1e-10f; + + /// Zero quaternion. All quaternion channels are set to zero. + public static readonly Quaternion zero = new Quaternion(0f, 0f, 0f, 0f); + + /// + /// Calculates a Quaternion rotation of one vector to another. + /// + /// Starting vector. + /// Destination vector. + /// Quaternion rotation. + public static Quaternion FromToRotation(Vector3 from, Vector3 to) + { + float theta = Vector3.Dot(from.normalized, to.normalized); + if (theta >= 1f) + return Quaternion.identity; + + if (theta <= -1f) + { + Vector3 axis = Vector3.Cross(from, Vector3.right); + if (axis.sqrMagnitude == 0f) + axis = Vector3.Cross(from, Vector3.up); + + return Quaternion.AngleAxis(180f, axis); + } + + return Quaternion.AngleAxis(Mathf.Acos(theta) * Mathf.Rad2Deg, Vector3.Cross(from, to).normalized); + } + + /// + /// Adds two quaternion. + /// + /// Quaternion value. + /// Quaternion value. + /// Added Quaternion. + public static Quaternion Add(Quaternion rhs, Quaternion lhs) + { + float sign = Mathf.Sign(Quaternion.Dot(rhs, lhs)); + return new Quaternion(rhs.x + sign * lhs.x, rhs.y + sign * lhs.y, rhs.z + sign * lhs.z, rhs.w + sign * lhs.w); + } + + /// + /// Multiplies all Quaternion channels by a scale value. + /// + /// Quaternion value. + /// Scale value. + /// Scaled Quaternion. + public static Quaternion Scale(Quaternion q, float scale) + { + return new Quaternion(q.x * scale, q.y * scale, q.z * scale, q.w * scale); + } + + /// + /// Normalizes a Quaternion. Returns identity if normalized Quaternion is not finite. + /// + /// Quaternion value. + /// Normalized Quaternion. + public static Quaternion NormalizeSafe(Quaternion q) + { + float dot = Quaternion.Dot(q, q); + if (dot > k_FloatMin) + { + float rsqrt = 1.0f / Mathf.Sqrt(dot); + return new Quaternion(q.x * rsqrt, q.y * rsqrt, q.z * rsqrt, q.w * rsqrt); + } + + return Quaternion.identity; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs.meta new file mode 100644 index 00000000..c1bac5fe --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/QuaternionExt.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b62601624da18e54fb7fbc031f4ac8ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs b/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs new file mode 100644 index 00000000..29d516f8 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs @@ -0,0 +1,36 @@ +namespace UnityEngine.Animations.Rigging +{ + /// + /// Three-dimensional boolean vector. + /// + [System.Serializable] + public struct Vector3Bool + { + /// X component of the vector. + public bool x; + /// Y component of the vector. + public bool y; + /// Z component of the vector. + public bool z; + + /// + /// Constructor. + /// + /// Boolean value for x, y and z. + public Vector3Bool(bool val) + { + x = y = z = val; + } + + /// + /// Constructor. + /// + /// Boolean value for x. + /// Boolean value for y. + /// Boolean value for z. + public Vector3Bool(bool x, bool y, bool z) + { + this.x = x; this.y = y; this.z = z; + } + } +} diff --git a/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs.meta b/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs.meta new file mode 100644 index 00000000..75798e26 --- /dev/null +++ b/Packages/com.unity.animation.rigging/Runtime/Utils/Vector3Bool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 545e69336e8e62e47817399d2c47b673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.animation.rigging/package.json b/Packages/com.unity.animation.rigging/package.json new file mode 100644 index 00000000..1c3af70b --- /dev/null +++ b/Packages/com.unity.animation.rigging/package.json @@ -0,0 +1,41 @@ +{ + "name": "com.unity.animation.rigging", + "displayName": "Animation Rigging", + "version": "1.3.0", + "unity": "2022.2", + "unityRelease": "0a9", + "description": "Animation Rigging toolkit using Unity's Animation C# Jobs", + "keywords": [ + "Animation", + "Rigging", + "Constraints" + ], + "category": "Animation", + "dependencies": { + "com.unity.burst": "1.4.1", + "com.unity.test-framework": "1.1.24" + }, + "samples": [ + { + "displayName": "Animation Rigging Constraint Samples", + "description": "Import ConstraintSamples.unitypackage to see basic setup and usage of animation rigging constraints.", + "path": "Samples~/ConstraintSamples" + } + ], + "relatedPackages": { + "com.unity.animation.rigging.tests": "1.3.0" + }, + "_upm": { + "changelog": "- Added the `TransformHandle`'s function `GetLocalToParentMatrix` to get the matrix of an animation stream transform in local space.\n- Added the `TransformHandle`'s function `GetLocalToWorldMatrix` to get the matrix of an animation stream transform in world space.\n- Fixed handling negative scale in the `MultiAimConstraintJob` (case 1366549).\n- Fixed transforms in animator hierarchy, but not children of avatar root not resolving properly (case 1373387).\n- Fixed MultiAimConstraint evaluation with a world up axis (UM-1936).\n- Fixed crash when calling `RigBuilder.Build` by preventing rebuilding the PlayableGraph when in a preview context (case UUM-8599).\n- Fixed an issue where a misplaced `BoneHandles.shader` shader would cause the Scene View's Orientation Overlay to no longer render (case UUM-20874)." + }, + "upmCi": { + "footprint": "0dc36645a5d298886606134fa6733d8edda70840" + }, + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/index.html", + "repository": { + "url": "https://github.cds.internal.unity3d.com/unity/com.unity.animation.rigging.git", + "type": "git", + "revision": "56e7b7c84bac20a2a5885468b6849c14b3ef49f4" + }, + "_fingerprint": "68167b505d2b550a35f5d5b6daef6193de3c7460" +} diff --git a/Packages/com.unity.animation.rigging/package.json.meta b/Packages/com.unity.animation.rigging/package.json.meta new file mode 100644 index 00000000..d8982413 --- /dev/null +++ b/Packages/com.unity.animation.rigging/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f0b9721a4b1f95d4fada62c05f986049 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Editor/Deprecated.cs b/Packages/com.unity.render-pipelines.core/Editor/Deprecated.cs index cea5bdd6..e2431b6e 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Deprecated.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Deprecated.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; +using UnityEngine.UIElements; namespace UnityEditor.Rendering { @@ -182,7 +183,28 @@ namespace UnityEditor.Rendering } } + public abstract partial class DefaultVolumeProfileSettingsPropertyDrawer + { + /// + /// Context menu implementation for Default Volume Profile. + /// + /// Default Volume Profile Settings type + /// Render Pipeline type + [Obsolete("Use DefaultVolumeProfileSettingsPropertyDrawer.DefaultVolumeProfileSettingsContextMenu2 instead #from(6000.0)")] + public abstract class DefaultVolumeProfileSettingsContextMenu : IRenderPipelineGraphicsSettingsContextMenu + where TSetting : class, IDefaultVolumeProfileSettings + where TRenderPipeline : RenderPipeline + { + /// + /// Path where new Default Volume Profile will be created. + /// + [Obsolete("Not used anymore. #from(6000.0)")] + protected abstract string defaultVolumeProfilePath { get; } + [Obsolete("Not used anymore. #from(6000.0)")] + void IRenderPipelineGraphicsSettingsContextMenu.PopulateContextMenu(TSetting setting, PropertyDrawer property, ref GenericMenu menu){ } + } + } /// /// Builtin Drawer for Maskfield Debug Items. diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs index b126b6cf..45851c6f 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs @@ -1419,11 +1419,14 @@ namespace UnityEngine.Rendering if (m_BakingSet.hasDilation) { // This subsequent block needs to happen AFTER we call WriteBakingCells. - // Otherwise in cases where we change the spacing between probes, we end up loading cells with a certain layout in ForceSHBand + // Otherwise, in cases where we change the spacing between probes, we end up loading cells with a certain layout in ForceSHBand // And then we unload cells using the wrong layout in PerformDilation (after WriteBakingCells updates the baking set object) which leads to a broken internal state. // Don't use Disk streaming to avoid having to wait for it when doing dilation. probeRefVolume.ForceNoDiskStreaming(true); + // Increase the memory budget to make sure we can fit the current cell and all its neighbors when doing dilation. + var prevMemoryBudget = probeRefVolume.memoryBudget; + probeRefVolume.ForceMemoryBudget(ProbeVolumeTextureMemoryBudget.MemoryBudgetHigh); // Force maximum sh bands to perform baking, we need to store what sh bands was selected from the settings as we need to restore it after. var prevSHBands = probeRefVolume.shBands; probeRefVolume.ForceSHBand(ProbeVolumeSHBands.SphericalHarmonicsL2); @@ -1434,8 +1437,9 @@ namespace UnityEngine.Rendering using (new BakingCompleteProfiling(BakingCompleteProfiling.Stages.PerformDilation)) PerformDilation(); - // Need to restore the original state + // Restore the original state. probeRefVolume.ForceNoDiskStreaming(false); + probeRefVolume.ForceMemoryBudget(prevMemoryBudget); probeRefVolume.ForceSHBand(prevSHBands); } else diff --git a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.SidePanel.cs b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.SidePanel.cs index 8ae75375..bd77c36e 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.SidePanel.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.SidePanel.cs @@ -18,6 +18,14 @@ namespace UnityEditor.Rendering "Compute Pass" }; + static readonly string[] k_PassTypeNamesNotMergedMessage = + { + "This is a Legacy Render Pass. Only Raster Render Passes can be merged.", + "This is an Unsafe Render Pass. Only Raster Render Passes can be merged.", + "Pass merging was disabled.", + "This is a Compute Pass. Only Raster Render Passes can be merged." + }; + static partial class Names { public const string kPanelContainer = "panel-container"; @@ -370,8 +378,7 @@ namespace UnityEditor.Rendering else { CreateTextElement(passItem, "Pass break reasoning", Classes.kSubHeaderText); - var msg = $"This is a {k_PassTypeNames[(int) firstPassData.type]}. Only Raster Render Passes can be merged."; - msg = msg.Replace("a Unsafe", "an Unsafe"); + string msg = k_PassTypeNamesNotMergedMessage[(int)firstPassData.type]; CreateTextElement(passItem, msg); } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Settings/PropertyDrawers/DefaultVolumeProfileSettingsPropertyDrawer.cs b/Packages/com.unity.render-pipelines.core/Editor/Settings/PropertyDrawers/DefaultVolumeProfileSettingsPropertyDrawer.cs index b030fc73..bd307155 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Settings/PropertyDrawers/DefaultVolumeProfileSettingsPropertyDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Settings/PropertyDrawers/DefaultVolumeProfileSettingsPropertyDrawer.cs @@ -8,7 +8,7 @@ namespace UnityEditor.Rendering /// /// Base implementation for drawing Default Volume Profile UI in Graphics Settings. /// - public abstract class DefaultVolumeProfileSettingsPropertyDrawer : PropertyDrawer + public abstract partial class DefaultVolumeProfileSettingsPropertyDrawer : PropertyDrawer { // UUM-77758: Due to how PropertyDrawers are created and cached, there is no way to retrieve them reliably // later. We know that only one DefaultVolumeProfile exists at any given time, so we can access it through @@ -118,7 +118,7 @@ namespace UnityEditor.Rendering /// /// Default Volume Profile Settings type /// Render Pipeline type - public abstract class DefaultVolumeProfileSettingsContextMenu : IRenderPipelineGraphicsSettingsContextMenu + public abstract class DefaultVolumeProfileSettingsContextMenu2 : IRenderPipelineGraphicsSettingsContextMenu2 where TSetting : class, IDefaultVolumeProfileSettings where TRenderPipeline : RenderPipeline { @@ -127,7 +127,7 @@ namespace UnityEditor.Rendering /// protected abstract string defaultVolumeProfilePath { get; } - void IRenderPipelineGraphicsSettingsContextMenu.PopulateContextMenu(TSetting setting, PropertyDrawer _, ref GenericMenu menu) + void IRenderPipelineGraphicsSettingsContextMenu2.PopulateContextMenu(TSetting setting, SerializedProperty _, ref GenericMenu menu) { bool canCreateNewAsset = RenderPipelineManager.currentPipeline is TRenderPipeline; VolumeProfileUtils.AddVolumeProfileContextMenuItems(ref menu, diff --git a/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentEditor.cs b/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentEditor.cs index bb64826d..1792269b 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentEditor.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentEditor.cs @@ -279,13 +279,6 @@ namespace UnityEditor.Rendering var supportedOn = volumeComponentType.GetCustomAttribute(); m_LegacyPipelineTypes = supportedOn != null ? supportedOn.pipelineTypes : Array.Empty(); #pragma warning restore CS0618 - - EditorApplication.contextualPropertyMenu += OnPropertyContextMenu; - } - - void OnDestroy() - { - EditorApplication.contextualPropertyMenu -= OnPropertyContextMenu; } internal void DetermineVisibility(Type renderPipelineAssetType, Type renderPipelineType) @@ -393,23 +386,13 @@ namespace UnityEditor.Rendering profile != null && defaultProfile != profile) { + menu.AddSeparator(string.Empty); menu.AddItem(EditorGUIUtility.TrTextContent($"Show Default Volume Profile"), false, () => Selection.activeObject = defaultProfile); menu.AddItem(EditorGUIUtility.TrTextContent($"Apply Values to Default Volume Profile"), false, copyAction); } } - void OnPropertyContextMenu(GenericMenu menu, SerializedProperty property) - { - if (property.serializedObject.targetObject != target) - return; - - var targetComponent = property.serializedObject.targetObject as VolumeComponent; - - AddDefaultProfileContextMenuEntries(menu, VolumeManager.instance.globalDefaultProfile, - () => VolumeProfileUtils.AssignValuesToProfile(VolumeManager.instance.globalDefaultProfile, targetComponent, property)); - } - /// /// Unity calls this method after drawing the header for each VolumeComponentEditor /// diff --git a/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentListEditor.cs b/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentListEditor.cs index 47734b62..36eebd1b 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentListEditor.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Volume/VolumeComponentListEditor.cs @@ -445,13 +445,14 @@ namespace UnityEditor.Rendering if (!m_IsDefaultVolumeProfile) menu.AddItem(EditorGUIUtility.TrTextContent("Remove"), false, () => RemoveComponent(id)); - menu.AddSeparator(string.Empty); if (targetEditor.hasAdditionalProperties) + { + menu.AddSeparator(string.Empty); menu.AddAdvancedPropertiesBoolMenuItem(() => targetEditor.showAdditionalProperties, () => targetEditor.showAdditionalProperties ^= true); + } - menu.AddSeparator(string.Empty); targetEditor.AddDefaultProfileContextMenuEntries(menu, VolumeManager.instance.globalDefaultProfile, () => VolumeProfileUtils.CopyValuesToProfile(targetComponent, VolumeManager.instance.globalDefaultProfile)); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Common/CoreUnsafeUtils.cs b/Packages/com.unity.render-pipelines.core/Runtime/Common/CoreUnsafeUtils.cs index 0d39e81d..35dbc628 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Common/CoreUnsafeUtils.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Common/CoreUnsafeUtils.cs @@ -69,12 +69,18 @@ namespace UnityEngine.Rendering } /// - /// Pop an element of the queue. + /// Try to pop an element of the queue. /// /// Output result string. - /// True if an element was succesfuly poped. + /// True if an element was successfully popped. public bool TryPop(out string v) { + if (m_ReadCursor + sizeof(int) >= m_BufferEnd) + { + v = null; + return false; + } + var size = *(int*)m_ReadCursor; if (size != 0) { @@ -84,7 +90,7 @@ namespace UnityEngine.Rendering return true; } - v = default; + v = null; return false; } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Common/DynamicArray.cs b/Packages/com.unity.render-pipelines.core/Runtime/Common/DynamicArray.cs index 764d44b0..cfbdae31 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Common/DynamicArray.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Common/DynamicArray.cs @@ -685,8 +685,7 @@ namespace UnityEngine.Rendering } } - // C# SUCKS - // Had to copy paste because it's apparently impossible to pass a sort delegate where T is Comparable, otherwise some boxing happens and allocates... + // A copy/paste because it's apparently impossible to pass a sort delegate where T is Comparable, otherwise some boxing happens and allocates... // So two identical versions of the function, one with delegate but no Comparable and the other with just the comparable. static int Partition(Span data, int left, int right, DynamicArray.SortComparer comparer) where T : new() { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs index 1df80681..773befe1 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using Unity.Collections; +#if UNITY_EDITOR +using UnityEditor; +#endif using static UnityEngine.Rendering.DebugUI; using static UnityEngine.Rendering.DebugUI.Widget; @@ -106,6 +109,7 @@ namespace UnityEngine.Rendering else return new InstanceOcclusionEventStats(); } + static class Strings { public const string drawerSettingsContainerName = "GPU Resident Drawer Settings"; @@ -144,6 +148,7 @@ namespace UnityEngine.Rendering { return GPUResidentDrawer.GetDebugStats()?.instanceOcclusionEventStats.Length ?? 0; } + private static DebugUI.Table.Row AddInstanceCullerViewDataRow(int viewIndex) { return new DebugUI.Table.Row @@ -154,9 +159,32 @@ namespace UnityEngine.Rendering children = { new DebugUI.Value { displayName = "View Type", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).viewType }, - new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).viewInstanceID }, + new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + var viewStats = GetInstanceCullerViewStats(viewIndex); +#if UNITY_EDITOR + Object view = EditorUtility.InstanceIDToObject(viewStats.viewInstanceID); + if (view) + { + return $"{viewStats.viewInstanceID} ({view.name})"; + } +#endif + return viewStats.viewInstanceID; + } + }, new DebugUI.Value { displayName = "Split Index", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).splitIndex }, - new DebugUI.Value { displayName = "Visible Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).visibleInstances }, + new DebugUI.Value { displayName = "Visible Instances CPU | GPU", tooltip = "Visible instances after CPU culling and after GPU culling.", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + return $"{viewStats.visibleInstancesOnCPU} | {viewStats.visibleInstancesOnGPU}"; + } + }, + new DebugUI.Value { displayName = "Visible Primitives CPU | GPU", tooltip = "Visible primitives after CPU culling and after GPU culling.", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + return $"{viewStats.visiblePrimitivesOnCPU} | {viewStats.visiblePrimitivesOnGPU}"; + } + }, new DebugUI.Value { displayName = "Draw Commands", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).drawCommands }, } }; @@ -182,6 +210,16 @@ namespace UnityEngine.Rendering return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.culledInstances : "-"; } + private static object VisiblePrimitivesString(in InstanceOcclusionEventStats stats) + { + return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.visiblePrimitives : "-"; + } + + private static object CulledPrimitivesString(in InstanceOcclusionEventStats stats) + { + return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.culledPrimitives : "-"; + } + private static DebugUI.Table.Row AddInstanceOcclusionPassDataRow(int eventIndex) { return new DebugUI.Table.Row @@ -191,13 +229,27 @@ namespace UnityEngine.Rendering isHiddenCallback = () => { return eventIndex >= GetInstanceOcclusionEventCount(); }, children = { - new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceOcclusionEventStats(eventIndex).viewInstanceID }, + new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + var eventStats = GetInstanceOcclusionEventStats(eventIndex); +#if UNITY_EDITOR + Object view = EditorUtility.InstanceIDToObject(eventStats.viewInstanceID); + if (view) + { + return $"{eventStats.viewInstanceID} ({view.name})"; + } +#endif + return eventStats.viewInstanceID; + } + }, new DebugUI.Value { displayName = "Event Type", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"{GetInstanceOcclusionEventStats(eventIndex).eventType}" }, new DebugUI.Value { displayName = "Occluder Version", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => OccluderVersionString(GetInstanceOcclusionEventStats(eventIndex)) }, new DebugUI.Value { displayName = "Subview Mask", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"0x{GetInstanceOcclusionEventStats(eventIndex).subviewMask:X}" }, new DebugUI.Value { displayName = "Occlusion Test", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"{OcclusionTestString(GetInstanceOcclusionEventStats(eventIndex))}" }, new DebugUI.Value { displayName = "Visible Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => VisibleInstancesString(GetInstanceOcclusionEventStats(eventIndex)) }, new DebugUI.Value { displayName = "Culled Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => CulledInstancesString(GetInstanceOcclusionEventStats(eventIndex)) }, + new DebugUI.Value { displayName = "Visible Primitives", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => VisiblePrimitivesString(GetInstanceOcclusionEventStats(eventIndex)) }, + new DebugUI.Value { displayName = "Culled Primitives", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => CulledPrimitivesString(GetInstanceOcclusionEventStats(eventIndex)) }, } }; } @@ -298,6 +350,104 @@ namespace UnityEngine.Rendering } }); + instanceCullerStats.children.Add(new DebugUI.ValueTuple() + { + displayName = "Total Visible Instances (Cameras | Lights | Both)", + values = new[] + { + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDInstances = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType == BatchCullingViewType.Camera) + totalGRDInstances += viewStats.visibleInstancesOnGPU; + } + return totalGRDInstances; + } + }, + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDInstances = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType == BatchCullingViewType.Light) + totalGRDInstances += viewStats.visibleInstancesOnGPU; + } + return totalGRDInstances; + } + }, + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDInstances = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType != BatchCullingViewType.Filtering + && viewStats.viewType != BatchCullingViewType.Picking + && viewStats.viewType != BatchCullingViewType.SelectionOutline) + totalGRDInstances += viewStats.visibleInstancesOnGPU; + } + return totalGRDInstances; + } + }, + } + }); + + instanceCullerStats.children.Add(new DebugUI.ValueTuple() + { + displayName = "Total Visible Primitives (Cameras | Lights | Both)", + values = new[] + { + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDPrimitives = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType == BatchCullingViewType.Camera) + totalGRDPrimitives += viewStats.visiblePrimitivesOnGPU; + } + return totalGRDPrimitives; + } + }, + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDPrimitives = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType == BatchCullingViewType.Light) + totalGRDPrimitives += viewStats.visiblePrimitivesOnGPU; + } + return totalGRDPrimitives; + } + }, + new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => + { + int totalGRDPrimitives = 0; + + for (int viewIndex = 0; viewIndex < GetInstanceCullerViewCount(); viewIndex++) + { + var viewStats = GetInstanceCullerViewStats(viewIndex); + if (viewStats.viewType != BatchCullingViewType.Filtering + && viewStats.viewType != BatchCullingViewType.Picking + && viewStats.viewType != BatchCullingViewType.SelectionOutline) + totalGRDPrimitives += viewStats.visiblePrimitivesOnGPU; + } + return totalGRDPrimitives; + } + }, + } + }); + DebugUI.Table viewTable = new DebugUI.Table { displayName = "", diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs index dfc25d40..2a3c5227 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs @@ -196,12 +196,7 @@ namespace UnityEngine.Rendering Profiler.BeginSample("InstanceCullingBatcher.BuildBatch"); { - m_InstanceCullingBatcher.BuildBatch( - instances, - rendererData.materialID, - rendererData.meshID, - rendererData, true); - + m_InstanceCullingBatcher.BuildBatch(instances, rendererData, true); } Profiler.EndSample(); @@ -234,15 +229,11 @@ namespace UnityEngine.Rendering Profiler.BeginSample("InstanceCullingBatcher.BuildBatch"); { - m_InstanceCullingBatcher.BuildBatch( - instances.AsArray(), - rendererData.materialID, - rendererData.meshID, - rendererData, false); - instances.Dispose(); + m_InstanceCullingBatcher.BuildBatch(instances.AsArray(), rendererData, false); } Profiler.EndSample(); + instances.Dispose(); } Profiler.EndSample(); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs index aabf6728..b5fdf4ec 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs @@ -530,7 +530,7 @@ namespace UnityEngine.Rendering rendererIDs[i] = renderers[i] ? renderers[i].GetInstanceID() : 0; m_Batcher.UpdateSelectedRenderers(rendererIDs); - + rendererIDs.Dispose(); Profiler.EndSample(); @@ -567,7 +567,7 @@ namespace UnityEngine.Rendering var lodGroupTransformData = m_Dispatcher.GetTransformChangesAndClear(TransformTrackingType.GlobalTRS, Allocator.TempJob); var lodGroupData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); var meshDataSorted = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, sortByInstanceID: true, noScriptingArray: true); - var materialData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob); + var materialData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); var rendererData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); Profiler.EndSample(); @@ -597,8 +597,8 @@ namespace UnityEngine.Rendering ProcessRenderers(rendererData, unsupportedRenderers.AsArray()); Profiler.EndSample(); - Profiler.BeginSample("GPUResidentDrawer.ProcessRendererMaterialChanges"); - ProcessRendererMaterialChanges(rendererData.changedID, supportedChangedMaterials.AsArray(), supportedChangedPackedMaterialDatas.AsArray()); + Profiler.BeginSample("GPUResidentDrawer.ProcessRendererMaterialAndMeshChanges"); + ProcessRendererMaterialAndMeshChanges(rendererData.changedID, supportedChangedMaterials.AsArray(), supportedChangedPackedMaterialDatas.AsArray(), meshDataSorted.changedID); Profiler.EndSample(); lodGroupTransformData.Dispose(); @@ -620,7 +620,7 @@ namespace UnityEngine.Rendering UpdateSelection(); m_IsSelectionDirty = false; } - + m_FrameUpdateNeeded = false; #endif } @@ -655,9 +655,9 @@ namespace UnityEngine.Rendering m_BatchersContext.TransformLODGroups(transformedID); } - private void ProcessRendererMaterialChanges(NativeArray excludedRenderers, NativeArray changedMaterials, NativeArray changedPackedMaterialDatas) + private void ProcessRendererMaterialAndMeshChanges(NativeArray excludedRenderers, NativeArray changedMaterials, NativeArray changedPackedMaterialDatas, NativeArray changedMeshes) { - if (changedMaterials.Length == 0) + if (changedMaterials.Length == 0 && changedMeshes.Length == 0) return; Profiler.BeginSample("GPUResidentDrawer.GetMaterialsWithChangedPackedMaterial"); @@ -669,7 +669,7 @@ namespace UnityEngine.Rendering Profiler.EndSample(); - if (filteredMaterials.Count == 0) + if (filteredMaterials.Count == 0 && changedMeshes.Length == 0) { filteredMaterials.Dispose(); updatePackedMaterialCacheJob.Complete(); @@ -679,14 +679,14 @@ namespace UnityEngine.Rendering var sortedExcludedRenderers = new NativeArray(excludedRenderers, Allocator.TempJob); if (sortedExcludedRenderers.Length > 0) { - Profiler.BeginSample("ProcessRendererMaterialChanges.Sort"); + Profiler.BeginSample("ProcessRendererMaterialAndMeshChanges.Sort"); sortedExcludedRenderers.ParallelSort().Complete(); Profiler.EndSample(); } - Profiler.BeginSample("GPUResidentDrawer.FindRenderersFromMaterials"); + Profiler.BeginSample("GPUResidentDrawer.FindRenderersFromMaterialsOrMeshes"); - NativeList renderersWithChangedMaterials = FindRenderersFromMaterials(sortedExcludedRenderers, filteredMaterials, Allocator.TempJob); + var (renderersWithChangedMaterials, renderersWithChangedMeshes) = FindRenderersFromMaterialsOrMeshes(sortedExcludedRenderers, filteredMaterials, changedMeshes, Allocator.TempJob); filteredMaterials.Dispose(); Profiler.EndSample(); @@ -694,22 +694,35 @@ namespace UnityEngine.Rendering sortedExcludedRenderers.Dispose(); updatePackedMaterialCacheJob.Complete(); - if (renderersWithChangedMaterials.Length == 0) + if (renderersWithChangedMaterials.Length == 0 && renderersWithChangedMeshes.Length == 0) { renderersWithChangedMaterials.Dispose(); + renderersWithChangedMeshes.Dispose(); return; } Profiler.BeginSample("GPUResidentDrawer.UpdateRenderers"); { - var materialChangedInstances = new NativeArray(renderersWithChangedMaterials.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - ScheduleQueryRendererGroupInstancesJob(renderersWithChangedMaterials.AsArray(), materialChangedInstances).Complete(); + var changedMaterialsCount = renderersWithChangedMaterials.Length; + var changedMeshesCount = renderersWithChangedMeshes.Length; + var totalCount = changedMaterialsCount + changedMeshesCount; + + var changedInstances = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var changedRenderers = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + + NativeArray.Copy(renderersWithChangedMaterials.AsArray(), changedRenderers, changedMaterialsCount); + NativeArray.Copy(renderersWithChangedMeshes.AsArray(), changedRenderers.GetSubArray(changedMaterialsCount, changedMeshesCount), changedMeshesCount); + + ScheduleQueryRendererGroupInstancesJob(changedRenderers, changedInstances).Complete(); - m_Batcher.DestroyDrawInstances(materialChangedInstances); - materialChangedInstances.Dispose(); + m_Batcher.DestroyDrawInstances(changedInstances); m_Batcher.UpdateRenderers(renderersWithChangedMaterials.AsArray(), true); + m_Batcher.UpdateRenderers(renderersWithChangedMeshes.AsArray(), false); + changedInstances.Dispose(); + changedRenderers.Dispose(); renderersWithChangedMaterials.Dispose(); + renderersWithChangedMeshes.Dispose(); } Profiler.EndSample(); } @@ -814,14 +827,8 @@ namespace UnityEngine.Rendering if (materials.Length > 0) { - new ClassifyMaterialsJob - { - materialIDs = materials.AsReadOnly(), - batchMaterialHash = m_Batcher.instanceCullingBatcher.batchMaterialHash.AsReadOnly(), - unsupportedMaterialIDs = unsupportedMaterials, - supportedMaterialIDs = supportedMaterials, - supportedPackedMaterialDatas = supportedPackedMaterialDatas - }.Run(); + GPUResidentDrawerBurst.ClassifyMaterials(materials, m_Batcher.instanceCullingBatcher.batchMaterialHash.AsReadOnly(), + ref supportedMaterials, ref unsupportedMaterials, ref supportedPackedMaterialDatas); } } @@ -831,13 +838,8 @@ namespace UnityEngine.Rendering if (unsupportedMaterials.Length > 0) { - new FindUnsupportedRenderersJob - { - unsupportedMaterials = unsupportedMaterials.AsReadOnly(), - materialIDArrays = m_BatchersContext.sharedInstanceData.materialIDArrays, - rendererGroups = m_BatchersContext.sharedInstanceData.rendererGroupIDs, - unsupportedRenderers = unsupportedRenderers, - }.Run(); + GPUResidentDrawerBurst.FindUnsupportedRenderers(unsupportedMaterials, m_BatchersContext.sharedInstanceData.materialIDArrays, + m_BatchersContext.sharedInstanceData.rendererGroupIDs, ref unsupportedRenderers); } return unsupportedRenderers; @@ -847,125 +849,58 @@ namespace UnityEngine.Rendering { NativeHashSet filteredMaterials = new NativeHashSet(materials.Length, allocator); - new GetMaterialsWithChangedPackedMaterialJob - { - materialIDs = materials.AsReadOnly(), - packedMaterialDatas = packedMaterialDatas.AsReadOnly(), - packedMaterialHash = batcher.instanceCullingBatcher.packedMaterialHash.AsReadOnly(), - filteredMaterials = filteredMaterials - }.Run(); + GPUResidentDrawerBurst.GetMaterialsWithChangedPackedMaterial(materials, packedMaterialDatas, + batcher.instanceCullingBatcher.packedMaterialHash.AsReadOnly(), ref filteredMaterials); return filteredMaterials; } - private NativeList FindRenderersFromMaterials(NativeArray sortedExcludeRenderers, NativeHashSet materials, Allocator rendererListAllocator) + private (NativeList renderersWithMaterials, NativeList renderersWithMeshes) FindRenderersFromMaterialsOrMeshes(NativeArray sortedExcludeRenderers, NativeHashSet materials, NativeArray meshes, Allocator rendererListAllocator) { var sharedInstanceData = m_BatchersContext.sharedInstanceData; - NativeList renderers = new NativeList(sharedInstanceData.rendererGroupIDs.Length, rendererListAllocator); + NativeList renderersWithMaterials = new NativeList(sharedInstanceData.rendererGroupIDs.Length, rendererListAllocator); + NativeList renderersWithMeshes = new NativeList(sharedInstanceData.rendererGroupIDs.Length, rendererListAllocator); - var jobHandle = new FindRenderersFromMaterialJob + var jobHandle = new FindRenderersFromMaterialOrMeshJob { materialIDs = materials.AsReadOnly(), materialIDArrays = sharedInstanceData.materialIDArrays, + meshIDs = meshes.AsReadOnly(), + meshIDArray = sharedInstanceData.meshIDs, rendererGroupIDs = sharedInstanceData.rendererGroupIDs, sortedExcludeRendererIDs = sortedExcludeRenderers.AsReadOnly(), - selectedRenderGroups = renderers.AsParallelWriter(), - }.ScheduleBatch(sharedInstanceData.rendererGroupIDs.Length, FindRenderersFromMaterialJob.k_BatchSize); + selectedRenderGroupsForMaterials = renderersWithMaterials.AsParallelWriter(), + selectedRenderGroupsForMeshes = renderersWithMeshes.AsParallelWriter() + }.ScheduleBatch(sharedInstanceData.rendererGroupIDs.Length, FindRenderersFromMaterialOrMeshJob.k_BatchSize); jobHandle.Complete(); - return renderers; + return (renderersWithMaterials, renderersWithMeshes); } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct ClassifyMaterialsJob : IJob - { - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMaterialHash; - [ReadOnly] public NativeArray.ReadOnly materialIDs; - - public NativeList supportedMaterialIDs; - public NativeList unsupportedMaterialIDs; - public NativeList supportedPackedMaterialDatas; - - public void Execute() - { - var usedMaterialIDs = new NativeList(4, Allocator.TempJob); - - foreach (var materialID in materialIDs) - { - if (batchMaterialHash.ContainsKey(materialID)) - usedMaterialIDs.Add(materialID); - } - - if (usedMaterialIDs.IsEmpty) - { - usedMaterialIDs.Dispose(); - return; - } - - unsupportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - supportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - supportedPackedMaterialDatas.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - - int unsupportedMaterialCount = GPUDrivenProcessor.ClassifyMaterials(usedMaterialIDs.AsArray(), unsupportedMaterialIDs.AsArray(), supportedMaterialIDs.AsArray(), supportedPackedMaterialDatas.AsArray()); - - unsupportedMaterialIDs.Resize(unsupportedMaterialCount, NativeArrayOptions.ClearMemory); - supportedMaterialIDs.Resize(usedMaterialIDs.Length - unsupportedMaterialCount, NativeArrayOptions.ClearMemory); - supportedPackedMaterialDatas.Resize(supportedMaterialIDs.Length, NativeArrayOptions.ClearMemory); - - usedMaterialIDs.Dispose(); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct FindUnsupportedRenderersJob : IJob - { - [ReadOnly] public NativeArray.ReadOnly unsupportedMaterials; - [ReadOnly] public NativeArray.ReadOnly materialIDArrays; - [ReadOnly] public NativeArray.ReadOnly rendererGroups; - - public NativeList unsupportedRenderers; - - public unsafe void Execute() - { - if (unsupportedMaterials.Length == 0) - return; - - for (int arrayIndex = 0; arrayIndex < materialIDArrays.Length; arrayIndex++) - { - var materialIDs = materialIDArrays[arrayIndex]; - int rendererID = rendererGroups[arrayIndex]; - - for (int i = 0; i < materialIDs.Length; i++) - { - int materialID = materialIDs[i]; - - if (unsupportedMaterials.Contains(materialID)) - { - unsupportedRenderers.Add(rendererID); - break; - } - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct FindRenderersFromMaterialJob : IJobParallelForBatch + private unsafe struct FindRenderersFromMaterialOrMeshJob : IJobParallelForBatch { public const int k_BatchSize = 128; [ReadOnly] public NativeHashSet.ReadOnly materialIDs; [ReadOnly] public NativeArray.ReadOnly materialIDArrays; + [ReadOnly] public NativeArray.ReadOnly meshIDs; + [ReadOnly] public NativeArray.ReadOnly meshIDArray; [ReadOnly] public NativeArray.ReadOnly rendererGroupIDs; [ReadOnly] public NativeArray.ReadOnly sortedExcludeRendererIDs; - [WriteOnly] public NativeList.ParallelWriter selectedRenderGroups; + [WriteOnly] public NativeList.ParallelWriter selectedRenderGroupsForMaterials; + [WriteOnly] public NativeList.ParallelWriter selectedRenderGroupsForMeshes; public void Execute(int startIndex, int count) { - int* renderersToAddPtr = stackalloc int[k_BatchSize]; - var renderersToAdd = new UnsafeList(renderersToAddPtr, k_BatchSize); - renderersToAdd.Length = 0; + int* renderersToAddForMaterialsPtr = stackalloc int[k_BatchSize]; + var renderersToAddForMaterials = new UnsafeList(renderersToAddForMaterialsPtr, k_BatchSize); + renderersToAddForMaterials.Length = 0; + + int* renderersToAddForMeshesPtr = stackalloc int[k_BatchSize]; + var renderersToAddForMeshes = new UnsafeList(renderersToAddForMeshesPtr, k_BatchSize); + renderersToAddForMeshes.Length = 0; for (int index = 0; index < count; index++) { @@ -976,45 +911,33 @@ namespace UnityEngine.Rendering if (sortedExcludeRendererIDs.BinarySearch(rendererID) >= 0) continue; - var rendererMaterials = materialIDArrays[rendererIndex]; - - for (int materialIndex = 0; materialIndex < rendererMaterials.Length; materialIndex++) { - var materialID = rendererMaterials[materialIndex]; - if (materialIDs.Contains(materialID)) + var meshID = meshIDArray[rendererIndex]; + if (meshIDs.Contains(meshID)) { - renderersToAdd.AddNoResize(rendererID); - break; + renderersToAddForMeshes.AddNoResize(rendererID); + // We can skip the material check if we found a mesh match since at this point + // the renderer is already added and will be processed by the mesh branch + continue; } } - } - - selectedRenderGroups.AddRangeNoResize(renderersToAddPtr, renderersToAdd.Length); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct GetMaterialsWithChangedPackedMaterialJob : IJob - { - [ReadOnly] public NativeArray.ReadOnly materialIDs; - [ReadOnly] public NativeArray.ReadOnly packedMaterialDatas; - [ReadOnly] public NativeParallelHashMap.ReadOnly packedMaterialHash; - - [WriteOnly] public NativeHashSet filteredMaterials; - - public void Execute() - { - for (int index = 0; index < materialIDs.Length ; index++) - { - var materialID = materialIDs[index]; - var newPackedMaterialData = packedMaterialDatas[index]; - - // Has its packed material changed? If the material isn't in the packed material cache, consider the material has changed. - if (packedMaterialHash.TryGetValue(materialID, out var packedMaterial) && packedMaterial.Equals(newPackedMaterialData)) - continue; + { + var rendererMaterials = materialIDArrays[rendererIndex]; - filteredMaterials.Add(materialID); + for (int materialIndex = 0; materialIndex < rendererMaterials.Length; materialIndex++) + { + var materialID = rendererMaterials[materialIndex]; + if (materialIDs.Contains(materialID)) + { + renderersToAddForMaterials.AddNoResize(rendererID); + break; + } + } + } } + + selectedRenderGroupsForMaterials.AddRangeNoResize(renderersToAddForMaterialsPtr, renderersToAddForMaterials.Length); + selectedRenderGroupsForMeshes.AddRangeNoResize(renderersToAddForMeshesPtr, renderersToAddForMeshes.Length); } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs new file mode 100644 index 00000000..ddb0db8c --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs @@ -0,0 +1,80 @@ +using Unity.Collections; +using UnityEngine.Rendering; +using Unity.Burst; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class GPUResidentDrawerBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void ClassifyMaterials(in NativeArray materialIDs, in NativeParallelHashMap.ReadOnly batchMaterialHash, + ref NativeList supportedMaterialIDs, ref NativeList unsupportedMaterialIDs, ref NativeList supportedPackedMaterialDatas) + { + var usedMaterialIDs = new NativeList(4, Allocator.Temp); + + foreach (var materialID in materialIDs) + { + if (batchMaterialHash.ContainsKey(materialID)) + usedMaterialIDs.Add(materialID); + } + + if (usedMaterialIDs.IsEmpty) + { + usedMaterialIDs.Dispose(); + return; + } + + unsupportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); + supportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); + supportedPackedMaterialDatas.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); + + int unsupportedMaterialCount = GPUDrivenProcessor.ClassifyMaterials(usedMaterialIDs.AsArray(), unsupportedMaterialIDs.AsArray(), supportedMaterialIDs.AsArray(), supportedPackedMaterialDatas.AsArray()); + + unsupportedMaterialIDs.Resize(unsupportedMaterialCount, NativeArrayOptions.ClearMemory); + supportedMaterialIDs.Resize(usedMaterialIDs.Length - unsupportedMaterialCount, NativeArrayOptions.ClearMemory); + supportedPackedMaterialDatas.Resize(supportedMaterialIDs.Length, NativeArrayOptions.ClearMemory); + + usedMaterialIDs.Dispose(); + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void FindUnsupportedRenderers(in NativeArray unsupportedMaterials, in NativeArray.ReadOnly materialIDArrays, in NativeArray.ReadOnly rendererGroups, + ref NativeList unsupportedRenderers) + { + for (int arrayIndex = 0; arrayIndex < materialIDArrays.Length; arrayIndex++) + { + var materialIDs = materialIDArrays[arrayIndex]; + int rendererID = rendererGroups[arrayIndex]; + + for (int i = 0; i < materialIDs.Length; i++) + { + int materialID = materialIDs[i]; + + if (unsupportedMaterials.Contains(materialID)) + { + unsupportedRenderers.Add(rendererID); + break; + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void GetMaterialsWithChangedPackedMaterial(in NativeArray materialIDs, in NativeArray packedMaterialDatas, + in NativeParallelHashMap.ReadOnly packedMaterialHash, ref NativeHashSet filteredMaterials) + { + for (int index = 0; index < materialIDs.Length ; index++) + { + var materialID = materialIDs[index]; + var newPackedMaterialData = packedMaterialDatas[index]; + + // Has its packed material changed? If the material isn't in the packed material cache, consider the material has changed. + if (packedMaterialHash.TryGetValue(materialID, out var packedMaterial) && packedMaterial.Equals(newPackedMaterialData)) + continue; + + filteredMaterials.Add(materialID); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta new file mode 100644 index 00000000..ececc7dd --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2f50a3b1f0997d342837e27ab3b95e6f \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs index 15d05fb0..36102b9a 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs @@ -10,7 +10,10 @@ namespace UnityEngine.Rendering public BatchCullingViewType viewType; public int viewInstanceID; public int splitIndex; - public int visibleInstances; + public int visibleInstancesOnCPU; + public int visibleInstancesOnGPU; + public int visiblePrimitivesOnCPU; + public int visiblePrimitivesOnGPU; public int drawCommands; } @@ -29,6 +32,8 @@ namespace UnityEngine.Rendering public OcclusionTest occlusionTest; public int visibleInstances; public int culledInstances; + public int visiblePrimitives; + public int culledPrimitives; } internal struct DebugOccluderStats @@ -55,6 +60,48 @@ namespace UnityEngine.Rendering occluderStats = new NativeList(Allocator.Persistent); } + public void FinalizeInstanceCullerViewStats() + { + // For each view, update the on GPU instance and primitive counts. The final rendered primitive and + // instance count can be found at the last pass of all the occlusion passes. + for (int viewIndex = 0; viewIndex < instanceCullerStats.Length; viewIndex++) + { + InstanceCullerViewStats cullerStats = instanceCullerStats[viewIndex]; + InstanceOcclusionEventStats lastOcclusionEventStats = GetLastInstanceOcclusionEventStatsForView(viewIndex); + + if (lastOcclusionEventStats.viewInstanceID == cullerStats.viewInstanceID) + { + // The Min test is because the SelectionOutline view (and probably picking as well) share the same viewInstanceID with + // the scene camera for instance, so we pick up the camera's occlusion event. And we can't have more instances on GPU than we had on CPU. + cullerStats.visibleInstancesOnGPU = Math.Min(lastOcclusionEventStats.visibleInstances, cullerStats.visibleInstancesOnCPU); + cullerStats.visiblePrimitivesOnGPU = Math.Min(lastOcclusionEventStats.visiblePrimitives, cullerStats.visiblePrimitivesOnCPU); + } + else + { + // There was no occlusion culling for this view, so reuse the same counts as on the CPU. + cullerStats.visibleInstancesOnGPU = cullerStats.visibleInstancesOnCPU; + cullerStats.visiblePrimitivesOnGPU = cullerStats.visiblePrimitivesOnCPU; + } + + instanceCullerStats[viewIndex] = cullerStats; + } + } + + private InstanceOcclusionEventStats GetLastInstanceOcclusionEventStatsForView(int viewIndex) + { + if (viewIndex < instanceCullerStats.Length) + { + int viewInstanceID = instanceCullerStats[viewIndex].viewInstanceID; + for (int passIndex = instanceOcclusionEventStats.Length - 1; passIndex >= 0; passIndex--) + { + if (instanceOcclusionEventStats[passIndex].viewInstanceID == viewInstanceID) + return instanceOcclusionEventStats[passIndex]; + } + } + + return new InstanceOcclusionEventStats(); + } + public void Dispose() { if (instanceCullerStats.IsCreated) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs index a980a3e2..1c206649 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs @@ -429,7 +429,21 @@ namespace UnityEngine.Rendering return instanceData.localToWorldIsFlippedBits.Get(instanceIndex); } - unsafe public void Execute(int batchIndex) + + static int GetPrimitiveCount(int indexCount, MeshTopology topology, bool nativeQuads) + { + switch (topology) + { + case MeshTopology.Triangles: return indexCount / 3; + case MeshTopology.Quads: return nativeQuads ? (indexCount / 4) : (indexCount / 4 * 2); + case MeshTopology.Lines: return indexCount / 2; + case MeshTopology.LineStrip: return (indexCount >= 1) ? (indexCount - 1) : 0; + case MeshTopology.Points: return indexCount; + default: Debug.Assert(false, "unknown primitive type"); return 0; + } + } + + public void Execute(int batchIndex) { // figure out how many combinations of views/features we need to partition by int configCount = binningConfig.visibilityConfigCount; @@ -529,7 +543,12 @@ namespace UnityEngine.Rendering int visibleCount = visibleCountPerView[viewIndex]; if (visibleCount > 0) + { + int primitiveCount = GetPrimitiveCount((int)drawBatch.procInfo.indexCount, drawBatch.procInfo.topology, false); + Interlocked.Add(ref UnsafeUtility.AsRef(counterPtr + (int)InstanceCullerSplitDebugCounter.VisibleInstances), visibleCount); + Interlocked.Add(ref UnsafeUtility.AsRef(counterPtr + (int)InstanceCullerSplitDebugCounter.VisiblePrimitives), visibleCount * primitiveCount); + } } } } @@ -854,7 +873,7 @@ namespace UnityEngine.Rendering firstIndex = drawBatch.procInfo.firstIndex, baseVertex = drawBatch.procInfo.baseVertex, firstInstanceGlobalIndex = (uint)instanceInfoGlobalIndex, - maxInstanceCount = (uint)visibleInstanceCount, + maxInstanceCountAndTopology = ((uint)visibleInstanceCount << 3) | (uint)drawBatch.procInfo.topology, }; output.indirectDrawCommands[drawCommandOffset] = new BatchDrawCommandIndirect { @@ -1215,6 +1234,7 @@ namespace UnityEngine.Rendering internal enum InstanceCullerSplitDebugCounter { VisibleInstances, + VisiblePrimitives, DrawCommands, Count, } @@ -1293,7 +1313,10 @@ namespace UnityEngine.Rendering viewType = info.viewType, viewInstanceID = info.viewInstanceID, splitIndex = info.splitIndex, - visibleInstances = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.VisibleInstances], + visibleInstancesOnCPU = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.VisibleInstances], + visibleInstancesOnGPU = 0, // Unknown at this point, will be filled in later + visiblePrimitivesOnCPU = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.VisiblePrimitives], + visiblePrimitivesOnGPU = 0, // Unknown at this point, will be filled in later drawCommands = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.DrawCommands], }); } @@ -1442,8 +1465,10 @@ namespace UnityEngine.Rendering } int counterBase = index * (int)InstanceOcclusionTestDebugCounter.Count; - int occludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.Occluded]; - int notOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.NotOccluded]; + int instancesOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.InstancesOccluded]; + int instancesNotOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.InstancesNotOccluded]; + int primitivesOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.PrimitivesOccluded]; + int primitivesNotOccludedCounter = m_LatestCounters[counterBase + (int)InstanceOcclusionTestDebugCounter.PrimitivesNotOccluded]; debugStats.instanceOcclusionEventStats.Add(new InstanceOcclusionEventStats { @@ -1452,8 +1477,10 @@ namespace UnityEngine.Rendering occluderVersion = occluderVersion, subviewMask = info.subviewMask, occlusionTest = info.occlusionTest, - visibleInstances = notOccludedCounter, - culledInstances = occludedCounter, + visibleInstances = instancesNotOccludedCounter, + culledInstances = instancesOccludedCounter, + visiblePrimitives = primitivesNotOccludedCounter, + culledPrimitives = primitivesOccludedCounter, }); } } @@ -1531,25 +1558,6 @@ namespace UnityEngine.Rendering m_CommandBuffer.name = "EnsureValidOcclusionTestResults"; } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct SetupCullingJobInput : IJob - { - public float lodBias; - [NativeDisableUnsafePtrRestriction] public BatchCullingContext* context; - [NativeDisableUnsafePtrRestriction] public ReceiverPlanes* receiverPlanes; - [NativeDisableUnsafePtrRestriction] public ReceiverSphereCuller* receiverSphereCuller; - [NativeDisableUnsafePtrRestriction] public FrustumPlaneCuller* frustumPlaneCuller; - [NativeDisableUnsafePtrRestriction] public float* screenRelativeMetric; - - public void Execute() - { - *receiverPlanes = ReceiverPlanes.Create(*context, Allocator.TempJob); - *receiverSphereCuller = ReceiverSphereCuller.Create(*context, Allocator.TempJob); - *frustumPlaneCuller = FrustumPlaneCuller.Create(*context, receiverPlanes->planes.AsArray(), *receiverSphereCuller, Allocator.TempJob); - *screenRelativeMetric = LODGroupRenderingUtils.CalculateScreenRelativeMetric(context->lodParameters, lodBias); - } - } - private unsafe JobHandle CreateFrustumCullingJob( in BatchCullingContext cc, in CPUInstanceData.ReadOnly instanceData, @@ -1561,7 +1569,7 @@ namespace UnityEngine.Rendering NativeArray rendererVisibilityMasks, NativeArray rendererCrossFadeValues) { - Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCullingBatcher supports up to 6 culling splits."); + Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCuller supports up to 6 culling splits."); ReceiverPlanes receiverPlanes; ReceiverSphereCuller receiverSphereCuller; @@ -1570,16 +1578,8 @@ namespace UnityEngine.Rendering fixed (BatchCullingContext* contextPtr = &cc) { - new SetupCullingJobInput() - { - lodBias = QualitySettings.lodBias, - context = contextPtr, - frustumPlaneCuller = &frustumPlaneCuller, - receiverPlanes = &receiverPlanes, - receiverSphereCuller = &receiverSphereCuller, - screenRelativeMetric = &screenRelativeMetric, - - }.Run(); + InstanceCullerBurst.SetupCullingJobInput(QualitySettings.lodBias, contextPtr, &receiverPlanes, &receiverSphereCuller, + &frustumPlaneCuller, &screenRelativeMetric); } if (occlusionCullingCommon != null) @@ -2296,6 +2296,7 @@ namespace UnityEngine.Rendering { m_SplitDebugArray.MoveToDebugStatsAndClear(m_DebugStats); m_OcclusionEventDebugArray.MoveToDebugStatsAndClear(m_DebugStats); + m_DebugStats.FinalizeInstanceCullerViewStats(); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs new file mode 100644 index 00000000..d123762d --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs @@ -0,0 +1,19 @@ +using Unity.Collections; +using Unity.Burst; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class InstanceCullerBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void SetupCullingJobInput(float lodBias, BatchCullingContext* context, ReceiverPlanes* receiverPlanes, + ReceiverSphereCuller* receiverSphereCuller, FrustumPlaneCuller* frustumPlaneCuller, float* screenRelativeMetric) + { + *receiverPlanes = ReceiverPlanes.Create(*context, Allocator.TempJob); + *receiverSphereCuller = ReceiverSphereCuller.Create(*context, Allocator.TempJob); + *frustumPlaneCuller = FrustumPlaneCuller.Create(*context, receiverPlanes->planes.AsArray(), *receiverSphereCuller, Allocator.TempJob); + *screenRelativeMetric = LODGroupRenderingUtils.CalculateScreenRelativeMetric(context->lodParameters, lodBias); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta new file mode 100644 index 00000000..dc1b8acb --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 99f3de5decfa27b47a4ab725fc059f50 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs index ff4cd2a3..c2b526d2 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs @@ -8,11 +8,6 @@ using Unity.Collections.LowLevel.Unsafe; using Unity.Burst; using UnityEngine.Profiling; -[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.RegisterNewInstancesJob))] -[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.RegisterNewInstancesJob))] -[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstancesJob))] -[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstancesJob))] - namespace UnityEngine.Rendering { internal delegate void OnCullingCompleteCallback(JobHandle jobHandle, in BatchCullingContext cullingContext, in BatchCullingOutput cullingOutput); @@ -191,48 +186,109 @@ namespace UnityEngine.Rendering } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct FindNonRegisteredInstancesJob : IJobParallelForBatch where T : unmanaged + internal struct FindNonRegisteredMeshesJob : IJobParallelForBatch { public const int k_BatchSize = 128; [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeParallelHashMap hashMap; + [ReadOnly] public NativeParallelHashMap hashMap; [WriteOnly] public NativeList.ParallelWriter outInstancesWriter; public unsafe void Execute(int startIndex, int count) { - int* notFoundinstanceIDs = stackalloc int[k_BatchSize]; - int length = 0; + int* notFoundinstanceIDsPtr = stackalloc int[k_BatchSize]; + var notFoundinstanceIDs = new UnsafeList(notFoundinstanceIDsPtr, k_BatchSize); + + notFoundinstanceIDs.Length = 0; + + for (int i = startIndex; i < startIndex + count; ++i) + { + int instanceID = instanceIDs[i]; + + if (!hashMap.ContainsKey(instanceID)) + notFoundinstanceIDs.AddNoResize(instanceID); + } + + outInstancesWriter.AddRangeNoResize(notFoundinstanceIDsPtr, notFoundinstanceIDs.Length); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct FindNonRegisteredMaterialsJob : IJobParallelForBatch + { + public const int k_BatchSize = 128; + + [ReadOnly] public NativeArray instanceIDs; + [ReadOnly] public NativeArray packedMaterialDatas; + [ReadOnly] public NativeParallelHashMap hashMap; + + [WriteOnly] public NativeList.ParallelWriter outInstancesWriter; + [WriteOnly] public NativeList.ParallelWriter outPackedMaterialDatasWriter; + + public unsafe void Execute(int startIndex, int count) + { + int* notFoundinstanceIDsPtr = stackalloc int[k_BatchSize]; + var notFoundinstanceIDs = new UnsafeList(notFoundinstanceIDsPtr, k_BatchSize); + + GPUDrivenPackedMaterialData* notFoundPackedMaterialDatasPtr = stackalloc GPUDrivenPackedMaterialData[k_BatchSize]; + var notFoundPackedMaterialDatas = new UnsafeList(notFoundPackedMaterialDatasPtr, k_BatchSize); + + notFoundinstanceIDs.Length = 0; + notFoundPackedMaterialDatas.Length = 0; for (int i = startIndex; i < startIndex + count; ++i) { int instanceID = instanceIDs[i]; if (!hashMap.ContainsKey(instanceID)) - notFoundinstanceIDs[length++] = instanceID; + { + notFoundinstanceIDs.AddNoResize(instanceID); + notFoundPackedMaterialDatas.AddNoResize(packedMaterialDatas[i]); + } } - outInstancesWriter.AddRangeNoResize(notFoundinstanceIDs, length); + outInstancesWriter.AddRangeNoResize(notFoundinstanceIDsPtr, notFoundinstanceIDs.Length); + outPackedMaterialDatasWriter.AddRangeNoResize(notFoundPackedMaterialDatasPtr, notFoundPackedMaterialDatas.Length); } } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RegisterNewInstancesJob : IJobParallelFor where T : unmanaged + internal struct RegisterNewMeshesJob : IJobParallelFor { public const int k_BatchSize = 128; [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeArray batchIDs; + [ReadOnly] public NativeArray batchIDs; - [WriteOnly] public NativeParallelHashMap.ParallelWriter hashMap; + [WriteOnly] public NativeParallelHashMap.ParallelWriter hashMap; - public unsafe void Execute(int index) + public void Execute(int index) { hashMap.TryAdd(instanceIDs[index], batchIDs[index]); } } + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct RegisterNewMaterialsJob : IJobParallelFor + { + public const int k_BatchSize = 128; + + [ReadOnly] public NativeArray instanceIDs; + [ReadOnly] public NativeArray packedMaterialDatas; + [ReadOnly] public NativeArray batchIDs; + + [WriteOnly] public NativeParallelHashMap.ParallelWriter batchMaterialHashMap; + [WriteOnly] public NativeParallelHashMap.ParallelWriter packedMaterialHashMap; + + public void Execute(int index) + { + var instanceID = instanceIDs[index]; + batchMaterialHashMap.TryAdd(instanceID, batchIDs[index]); + packedMaterialHashMap.TryAdd(instanceID, packedMaterialDatas[index]); + } + } + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal struct RemoveDrawInstanceIndicesJob : IJob { @@ -328,245 +384,6 @@ namespace UnityEngine.Rendering } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct CreateDrawBatchesJob : IJob - { - [ReadOnly] public bool implicitInstanceIndices; - [ReadOnly] public NativeArray instances; - [ReadOnly] public GPUDrivenRendererGroupData rendererData; - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMeshHash; - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMaterialHash; - [ReadOnly] public NativeParallelHashMap.ReadOnly packedMaterialDataHash; - - public NativeParallelHashMap rangeHash; - public NativeList drawRanges; - public NativeParallelHashMap batchHash; - public NativeList drawBatches; - - [WriteOnly] public NativeList drawInstances; - - private ref DrawRange EditDrawRange(in RangeKey key) - { - int drawRangeIndex; - - if (!rangeHash.TryGetValue(key, out drawRangeIndex)) - { - var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 }; - drawRangeIndex = drawRanges.Length; - rangeHash.Add(key, drawRangeIndex); - drawRanges.Add(drawRange); - } - - ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex); - Assert.IsTrue(data.key.Equals(key)); - - return ref data; - } - - private ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor) - { - var procInfo = new MeshProceduralInfo(); - procInfo.topology = subMeshDescriptor.topology; - procInfo.baseVertex = (uint)subMeshDescriptor.baseVertex; - procInfo.firstIndex = (uint)subMeshDescriptor.indexStart; - procInfo.indexCount = (uint)subMeshDescriptor.indexCount; - - int drawBatchIndex; - - if (!batchHash.TryGetValue(key, out drawBatchIndex)) - { - var drawBatch = new DrawBatch() { key = key, instanceCount = 0, instanceOffset = 0, procInfo = procInfo }; - drawBatchIndex = drawBatches.Length; - batchHash.Add(key, drawBatchIndex); - drawBatches.Add(drawBatch); - } - - ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex); - Assert.IsTrue(data.key.Equals(key)); - - return ref data; - } - - public void ProcessRenderer(int i) - { - var meshIndex = rendererData.meshIndex[i]; - var meshID = rendererData.meshID[meshIndex]; - var submeshCount = rendererData.subMeshCount[meshIndex]; - var subMeshDescOffset = rendererData.subMeshDescOffset[meshIndex]; - var batchMeshID = batchMeshHash[meshID]; - var rendererGroupID = rendererData.rendererGroupID[i]; - var startSubMesh = rendererData.subMeshStartIndex[i]; - var gameObjectLayer = rendererData.gameObjectLayer[i]; - var renderingLayerMask = rendererData.renderingLayerMask[i]; - var materialsOffset = rendererData.materialsOffset[i]; - var materialsCount = rendererData.materialsCount[i]; - var lightmapIndex = rendererData.lightmapIndex[i]; - var packedRendererData = rendererData.packedRendererData[i]; - var rendererPriority = rendererData.rendererPriority[i]; - - int instanceCount; - int instanceOffset; - - if (implicitInstanceIndices) - { - instanceCount = 1; - instanceOffset = i; - } - else - { - instanceCount = rendererData.instancesCount[i]; - instanceOffset = rendererData.instancesOffset[i]; - } - - if (instanceCount == 0) - return; - - const int kLightmapIndexMask = 0xffff; - const int kLightmapIndexInfluenceOnly = 0xfffe; - - var overridenComponents = InstanceComponentGroup.Default; - - // Add per-instance wind parameters - if(packedRendererData.hasTree) - overridenComponents |= InstanceComponentGroup.Wind; - - var lmIndexMasked = lightmapIndex & kLightmapIndexMask; - - // Object doesn't have a valid lightmap Index, -> uses probes for lighting - if (lmIndexMasked >= kLightmapIndexInfluenceOnly) - { - // Only add the component when needed to store blended results (shader will use the ambient probe when not present) - if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) - overridenComponents |= InstanceComponentGroup.LightProbe; - } - else - { - // Add per-instance lightmap parameters - overridenComponents |= InstanceComponentGroup.Lightmap; - } - - // Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey). - Span packedMaterialDatas = stackalloc GPUDrivenPackedMaterialData[materialsCount]; - - var supportsIndirect = true; - for (int matIndex = 0; matIndex < materialsCount; ++matIndex) - { - if (matIndex >= submeshCount) - { - Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); - continue; - } - - var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; - GPUDrivenPackedMaterialData packedMaterialData; - - if (rendererData.packedMaterialData.Length > 0) - { - packedMaterialData = rendererData.packedMaterialData[materialIndex]; - } - else - { - var materialID = rendererData.materialID[materialIndex]; - bool isFound = packedMaterialDataHash.TryGetValue(materialID, out packedMaterialData); - Assert.IsTrue(isFound); - } - supportsIndirect &= packedMaterialData.isIndirectSupported; - - packedMaterialDatas[matIndex] = packedMaterialData; - } - - var rangeKey = new RangeKey - { - layer = (byte)gameObjectLayer, - renderingLayerMask = renderingLayerMask, - motionMode = packedRendererData.motionVecGenMode, - shadowCastingMode = packedRendererData.shadowCastingMode, - staticShadowCaster = packedRendererData.staticShadowCaster, - rendererPriority = rendererPriority, - supportsIndirect = supportsIndirect - }; - - ref DrawRange drawRange = ref EditDrawRange(rangeKey); - - for (int matIndex = 0; matIndex < materialsCount; ++matIndex) - { - if (matIndex >= submeshCount) - { - Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); - continue; - } - - var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; - var materialID = rendererData.materialID[materialIndex]; - var packedMaterialData = packedMaterialDatas[matIndex]; - - if (materialID == 0) - { - Debug.LogWarning("Material in the shared materials list is null. Object will be partially rendered."); - continue; - } - - batchMaterialHash.TryGetValue(materialID, out BatchMaterialID batchMaterialID); - - // We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch. - var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked; - - // Let the engine know if we've opted out of lightmap texture arrays - flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword; - - // assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors - // (otherwise this flag is set dynamically during culling only when the transform is changing) - if (packedMaterialData.isMotionVectorsPassEnabled) - flags |= BatchDrawCommandFlags.HasMotion; - - if (packedMaterialData.isTransparent) - flags |= BatchDrawCommandFlags.HasSortingPosition; - - { - var submeshIndex = startSubMesh + matIndex; - var subMeshDesc = rendererData.subMeshDesc[subMeshDescOffset + submeshIndex]; - - var drawKey = new DrawKey - { - materialID = batchMaterialID, - meshID = batchMeshID, - submeshIndex = submeshIndex, - flags = flags, - transparentInstanceId = packedMaterialData.isTransparent ? rendererGroupID : 0, - range = rangeKey, - overridenComponents = (uint)overridenComponents, - // When we've opted out of lightmap texture arrays, we - // need to pass in a valid lightmap index. The engine - // uses this index for sorting and for breaking the - // batch when lightmaps change across draw calls, and - // for binding the correct light map. - lightmapIndex = lightmapIndex - }; - - ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMeshDesc); - - if (drawBatch.instanceCount == 0) - ++drawRange.drawCount; - - drawBatch.instanceCount += instanceCount; - - for (int j = 0; j < instanceCount; ++j) - { - var instanceIndex = instanceOffset + j; - InstanceHandle instance = instances[instanceIndex]; - drawInstances.Add(new DrawInstance { key = drawKey, instanceIndex = instance.index }); - } - } - } - } - - public void Execute() - { - for (int i = 0; i < rendererData.rendererGroupID.Length; ++i) - ProcessRenderer(i); - } - } - internal class CPUDrawInstanceData { public NativeList drawInstances => m_DrawInstances; @@ -664,23 +481,16 @@ namespace UnityEngine.Rendering internalDrawIndex.Dispose(); } - public unsafe void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) + public void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) { Profiler.BeginSample("DestroyDrawInstanceIndices.ParallelSort"); drawInstanceIndicesToDestroy.ParallelSort().Complete(); Profiler.EndSample(); - var removeDrawInstanceIndicesJob = new RemoveDrawInstanceIndicesJob - { - drawInstanceIndices = drawInstanceIndicesToDestroy, - drawInstances = m_DrawInstances, - drawBatches = m_DrawBatches, - drawRanges = m_DrawRanges, - batchHash = m_BatchHash, - rangeHash = m_RangeHash - }; - - removeDrawInstanceIndicesJob.Run(); + Profiler.BeginSample("DestroyDrawInstanceIndices.RemoveDrawInstanceIndices"); + InstanceCullingBatcherBurst.RemoveDrawInstanceIndices(drawInstanceIndicesToDestroy, ref m_DrawInstances, ref m_RangeHash, + ref m_BatchHash, ref m_DrawRanges, ref m_DrawBatches); + Profiler.EndSample(); } public unsafe void DestroyDrawInstances(NativeArray destroyedInstances) @@ -1011,41 +821,45 @@ namespace UnityEngine.Rendering private void RegisterBatchMeshes(NativeArray meshIDs) { var newMeshIDs = new NativeList(meshIDs.Length, Allocator.TempJob); - new FindNonRegisteredInstancesJob + new FindNonRegisteredMeshesJob { instanceIDs = meshIDs, hashMap = m_BatchMeshHash, outInstancesWriter = newMeshIDs.AsParallelWriter() } - .ScheduleBatch(meshIDs.Length, FindNonRegisteredInstancesJob.k_BatchSize).Complete(); + .ScheduleBatch(meshIDs.Length, FindNonRegisteredMeshesJob.k_BatchSize).Complete(); var newBatchMeshIDs = new NativeArray(newMeshIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); m_BRG.RegisterMeshes(newMeshIDs.AsArray(), newBatchMeshIDs); int totalMeshesNum = m_BatchMeshHash.Count() + newBatchMeshIDs.Length; m_BatchMeshHash.Capacity = Math.Max(m_BatchMeshHash.Capacity, Mathf.CeilToInt(totalMeshesNum / 1023.0f) * 1024); - new RegisterNewInstancesJob + new RegisterNewMeshesJob { instanceIDs = newMeshIDs.AsArray(), batchIDs = newBatchMeshIDs, hashMap = m_BatchMeshHash.AsParallelWriter() } - .Schedule(newMeshIDs.Length, RegisterNewInstancesJob.k_BatchSize).Complete(); + .Schedule(newMeshIDs.Length, RegisterNewMeshesJob.k_BatchSize).Complete(); newMeshIDs.Dispose(); newBatchMeshIDs.Dispose(); } - private void RegisterBatchMaterials(in NativeArray usedMaterialIDs) + private void RegisterBatchMaterials(in NativeArray usedMaterialIDs, in NativeArray usedPackedMaterialDatas) { + Debug.Assert(usedMaterialIDs.Length == usedPackedMaterialDatas.Length, "Each material ID should correspond to one packed material data."); var newMaterialIDs = new NativeList(usedMaterialIDs.Length, Allocator.TempJob); - new FindNonRegisteredInstancesJob + var newPackedMaterialDatas = new NativeList(usedMaterialIDs.Length, Allocator.TempJob); + new FindNonRegisteredMaterialsJob { instanceIDs = usedMaterialIDs, + packedMaterialDatas = usedPackedMaterialDatas, hashMap = m_BatchMaterialHash, - outInstancesWriter = newMaterialIDs.AsParallelWriter() + outInstancesWriter = newMaterialIDs.AsParallelWriter(), + outPackedMaterialDatasWriter = newPackedMaterialDatas.AsParallelWriter() } - .ScheduleBatch(usedMaterialIDs.Length, FindNonRegisteredInstancesJob.k_BatchSize).Complete(); + .ScheduleBatch(usedMaterialIDs.Length, FindNonRegisteredMaterialsJob.k_BatchSize).Complete(); var newBatchMaterialIDs = new NativeArray(newMaterialIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); m_BRG.RegisterMaterials(newMaterialIDs.AsArray(), newBatchMaterialIDs); @@ -1054,15 +868,18 @@ namespace UnityEngine.Rendering m_BatchMaterialHash.Capacity = Math.Max(m_BatchMaterialHash.Capacity, Mathf.CeilToInt(totalMaterialsNum / 1023.0f) * 1024); m_PackedMaterialHash.Capacity = m_BatchMaterialHash.Capacity; - new RegisterNewInstancesJob + new RegisterNewMaterialsJob { instanceIDs = newMaterialIDs.AsArray(), + packedMaterialDatas = newPackedMaterialDatas.AsArray(), batchIDs = newBatchMaterialIDs, - hashMap = m_BatchMaterialHash.AsParallelWriter() + batchMaterialHashMap = m_BatchMaterialHash.AsParallelWriter(), + packedMaterialHashMap = m_PackedMaterialHash.AsParallelWriter() } - .Schedule(newMaterialIDs.Length, RegisterNewInstancesJob.k_BatchSize).Complete(); + .Schedule(newMaterialIDs.Length, RegisterNewMaterialsJob.k_BatchSize).Complete(); newMaterialIDs.Dispose(); + newPackedMaterialDatas.Dispose(); newBatchMaterialIDs.Dispose(); } @@ -1078,31 +895,23 @@ namespace UnityEngine.Rendering public void BuildBatch( NativeArray instances, - NativeArray usedMaterialIDs, - NativeArray usedMeshIDs, in GPUDrivenRendererGroupData rendererData, bool registerMaterialsAndMeshes) { if (registerMaterialsAndMeshes) { - RegisterBatchMaterials(usedMaterialIDs); - RegisterBatchMeshes(usedMeshIDs); + RegisterBatchMaterials(rendererData.materialID, rendererData.packedMaterialData); + RegisterBatchMeshes(rendererData.meshID); } - new CreateDrawBatchesJob - { - implicitInstanceIndices = rendererData.instancesCount.Length == 0, - instances = instances, - rendererData = rendererData, - batchMeshHash = m_BatchMeshHash.AsReadOnly(), - batchMaterialHash = m_BatchMaterialHash.AsReadOnly(), - packedMaterialDataHash = m_PackedMaterialHash.AsReadOnly(), - rangeHash = m_DrawInstanceData.rangeHash, - drawRanges = m_DrawInstanceData.drawRanges, - batchHash = m_DrawInstanceData.batchHash, - drawBatches = m_DrawInstanceData.drawBatches, - drawInstances = m_DrawInstanceData.drawInstances - }.Run(); + var rangeHash = m_DrawInstanceData.rangeHash; + var drawRanges = m_DrawInstanceData.drawRanges; + var batchHash = m_DrawInstanceData.batchHash; + var drawBatches = m_DrawInstanceData.drawBatches; + var drawInstances = m_DrawInstanceData.drawInstances; + + InstanceCullingBatcherBurst.CreateDrawBatches(rendererData.instancesCount.Length == 0, instances, rendererData, + m_BatchMeshHash, m_BatchMaterialHash, m_PackedMaterialHash, ref rangeHash, ref drawRanges, ref batchHash, ref drawBatches, ref drawInstances); m_DrawInstanceData.NeedsRebuild(); UpdateInstanceDataBufferLayoutVersion(); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs new file mode 100644 index 00000000..b3e353d6 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs @@ -0,0 +1,301 @@ +using System; +using Unity.Collections; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class InstanceCullingBatcherBurst + { + private static void RemoveDrawRange(in RangeKey key, ref NativeParallelHashMap rangeHash, ref NativeList drawRanges) + { + int drawRangeIndex = rangeHash[key]; + + ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1); + rangeHash[lastDrawRange.key] = drawRangeIndex; + + rangeHash.Remove(key); + drawRanges.RemoveAtSwapBack(drawRangeIndex); + } + + private static void RemoveDrawBatch(in DrawKey key, ref NativeList drawRanges, ref NativeParallelHashMap rangeHash, + ref NativeParallelHashMap batchHash, ref NativeList drawBatches) + { + int drawBatchIndex = batchHash[key]; + + int drawRangeIndex = rangeHash[key.range]; + ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); + + Assert.IsTrue(drawRange.drawCount > 0); + + if (--drawRange.drawCount == 0) + RemoveDrawRange(drawRange.key, ref rangeHash, ref drawRanges); + + ref DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1); + batchHash[lastDrawBatch.key] = drawBatchIndex; + + batchHash.Remove(key); + drawBatches.RemoveAtSwapBack(drawBatchIndex); + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void RemoveDrawInstanceIndices(in NativeArray drawInstanceIndices, ref NativeList drawInstances, ref NativeParallelHashMap rangeHash, + ref NativeParallelHashMap batchHash, ref NativeList drawRanges, ref NativeList drawBatches) + { + var drawInstancesPtr = (DrawInstance*)drawInstances.GetUnsafePtr(); + var drawInstancesNewBack = drawInstances.Length - 1; + + for (int indexRev = drawInstanceIndices.Length - 1; indexRev >= 0; --indexRev) + { + int indexToRemove = drawInstanceIndices[indexRev]; + DrawInstance* drawInstance = drawInstancesPtr + indexToRemove; + + int drawBatchIndex = batchHash[drawInstance->key]; + ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); + + Assert.IsTrue(drawBatch.instanceCount > 0); + + if (--drawBatch.instanceCount == 0) + RemoveDrawBatch(drawBatch.key, ref drawRanges, ref rangeHash, ref batchHash, ref drawBatches); + + UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance)); + } + + drawInstances.ResizeUninitialized(drawInstancesNewBack + 1); + } + + private static ref DrawRange EditDrawRange(in RangeKey key, NativeParallelHashMap rangeHash, NativeList drawRanges) + { + int drawRangeIndex; + + if (!rangeHash.TryGetValue(key, out drawRangeIndex)) + { + var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 }; + drawRangeIndex = drawRanges.Length; + rangeHash.Add(key, drawRangeIndex); + drawRanges.Add(drawRange); + } + + ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex); + Assert.IsTrue(data.key.Equals(key)); + + return ref data; + } + + private static ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor, NativeParallelHashMap batchHash, NativeList drawBatches) + { + var procInfo = new MeshProceduralInfo(); + procInfo.topology = subMeshDescriptor.topology; + procInfo.baseVertex = (uint)subMeshDescriptor.baseVertex; + procInfo.firstIndex = (uint)subMeshDescriptor.indexStart; + procInfo.indexCount = (uint)subMeshDescriptor.indexCount; + + int drawBatchIndex; + + if (!batchHash.TryGetValue(key, out drawBatchIndex)) + { + var drawBatch = new DrawBatch() { key = key, instanceCount = 0, instanceOffset = 0, procInfo = procInfo }; + drawBatchIndex = drawBatches.Length; + batchHash.Add(key, drawBatchIndex); + drawBatches.Add(drawBatch); + } + + ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex); + Assert.IsTrue(data.key.Equals(key)); + + return ref data; + } + + private static void ProcessRenderer(int i, bool implicitInstanceIndices, in GPUDrivenRendererGroupData rendererData, + NativeParallelHashMap batchMeshHash, NativeParallelHashMap packedMaterialDataHash, + NativeParallelHashMap batchMaterialHash, NativeArray instances, NativeList drawInstances, + NativeParallelHashMap rangeHash, NativeList drawRanges, NativeParallelHashMap batchHash, + NativeList drawBatches) + { + var meshIndex = rendererData.meshIndex[i]; + var meshID = rendererData.meshID[meshIndex]; + var submeshCount = rendererData.subMeshCount[meshIndex]; + var subMeshDescOffset = rendererData.subMeshDescOffset[meshIndex]; + var batchMeshID = batchMeshHash[meshID]; + var rendererGroupID = rendererData.rendererGroupID[i]; + var startSubMesh = rendererData.subMeshStartIndex[i]; + var gameObjectLayer = rendererData.gameObjectLayer[i]; + var renderingLayerMask = rendererData.renderingLayerMask[i]; + var materialsOffset = rendererData.materialsOffset[i]; + var materialsCount = rendererData.materialsCount[i]; + var lightmapIndex = rendererData.lightmapIndex[i]; + var packedRendererData = rendererData.packedRendererData[i]; + var rendererPriority = rendererData.rendererPriority[i]; + + int instanceCount; + int instanceOffset; + + if (implicitInstanceIndices) + { + instanceCount = 1; + instanceOffset = i; + } + else + { + instanceCount = rendererData.instancesCount[i]; + instanceOffset = rendererData.instancesOffset[i]; + } + + if (instanceCount == 0) + return; + + const int kLightmapIndexMask = 0xffff; + const int kLightmapIndexInfluenceOnly = 0xfffe; + + var overridenComponents = InstanceComponentGroup.Default; + + // Add per-instance wind parameters + if(packedRendererData.hasTree) + overridenComponents |= InstanceComponentGroup.Wind; + + var lmIndexMasked = lightmapIndex & kLightmapIndexMask; + + // Object doesn't have a valid lightmap Index, -> uses probes for lighting + if (lmIndexMasked >= kLightmapIndexInfluenceOnly) + { + // Only add the component when needed to store blended results (shader will use the ambient probe when not present) + if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) + overridenComponents |= InstanceComponentGroup.LightProbe; + } + else + { + // Add per-instance lightmap parameters + overridenComponents |= InstanceComponentGroup.Lightmap; + } + + // Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey). + Span packedMaterialDatas = stackalloc GPUDrivenPackedMaterialData[materialsCount]; + + var supportsIndirect = true; + for (int matIndex = 0; matIndex < materialsCount; ++matIndex) + { + if (matIndex >= submeshCount) + { + Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); + continue; + } + + var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; + GPUDrivenPackedMaterialData packedMaterialData; + + if (rendererData.packedMaterialData.Length > 0) + { + packedMaterialData = rendererData.packedMaterialData[materialIndex]; + } + else + { + var materialID = rendererData.materialID[materialIndex]; + bool isFound = packedMaterialDataHash.TryGetValue(materialID, out packedMaterialData); + Assert.IsTrue(isFound, "Packed material data not found."); + } + supportsIndirect &= packedMaterialData.isIndirectSupported; + + packedMaterialDatas[matIndex] = packedMaterialData; + } + + var rangeKey = new RangeKey + { + layer = (byte)gameObjectLayer, + renderingLayerMask = renderingLayerMask, + motionMode = packedRendererData.motionVecGenMode, + shadowCastingMode = packedRendererData.shadowCastingMode, + staticShadowCaster = packedRendererData.staticShadowCaster, + rendererPriority = rendererPriority, + supportsIndirect = supportsIndirect + }; + + ref DrawRange drawRange = ref EditDrawRange(rangeKey, rangeHash, drawRanges); + + for (int matIndex = 0; matIndex < materialsCount; ++matIndex) + { + if (matIndex >= submeshCount) + { + Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); + continue; + } + + var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; + var materialID = rendererData.materialID[materialIndex]; + var packedMaterialData = packedMaterialDatas[matIndex]; + + if (materialID == 0) + { + Debug.LogWarning("Material in the shared materials list is null. Object will be partially rendered."); + continue; + } + + batchMaterialHash.TryGetValue(materialID, out BatchMaterialID batchMaterialID); + + // We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch. + var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked; + + // Let the engine know if we've opted out of lightmap texture arrays + flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword; + + // assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors + // (otherwise this flag is set dynamically during culling only when the transform is changing) + if (packedMaterialData.isMotionVectorsPassEnabled) + flags |= BatchDrawCommandFlags.HasMotion; + + if (packedMaterialData.isTransparent) + flags |= BatchDrawCommandFlags.HasSortingPosition; + + { + var submeshIndex = startSubMesh + matIndex; + var subMeshDesc = rendererData.subMeshDesc[subMeshDescOffset + submeshIndex]; + var drawKey = new DrawKey + { + materialID = batchMaterialID, + meshID = batchMeshID, + submeshIndex = submeshIndex, + flags = flags, + transparentInstanceId = packedMaterialData.isTransparent ? rendererGroupID : 0, + range = rangeKey, + overridenComponents = (uint)overridenComponents, + // When we've opted out of lightmap texture arrays, we + // need to pass in a valid lightmap index. The engine + // uses this index for sorting and for breaking the + // batch when lightmaps change across draw calls, and + // for binding the correct light map. + lightmapIndex = lightmapIndex + }; + + ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMeshDesc, batchHash, drawBatches); + + if (drawBatch.instanceCount == 0) + ++drawRange.drawCount; + + drawBatch.instanceCount += instanceCount; + + for (int j = 0; j < instanceCount; ++j) + { + var instanceIndex = instanceOffset + j; + InstanceHandle instance = instances[instanceIndex]; + drawInstances.Add(new DrawInstance { key = drawKey, instanceIndex = instance.index }); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void CreateDrawBatches(bool implicitInstanceIndices, in NativeArray instances, in GPUDrivenRendererGroupData rendererData, + in NativeParallelHashMap batchMeshHash, in NativeParallelHashMap batchMaterialHash, + in NativeParallelHashMap packedMaterialDataHash, + ref NativeParallelHashMap rangeHash, ref NativeList drawRanges, ref NativeParallelHashMap batchHash, ref NativeList drawBatches, + ref NativeList drawInstances) + { + for (int i = 0; i < rendererData.rendererGroupID.Length; ++i) + ProcessRenderer(i, implicitInstanceIndices, rendererData, batchMeshHash, packedMaterialDataHash, batchMaterialHash, instances, + drawInstances, rangeHash, drawRanges, batchHash, drawBatches); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta new file mode 100644 index 00000000..1b344d83 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 07b72b6a7afa9b448b3103bb66d57ca0 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs index 27c3fd30..5de08a63 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs @@ -482,213 +482,6 @@ namespace UnityEngine.Rendering } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct ReallocateInstancesJob : IJob - { - [ReadOnly] public bool implicitInstanceIndices; - [ReadOnly] public NativeArray rendererGroupIDs; - [ReadOnly] public NativeArray packedRendererData; - [ReadOnly] public NativeArray instanceOffsets; - [ReadOnly] public NativeArray instanceCounts; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeArray instances; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - for (int i = 0; i < rendererGroupIDs.Length; ++i) - { - var rendererGroupID = rendererGroupIDs[i]; - var hasTree = packedRendererData[i].hasTree; - - int instanceCount; - int instanceOffset; - - if (implicitInstanceIndices) - { - instanceCount = 1; - instanceOffset = i; - } - else - { - instanceCount = instanceCounts[i]; - instanceOffset = instanceOffsets[i]; - } - - SharedInstanceHandle sharedInstance; - - if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) - { - sharedInstance = instanceData.Get_SharedInstance(instance); - - int currentInstancesCount = sharedInstanceData.Get_RefCount(sharedInstance); - int instancesToFreeCount = currentInstancesCount - instanceCount; - - if (instancesToFreeCount > 0) - { - bool success = true; - int freedInstancesCount = 0; - - for (int j = 0; j < instanceCount; ++j) - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - - Assert.IsTrue(success); - - while (success) - { - instanceData.Remove(instance); - instanceAllocators.FreeInstance(instance); - - rendererGroupInstanceMultiHash.Remove(it); - ++freedInstancesCount; - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - } - - Assert.AreEqual(instancesToFreeCount, freedInstancesCount); - } - } - else - { - sharedInstance = instanceAllocators.AllocateSharedInstance(); - sharedInstanceData.AddNoGrow(sharedInstance); - } - - if (instanceCount > 0) - { - sharedInstanceData.Set_RefCount(sharedInstance, instanceCount); - - for (int j = 0; j < instanceCount; ++j) - { - int instanceIndex = instanceOffset + j; - - if (instances[instanceIndex].valid) - continue; - - InstanceHandle newInstance; - - if (!hasTree) - newInstance = instanceAllocators.AllocateInstance(InstanceType.MeshRenderer); - else - newInstance = instanceAllocators.AllocateInstance(InstanceType.SpeedTree); - - instanceData.AddNoGrow(newInstance); - int index = instanceData.InstanceToIndex(newInstance); - instanceData.sharedInstances[index] = sharedInstance; - instanceData.movedInCurrentFrameBits.Set(index, false); - instanceData.movedInPreviousFrameBits.Set(index, false); - instanceData.visibleInPreviousFrameBits.Set(index, false); - - rendererGroupInstanceMultiHash.Add(rendererGroupID, newInstance); - instances[instanceIndex] = newInstance; - } - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct FreeInstancesJob : IJob - { - [ReadOnly] public NativeArray instances; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - foreach (var instance in instances) - { - if (!instanceData.IsValidInstance(instance)) - continue; - - int instanceIndex = instanceData.InstanceToIndex(instance); - SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; - int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); - int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; - var rendererGroupID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex]; - - Assert.IsTrue(refCount > 0); - - if (refCount > 1) - { - sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - - instanceData.Remove(instance); - instanceAllocators.FreeInstance(instance); - - //@ This will have quadratic cost. Optimize later. - for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var i, out var it); success;) - { - if (instance.Equals(i)) - { - rendererGroupInstanceMultiHash.Remove(it); - break; - } - success = rendererGroupInstanceMultiHash.TryGetNextValue(out i, ref it); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct FreeRendererGroupInstancesJob : IJob - { - [ReadOnly] public NativeArray rendererGroupsID; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - foreach (var rendererGroupID in rendererGroupsID) - { - for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it); success;) - { - SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); - int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); - int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; - - Assert.IsTrue(refCount > 0); - - if (refCount > 1) - { - sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - - instanceData.Remove(instance); - instanceAllocators.FreeInstance(instance); - - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - } - - rendererGroupInstanceMultiHash.Remove(rendererGroupID); - } - } - } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] private unsafe struct UpdateRendererInstancesJob : IJobParallelFor { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs index 9ed79f92..1649aeec 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs @@ -475,24 +475,21 @@ namespace UnityEngine.Rendering m_InstanceData.EnsureFreeInstances(newInstancesCount); m_SharedInstanceData.EnsureFreeInstances(newSharedInstancesCount); - new ReallocateInstancesJob { implicitInstanceIndices = implicitInstanceIndices, rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, - rendererGroupIDs = rendererData.rendererGroupID, packedRendererData = rendererData.packedRendererData, instanceOffsets = rendererData.instancesOffset, - instanceCounts = rendererData.instancesCount, instances = instances }.Run(); + InstanceDataSystemBurst.ReallocateInstances(implicitInstanceIndices, rendererData.rendererGroupID, rendererData.packedRendererData, + rendererData.instancesOffset, rendererData.instancesCount, ref m_InstanceAllocators, ref m_InstanceData, + ref m_SharedInstanceData, ref instances, ref m_RendererGroupInstanceMultiHash); } public void FreeRendererGroupInstances(NativeArray rendererGroupsID) { - new FreeRendererGroupInstancesJob { rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, - rendererGroupsID = rendererGroupsID }.Run(); + InstanceDataSystemBurst.FreeRendererGroupInstances(rendererGroupsID.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, + ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash); } public void FreeInstances(NativeArray instances) { - new FreeInstancesJob { rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, - instances = instances }.Run(); + InstanceDataSystemBurst.FreeInstances(instances.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, + ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash); } public JobHandle ScheduleUpdateInstanceDataJob(NativeArray instances, in GPUDrivenRendererGroupData rendererData, NativeParallelHashMap lodGroupDataMap) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs new file mode 100644 index 00000000..986472f5 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs @@ -0,0 +1,191 @@ +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class InstanceDataSystemBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void ReallocateInstances(bool implicitInstanceIndices, in NativeArray rendererGroupIDs, in NativeArray packedRendererData, + in NativeArray instanceOffsets, in NativeArray instanceCounts, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, + ref CPUSharedInstanceData sharedInstanceData, ref NativeArray instances, + ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) + { + for (int i = 0; i < rendererGroupIDs.Length; ++i) + { + var rendererGroupID = rendererGroupIDs[i]; + var hasTree = packedRendererData[i].hasTree; + + int instanceCount; + int instanceOffset; + + if (implicitInstanceIndices) + { + instanceCount = 1; + instanceOffset = i; + } + else + { + instanceCount = instanceCounts[i]; + instanceOffset = instanceOffsets[i]; + } + + SharedInstanceHandle sharedInstance; + + if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) + { + sharedInstance = instanceData.Get_SharedInstance(instance); + + int currentInstancesCount = sharedInstanceData.Get_RefCount(sharedInstance); + int instancesToFreeCount = currentInstancesCount - instanceCount; + + if (instancesToFreeCount > 0) + { + bool success = true; + int freedInstancesCount = 0; + + for (int j = 0; j < instanceCount; ++j) + success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); + + Assert.IsTrue(success); + + while (success) + { + var idx = instanceData.InstanceToIndex(instance); + instanceData.Remove(instance); + + instanceAllocators.FreeInstance(instance); + + rendererGroupInstanceMultiHash.Remove(it); + ++freedInstancesCount; + success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); + } + + Assert.AreEqual(instancesToFreeCount, freedInstancesCount); + } + } + else + { + sharedInstance = instanceAllocators.AllocateSharedInstance(); + sharedInstanceData.AddNoGrow(sharedInstance); + } + + if (instanceCount > 0) + { + sharedInstanceData.Set_RefCount(sharedInstance, instanceCount); + + for (int j = 0; j < instanceCount; ++j) + { + int instanceIndex = instanceOffset + j; + + if (instances[instanceIndex].valid) + continue; + + InstanceHandle newInstance; + + if (!hasTree) + newInstance = instanceAllocators.AllocateInstance(InstanceType.MeshRenderer); + else + newInstance = instanceAllocators.AllocateInstance(InstanceType.SpeedTree); + + instanceData.AddNoGrow(newInstance); + int index = instanceData.InstanceToIndex(newInstance); + instanceData.sharedInstances[index] = sharedInstance; + instanceData.movedInCurrentFrameBits.Set(index, false); + instanceData.movedInPreviousFrameBits.Set(index, false); + instanceData.visibleInPreviousFrameBits.Set(index, false); + + rendererGroupInstanceMultiHash.Add(rendererGroupID, newInstance); + instances[instanceIndex] = newInstance; + } + } + else + { + sharedInstanceData.Remove(sharedInstance); + instanceAllocators.FreeSharedInstance(sharedInstance); + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void FreeRendererGroupInstances(in NativeArray.ReadOnly rendererGroupsID, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, + ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) + { + foreach (var rendererGroupID in rendererGroupsID) + { + for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it); success;) + { + SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); + int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); + int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; + + Assert.IsTrue(refCount > 0); + + if (refCount > 1) + { + sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; + } + else + { + sharedInstanceData.Remove(sharedInstance); + instanceAllocators.FreeSharedInstance(sharedInstance); + } + + var idx = instanceData.InstanceToIndex(instance); + instanceData.Remove(instance); + instanceAllocators.FreeInstance(instance); + + success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); + } + + rendererGroupInstanceMultiHash.Remove(rendererGroupID); + } + } + + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void FreeInstances(in NativeArray.ReadOnly instances, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, + ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) + { + foreach (var instance in instances) + { + if (!instanceData.IsValidInstance(instance)) + continue; + + int instanceIndex = instanceData.InstanceToIndex(instance); + SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; + int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); + int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; + var rendererGroupID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex]; + + Assert.IsTrue(refCount > 0); + + if (refCount > 1) + { + sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; + } + else + { + sharedInstanceData.Remove(sharedInstance); + instanceAllocators.FreeSharedInstance(sharedInstance); + } + + instanceData.Remove(instance); + instanceAllocators.FreeInstance(instance); + + //@ This will have quadratic cost. Optimize later. + for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var i, out var it); success;) + { + if (instance.Equals(i)) + { + rendererGroupInstanceMultiHash.Remove(it); + break; + } + success = rendererGroupInstanceMultiHash.TryGetNextValue(out i, ref it); + } + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta new file mode 100644 index 00000000..8613808f --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 302f596d55264be4ba359e52ec407766 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs index 6bc76a17..66adf030 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs @@ -74,8 +74,10 @@ namespace UnityEngine.Rendering [GenerateHLSL(needAccessors = false)] internal enum InstanceOcclusionTestDebugCounter { - Occluded, - NotOccluded, + InstancesOccluded, + InstancesNotOccluded, + PrimitivesOccluded, + PrimitivesNotOccluded, Count, } @@ -93,7 +95,7 @@ namespace UnityEngine.Rendering public uint firstIndex; public uint baseVertex; public uint firstInstanceGlobalIndex; - public uint maxInstanceCount; + public uint maxInstanceCountAndTopology; // [31:3]=max_instance_count, [2:0]=topology } internal struct IndirectBufferAllocInfo @@ -280,7 +282,7 @@ namespace UnityEngine.Rendering occluderDepthPyramid = RTHandles.Alloc( occluderDepthPyramidSize.x, occluderDepthPyramidSize.y, format: GraphicsFormat.R32_SFloat, - dimension: TextureDimension.Tex2D, + dimension: TextureDimension.Tex2D, filterMode: FilterMode.Point, wrapMode: TextureWrapMode.Clamp, enableRandomWrite: true, diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl index f9aab8a0..832151f2 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl @@ -7,9 +7,11 @@ // // UnityEngine.Rendering.InstanceOcclusionTestDebugCounter: static fields // -#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_OCCLUDED (0) -#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_NOT_OCCLUDED (1) -#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_COUNT (2) +#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_INSTANCES_OCCLUDED (0) +#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_INSTANCES_NOT_OCCLUDED (1) +#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_PRIMITIVES_OCCLUDED (2) +#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_PRIMITIVES_NOT_OCCLUDED (3) +#define INSTANCEOCCLUSIONTESTDEBUGCOUNTER_COUNT (4) // Generated from UnityEngine.Rendering.IndirectDrawInfo // PackingRules = Exact @@ -19,7 +21,7 @@ struct IndirectDrawInfo uint firstIndex; uint baseVertex; uint firstInstanceGlobalIndex; - uint maxInstanceCount; + uint maxInstanceCountAndTopology; }; // Generated from UnityEngine.Rendering.IndirectInstanceInfo diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs index 42705f50..b9e47a3b 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs @@ -86,52 +86,6 @@ namespace UnityEngine.Rendering } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct AllocateOrGetLODGroupDataInstancesJob : IJob - { - [ReadOnly] public NativeArray lodGroupsID; - - public NativeList lodGroupsData; - public NativeList lodGroupCullingData; - public NativeParallelHashMap lodGroupDataHash; - public NativeList freeLODGroupDataHandles; - - [WriteOnly] public NativeArray lodGroupInstances; - - [NativeDisableUnsafePtrRestriction] public int* previousRendererCount; - - public void Execute() - { - int freeHandlesCount = freeLODGroupDataHandles.Length; - int lodDataLength = lodGroupsData.Length; - - for (int i = 0; i < lodGroupsID.Length; ++i) - { - int lodGroupID = lodGroupsID[i]; - - if (!lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) - { - if (freeHandlesCount == 0) - lodGroupInstance = new GPUInstanceIndex() { index = lodDataLength++ }; - else - lodGroupInstance = freeLODGroupDataHandles[--freeHandlesCount]; - - lodGroupDataHash.TryAdd(lodGroupID, lodGroupInstance); - } - else - { - *previousRendererCount += lodGroupsData.ElementAt(lodGroupInstance.index).rendererCount; - } - - lodGroupInstances[i] = lodGroupInstance; - } - - freeLODGroupDataHandles.ResizeUninitialized(freeHandlesCount); - lodGroupsData.ResizeUninitialized(lodDataLength); - lodGroupCullingData.ResizeUninitialized(lodDataLength); - } - } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal unsafe struct UpdateLODGroupDataJob : IJobParallelFor { @@ -219,38 +173,6 @@ namespace UnityEngine.Rendering } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct FreeLODGroupDataJob : IJob - { - [ReadOnly] public NativeArray destroyedLODGroupsID; - - public NativeList lodGroupsData; - public NativeParallelHashMap lodGroupDataHash; - public NativeList freeLODGroupDataHandles; - - [NativeDisableUnsafePtrRestriction] public int* removedRendererCount; - - public void Execute() - { - foreach (int lodGroupID in destroyedLODGroupsID) - { - if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) - { - Assert.IsTrue(lodGroupInstance.valid); - - lodGroupDataHash.Remove(lodGroupID); - freeLODGroupDataHandles.Add(lodGroupInstance); - - ref LODGroupData lodGroupData = ref lodGroupsData.ElementAt(lodGroupInstance.index); - Assert.IsTrue(lodGroupData.valid); - - *removedRendererCount += lodGroupData.rendererCount; - lodGroupData.valid = false; - } - } - } - } - internal class LODGroupDataPool : IDisposable { private NativeList m_LODGroupData; @@ -329,18 +251,9 @@ namespace UnityEngine.Rendering var lodGroupInstances = new NativeArray(inputData.lodGroupID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - int previousRendererCount = 0; - - new AllocateOrGetLODGroupDataInstancesJob - { - lodGroupsID = inputData.lodGroupID, - lodGroupsData = m_LODGroupData, - lodGroupCullingData = m_LODGroupCullingData, - lodGroupDataHash = m_LODGroupDataHash, - freeLODGroupDataHandles = m_FreeLODGroupDataHandles, - lodGroupInstances = lodGroupInstances, - previousRendererCount = &previousRendererCount - }.Run(); + int previousRendererCount = LODGroupDataPoolBurst.AllocateOrGetLODGroupDataInstances(inputData.lodGroupID, + ref m_LODGroupData, ref m_LODGroupCullingData, + ref m_LODGroupDataHash, ref m_FreeLODGroupDataHandles, ref lodGroupInstances); m_CrossfadedRendererCount -= previousRendererCount; Assert.IsTrue(m_CrossfadedRendererCount >= 0); @@ -367,21 +280,12 @@ namespace UnityEngine.Rendering lodGroupInstances.Dispose(); } - public unsafe void FreeLODGroupData(NativeArray destroyedLODGroupsID) + public void FreeLODGroupData(NativeArray destroyedLODGroupsID) { if (destroyedLODGroupsID.Length == 0) return; - int removedRendererCount = 0; - - new FreeLODGroupDataJob - { - destroyedLODGroupsID = destroyedLODGroupsID, - lodGroupsData = m_LODGroupData, - lodGroupDataHash = m_LODGroupDataHash, - freeLODGroupDataHandles = m_FreeLODGroupDataHandles, - removedRendererCount = &removedRendererCount - }.Run(); + int removedRendererCount = LODGroupDataPoolBurst.FreeLODGroupData(destroyedLODGroupsID, ref m_LODGroupData, ref m_LODGroupDataHash, ref m_FreeLODGroupDataHandles); m_CrossfadedRendererCount -= removedRendererCount; Assert.IsTrue(m_CrossfadedRendererCount >= 0); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs new file mode 100644 index 00000000..efe0342a --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs @@ -0,0 +1,72 @@ +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class LODGroupDataPoolBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static int FreeLODGroupData(in NativeArray destroyedLODGroupsID, ref NativeList lodGroupsData, + ref NativeParallelHashMap lodGroupDataHash, ref NativeList freeLODGroupDataHandles) + { + int removedRendererCount = 0; + + foreach (int lodGroupID in destroyedLODGroupsID) + { + if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) + { + Assert.IsTrue(lodGroupInstance.valid); + + lodGroupDataHash.Remove(lodGroupID); + freeLODGroupDataHandles.Add(lodGroupInstance); + + ref LODGroupData lodGroupData = ref lodGroupsData.ElementAt(lodGroupInstance.index); + Assert.IsTrue(lodGroupData.valid); + + removedRendererCount += lodGroupData.rendererCount; + lodGroupData.valid = false; + } + } + + return removedRendererCount; + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static int AllocateOrGetLODGroupDataInstances(in NativeArray lodGroupsID, ref NativeList lodGroupsData, ref NativeList lodGroupCullingData, + ref NativeParallelHashMap lodGroupDataHash, ref NativeList freeLODGroupDataHandles, ref NativeArray lodGroupInstances) + { + int freeHandlesCount = freeLODGroupDataHandles.Length; + int lodDataLength = lodGroupsData.Length; + int previousRendererCount = 0; + + for (int i = 0; i < lodGroupsID.Length; ++i) + { + int lodGroupID = lodGroupsID[i]; + + if (!lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) + { + if (freeHandlesCount == 0) + lodGroupInstance = new GPUInstanceIndex() { index = lodDataLength++ }; + else + lodGroupInstance = freeLODGroupDataHandles[--freeHandlesCount]; + + lodGroupDataHash.TryAdd(lodGroupID, lodGroupInstance); + } + else + { + previousRendererCount += lodGroupsData.ElementAt(lodGroupInstance.index).rendererCount; + } + + lodGroupInstances[i] = lodGroupInstance; + } + + freeLODGroupDataHandles.ResizeUninitialized(freeHandlesCount); + lodGroupsData.ResizeUninitialized(lodDataLength); + lodGroupCullingData.ResizeUninitialized(lodDataLength); + + return previousRendererCount; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta new file mode 100644 index 00000000..6e2ab16b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ad57195e4230c9344a64d902de871991 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeBrickPool.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeBrickPool.cs index 3a3ef283..e6e254f5 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeBrickPool.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeBrickPool.cs @@ -281,6 +281,7 @@ namespace UnityEngine.Rendering if (!ignoreErrorLog) Debug.LogError("Cannot allocate more brick chunks, probe volume brick pool is full."); + Deallocate(outAllocations); outAllocations.Clear(); return false; // failure case, pool is full } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs index b3b2882a..6ece01ff 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.cs @@ -1078,6 +1078,11 @@ namespace UnityEngine.Rendering m_VertexSampling = value; } + internal void ForceMemoryBudget(ProbeVolumeTextureMemoryBudget budget) + { + m_MemoryBudget = budget; + } + // This is used for steps such as dilation that require the maximum order allowed to be loaded at all times. Should really never be used as a general purpose function. internal void ForceSHBand(ProbeVolumeSHBands shBands) { @@ -1680,6 +1685,9 @@ namespace UnityEngine.Rendering //Ensure that all currently loaded scenes belong to the same set. foreach (var data in perSceneDataList) { + if (UnityEditor.SceneManagement.EditorSceneManager.IsPreviewScene(data.gameObject.scene)) + continue; // Ignore preview scenes - they are needed to make closed subscenes work + var set = ProbeVolumeBakingSet.GetBakingSetForScene(data.gameObject.scene); if (set != bakingSet) return false; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl index 97d58df8..73e63fd8 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolume.hlsl @@ -749,6 +749,7 @@ void EvaluateAPVL1L2(APVSample apvSample, float3 N, out float3 diffuseLighting) // ------------------------------------------------------------- void EvaluateAdaptiveProbeVolume(APVSample apvSample, float3 normalWS, out float3 bakeDiffuseLighting) { + bakeDiffuseLighting = float3(0.0f, 0.0f, 0.0f); if (apvSample.status != APV_SAMPLE_STATUS_INVALID) { apvSample.Decode(); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumePerSceneData.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumePerSceneData.cs index 4fc973f5..6cc04a60 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumePerSceneData.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumePerSceneData.cs @@ -95,7 +95,7 @@ namespace UnityEngine.Rendering void OnEnable() { - #if UNITY_EDITOR +#if UNITY_EDITOR // In the editor, always refresh the GUID as it may become out of date is scene is duplicated or other weird things // This field is serialized, so it will be available in standalones, where it can't change anymore. // Only change the GUID if the new one is valid. @@ -105,7 +105,7 @@ namespace UnityEngine.Rendering sceneGUID = newGUID; EditorUtility.SetDirty(this); } - #endif +#endif ProbeReferenceVolume.instance.RegisterPerSceneData(this); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareDataSRP.cs b/Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareDataSRP.cs index 61945b8f..4db6db78 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareDataSRP.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareDataSRP.cs @@ -1,4 +1,3 @@ -using NUnit.Framework; using UnityEngine.Serialization; namespace UnityEngine.Rendering diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.Debug.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.Debug.cs index c3d6ec3d..a563ae62 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.Debug.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.Debug.cs @@ -99,6 +99,9 @@ namespace UnityEngine.Rendering.RenderGraphModule.NativeRenderPassCompiler case PassBreakReason.DifferentShadingRateStates: message += $"{prevPassName} uses different shading rate states than {passName}."; break; + case PassBreakReason.PassMergingDisabled: + message += "The pass merging is disabled."; + break; default: throw new ArgumentOutOfRangeException(); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/IRenderGraphBuilder.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/IRenderGraphBuilder.cs index cb9e6d8e..ceb9ff8c 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/IRenderGraphBuilder.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/IRenderGraphBuilder.cs @@ -228,6 +228,10 @@ namespace UnityEngine.Rendering.RenderGraphModule /// to match the index passed to SetInputAttachment for this texture. /// /// + /// + /// This API is not universally supported across all platforms. In particular, using input attachments in combination with MSAA may be unsupported on certain targets. + /// To ensure compatibility, use `RenderGraphUtils.IsFramebufferFetchSupportedOnCurrentPlatform` to verify support at runtime, as platform capabilities may vary. + /// /// Texture to use during this pass. /// Index the shader will use to access this texture. /// How this pass will access the texture. Default value is set to AccessFlag.Read. Writing is currently not supported on any platform. diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs index f28e7357..8913e109 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs @@ -529,7 +529,7 @@ namespace UnityEngine.Rendering.RenderGraphModule nativeCompiler?.Cleanup(); m_CompilationCache?.Clear(); - + DelegateHashCodeUtils.ClearCache(); } @@ -751,6 +751,25 @@ namespace UnityEngine.Rendering.RenderGraphModule return m_Resources.CreateTexture(m_Resources.GetTextureResourceDesc(texture.handle)); } + /// + /// Create a new Render Graph Texture resource using the descriptor from another texture. + /// + /// + /// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc(). + /// + /// Texture from which the descriptor should be used. + /// The destination texture name. + /// Texture needs to be cleared on first use. + /// A new TextureHandle. + public TextureHandle CreateTexture(TextureHandle texture, string name, bool clear = false) + { + var destinationDesc = GetTextureDesc(texture); + destinationDesc.name = name; + destinationDesc.clearBuffer = clear; + + return m_Resources.CreateTexture(destinationDesc); + } + /// /// Create a new Render Graph Texture if the passed handle is invalid and use said handle as output. /// If the passed handle is valid, no texture is created. diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilders.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilders.cs index c5248f75..5c1eac07 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilders.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphBuilders.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using UnityEngine.Experimental.Rendering; +using static UnityEngine.Rendering.RenderGraphModule.RenderGraph; namespace UnityEngine.Rendering.RenderGraphModule { @@ -399,6 +400,8 @@ namespace UnityEngine.Rendering.RenderGraphModule public void SetInputAttachment(TextureHandle tex, int index, AccessFlags flags, int mipLevel, int depthSlice) { + CheckFrameBufferFetchEmulationIsSupported(tex); + CheckUseFragment(tex, false); ResourceHandle result = UseResource(tex.handle, flags); // Note the version for the attachments is a bit arbitrary so we just use the latest for now @@ -514,6 +517,25 @@ namespace UnityEngine.Rendering.RenderGraphModule } } + [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] + void CheckFrameBufferFetchEmulationIsSupported(in TextureHandle tex) + { + if (enableValidityChecks) + { + if (!Util.RenderGraphUtils.IsFramebufferFetchEmulationSupportedOnCurrentPlatform()) + { + throw new InvalidOperationException($"This API is not supported on the current platform: {SystemInfo.graphicsDeviceType}"); + } + + if (!Util.RenderGraphUtils.IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform()) + { + var sourceInfo = m_RenderGraph.GetRenderTargetInfo(tex); + if (sourceInfo.bindMS) + throw new InvalidOperationException($"This API is not supported with MSAA attachments on the current platform: {SystemInfo.graphicsDeviceType}"); + } + } + } + public void SetShadingRateImageAttachment(in TextureHandle sriTextureHandle) { CheckNotUseFragment(sriTextureHandle); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphUtilsBlit.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphUtilsBlit.cs index 922466b7..7eece9d0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphUtilsBlit.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphUtilsBlit.cs @@ -17,6 +17,9 @@ namespace UnityEngine.Rendering.RenderGraphModule.Util /// Returns true if the shader features required by the copy pass is supported for MSAA, otherwise will it return false. public static bool CanAddCopyPassMSAA() { + if (!IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform()) + return false; + return Blitter.CanCopyMSAA(); } @@ -27,9 +30,53 @@ namespace UnityEngine.Rendering.RenderGraphModule.Util /// Returns true if the shader features required by the copy pass is supported for MSAA, otherwise will it return false. public static bool CanAddCopyPassMSAA(in TextureDesc sourceDesc) { + if (!IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform()) + return false; + return Blitter.CanCopyMSAA(sourceDesc); } + internal static bool IsFramebufferFetchEmulationSupportedOnCurrentPlatform() + { +#if PLATFORM_WEBGL + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3) + return false; +#endif + return true; + } + + internal static bool IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform() + { + // TODO: Temporarily disable this utility pending a more efficient solution for supporting or disabling framebuffer fetch emulation on PS4/PS5. + return (SystemInfo.graphicsDeviceType != GraphicsDeviceType.PlayStation4 + && SystemInfo.graphicsDeviceType != GraphicsDeviceType.PlayStation5 && SystemInfo.graphicsDeviceType != GraphicsDeviceType.PlayStation5NGGC); + } + + /// + /// Determines whether framebuffer fetch is supported on the current platform for the given texture. + /// This includes checking both general support for framebuffer fetch emulation and specific support + /// for multisampled (MSAA) textures. + /// + /// The RenderGraph adding this pass to. + /// The texture handle to validate for framebuffer fetch compatibility. + /// + /// Returns true if framebuffer fetch is supported on the current platform for the given texture; + /// otherwise, returns false. + /// + public static bool IsFramebufferFetchSupportedOnCurrentPlatform(this RenderGraph graph, in TextureHandle tex) + { + if (!IsFramebufferFetchEmulationSupportedOnCurrentPlatform()) + return false; + + if (!IsFramebufferFetchEmulationMSAASupportedOnCurrentPlatform()) + { + var sourceInfo = graph.GetRenderTargetInfo(tex); + if (sourceInfo.msaaSamples > 1) + return sourceInfo.bindMS; + } + return true; + } + class CopyPassData { public bool isMSAA; @@ -90,7 +137,7 @@ namespace UnityEngine.Rendering.RenderGraphModule.Util // It would have 1 if the MSAA pass is not able to be used for target and 2 otherwise. // https://docs.unity3d.com/2017.4/Documentation/Manual/SL-ShaderCompileTargets.html // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-get-sample-position - if (isMSAA && !Blitter.CanCopyMSAA(sourceDesc)) + if (isMSAA && !CanAddCopyPassMSAA(sourceDesc)) throw new ArgumentException("Target does not support MSAA for AddCopyPass. Please use the blit alternative or use non MSAA textures."); using (var builder = graph.AddRasterRenderPass(passName, out var passData, file, line)) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute index 883bfe6c..fd055762 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute @@ -61,7 +61,7 @@ SphereBound LoadInstanceBoundingSphere(uint instanceID) { float4 data = asfloat(_InstanceDataBuffer.Load4(_BoundingSphereInstanceDataAddress + instanceID * 16)); SphereBound b; - b.center = data.xyz; + b.center = data.xyz; b.radius = data.w; return b; } @@ -105,7 +105,7 @@ void CopyInstances(uint dispatchIdx : SV_DispatchThreadID) uint argsBase = DRAW_ARGS_INDEX(dispatchIdx); _DrawArgs[argsBase + 0] = drawInfo.indexCount; // IndirectDrawIndexedArgs.indexCountPerInstance - _DrawArgs[argsBase + 1] = drawInfo.maxInstanceCount << _InstanceMultiplierShift; // IndirectDrawIndexedArgs.instanceCount + _DrawArgs[argsBase + 1] = (drawInfo.maxInstanceCountAndTopology >> 3) << _InstanceMultiplierShift; // IndirectDrawIndexedArgs.instanceCount _DrawArgs[argsBase + 2] = drawInfo.firstIndex; // IndirectDrawIndexedArgs.startIndex _DrawArgs[argsBase + 3] = drawInfo.baseVertex; // IndirectDrawIndexedArgs.baseVertexIndex _DrawArgs[argsBase + 4] = 0; // IndirectDrawIndexedArgs.startInstance @@ -120,6 +120,19 @@ void CopyInstances(uint dispatchIdx : SV_DispatchThreadID) } } +uint GetPrimitiveCount(uint indexCount, uint topology, bool nativeQuads) +{ + switch (topology) + { + case /*MeshTopology.Triangles*/ 0: return indexCount / 3; + case /*MeshTopology.Quads*/ 2: return nativeQuads ? (indexCount / 4) : (indexCount / 4 * 2); + case /*MeshTopology.Lines*/ 3: return indexCount / 2; + case /*MeshTopology.LineStrip*/ 4: return (indexCount >= 1) ? (indexCount - 1) : 0; + case /*MeshTopology.Points*/ 5: return indexCount; + default: return 0; + } +} + [numthreads(64,1,1)] void CullInstances(uint instanceInfoOffset : SV_DispatchThreadID) { @@ -157,7 +170,7 @@ void CullInstances(uint instanceInfoOffset : SV_DispatchThreadID) isOccludedInAll = false; } isVisible = !isOccludedInAll; - + #ifdef OCCLUSION_FIRST_PASS // if we failed the occlusion check, then add to the list for the second pass if (!isVisible) @@ -173,8 +186,16 @@ void CullInstances(uint instanceInfoOffset : SV_DispatchThreadID) if (_DebugCounterIndex >= 0) { // TODO: sum each within wave, first thread in wave issues atomic add to memory - int counterIndex = isVisible ? INSTANCEOCCLUSIONTESTDEBUGCOUNTER_NOT_OCCLUDED : INSTANCEOCCLUSIONTESTDEBUGCOUNTER_OCCLUDED; + int counterIndex = isVisible ? INSTANCEOCCLUSIONTESTDEBUGCOUNTER_INSTANCES_NOT_OCCLUDED : INSTANCEOCCLUSIONTESTDEBUGCOUNTER_INSTANCES_OCCLUDED; InterlockedAdd(_OcclusionDebugCounters[_DebugCounterIndex*INSTANCEOCCLUSIONTESTDEBUGCOUNTER_COUNT + counterIndex], 1); + + IndirectDrawInfo drawInfo = LoadDrawInfo(drawOffset); + uint argsBase = DRAW_ARGS_INDEX(drawOffset); + uint indexCount = _DrawArgs[argsBase + 0]; // IndirectDrawIndexedArgs.indexCountPerInstance + uint topology = drawInfo.maxInstanceCountAndTopology & 7; + uint primitiveCount = GetPrimitiveCount(indexCount, topology, false); + counterIndex = isVisible ? INSTANCEOCCLUSIONTESTDEBUGCOUNTER_PRIMITIVES_NOT_OCCLUDED : INSTANCEOCCLUSIONTESTDEBUGCOUNTER_PRIMITIVES_OCCLUDED; + InterlockedAdd(_OcclusionDebugCounters[_DebugCounterIndex*INSTANCEOCCLUSIONTESTDEBUGCOUNTER_COUNT + counterIndex], primitiveCount); } if (isVisible) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blitter.cs b/Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blitter.cs index 56644868..70d72e3b 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blitter.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blitter.cs @@ -712,6 +712,42 @@ namespace UnityEngine.Rendering BlitTexture(cmd.m_WrappedCommandBuffer, source, scaleBias, material, pass); } + /// + /// Adds in a a command to copy a texture identified by its into + /// the currently bound render target's color buffer, using a user material and specific shader pass. + /// + /// + /// The source texture will be bound to the "_BlitTexture" shader property. + /// The scaleBias parameter controls the rectangle of pixels in the source texture to copy by manipulating + /// the source texture coordinates. The X and Y coordinates store the scaling factor to apply to these texture + /// coordinates, while the Z and W coordinates store the texture coordinate offsets. The operation will always + /// write to the full destination render target rectangle. + /// + /// Command Buffer used for recording the action. + /// RTHandle of the source texture to copy from. + /// Scale and bias for sampling the source texture. + /// The material to use for writing to the destination target. + /// The index of the pass to use in the material's shader. + /// + /// + /// + public static void BlitTexture(UnsafeCommandBuffer cmd, RTHandle source, Vector4 scaleBias, Material material, int pass) + { + BlitTexture(cmd.m_WrappedCommandBuffer, source, scaleBias, material, pass); + } + /// /// Adds in a a command to copy a texture identified by its into /// the currently bound render target's color buffer, using a user material and specific shader pass. diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeManager.cs b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeManager.cs index 421eadd8..d007439e 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeManager.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Volume/VolumeManager.cs @@ -137,11 +137,23 @@ namespace UnityEngine.Rendering .OrderBy(i => i.Item1) .ToList(); } + + Type[] m_BaseComponentTypeArray; /// /// The current list of all available types that derive from . /// - public Type[] baseComponentTypeArray { get; internal set; } // internal only for tests + public Type[] baseComponentTypeArray + { + get + { + if (isInitialized) + return m_BaseComponentTypeArray; + + throw new InvalidOperationException($"{nameof(VolumeManager)}.{nameof(instance)}.{nameof(baseComponentTypeArray)} cannot be called before the {nameof(VolumeManager)} is initialized. (See {nameof(VolumeManager)}.{nameof(instance)}.{nameof(isInitialized)} and {nameof(RenderPipelineManager)} for creation callback)."); + } + internal set => m_BaseComponentTypeArray = value; // internal only for tests + } /// /// Global default profile that provides default values for volume components. VolumeManager applies @@ -242,13 +254,19 @@ namespace UnityEngine.Rendering Debug.Assert(m_CreatedVolumeStacks.Count == 0); LoadBaseTypes(GraphicsSettings.currentRenderPipelineAssetType); + InitializeInternal(globalDefaultVolumeProfile, qualityDefaultVolumeProfile); + } + + //This is called by test where the basetypes are tuned for the purpose of the test. + internal void InitializeInternal(VolumeProfile globalDefaultVolumeProfile = null, VolumeProfile qualityDefaultVolumeProfile = null) + { InitializeVolumeComponents(); globalDefaultProfile = globalDefaultVolumeProfile; qualityDefaultProfile = qualityDefaultVolumeProfile; EvaluateVolumeDefaultState(); - m_DefaultStack = CreateStack(); + m_DefaultStack = CreateStackInternal(); stack = m_DefaultStack; isInitialized = true; @@ -347,9 +365,17 @@ namespace UnityEngine.Rendering /// /// public VolumeStack CreateStack() + { + if (!isInitialized) + throw new InvalidOperationException($"{nameof(VolumeManager)}.{nameof(instance)}.{nameof(CreateStack)}() cannot be called before the {nameof(VolumeManager)} is initialized. (See {nameof(VolumeManager)}.{nameof(instance)}.{nameof(isInitialized)} and {nameof(RenderPipelineManager)} for creation callback)."); + + return CreateStackInternal(); + } + + VolumeStack CreateStackInternal() { var stack = new VolumeStack(); - stack.Reload(baseComponentTypeArray); + stack.Reload(m_BaseComponentTypeArray); m_CreatedVolumeStacks.Add(stack); return stack; } @@ -421,15 +447,18 @@ namespace UnityEngine.Rendering list.Add(t); } - baseComponentTypeArray = list.ToArray(); + m_BaseComponentTypeArray = list.ToArray(); } } internal void InitializeVolumeComponents() { + if (m_BaseComponentTypeArray == null || m_BaseComponentTypeArray.Length == 0) + return; + // Call custom static Init method if present var flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - foreach (var type in baseComponentTypeArray) + foreach (var type in m_BaseComponentTypeArray) { var initMethod = type.GetMethod("Init", flags); if (initMethod != null) @@ -440,9 +469,9 @@ namespace UnityEngine.Rendering } // Evaluate static default values for VolumeComponents, which is the baseline to reset the values to at the start of Update. - internal void EvaluateVolumeDefaultState() + void EvaluateVolumeDefaultState() { - if (baseComponentTypeArray == null || baseComponentTypeArray.Length == 0) + if (m_BaseComponentTypeArray == null || m_BaseComponentTypeArray.Length == 0) return; using var profilerScope = k_ProfilerMarkerEvaluateVolumeDefaultState.Auto(); @@ -453,7 +482,7 @@ namespace UnityEngine.Rendering // First, default-construct all VolumeComponents List componentsDefaultStateList = new(); - foreach (var type in baseComponentTypeArray) + foreach (var type in m_BaseComponentTypeArray) { componentsDefaultStateList.Add((VolumeComponent) ScriptableObject.CreateInstance(type)); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/XR/XRPass.cs b/Packages/com.unity.render-pipelines.core/Runtime/XR/XRPass.cs index 65c9de7b..22cc9761 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/XR/XRPass.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/XR/XRPass.cs @@ -24,6 +24,7 @@ namespace UnityEngine.Experimental.Rendering internal int cullingPassId; internal bool copyDepth; internal bool hasMotionVectorPass; + internal bool spaceWarpRightHandedNDC; #if ENABLE_VR && ENABLE_XR_MODULE internal UnityEngine.XR.XRDisplaySubsystem.XRRenderPass xrSdkRenderPass; @@ -105,6 +106,22 @@ namespace UnityEngine.Experimental.Rendering /// public bool hasMotionVectorPass { get; private set; } + /// + /// Reports which NDC convention the render pipeline should use when calculating motion vectors. + /// if true, motion vector data must use the right-handed NDC space. If false motion vector data + /// must use the left-handed NDC space. + /// + /// + /// The render pipeline must write motion vector data to the . + /// + /// > [!NOTE] + /// > The OpenXR specification doesn't specify which coordinate space convention to use for the + /// > motion vector data. Unity only supports SpaceWarp when using the Vulkan graphics API, which uses the right-handed convention for normalized device coordinates, but + /// > devices still can choose either convention for motion data when the + /// > application is using the Vulkan graphics API. + /// + public bool spaceWarpRightHandedNDC { get; private set; } + /// /// If true, is the first pass of a xr camera /// @@ -480,6 +497,7 @@ namespace UnityEngine.Experimental.Rendering motionVectorRenderTarget = new RenderTargetIdentifier(createInfo.motionVectorRenderTarget, 0, CubemapFace.Unknown, -1); motionVectorRenderTargetDesc = createInfo.motionVectorRenderTargetDesc; hasMotionVectorPass = createInfo.hasMotionVectorPass; + spaceWarpRightHandedNDC = createInfo.spaceWarpRightHandedNDC; m_OcclusionMesh.SetMaterial(createInfo.occlusionMeshMaterial); occlusionMeshScale = createInfo.occlusionMeshScale; foveatedRenderingInfo = createInfo.foveatedRenderingInfo; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/XR/XRSystem.cs b/Packages/com.unity.render-pipelines.core/Runtime/XR/XRSystem.cs index aa80bd23..62f25ce4 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/XR/XRSystem.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/XR/XRSystem.cs @@ -533,6 +533,7 @@ namespace UnityEngine.Experimental.Rendering multipassId = layout.GetActivePasses().Count, cullingPassId = xrRenderPass.cullingPassIndex, copyDepth = xrRenderPass.shouldFillOutDepth, + spaceWarpRightHandedNDC = xrRenderPass.spaceWarpRightHandedNDC, xrSdkRenderPass = xrRenderPass }; diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/FixedBufferStringQueueTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/FixedBufferStringQueueTests.cs index 5064885e..94ca1e77 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/FixedBufferStringQueueTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/FixedBufferStringQueueTests.cs @@ -36,6 +36,31 @@ namespace UnityEngine.Rendering.Tests Assert.False(buffer.TryPush("amet, consectetur adipiscing")); Assert.AreEqual(1, buffer.Count); + + Assert.True(buffer.TryPop(out string v) && v == "Lorem ipsum dolor sit"); + Assert.False(buffer.TryPop(out v) && v == null); + } + + [Test] + public void PushAndPopOutOfBufferRange_StringSizeNotDivisibleBy4() + { + // UUM-104687: Buffer is created with size of 32 bytes. The test fills the first 30 bytes with a string, + // so 2 bytes are left over in the buffer. After we pop the string out, we check that the next TryPop + // doesn't try to read out of bounds when trying to read the string length. + + const int bufferLength = 32; + const int bytesToFill = bufferLength - 2; + const int bytesForString = bytesToFill - sizeof(int); + const int numCharacters = bytesForString / sizeof(char); + + string testValue = new string('a', numCharacters); + + byte* bufferStart = stackalloc byte[bufferLength]; + CoreUnsafeUtils.FixedBufferStringQueue buffer = new CoreUnsafeUtils.FixedBufferStringQueue(bufferStart, bufferLength); + + Assume.That(buffer.TryPush(testValue)); + Assume.That(buffer.TryPop(out string v)); + Assert.False(buffer.TryPop(out v)); } [Test] @@ -58,5 +83,6 @@ namespace UnityEngine.Rendering.Tests Assert.True(buffer.TryPop(out string v) && v == "elit, sed do eiusmod"); Assert.True(buffer.TryPop(out v) && v == "tempor incididunt ut labore"); } + } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs index 15002f73..ba153916 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs @@ -189,10 +189,11 @@ namespace UnityEngine.Rendering.Tests var callbackCounter = new BoxedCounter(); cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; var materials = new NativeParallelHashSet(10, Allocator.Temp); @@ -221,6 +222,8 @@ namespace UnityEngine.Rendering.Tests using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brg.UpdateRenderers(objIDs.AsArray()); var cameraObject = new GameObject("myCamera"); @@ -228,6 +231,8 @@ namespace UnityEngine.Rendering.Tests SubmitCameraRenderRequest(mainCamera); + brg.OnEndContextRendering(); + Assert.AreEqual(1, callbackCounter.Value); mainCamera = null; @@ -268,16 +273,19 @@ namespace UnityEngine.Rendering.Tests cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; callbackCounter.Value = drawCommands.visibleInstanceCount; }; using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brg.UpdateRenderers(objIDs); var cameraObject = new GameObject("SceneViewCamera"); @@ -298,6 +306,8 @@ namespace UnityEngine.Rendering.Tests brg.OnEndCameraRendering(mainCamera); Assert.AreEqual(callbackCounter.Value, 1); + brg.OnEndContextRendering(); + GameObject.DestroyImmediate(cameraObject); brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs, instances).Complete(); brg.DestroyDrawInstances(instances); @@ -346,10 +356,11 @@ namespace UnityEngine.Rendering.Tests var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; var drawCommandCount = 0U; @@ -370,6 +381,8 @@ namespace UnityEngine.Rendering.Tests using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brg.UpdateRenderers(objIDs.AsArray()); var cameraObject = new GameObject("myCamera"); @@ -377,6 +390,8 @@ namespace UnityEngine.Rendering.Tests SubmitCameraRenderRequest(mainCamera); + brg.OnEndContextRendering(); + mainCamera = null; GameObject.DestroyImmediate(cameraObject); @@ -458,10 +473,11 @@ namespace UnityEngine.Rendering.Tests var expectedDrawCommandCount = 2; cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; var drawCommandCount = 0U; @@ -482,6 +498,8 @@ namespace UnityEngine.Rendering.Tests using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brgContext.UpdateLODGroups(lodGroupInstancesID.AsArray()); brg.UpdateRenderers(objIDs.AsArray()); @@ -526,6 +544,8 @@ namespace UnityEngine.Rendering.Tests expectedDrawCommandCount = 1; SubmitCameraRenderRequest(mainCamera); + brg.OnEndContextRendering(); + Assert.AreEqual(7, callbackCounter.Value); mainCamera = null; @@ -611,10 +631,11 @@ namespace UnityEngine.Rendering.Tests var expectedDrawCommandCount = 0; cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; unsafe @@ -633,6 +654,8 @@ namespace UnityEngine.Rendering.Tests using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brgContext.UpdateLODGroups(lodGroupInstancesID.AsArray()); brg.UpdateRenderers(objIDs.AsArray()); @@ -684,6 +707,8 @@ namespace UnityEngine.Rendering.Tests cameraObject.transform.position = new Vector3(0.0f, 0.0f, -4.0f); SubmitCameraRenderRequest(mainCamera); + brg.OnEndContextRendering(); + mainCamera = null; GameObject.DestroyImmediate(cameraObject); @@ -744,10 +769,11 @@ namespace UnityEngine.Rendering.Tests var expectedDrawCommandCount = 0; cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { + jobHandle.Complete(); + if (cc.viewType != BatchCullingViewType.Camera) return; - jobHandle.Complete(); BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; unsafe @@ -766,6 +792,8 @@ namespace UnityEngine.Rendering.Tests using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) { + brg.OnBeginContextRendering(); + brg.UpdateRenderers(objIDs.AsArray()); var cameraObject = new GameObject("myCamera"); @@ -801,6 +829,8 @@ namespace UnityEngine.Rendering.Tests cameraObject.transform.position = new Vector3(0.0f, 0.0f, -10.0f); SubmitCameraRenderRequest(mainCamera); + brg.OnEndContextRendering(); + mainCamera = null; GameObject.DestroyImmediate(cameraObject); diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs deleted file mode 100644 index cbdb338f..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentCopyPasteTests.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Tests; - -namespace UnityEditor.Rendering.Tests -{ - class VolumeComponentCopyPasteTests - { - static T CreateComponent() where T : CopyPasteTestComponent1 => ScriptableObject.CreateInstance(); - - CopyPasteTestComponent1 m_Src1; - CopyPasteTestComponent2 m_Src2; - CopyPasteTestComponent3 m_Src3; - CopyPasteTestComponent1 m_Dst1; - CopyPasteTestComponent2 m_Dst2; - CopyPasteTestComponent3 m_Dst3; - CopyPasteTestComponent1 m_Default1; - CopyPasteTestComponent2 m_Default2; - CopyPasteTestComponent3 m_Default3; - - [SetUp] - public void SetUp() - { - EditorGUIUtility.systemCopyBuffer = ""; - - m_Src1 = CreateComponent().WithModifiedValues(); - m_Src2 = CreateComponent().WithModifiedValues(); - m_Src3 = CreateComponent().WithModifiedValues(); - m_Dst1 = CreateComponent(); - m_Dst2 = CreateComponent(); - m_Dst3 = CreateComponent(); - m_Default1 = CreateComponent(); - m_Default2 = CreateComponent(); - m_Default3 = CreateComponent(); - } - - [Test] - public void CopyPasteSingle() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - m_Src1.AssertEquality(m_Dst1, Assert.AreNotEqual); - VolumeComponentCopyPaste.PasteSettings(m_Dst1); - m_Src1.AssertEquality(m_Dst1, Assert.AreEqual); - } - - [Test] - public void CopyPasteSingleUndoRedo() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - VolumeComponentCopyPaste.PasteSettings(m_Dst1); - Undo.PerformUndo(); - m_Dst1.AssertEquality(m_Default1, Assert.AreEqual); // paste target is unchanged - Undo.PerformRedo(); - m_Dst1.AssertEquality(m_Src1, Assert.AreEqual); // paste target matches source - } - - [Test] - public void CopyPasteMultiple() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1, m_Src2, m_Src3 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreNotEqual); - m_Src2.AssertEquality(m_Dst2, Assert.AreNotEqual); - m_Src3.AssertEquality(m_Dst3, Assert.AreNotEqual); - - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst1, m_Dst2, m_Dst3 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreEqual); - m_Src2.AssertEquality(m_Dst2, Assert.AreEqual); - m_Src3.AssertEquality(m_Dst3, Assert.AreEqual); - } - - [Test] - public void CopyPasteMultipleInDifferentOrder() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1, m_Src2, m_Src3 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreNotEqual); - m_Src2.AssertEquality(m_Dst2, Assert.AreNotEqual); - m_Src3.AssertEquality(m_Dst3, Assert.AreNotEqual); - - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst3, m_Dst1, m_Dst2 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreEqual); - m_Src2.AssertEquality(m_Dst2, Assert.AreEqual); - m_Src3.AssertEquality(m_Dst3, Assert.AreEqual); - } - - [Test] - public void CopyPasteMultipleToSingleComponent() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1, m_Src2, m_Src3 }); - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst3 }); - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst2 }); - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst1 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreEqual); - m_Src2.AssertEquality(m_Dst2, Assert.AreEqual); - m_Src3.AssertEquality(m_Dst3, Assert.AreEqual); - } - - [Test] - public void CopyPasteSingleToMultipleComponent() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1 }); - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst3, m_Dst1, m_Dst2 }); - m_Src1.AssertEquality(m_Dst1, Assert.AreEqual); - } - - [Test] - public void CopyPasteMultipleUndoRedo() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1, m_Src2, m_Src3 }); - VolumeComponentCopyPaste.PasteSettings(new List { m_Dst1, m_Dst2, m_Dst3 }); - - Undo.PerformUndo(); - - // paste target is unchanged - m_Dst1.AssertEquality(m_Default1, Assert.AreEqual); - m_Dst2.AssertEquality(m_Default2, Assert.AreEqual); - m_Dst3.AssertEquality(m_Default3, Assert.AreEqual); - - Undo.PerformRedo(); - - // paste target matches source - m_Dst1.AssertEquality(m_Src1, Assert.AreEqual); - m_Dst2.AssertEquality(m_Src2, Assert.AreEqual); - m_Dst3.AssertEquality(m_Src3, Assert.AreEqual); - } - - [Test] - public void CannotPasteWithEmptyCopyBuffer() - { - Assert.False(VolumeComponentCopyPaste.CanPaste(m_Src1)); - } - - [Test] - public void CanPasteToSelf() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - Assert.True(VolumeComponentCopyPaste.CanPaste(m_Src1)); - } - - [Test] - public void CanPasteToMatchingType() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - Assert.True(VolumeComponentCopyPaste.CanPaste(m_Dst1)); - } - - [Test] - public void CannotPasteToDifferentType() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - Assert.False(VolumeComponentCopyPaste.CanPaste(m_Dst3)); - } - - [Test] - public void CanPasteIfSingleMatchingType() - { - VolumeComponentCopyPaste.CopySettings(m_Src1); - Assert.True(VolumeComponentCopyPaste.CanPaste(new List { m_Dst1, m_Dst2, m_Dst3 })); - } - - [Test] - public void CanPasteIfMultipleMatchingTypes() - { - VolumeComponentCopyPaste.CopySettings(new List { m_Src1, m_Src2, m_Src3 }); - Assert.True(VolumeComponentCopyPaste.CanPaste(new List { m_Dst1, m_Dst3, m_Dst2 })); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs deleted file mode 100644 index a0af0015..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentEditorSupportedOnTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Tests; - -namespace UnityEditor.Rendering.Tests -{ - class VolumeComponentEditorSupportedOnTests : RenderPipelineTests - { - static TestCaseData[] s_TestCaseDataGetItem = - { - new TestCaseData( - null, - new[] { typeof(VolumeComponentSupportedEverywhere) }, - new[] { typeof(VolumeComponentSupportedOnAnySRP), typeof(VolumeComponentSupportedOnCustomSRP) }) - .SetName("Given null SRP asset (Builtin), volumeManager.baseComponentTypeArray contains volume component without attribute but not others"), - - new TestCaseData( - typeof(CustomRenderPipelineAsset), - new[] { typeof(VolumeComponentSupportedEverywhere), typeof(VolumeComponentSupportedOnAnySRP), typeof(VolumeComponentSupportedOnCustomSRP)}, - new Type[] {}) - .SetName("Given CustomRenderPipelineAsset, volumeManager.baseComponentTypeArray contains all volume components"), - - new TestCaseData( - typeof(SecondCustomRenderPipelineAsset), - new[] { typeof(VolumeComponentSupportedEverywhere), typeof(VolumeComponentSupportedOnAnySRP)}, - new[] { typeof(VolumeComponentSupportedOnCustomSRP) }) - .SetName("Given SecondCustomRenderPipelineAsset, volumeManager.baseComponentTypeArray does not contains component that only supports CustomSRP") - }; - - [Test, TestCaseSource(nameof(s_TestCaseDataGetItem))] - public void TestVolumeManagerSupportedOnFiltering(Type renderPipelineAssetType, Type[] expectedTypes, Type[] notExpectedTypes) - { - var volumeManager = new VolumeManager(); - volumeManager.LoadBaseTypes(renderPipelineAssetType); - - foreach (var expectedType in expectedTypes) - Assert.That(() => volumeManager.baseComponentTypeArray.First(t => t == expectedType), Throws.Nothing); - - foreach (var notExpectedType in notExpectedTypes) - Assert.That(() => volumeManager.baseComponentTypeArray.First(t => t == notExpectedType), Throws.InvalidOperationException); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs deleted file mode 100644 index 08811045..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentTests.cs +++ /dev/null @@ -1,425 +0,0 @@ -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Tests; -using ActionTest = System.Action; - -namespace UnityEditor.Rendering.Tests -{ - class TestAnimationCurveVolumeComponent : VolumeComponent - { - public AnimationCurveParameter testParameter = new(AnimationCurve.Linear(0.5f, 10.0f, 1.0f, 15.0f), true); - } - - class VolumeComponentAnimCurveTests - { - #region Interpolation - static bool TestAnimationCurveInterp(AnimationCurve lhsCurve, AnimationCurve rhsCurve, float t, float startTime, float endTime, int numSteps, float eps, bool debugPrint) - { - AnimationCurve midCurve = new AnimationCurve(lhsCurve.keys); - KeyframeUtility.InterpAnimationCurve(ref midCurve, rhsCurve, t); - - for (int i = 0; i <= numSteps; i++) - { - float timeT = ((float)i) / ((float)numSteps); - float currTime = Mathf.Lerp(startTime, endTime, timeT); - - float lhsVal = lhsCurve.Evaluate(currTime); - float rhsVal = rhsCurve.Evaluate(currTime); - - float expectedVal = Mathf.Lerp(lhsVal, rhsVal, t); - - float actualVal = midCurve.Evaluate(currTime); - - float offset = actualVal - expectedVal; - if (debugPrint) - { - Debug.Log(i.ToString() + ": " + offset.ToString()); - } - - if (Mathf.Abs(offset) >= eps) - { - return false; - } - } - - return true; - } - - static AnimationCurve CreateTestCurve(int index) - { - AnimationCurve testCurve = new AnimationCurve(); - if (index == 0) - { - testCurve.AddKey(new Keyframe(0.0f, 3.0f, 2.0f, 2.0f)); - testCurve.AddKey(new Keyframe(4.0f, 2.0f, -1.0f, -1.0f)); - testCurve.AddKey(new Keyframe(7.0f, 2.6f, -1.0f, -1.0f)); - } - else if (index == 1) - { - testCurve.AddKey(new Keyframe(-1.0f, 3.0f, 2.0f, 2.0f)); - testCurve.AddKey(new Keyframe(4.0f, 2.0f, 3.0f, 3.0f)); - testCurve.AddKey(new Keyframe(5.0f, 2.6f, 0.0f, 0.0f)); - testCurve.AddKey(new Keyframe(9.0f, 2.6f, -5.0f, -5.0f)); - } - else if (index == 2) - { - // Needed for the same positions as curve 0 but different values and tangents - testCurve.AddKey(new Keyframe(0.0f, 1.0f, -1.0f, 3.0f)); - testCurve.AddKey(new Keyframe(4.0f, 6.0f, -9.0f, -2.0f)); - testCurve.AddKey(new Keyframe(7.0f, 5.2f, -3.0f, -4.0f)); - } - else - { - // Need for the test case where two curves have no overlap - testCurve.AddKey(new Keyframe(11.0f, 1.0f, -1.0f, 3.0f)); - testCurve.AddKey(new Keyframe(14.0f, 6.0f, -9.0f, -2.0f)); - testCurve.AddKey(new Keyframe(17.0f, 5.2f, -3.0f, -4.0f)); - } - - return testCurve; - } - - static TestCaseData[] s_AnimationCurveTestDatas = - { - new TestCaseData(CreateTestCurve(0), CreateTestCurve(1), 0.25f) - .SetName("CurveTest 1"), - new TestCaseData(CreateTestCurve(1), CreateTestCurve(2), 0.25f) - .SetName("CurveTest 2"), - new TestCaseData(CreateTestCurve(0), CreateTestCurve(2), 0.25f) - .SetName("CurveTest Same Positions"), - new TestCaseData(CreateTestCurve(0), CreateTestCurve(3), 0.25f) - .SetName("CurveTest No Overlap"), - }; - - [Test, TestCaseSource(nameof(s_AnimationCurveTestDatas))] - public void RenderInterpolateAnimationCurve(AnimationCurve lhsCurve, AnimationCurve rhsCurve, float t) - { - Assert.IsTrue(TestAnimationCurveInterp(lhsCurve, rhsCurve, t, -5.0f, 20.0f, 100, 1e-5f, false)); - } - - #endregion - - static TestCaseData[] s_AnimationCurveKeysNotSharedTestDatas = - { - new TestCaseData(null) - .SetName("Reloading the stack makes the parameters be the same as TestAnimationCurveVolumeComponent") - .Returns((2,2,2)), - new TestCaseData((ActionTest)((parameterInterpolated, _, _, stack, volumeManager) => - { - // The replace data will call: AnimationCurveParameter.SetValue make sure the C++ reference is not shared - volumeManager.ReplaceData(stack); - - // Check that the value that stores the interpolated data, if is modified both default values are modified - parameterInterpolated.RemoveKey(1); - })) - .SetName("When Replacing the current interpolated values by the ones in the default, applying modifications to the interpolated parameter do not modify the default parameters") - .Returns((1,2,2)), - new TestCaseData((ActionTest)((_, _, defaultComponentParameterUsedToInitializeStack, _, _) => - { - defaultComponentParameterUsedToInitializeStack.AddKey(0.0f, 1.0f); - })) - .SetName("When modifying the default component used to initialize the stack, the parameters on the stack remain the same, as they should be cloned") - .Returns((2,2,3)), - new TestCaseData((ActionTest)((_, defaultParameterForFastAccess, _, _, _) => - { - defaultParameterForFastAccess.AddKey(0.0f, 1.0f); - defaultParameterForFastAccess.AddKey(0.6f, 2.0f); - })) - .SetName("Check that the default parameter on the stack do not modifies the interpolated value or either the default used to initialize the stack") - .Returns((2,4,2)), - new TestCaseData((ActionTest)((_, defaultParameterForFastAccess, _, stack, volumeManager) => - { - defaultParameterForFastAccess.AddKey(0.0f, 1.0f); - defaultParameterForFastAccess.AddKey(0.6f, 2.0f); - - volumeManager.ReplaceData(stack); - })) - .SetName("Check that ReplaceData should have modified the interpolated value with the default value stored in the stack and not the one used from the default") - .Returns((4,4,2)), - new TestCaseData((ActionTest)((_, _, _, stack, _) => - { - stack.Clear(); - })) - .SetName("Check that clearing the stack should modify and release memory from the volume components that are locally in the stack, but not the default volume used to initialize the stack or the parameterDefaultState stored in VolumeManager") - .Returns((-1,2,2)), - }; - - private TestAnimationCurveVolumeComponent m_DefaultComponent; - - [SetUp] - public void Setup() - { - m_DefaultComponent = ScriptableObject.CreateInstance(); - } - - [TearDown] - public void TearDown() - { - ScriptableObject.DestroyImmediate(m_DefaultComponent); - } - - [Test, Description("UUM-20458, UUM-20456"), TestCaseSource(nameof(s_AnimationCurveKeysNotSharedTestDatas))] - public (int, int, int) AnimationCurveParameterKeysAreNotShared(ActionTest actionToPerform) - { - var vm = new VolumeManager(); - vm.baseComponentTypeArray = new[] {typeof(TestAnimationCurveVolumeComponent)}; - vm.EvaluateVolumeDefaultState(); - - // Initialize the stack - var stack = vm.CreateStack(); - - actionToPerform?.Invoke( - stack.parameters[0].GetValue(), // parameterInterpolated - vm.m_ParametersDefaultState[0].GetValue(), // defaultParameterForFastAccess - m_DefaultComponent.testParameter.GetValue(), // defaultComponentParameterUsedToInitializeStack - stack, - vm); - - return ( - stack.parameters == null ? - -1 : stack.parameters[0].GetValue().length, // parameterInterpolated - vm.m_ParametersDefaultState == null ? - -1 : vm.m_ParametersDefaultState[0].GetValue().length, // defaultParameterForFastAccess - m_DefaultComponent.testParameter.GetValue().length // defaultComponentParameterUsedToInitializeStack - ); - } - } - - class VolumeComponentEditorTests : RenderPipelineTests - { -#pragma warning disable CS0618 - - [HideInInspector] - [VolumeComponentMenu("Tests/No Additional")] - [SupportedOnRenderPipeline(typeof(CustomRenderPipelineAsset))] - class VolumeComponentNoAdditionalAttributes : VolumeComponent - { - public MinFloatParameter parameter = new MinFloatParameter(0f, 0f); - } - - [HideInInspector] - [VolumeComponentMenu("Tests/All Additional")] - [SupportedOnRenderPipeline(typeof(CustomRenderPipelineAsset))] - class VolumeComponentAllAdditionalAttributes : VolumeComponent - { - [AdditionalProperty] - public MinFloatParameter parameter1 = new MinFloatParameter(0f, 0f); - - [AdditionalProperty] - public FloatParameter parameter2 = new MinFloatParameter(0f, 0f); - } - - [HideInInspector] - [VolumeComponentMenu("Tests/Mixed Additional")] - [SupportedOnRenderPipeline(typeof(CustomRenderPipelineAsset))] - class VolumeComponentMixedAdditionalAttributes : VolumeComponent - { - public MinFloatParameter parameter1 = new MinFloatParameter(0f, 0f); - - [AdditionalProperty] - public FloatParameter parameter2 = new MinFloatParameter(0f, 0f); - - public MinFloatParameter parameter3 = new MinFloatParameter(0f, 0f); - - [AdditionalProperty] - public FloatParameter parameter4 = new MinFloatParameter(0f, 0f); - } -#pragma warning restore CS0618 - - - private (VolumeComponent, VolumeComponentEditor) CreateEditorAndComponent(Type volumeComponentType) - { - var component = (VolumeComponent)ScriptableObject.CreateInstance(volumeComponentType); - var editor = (VolumeComponentEditor)Editor.CreateEditor(component); - editor.Invoke("Init"); - return (component, editor); - } - - private void DestroyEditorAndComponent(VolumeComponent component, VolumeComponentEditor editor) - { - ScriptableObject.DestroyImmediate(editor); - ScriptableObject.DestroyImmediate(component); - } - - [Test] - public void TestOverridesChanges() - { - (VolumeComponent component, VolumeComponentEditor editor) = CreateEditorAndComponent(typeof(VolumeComponentMixedAdditionalAttributes)); - - component.SetAllOverridesTo(false); - bool allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", false); - Assert.True(allOverridesState); - - component.SetAllOverridesTo(true); - - // Was the change correct? - allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", true); - Assert.True(allOverridesState); - - // Enable the advance mode on the editor - editor.showAdditionalProperties = true; - - // Everything is false - component.SetAllOverridesTo(false); - - // Disable the advance mode on the editor - editor.showAdditionalProperties = false; - - // Now just set to true the overrides of non additional properties - editor.Invoke("SetOverridesTo", true); - - // Check that the non additional properties must be false - allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", true); - Assert.False(allOverridesState); - - DestroyEditorAndComponent(component, editor); - } - - static TestCaseData[] s_AdditionalAttributesTestCaseDatas = - { - new TestCaseData(typeof(VolumeComponentNoAdditionalAttributes)) - .Returns(Array.Empty()) - .SetName("VolumeComponentNoAdditionalAttributes"), - new TestCaseData(typeof(VolumeComponentAllAdditionalAttributes)) - .Returns(new string[2] {"parameter1", "parameter2"}) - .SetName("VolumeComponentAllAdditionalAttributes"), - new TestCaseData(typeof(VolumeComponentMixedAdditionalAttributes)) - .Returns(new string[2] {"parameter2", "parameter4"}) - .SetName("VolumeComponentMixedAdditionalAttributes"), - }; - - [Test, TestCaseSource(nameof(s_AdditionalAttributesTestCaseDatas))] - public string[] AdditionalProperties(Type volumeComponentType) - { - (VolumeComponent component, VolumeComponentEditor editor) = CreateEditorAndComponent(volumeComponentType); - - var fields = component - .GetFields() - .Where(f => f.GetCustomAttribute() != null) - .Select(f => f.Name) - .ToArray(); - - var notAdditionalParameters = editor.GetField("m_VolumeNotAdditionalParameters") as List; - Assert.True(fields.Count() + notAdditionalParameters.Count == component.parameters.Count); - - DestroyEditorAndComponent(component, editor); - - return fields; - } - - #region Decorators Handling Test - - [HideInInspector] - class VolumeComponentDecorators : VolumeComponent - { - [Tooltip("Increase to make the noise texture appear bigger and less")] - public FloatParameter _NoiseTileSize = new FloatParameter(25.0f); - - [InspectorName("Color")] - public ColorParameter _FogColor = new ColorParameter(Color.grey); - - [InspectorName("Size and occurrence"), Tooltip("Increase to make patches SMALLER, and frequent")] - public ClampedFloatParameter _HighNoiseSpaceFreq = new ClampedFloatParameter(0.1f, 0.1f, 1f); - } - - readonly (string displayName, string tooltip)[] k_ExpectedResults = - { - (string.Empty, "Increase to make the noise texture appear bigger and less"), - ("Color", string.Empty), - ("Size and occurrence", "Increase to make patches SMALLER, and frequent") - }; - - [Test] - public void TestHandleParameterDecorators() - { - (VolumeComponent component, VolumeComponentEditor editor) = CreateEditorAndComponent(typeof(VolumeComponentDecorators)); - - var parameters = - editor.GetField("m_Parameters") as List<(GUIContent displayName, int displayOrder, - SerializedDataParameter param)>; - - Assert.True(parameters != null && parameters.Count() == k_ExpectedResults.Count()); - - for (int i = 0; i < k_ExpectedResults.Count(); ++i) - { - var property = parameters[i].param; - var title = new GUIContent(parameters[i].displayName); - - editor.Invoke("HandleDecorators", property, title); - - Assert.True(k_ExpectedResults[i].displayName == title.text); - Assert.True(k_ExpectedResults[i].tooltip == title.tooltip); - } - - DestroyEditorAndComponent(component, editor); - } - - #endregion - - [Test] - public void TestSupportedOnAvoidedIfHideInInspector() - { - SetupRenderPipeline(); - - Type[] componentTypesWithHideInInspectorAttribute = - { - typeof(VolumeComponentNoAdditionalAttributes), - typeof(VolumeComponentAllAdditionalAttributes), - typeof(VolumeComponentMixedAdditionalAttributes) - }; - - var volumeManager = new VolumeManager(); - volumeManager.Initialize(); - - var types = volumeManager.baseComponentTypeArray; - Assert.NotNull(types); - foreach (var t in componentTypesWithHideInInspectorAttribute) - Assert.True(types.Contains(t)); - - var typesForDisplay = volumeManager.GetVolumeComponentsForDisplay(typeof(CustomRenderPipelineAsset)); - Assert.NotNull(typesForDisplay); - foreach (var t in componentTypesWithHideInInspectorAttribute) - Assert.False(typesForDisplay.Any(p => p.Item2 == t)); - - volumeManager.Deinitialize(); - } - - [Test] - public void VolumeManagerLifetime() - { - var volumeManager = new VolumeManager(); - Assert.IsFalse(volumeManager.isInitialized); - - volumeManager.Initialize(); - - Assert.IsTrue(volumeManager.isInitialized); - - volumeManager.Deinitialize(); - - Assert.IsFalse(volumeManager.isInitialized); - } - - [Test] - public void TestInputForGetVolumeComponentsForDisplay() - { - var volumeManager = new VolumeManager(); - volumeManager.Initialize(); - - Assert.That(() => volumeManager.GetVolumeComponentsForDisplay(typeof(CustomRenderPipeline)), Throws.ArgumentException); - Assert.IsNotNull(volumeManager.GetVolumeComponentsForDisplay(null)); - } - - [Test] - public void TestVolumeManagerFetchsTypesWhenNotInitialized() - { - var volumeManager = new VolumeManager(); - var typesForDisplay = volumeManager.GetVolumeComponentsForDisplay(typeof(CustomRenderPipelineAsset)); - Assert.IsTrue(typesForDisplay.Count > 0); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs deleted file mode 100644 index e23425a7..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/Volumes/VolumeComponentVisibilityTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using NUnit.Framework; -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Tests; - -namespace UnityEditor.Rendering.Tests -{ - class VolumeComponentVisibilityTests : RenderPipelineTests - { - static TestCaseData[] s_TestCaseDataGetItem = - { - new TestCaseData(typeof(VolumeComponentNoSupportedOn), null) - .SetName($"Given BuiltIn pipeline When checking visibility for {nameof(VolumeComponent)} editor without attributes Then it's hidden in {nameof(VolumeComponentEditor)}") - .Returns(false), - new TestCaseData(typeof(VolumeComponentNotSpecifiedSupportedOn), null) - .SetName( - $"Given BuiltIn pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} without parameters Then it's hidden in {nameof(VolumeComponentEditor)}") - .Returns(false), - new TestCaseData(typeof(VolumeComponentCustomRenderPipelineAsset), null) - .SetName( - $"Given BuiltIn pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} with {nameof(CustomRenderPipelineAsset)} Then it's hidden in {nameof(VolumeComponentEditor)}") - .Returns(false), - new TestCaseData(typeof(VolumeComponentNoSupportedOn), typeof(SecondCustomRenderPipelineAsset)) - .SetName($"Given {nameof(SecondCustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor without attributes Then it's visible in {nameof(VolumeComponentEditor)}") - .Returns(true), - new TestCaseData(typeof(VolumeComponentNotSpecifiedSupportedOn), typeof(SecondCustomRenderPipelineAsset)) - .SetName( - $"Given {nameof(SecondCustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} without parameters Then it's visible in {nameof(VolumeComponentEditor)}") - .Returns(true), - new TestCaseData(typeof(VolumeComponentCustomRenderPipelineAsset), typeof(SecondCustomRenderPipelineAsset)) - .SetName( - $"Given {nameof(SecondCustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} with {nameof(CustomRenderPipelineAsset)} Then it's hidden in {nameof(VolumeComponentEditor)}") - .Returns(false), - new TestCaseData(typeof(VolumeComponentNoSupportedOn), typeof(CustomRenderPipelineAsset)) - .SetName($"Given {nameof(CustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor without attributes Then it's visible in {nameof(VolumeComponentEditor)}") - .Returns(true), - new TestCaseData(typeof(VolumeComponentNotSpecifiedSupportedOn), typeof(CustomRenderPipelineAsset)) - .SetName( - $"Given {nameof(CustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} without parameters Then it's visible in {nameof(VolumeComponentEditor)}") - .Returns(true), - new TestCaseData(typeof(VolumeComponentCustomRenderPipelineAsset), typeof(CustomRenderPipelineAsset)) - .SetName( - $"Given {nameof(CustomRenderPipelineAsset)} pipeline When checking visibility for {nameof(VolumeComponent)} editor with {nameof(SupportedOnRenderPipelineAttribute)} with {nameof(CustomRenderPipelineAsset)} Then it's visible in {nameof(VolumeComponentEditor)}") - .Returns(true), - }; - - [Test, TestCaseSource(nameof(s_TestCaseDataGetItem))] - public bool DetermineVisibilityMethodTests(Type volumeComponentType, Type renderPipelineAssetType) - { - //Arrange - SetupRenderPipeline(renderPipelineAssetType); - var component = (VolumeComponent)ScriptableObject.CreateInstance(volumeComponentType); - var editor = (VolumeComponentEditor)Editor.CreateEditor(component); - editor.Init(); - - //Act - editor.DetermineVisibility(renderPipelineAssetType, RenderPipelineManager.currentPipeline?.GetType()); - bool visible = editor.visible; - - ScriptableObject.DestroyImmediate(editor); - ScriptableObject.DestroyImmediate(component); - - //Assert - return visible; - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs deleted file mode 100644 index 8fed189b..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace UnityEngine.Rendering.Tests -{ - [Serializable] - class TestVolume : VolumeComponent - { - public static readonly float k_DefaultValue = 123.0f; - public static readonly float k_OverrideValue = 456.0f; - public static readonly float k_OverrideValue2 = 789.0f; - public static readonly float k_OverrideValue3 = 999.0f; - - public FloatParameter param = new(k_DefaultValue); - - public bool IsActive() => true; - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs.meta deleted file mode 100644 index 3767065d..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/TestVolume.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 74955a4b0b4243bc87231e8b59ed9140 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs deleted file mode 100644 index 654901d7..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using NUnit.Framework; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - internal class CopyPasteTestComponent1 : VolumeComponent - { - public FloatParameter p1 = new FloatParameter(0f); - public IntParameter p2 = new IntParameter(0); - - public CopyPasteTestComponent1 WithModifiedValues() - { - p1.value = 123.0f; - p2.value = 123; - return this; - } - - public void AssertEquality(CopyPasteTestComponent1 other, Action assertionFunction) - { - Assert.AreEqual(GetType(), other.GetType()); - assertionFunction(p1.value, other.p1.value); - assertionFunction(p2.value, other.p2.value); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs.meta deleted file mode 100644 index b026539d..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent1.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5c6e2978d9a7a1047939ccd282f5079c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs deleted file mode 100644 index e79500cc..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - internal class CopyPasteTestComponent2 : CopyPasteTestComponent1 - { - public BoolParameter p21 = new BoolParameter(false); - - public new CopyPasteTestComponent2 WithModifiedValues() - { - base.WithModifiedValues(); - p21.value = true; - return this; - } - - public void AssertEquality(CopyPasteTestComponent2 other, Action assertionFunction) - { - base.AssertEquality(other, assertionFunction); - assertionFunction(p21.value, other.p21.value); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs.meta deleted file mode 100644 index 8ff55b89..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent2.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bc107f21e1c9f754fac8890d0505cd0f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs deleted file mode 100644 index 3fe5089a..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - internal class CopyPasteTestComponent3 : CopyPasteTestComponent1 - { - public ColorParameter p31 = new ColorParameter(Color.black); - - public new CopyPasteTestComponent3 WithModifiedValues() - { - base.WithModifiedValues(); - p31.value = Color.green; - return this; - } - - public void AssertEquality(CopyPasteTestComponent3 other, Action assertionFunction) - { - base.AssertEquality(other, assertionFunction); - assertionFunction(p31.value, other.p31.value); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs.meta deleted file mode 100644 index 7a780a93..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/CopyPasteTestComponent3.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6f2a4a20004d56f4483343b4af1f3eca -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs deleted file mode 100644 index 8585802d..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UnityEngine; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [HideInInspector] - [VolumeComponentMenu("Supported On Tests/Not Specified Pipeline Supported On")] - [SupportedOnRenderPipeline(typeof(CustomRenderPipelineAsset))] - class VolumeComponentCustomRenderPipelineAsset : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs.meta deleted file mode 100644 index 5cfa7320..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentCustomRenderPipelineAsset.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9ba68039029f7524aa94bcfe2a986c7d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs deleted file mode 100644 index 5a3f7c72..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UnityEngine; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [HideInInspector] - [VolumeComponentMenu("Supported On Tests/No supported on")] - class VolumeComponentNoSupportedOn : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs.meta deleted file mode 100644 index e48ff00e..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNoSupportedOn.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5fe8fd682ff14384ea2c45211af634e2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs deleted file mode 100644 index 9d8f3c1c..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UnityEngine; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [HideInInspector] - [VolumeComponentMenu("Supported On Tests/Not Specified Pipeline Supported On")] - [SupportedOnRenderPipeline] - class VolumeComponentNotSpecifiedSupportedOn : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs.meta deleted file mode 100644 index 8acdacb8..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentNotSpecifiedSupportedOn.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f6fd2d45343493144b9c70df7e45b91a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs deleted file mode 100644 index b343811b..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [VolumeComponentMenu("SupportedOnTests/SupportedEverywhere")] - class VolumeComponentSupportedEverywhere : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs.meta deleted file mode 100644 index ae76c5f4..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedEverywhere.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 55d05e3ee524c534cb717d062ee589df -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs deleted file mode 100644 index e76ca139..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [VolumeComponentMenu("SupportedOnTests/SupportedOnAnySRP")] - [SupportedOnRenderPipeline] - class VolumeComponentSupportedOnAnySRP : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs.meta deleted file mode 100644 index 76308b67..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnAnySRP.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a396658632bc7024b85322b3cd04b825 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs deleted file mode 100644 index 39b8c55e..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.Tests -{ - [VolumeComponentMenu("SupportedOnTests/SupportedOnCustomSRP")] - [SupportedOnRenderPipeline(typeof(CustomRenderPipelineAsset))] - class VolumeComponentSupportedOnCustomSRP : VolumeComponent - { - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs.meta deleted file mode 100644 index c93aadd1..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeComponents/VolumeComponentSupportedOnCustomSRP.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0ba13ceddca716542ab5d1e218c6547b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs deleted file mode 100644 index ec81fc9f..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; - -namespace UnityEngine.Rendering.Tests -{ - [TestFixture("Local")] - [TestFixture("Global")] - class VolumeManagerTests - { - readonly LayerMask k_defaultLayer = 1; - VolumeProfile m_VolumeProfile; - readonly List m_Objects = new(); - readonly bool m_IsGlobal; - - VolumeManager volumeManager { get; set; } - VolumeStack stack => volumeManager.stack; - GameObject camera { get; set; } - - public VolumeManagerTests(string volumeType) - { - m_IsGlobal = volumeType switch - { - "Global" => true, - "Local" => false, - _ => throw new ArgumentException(volumeType) - }; - } - - [SetUp] - public void Setup() - { - m_VolumeProfile = ScriptableObject.CreateInstance(); - - var volumeComponent = m_VolumeProfile.Add(); - volumeComponent.param.Override(TestVolume.k_OverrideValue); - - volumeManager = new VolumeManager(); - volumeManager.Initialize(); - camera = new GameObject("Camera", typeof(Camera)); - m_Objects.Add(camera); - } - - [TearDown] - public void TearDown() - { - CoreUtils.Destroy(m_VolumeProfile); - - foreach (var go in m_Objects) - CoreUtils.Destroy(go); - - volumeManager.Deinitialize(); - } - - static IEnumerable TestCaseSources() - { - yield return new TestCaseData( - new Action((vm, v) => vm.Unregister(v)), - new Action((vm, v) => vm.Register(v))) - .SetName("Parameter evaluation is correct when volume is unregistered and registered"); - - yield return new TestCaseData( - new Action((vm, v) => v.enabled = false), - new Action((vm, v) => v.enabled = true)) - .SetName("Parameter evaluation is correct when volume is disabled and enabled"); - - yield return new TestCaseData( - new Action((vm, v) => v.profileRef.components[0].SetAllOverridesTo(false)), - new Action((vm, v) => v.profileRef.components[0].SetAllOverridesTo(true))) - .SetName("Parameter evaluation is correct when overrides are disabled and enabled"); - } - - Volume CreateVolume(string name) - { - var volumeGameObject = new GameObject(name, typeof(Volume)); - if (!m_IsGlobal) - volumeGameObject.AddComponent(); - var volume = volumeGameObject.GetComponent(); - volume.isGlobal = m_IsGlobal; - - m_Objects.Add(volume.gameObject); - - return volume; - } - - [TestCaseSource(nameof(TestCaseSources))] - public void ParameterEvaluationTest(Action disableAction, Action enableAction) - { - var volume = CreateVolume("Volume"); - volume.sharedProfile = m_VolumeProfile; - volumeManager.Register(volume); - - volumeManager.Update(camera.transform, k_defaultLayer); - Assert.AreEqual(true, stack.requiresReset); // Local volume present - stack reset needed - Assert.AreEqual(TestVolume.k_OverrideValue, stack.GetComponent().param.value); - - disableAction.Invoke(volumeManager, volume); - volumeManager.Update(camera.transform, k_defaultLayer); - - Assert.AreEqual(TestVolume.k_DefaultValue, stack.GetComponent().param.value); // Value still resets to default - - enableAction.Invoke(volumeManager, volume); - volumeManager.Update(camera.transform, k_defaultLayer); - - Assert.AreEqual(true, stack.requiresReset); // Local volume is back - stack reset needed - Assert.AreEqual(TestVolume.k_OverrideValue, stack.GetComponent().param.value); // Value overridden again - } - - [Test] - public void ParameterOverrideTest() - { - var volume = CreateVolume("Volume"); - volume.priority = 0f; - volume.sharedProfile = m_VolumeProfile; - volumeManager.Register(volume); - - volumeManager.Update(camera.transform, k_defaultLayer); - Assert.AreEqual(TestVolume.k_OverrideValue, stack.GetComponent().param.value); - - const float PriorityOverrideValue = 999.0f; - var priorityVolumeProfile = ScriptableObject.CreateInstance(); - var priorityVolumeComponent = priorityVolumeProfile.Add(); - priorityVolumeComponent.param.Override(PriorityOverrideValue); - - var volume1 = CreateVolume("Volume Priority 1"); - volume1.priority = 1f; - volume1.sharedProfile = priorityVolumeProfile; - volumeManager.Register(volume1); - - volumeManager.Update(camera.transform, k_defaultLayer); - Assert.AreEqual(PriorityOverrideValue, stack.GetComponent().param.value); - - volume.priority = 2f; // Raise priority of the original volume to be higher - volumeManager.SetLayerDirty(volume.gameObject.layer); // Mark dirty to apply new priority (normally done by Volume.Update()) - - volumeManager.Update(camera.transform, k_defaultLayer); - Assert.AreEqual(TestVolume.k_OverrideValue, stack.GetComponent().param.value); - - CoreUtils.Destroy(priorityVolumeProfile); - } - } - - class VolumeManagerDefaultProfileTests - { - VolumeProfile m_VolumeProfile1; - VolumeProfile m_VolumeProfile2; - VolumeProfile m_VolumeProfile3; - VolumeManager volumeManager { get; set; } - VolumeStack stack => volumeManager.stack; - TestVolume GetDefaultState() => volumeManager.GetVolumeComponentDefaultState(typeof(TestVolume)) as TestVolume; - - [SetUp] - public void Setup() - { - m_VolumeProfile1 = ScriptableObject.CreateInstance(); - m_VolumeProfile1.Add().param.Override(TestVolume.k_OverrideValue); - - m_VolumeProfile2 = ScriptableObject.CreateInstance(); - m_VolumeProfile2.Add().param.Override(TestVolume.k_OverrideValue2); - - m_VolumeProfile3 = ScriptableObject.CreateInstance(); - m_VolumeProfile3.Add().param.Override(TestVolume.k_OverrideValue3); - } - - [TearDown] - public void TearDown() - { - CoreUtils.Destroy(m_VolumeProfile1); - CoreUtils.Destroy(m_VolumeProfile2); - CoreUtils.Destroy(m_VolumeProfile3); - volumeManager.Deinitialize(); - } - - [Test] - public void ParameterIsCorrectByDefault() - { - volumeManager = new VolumeManager(); - volumeManager.Initialize(); - - LayerMask defaultLayer = 1; - var camera = new GameObject("Camera", typeof(Camera)); - Assert.AreEqual(true, stack.requiresReset); // Initially, reset is required - Assert.AreEqual(TestVolume.k_DefaultValue, stack.GetComponent().param.value); // Default value retrievable without calling Update() - volumeManager.Update(camera.transform, defaultLayer); - Assert.AreEqual(false, stack.requiresReset); // No volumes - no stack reset needed - Assert.AreEqual(TestVolume.k_DefaultValue, stack.GetComponent().param.value); - } - - [Test] - public void DefaultProfilesAreAppliedToDefaultState() - { - volumeManager = new VolumeManager(); - volumeManager.Initialize(m_VolumeProfile1, null); - - Assert.AreEqual(TestVolume.k_OverrideValue, GetDefaultState().param.value); - - volumeManager.SetQualityDefaultProfile(m_VolumeProfile2); - Assert.AreEqual(TestVolume.k_OverrideValue2, GetDefaultState().param.value); - - volumeManager.SetCustomDefaultProfiles(new List { m_VolumeProfile3 }); - Assert.AreEqual(TestVolume.k_OverrideValue3, GetDefaultState().param.value); - - volumeManager.SetGlobalDefaultProfile(null); - Assert.AreEqual(TestVolume.k_OverrideValue3, GetDefaultState().param.value); - - volumeManager.SetQualityDefaultProfile(null); - Assert.AreEqual(TestVolume.k_OverrideValue3, GetDefaultState().param.value); - - volumeManager.SetCustomDefaultProfiles(null); - Assert.AreEqual(TestVolume.k_DefaultValue, GetDefaultState().param.value); - } - - [Test] - public void CustomDefaultProfilesAreAppliedInOrder() - { - volumeManager = new VolumeManager(); - volumeManager.Initialize(); - - volumeManager.SetCustomDefaultProfiles(new List { m_VolumeProfile1, m_VolumeProfile2 }); - Assert.AreEqual(TestVolume.k_OverrideValue2, GetDefaultState().param.value); - - volumeManager.SetCustomDefaultProfiles(new List { m_VolumeProfile2, m_VolumeProfile1 }); - Assert.AreEqual(TestVolume.k_OverrideValue, GetDefaultState().param.value); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs.meta deleted file mode 100644 index 221d8a36..00000000 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/VolumeManagerTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f7eb520e400949209a91ed78830d1731 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/package.json b/Packages/com.unity.render-pipelines.core/package.json index 1df56e30..8e9bebb6 100644 --- a/Packages/com.unity.render-pipelines.core/package.json +++ b/Packages/com.unity.render-pipelines.core/package.json @@ -14,5 +14,5 @@ "com.unity.modules.jsonserialize": "1.0.0", "com.unity.rendering.light-transport": "1.0.1" }, - "_fingerprint": "79d3abb9adf1668400072f165c52ac7c03b1bca9" + "_fingerprint": "943f3af6fe14719bad5cae2cf73687f86e685e1b" } diff --git a/Packages/com.unity.render-pipelines.high-definition-config/package.json b/Packages/com.unity.render-pipelines.high-definition-config/package.json index 92dcc54d..d080c346 100644 --- a/Packages/com.unity.render-pipelines.high-definition-config/package.json +++ b/Packages/com.unity.render-pipelines.high-definition-config/package.json @@ -7,5 +7,5 @@ "dependencies": { "com.unity.render-pipelines.core": "17.1.0" }, - "_fingerprint": "df264f43ceeeb27fd645ba51a52555c620dd9154" + "_fingerprint": "8c699487ee0c5bb938902fb2e91075c921a1fb5f" } diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs index 78c94c8a..62f10b21 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/Decal/DecalProjectorEditor.cs @@ -630,9 +630,9 @@ namespace UnityEditor.Rendering.HighDefinition m_SizeValues[axe].floatValue = newSize; } - internal void MinMaxSliderWithFields(GUIContent label, ref float minValue, ref float maxValue, float minLimit, float maxLimit) + internal void MinMaxSliderWithFields(Rect rect, GUIContent label, ref float minValue, ref float maxValue, float minLimit, float maxLimit) { - var rect = EditorGUILayout.GetControlRect(); + // Reserve label space and push the slider rect to the right rect = EditorGUI.PrefixLabel(rect, label); const float fieldWidth = 40, padding = 4; @@ -662,6 +662,45 @@ namespace UnityEditor.Rendering.HighDefinition } } + void DoRenderingLayerMask() + { + Rect rect = EditorGUILayout.GetControlRect(true, 18f); + EditorGUI.BeginProperty(rect, k_DecalLayerMaskContent, m_DecalLayerMask); + + var mask = m_DecalLayerMask.uintValue; + EditorGUI.BeginChangeCheck(); + mask = EditorGUI.RenderingLayerMaskField(rect, k_DecalLayerMaskContent, (RenderingLayerMask)mask, EditorStyles.layerMaskField); + if (EditorGUI.EndChangeCheck()) + { + m_DecalLayerMask.intValue = unchecked((int) mask); + serializedObject.ApplyModifiedProperties(); + } + + EditorGUI.EndProperty(); + } + + void DoAngleFade() + { + // The slider edits 2 different properties. Both can be overridden separately. + var rect = EditorGUILayout.GetControlRect(); + EditorGUI.BeginProperty(rect, k_AngleFadeContent, m_StartAngleFadeProperty); + EditorGUI.BeginProperty(rect, k_AngleFadeContent, m_EndAngleFadeProperty); + + float angleFadeMinValue = m_StartAngleFadeProperty.floatValue; + float angleFadeMaxValue = m_EndAngleFadeProperty.floatValue; + EditorGUI.BeginChangeCheck(); + MinMaxSliderWithFields(rect,k_AngleFadeContent, ref angleFadeMinValue, ref angleFadeMaxValue, 0.0f, 180.0f); + if (EditorGUI.EndChangeCheck()) + { + m_StartAngleFadeProperty.floatValue = angleFadeMinValue; + m_EndAngleFadeProperty.floatValue = angleFadeMaxValue; + serializedObject.ApplyModifiedProperties(); + } + + EditorGUI.EndProperty(); + EditorGUI.EndProperty(); + } + public override void OnInspectorGUI() { bool supportDecals = false; @@ -735,14 +774,7 @@ namespace UnityEditor.Rendering.HighDefinition decalLayerEnabled = supportDecals && hdrp.currentPlatformRenderPipelineSettings.supportDecalLayers; using (new EditorGUI.DisabledScope(!decalLayerEnabled)) { - var mask = m_DecalLayerMask.uintValue; - EditorGUI.BeginChangeCheck(); - mask = EditorGUILayout.RenderingLayerMaskField(k_DecalLayerMaskContent, mask); - if (EditorGUI.EndChangeCheck()) - { - m_DecalLayerMask.intValue = unchecked((int) mask); - EditorUtility.SetDirty(m_DecalLayerMask.serializedObject.targetObject); - } + DoRenderingLayerMask(); } } @@ -758,15 +790,7 @@ namespace UnityEditor.Rendering.HighDefinition EditorGUILayout.PropertyField(m_FadeScaleProperty, k_FadeScaleContent); using (new EditorGUI.DisabledScope(!decalLayerEnabled)) { - float angleFadeMinValue = m_StartAngleFadeProperty.floatValue; - float angleFadeMaxValue = m_EndAngleFadeProperty.floatValue; - EditorGUI.BeginChangeCheck(); - MinMaxSliderWithFields(k_AngleFadeContent, ref angleFadeMinValue, ref angleFadeMaxValue, 0.0f, 180.0f); - if (EditorGUI.EndChangeCheck()) - { - m_StartAngleFadeProperty.floatValue = angleFadeMinValue; - m_EndAngleFadeProperty.floatValue = angleFadeMaxValue; - } + DoAngleFade(); } if (!decalLayerEnabled) @@ -985,7 +1009,7 @@ namespace UnityEditor.Rendering.HighDefinition } if (shader != null) - { + { var material = new Material(shader); AssetDatabase.CreateAsset(material, materialName); ProjectWindowUtil.ShowCreatedAsset(material); diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/FogVolumePropertyBlock.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/FogVolumePropertyBlock.cs index 1400885b..fd745434 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/FogVolumePropertyBlock.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/FogVolumePropertyBlock.cs @@ -20,6 +20,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph public static GUIContent blendMode = new GUIContent("Blend Mode", "Determines how the fog volume will blend with other fogs in the scene."); public static GUIContent singleScatteringAlbedo = new GUIContent("Single Scattering Albedo", "The color this fog scatters light to."); public static GUIContent fogDistance = new GUIContent("Fog Distance", "Density at the base of the fog. Determines how far you can see through the fog in meters."); + public static GUIContent debugSymbolsText = new GUIContent("Debug Symbols", "When enabled, HDRP activates d3d11 debug symbols for this Shader."); } protected override string title => "Fog Volume Options"; @@ -35,6 +36,9 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph // AddProperty(Styles.singleScatteringAlbedo, () => fogData.singleScatteringAlbedo, (newValue) => fogData.singleScatteringAlbedo = newValue); // AddProperty(Styles.fogDistance, () => fogData.fogDistance, (newValue) => fogData.fogDistance = newValue); AddProperty(Styles.blendMode, () => fogData.blendMode, (newValue) => fogData.blendMode = newValue); + + if (Unsupported.IsDeveloperMode()) + AddProperty(Styles.debugSymbolsText, () => systemData.debugSymbols, (newValue) => systemData.debugSymbols = newValue); } } } diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/FogVolumeSubTarget.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/FogVolumeSubTarget.cs index 5d10995e..3b5279bb 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/FogVolumeSubTarget.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/FogVolumeSubTarget.cs @@ -172,7 +172,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph { // Definition displayName = HDShaderPassNames.s_FogVolumeVoxelizeStr, - referenceName = "SHADERPASS_FOGVOLUME_VOXELIZATION", + referenceName = "SHADERPASS_FOG_VOLUME_VOXELIZATION", lightMode = HDShaderPassNames.s_FogVolumeVoxelizeStr, useInPreview = false, @@ -200,7 +200,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph { // Definition displayName = "ShaderGraphPreview", - referenceName = "SHADERPASS_FOGVOLUME_PREVIEW", + referenceName = "SHADERPASS_FOG_VOLUME_PREVIEW", lightMode = "ShaderGraphPreview", useInPreview = true, @@ -228,7 +228,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph { // Definition displayName = HDShaderPassNames.s_VolumetricFogVFXOverdrawDebugStr, - referenceName = "SHADERPASS_FOGVOLUME_OVERDRAW_DEBUG", + referenceName = "SHADERPASS_FOG_VOLUME_OVERDRAW_DEBUG", lightMode = HDShaderPassNames.s_VolumetricFogVFXOverdrawDebugStr, useInPreview = true, @@ -308,6 +308,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph const string kPacking = "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"; const string kColor = "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"; const string kFunctions = "Packages/com.unity.shadergraph/ShaderGraphLibrary/Functions.hlsl"; + const string kVoxelizationTransforms = "Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl"; const string kVoxelizePass = "Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassVoxelize.hlsl"; const string kPreviewPass = "Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassPreview.hlsl"; const string kOverdrawPass = "Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/OverdrawDebug.hlsl"; @@ -318,6 +319,7 @@ namespace UnityEditor.Rendering.HighDefinition.ShaderGraph { kColor, IncludeLocation.Pregraph }, { kFunctions, IncludeLocation.Pregraph }, { CoreIncludes.MinimalCorePregraph }, + { kVoxelizationTransforms, IncludeLocation.Pregraph }, { kVoxelizePass, IncludeLocation.Postgraph }, }; diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/OverdrawDebug.hlsl b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/OverdrawDebug.hlsl index 7e079bfa..24b54722 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/OverdrawDebug.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/OverdrawDebug.hlsl @@ -1,4 +1,4 @@ -#if SHADERPASS != SHADERPASS_FOGVOLUME_OVERDRAW_DEBUG +#if SHADERPASS != SHADERPASS_FOG_VOLUME_OVERDRAW_DEBUG #error SHADERPASS_is_not_correctly_define #endif diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassPreview.hlsl b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassPreview.hlsl index bd90a161..cc785e80 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassPreview.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassPreview.hlsl @@ -1,4 +1,4 @@ -#if SHADERPASS != SHADERPASS_FOGVOLUME_PREVIEW +#if SHADERPASS != SHADERPASS_FOG_VOLUME_PREVIEW #error SHADERPASS_is_not_correctly_define #endif diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassVoxelize.hlsl b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassVoxelize.hlsl index 2f0ab60c..3f5e933c 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassVoxelize.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/ShaderPassVoxelize.hlsl @@ -1,4 +1,4 @@ -#if SHADERPASS != SHADERPASS_FOGVOLUME_VOXELIZATION +#if SHADERPASS != SHADERPASS_FOG_VOLUME_VOXELIZATION #error SHADERPASS_is_not_correctly_define #endif @@ -91,14 +91,13 @@ VertexToFragment Vert(uint instanceId : INSTANCEID_SEMANTIC, uint vertexId : VER return output; } -FragInputs BuildFragInputs(VertexToFragment v2f, float3 voxelPositionOS, float3 voxelClipSpace) +FragInputs BuildFragInputs(VertexToFragment v2f, float3 voxelPositionWS, float3 voxelClipSpace) { FragInputs output; ZERO_INITIALIZE(FragInputs, output); - float3 positionWS = mul(UNITY_MATRIX_M, float4(voxelPositionOS, 1)).xyz; output.positionSS = v2f.positionCS; - output.positionRWS = output.positionPredisplacementRWS = positionWS; + output.positionRWS = output.positionPredisplacementRWS = voxelPositionWS; output.positionPixel = uint2(v2f.positionCS.xy); output.texCoord0 = float4(saturate(voxelClipSpace * 0.5 + 0.5), 0); output.tangentToWorld = k_identity3x3; @@ -140,9 +139,11 @@ void Frag(VertexToFragment v2f, out float4 outColor : SV_Target0) float3 rayoriginWS = GetCurrentViewPosition(); float3 voxelCenterWS = rayoriginWS + sliceDistance * raycenterDirWS; + // Build rotation matrix from normalized OBB axes to transform the world space position float3x3 obbFrame = float3x3(_VolumetricMaterialObbRight.xyz, _VolumetricMaterialObbUp.xyz, cross(_VolumetricMaterialObbRight.xyz, _VolumetricMaterialObbUp.xyz)); - float3 voxelCenterBS = mul(voxelCenterWS - _VolumetricMaterialObbCenter.xyz + _WorldSpaceCameraPos.xyz, transpose(obbFrame)); + // Rotate world position around the center of the local fog OBB + float3 voxelCenterBS = mul(GetAbsolutePositionWS(voxelCenterWS - _VolumetricMaterialObbCenter.xyz), transpose(obbFrame)); float3 voxelCenterCS = (voxelCenterBS * rcp(_VolumetricMaterialObbExtents.xyz)); // Still need to clip pixels outside of the box because of the froxel buffer shape @@ -150,7 +151,7 @@ void Frag(VertexToFragment v2f, out float4 outColor : SV_Target0) if (!overlap) clip(-1); - FragInputs fragInputs = BuildFragInputs(v2f, voxelCenterBS, voxelCenterCS); + FragInputs fragInputs = BuildFragInputs(v2f, voxelCenterWS, voxelCenterCS); GetVolumeData(fragInputs, v2f.viewDirectionWS, albedo, extinction); // Accumulate volume parameters diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl new file mode 100644 index 00000000..44ad1219 --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl @@ -0,0 +1,36 @@ +#pragma once + +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricLighting.cs.hlsl" + +// Overrides the transform functions that would use object matrices in the fog as they are not available due to the indirect draw +// Instead we can re-build the object matrix from the OBB of the fog object + +float4x4 BuildWorldToObjectMatrixFromLocalFogOBB() +{ + float3x3 rotation = float3x3( + _VolumetricMaterialObbRight.xyz, + _VolumetricMaterialObbUp.xyz, + cross(_VolumetricMaterialObbRight.xyz, _VolumetricMaterialObbUp.xyz) + ); + + // inverse rotation + rotation = transpose(rotation); + + // inverse translation + float3 inverseTranslation = -(mul(_VolumetricMaterialObbCenter.xyz, rotation)); + + // Build matrix + float4x4 objectMatrix = 0; + objectMatrix._m00_m10_m20 = rotation[0]; + objectMatrix._m01_m11_m21 = rotation[1]; + objectMatrix._m02_m12_m22 = rotation[2]; + objectMatrix._m03_m13_m23_m33 = float4(inverseTranslation, 1); + + return objectMatrix; +} + +float3 TransformWorldToObjectFog(float3 positionRWS) +{ + float3 posWS = GetAbsolutePositionWS(positionRWS); + return mul(BuildWorldToObjectMatrixFromLocalFogOBB(), float4(posWS, 1)).xyz; +} diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl.meta b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl.meta similarity index 75% rename from Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl.meta rename to Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl.meta index 6799cecd..33154246 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl.meta +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VoxelizationTransforms.hlsl.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 361d34e4d312a544890385da951dbd12 +guid: 623770b432f006f49af1b65f726bf86f ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/Templates/Pixel.template.hlsl b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/Templates/Pixel.template.hlsl index 07067034..11bb670a 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/Templates/Pixel.template.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/Templates/Pixel.template.hlsl @@ -25,12 +25,20 @@ SurfaceDescriptionInputs FragInputsToSurfaceDescriptionInputs(FragInputs input, $SurfaceDescriptionInputs.TangentSpaceViewDirection: float3x3 tangentSpaceTransform = float3x3(output.WorldSpaceTangent,output.WorldSpaceBiTangent,output.WorldSpaceNormal); $SurfaceDescriptionInputs.TangentSpaceViewDirection: output.TangentSpaceViewDirection = TransformWorldToTangent(output.WorldSpaceViewDirection, tangentSpaceTransform); $SurfaceDescriptionInputs.WorldSpacePosition: output.WorldSpacePosition = input.positionRWS; +#if SHADERPASS != SHADERPASS_FOG_VOLUME_VOXELIZATION $SurfaceDescriptionInputs.ObjectSpacePosition: output.ObjectSpacePosition = TransformWorldToObject(input.positionRWS); +#else + $SurfaceDescriptionInputs.ObjectSpacePosition: output.ObjectSpacePosition = TransformWorldToObjectFog(input.positionRWS); +#endif $SurfaceDescriptionInputs.ViewSpacePosition: output.ViewSpacePosition = TransformWorldToView(input.positionRWS); $SurfaceDescriptionInputs.TangentSpacePosition: output.TangentSpacePosition = float3(0.0f, 0.0f, 0.0f); $SurfaceDescriptionInputs.AbsoluteWorldSpacePosition: output.AbsoluteWorldSpacePosition = GetAbsolutePositionWS(input.positionRWS); $SurfaceDescriptionInputs.WorldSpacePositionPredisplacement: output.WorldSpacePositionPredisplacement = input.positionPredisplacementRWS; +#if SHADERPASS != SHADERPASS_FOG_VOLUME_VOXELIZATION $SurfaceDescriptionInputs.ObjectSpacePositionPredisplacement: output.ObjectSpacePositionPredisplacement = TransformWorldToObject(input.positionPredisplacementRWS); +#else + $SurfaceDescriptionInputs.ObjectSpacePositionPredisplacement: output.ObjectSpacePositionPredisplacement = TransformWorldToObjectFog(input.positionPredisplacementRWS); +#endif $SurfaceDescriptionInputs.ViewSpacePositionPredisplacement: output.ViewSpacePositionPredisplacement = TransformWorldToView(input.positionPredisplacementRWS); $SurfaceDescriptionInputs.TangentSpacePositionPredisplacement: output.TangentSpacePositionPredisplacement = float3(0.0f, 0.0f, 0.0f); $SurfaceDescriptionInputs.AbsoluteWorldSpacePositionPredisplacement: output.AbsoluteWorldSpacePositionPredisplacement = GetAbsolutePositionWS(input.positionPredisplacementRWS); diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/HDRPDefaultVolumeProfileSettingsPropertyDrawer.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/HDRPDefaultVolumeProfileSettingsPropertyDrawer.cs index b9106bdb..f5695a30 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/HDRPDefaultVolumeProfileSettingsPropertyDrawer.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/HDRPDefaultVolumeProfileSettingsPropertyDrawer.cs @@ -92,7 +92,7 @@ namespace UnityEditor.Rendering.HighDefinition return profileLine; } - public class HDRPDefaultVolumeProfileSettingsContextMenu : DefaultVolumeProfileSettingsContextMenu + public class HDRPDefaultVolumeProfileSettingsContextMenu : DefaultVolumeProfileSettingsContextMenu2 { protected override string defaultVolumeProfilePath { diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Camera/HDCameraUI.Rendering.Drawers.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Camera/HDCameraUI.Rendering.Drawers.cs index f81d4b7d..2c90c94b 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Camera/HDCameraUI.Rendering.Drawers.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/Camera/HDCameraUI.Rendering.Drawers.cs @@ -148,10 +148,6 @@ namespace UnityEditor.Rendering.HighDefinition { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(p.deepLearningSuperSamplingUseOptimalSettings, HDRenderPipelineUI.Styles.DLSSUseOptimalSettingsContent); - using (new EditorGUI.DisabledScope(p.deepLearningSuperSamplingUseOptimalSettings.boolValue)) - { - EditorGUILayout.PropertyField(p.deepLearningSuperSamplingSharpening, HDRenderPipelineUI.Styles.DLSSSharpnessContent); - } EditorGUI.indentLevel--; } } diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/CustomPass/CustomPassDrawer.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/CustomPass/CustomPassDrawer.cs index 0a10648e..551a2507 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/CustomPass/CustomPassDrawer.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/CustomPass/CustomPassDrawer.cs @@ -172,6 +172,16 @@ namespace UnityEditor.Rendering.HighDefinition { EditorGUI.PropertyField(rect, m_TargetDepthBuffer, Styles.targetDepthBuffer); rect.y += Styles.defaultLineSpace; + + CustomPass.TargetBuffer requestedDepth = m_TargetDepthBuffer.GetEnumValue(); + if (m_CustomPass.getConstrainedDepthBuffer() != requestedDepth) + { + Rect helpBoxRect = rect; + float helpBoxHeight = EditorGUIUtility.singleLineHeight * 2; + helpBoxRect.height = helpBoxHeight; + EditorGUI.HelpBox(helpBoxRect, "Camera depth isn't supported when dynamic scaling is on. We will automatically fall back to not doing depth-testing for this pass.", MessageType.Warning); + rect.y += helpBoxHeight; + } } if ((commonPassUIFlags & PassUIFlag.ClearFlags) != 0) @@ -264,6 +274,13 @@ namespace UnityEditor.Rendering.HighDefinition } height += Styles.defaultLineSpace * lines; + + // Add height for the help box if it will be shown + if ((commonPassUIFlags & PassUIFlag.TargetDepthBuffer) != 0 && + m_CustomPass.getConstrainedDepthBuffer() != m_TargetDepthBuffer.GetEnumValue()) + { + height += EditorGUIUtility.singleLineHeight * 2; // Help box height + } } return height + GetPassHeight(property); diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs index b2a8067c..409355f1 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.Skin.cs @@ -277,7 +277,6 @@ namespace UnityEditor.Rendering.HighDefinition public static readonly GUIContent TAAUInjectionPoint = EditorGUIUtility.TrTextContent("TAA Upscale Injection Point", "The injection point at which to apply the upscaling."); public static readonly GUIContent STPInjectionPoint = EditorGUIUtility.TrTextContent("STP Injection Point", "The injection point at which to apply the upscaling."); public static readonly GUIContent DLSSUseOptimalSettingsContent = EditorGUIUtility.TrTextContent("DLSS Use Optimal Settings", "Sets the sharpness and scale automatically for NVIDIA Deep Learning Super Sampling, depending on the values of quality settings. When DLSS Optimal Settings is on, the percentage settings for Dynamic Resolution Scaling are ignored."); - public static readonly GUIContent DLSSSharpnessContent = EditorGUIUtility.TrTextContent("DLSS Sharpness", "NVIDIA Deep Learning Super Sampling pixel sharpness of upsampler. Controls how the DLSS upsampler will render edges on the image. More sharpness usually means more contrast and clearer image but can increase flickering and fireflies. This setting is ignored if optimal settings are used."); public static readonly GUIContent FSR2Title = EditorGUIUtility.TrTextContent("AMD FidelityFX Super Resolution 2.0 (FSR2)"); public static readonly GUIContent enableFSR2 = EditorGUIUtility.TrTextContent("Enable Fidelity FX 2.2", "Enables FidelityFX 2.0 Super Resolution (FSR2)."); @@ -314,7 +313,7 @@ namespace UnityEditor.Rendering.HighDefinition public const string FSR2WinTargetWarning = "HDRP does not support AMD Fidelity FX2 for the current build target and graphics device API. To enable FSR2, set your build target to Windows x86_64 and DirectX12."; public const string FSR2SwitchTarget64Button = "Fix"; public const string FSR2FeatureDetectedMsg = "Unity detected AMD Fidelity FX 2 Super Resolution and will ignore the Fallback Upscale Filter."; - public const string FSR2FeatureNotDetectedMsg = "Unity cannot detect Unity detected AMD Fidelity FX 2 Super Resolution and will use the Fallback Upscale Filter instead."; + public const string FSR2FeatureNotDetectedMsg = "Unity cannot detect AMD Fidelity FX 2 Super Resolution and will use the Fallback Upscale Filter instead."; public const string STPSwDrsWarningMsg = "STP cannot support dynamic resolution without hardware dynamic resolution mode. You can use the forced screen percentage feature to guarantee a fixed resoution for STP or HDRP will fall back to the next best supported upscaling filter instead."; diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs index 4fbbdad5..2fe854b4 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/RenderPipeline/HDRenderPipelineUI.cs @@ -686,10 +686,6 @@ namespace UnityEditor.Rendering.HighDefinition serialized.renderPipelineSettings.dynamicResolutionSettings.DLSSInjectionPoint.intValue = injectionPointVal; EditorGUILayout.PropertyField(serialized.renderPipelineSettings.dynamicResolutionSettings.DLSSUseOptimalSettings, Styles.DLSSUseOptimalSettingsContent); - using (new EditorGUI.DisabledScope(serialized.renderPipelineSettings.dynamicResolutionSettings.DLSSUseOptimalSettings.boolValue)) - { - EditorGUILayout.PropertyField(serialized.renderPipelineSettings.dynamicResolutionSettings.DLSSSharpness, Styles.DLSSSharpnessContent); - } --EditorGUI.indentLevel; } diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl index 61556bdf..c5882372 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDShadowAlgorithms.hlsl @@ -7,9 +7,15 @@ // For backward compatibility #ifdef SHADOW_LOW +#ifndef PUNCTUAL_SHADOW_LOW #define PUNCTUAL_SHADOW_LOW +#endif +#ifndef DIRECTIONAL_SHADOW_LOW #define DIRECTIONAL_SHADOW_LOW #endif +#endif + + // For non-fragment shaders we might skip the variant with the quality as shadows might not be used, if that's the case define something just to make the compiler happy in case the quality is not defined. #ifndef SHADER_STAGE_FRAGMENT diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.shader b/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.shader index 6ac37919..23099cf5 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.shader +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.shader @@ -1045,8 +1045,10 @@ Shader "HDRP/Lit" #ifndef SHADER_STAGE_FRAGMENT #define SHADOW_LOW + #ifndef USE_FPTL_LIGHTLIST #define USE_FPTL_LIGHTLIST #endif + #endif #define SHADERPASS SHADERPASS_FORWARD // In case of opaque we don't want to perform the alpha test, it is done in depth prepass and we use depth equal for ztest (setup from UI) diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs index 049bc962..57d787ca 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs @@ -1482,6 +1482,7 @@ namespace UnityEngine.Rendering.HighDefinition public bool decalsEnabled; public BufferHandle perVoxelOffset; + public BufferHandle lightList; public DBufferOutput dbuffer; public GBufferOutput gbuffer; public TextureHandle depthBuffer; @@ -1564,6 +1565,8 @@ namespace UnityEngine.Rendering.HighDefinition passData.decalsEnabled = (hdCamera.frameSettings.IsEnabled(FrameSettingsField.Decals)) && (DecalSystem.m_DecalDatasCount > 0); passData.perVoxelOffset = builder.ReadBuffer(lightLists.perVoxelOffset); + + passData.lightList = builder.ReadBuffer(lightLists.lightList); passData.dbuffer = ReadDBuffer(dbuffer, builder); passData.clearColorTexture = Compositor.CompositionManager.GetClearTextureForStackedCamera(hdCamera); // returns null if is not a stacked camera @@ -1583,6 +1586,9 @@ namespace UnityEngine.Rendering.HighDefinition BindDefaultTexturesLightingBuffers(context.defaultResources, context.cmd); + if (data.lightList.IsValid()) + context.cmd.SetGlobalBuffer(HDShaderIDs.g_vLightListTile, data.lightList); + BindDBufferGlobalData(data.dbuffer, context); DrawOpaqueRendererList(context, data.frameSettings, data.opaqueRendererList); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs index 8ae098fa..b1acd45b 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs @@ -922,13 +922,18 @@ namespace UnityEngine.Rendering.HighDefinition passData.parameters.drsSettings = currentAsset.currentPlatformRenderPipelineSettings.dynamicResolutionSettings; // Must check this with nvidia. After trying many things this gives the least amount of ghosting. // For now we clamp the exposure to a reasonable value. - passData.parameters.preExposure = Mathf.Clamp(hdCamera.GpuExposureValue(), 0.35f, 2.0f); + passData.parameters.preExposure = Mathf.Clamp(hdCamera.GpuExposureValue(), 0.20f, 2.0f); var viewHandles = new UpscalerResources.ViewResourceHandles(); viewHandles.source = builder.ReadTexture(source); viewHandles.output = builder.WriteTexture(GetPostprocessUpsampledOutputHandle(hdCamera, renderGraph, "DLSS destination")); viewHandles.depth = builder.ReadTexture(depthBuffer); viewHandles.motionVectors = builder.ReadTexture(motionVectors); + // Note: exposure texture input + // We skip providing exposureTexture since HDRP pre-applies exposure in GBuffer pass / light accumulation buffer, + // and doesn't use it later on in tonemapping. DLSS docs mention this texture is needed if used in tonemapping later on. + // Given we also provide an option to inject DLSS post-tonemapping, we can safely skip providing this input as + // it usually exacerbates ghosting within the HDRP use context. if (biasColorMask.IsValid()) viewHandles.biasColorMask = builder.ReadTexture(biasColorMask); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs index a782b175..4e3cb5b8 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs @@ -28,6 +28,8 @@ namespace UnityEngine.Rendering.HighDefinition const int m_MaxXRViewsCount = 4; + const int kIntelVendorId = 0x8086; + void InitializePrepass(HDRenderPipelineAsset hdAsset) { m_MSAAResolveMaterial = CoreUtils.CreateEngineMaterial(runtimeShaders.depthValuesPS); @@ -1407,7 +1409,7 @@ namespace UnityEngine.Rendering.HighDefinition { // Integrated Intel GPU on Mac don't support the texture format use for normal (RGBA_8UNORM) for SetRandomWriteTarget // So on Metal for now we don't patch normal buffer if we detect an intel GPU - if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal && SystemInfo.graphicsDeviceName.Contains("Intel")) + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal && SystemInfo.graphicsDeviceVendorID == kIntelVendorId) { return; } diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs index 0807c021..0f0eb310 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs @@ -45,8 +45,7 @@ namespace UnityEngine.Rendering.HighDefinition // Caution: We require sun light here as some skies use the sun light to render, it means that UpdateSkyEnvironment must be called after PrepareLightsForGPU. // TODO: Try to arrange code so we can trigger this call earlier and use async compute here to run sky convolution during other passes (once we move convolution shader to compute). - if (!m_CurrentDebugDisplaySettings.IsMatcapViewEnabled(hdCamera)) - m_SkyManager.UpdateEnvironment(m_RenderGraph, hdCamera, GetMainLight(), m_CurrentDebugDisplaySettings); + m_SkyManager.UpdateEnvironment(m_RenderGraph, hdCamera, GetMainLight(), m_CurrentDebugDisplaySettings); // We need to initialize the MipChainInfo here, so it will be available to any render graph pass that wants to use it during setup // Be careful, ComputePackedMipChainInfo needs the render texture size and not the viewport size. Otherwise it would compute the wrong size. diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs index c8950b8f..e2280115 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs @@ -2817,7 +2817,7 @@ namespace UnityEngine.Rendering.HighDefinition cameraXRSettings.viewCount = (uint)hdCamera.viewCount; cameraXRSettings.viewOffset = (uint)hdCamera.xr.multipassId; - VFXManager.ProcessCameraCommand(camera, cmd, cameraXRSettings, cullingResults); + VFXManager.ProcessCameraCommand(camera, cmd, cameraXRSettings, cullingResults, customPassCullingResults); if (GL.wireframe) { diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingLightLoop.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingLightLoop.hlsl index 3d1f5754..b2469ecd 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingLightLoop.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RaytracingLightLoop.hlsl @@ -24,7 +24,8 @@ void LightLoop( float3 V, PositionInputs posInput, PreLightData preLightData, BS { // Init LightLoop output structure ZERO_INITIALIZE(LightLoopOutput, lightLoopOutput); - + ApplyCameraRelativeXR(posInput.positionWS); + LightLoopContext context; context.contactShadow = 1.0; context.shadowContext = InitShadowContext(); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPass.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPass.cs index ff9c62c3..52081bb8 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPass.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPass.cs @@ -52,6 +52,24 @@ namespace UnityEngine.Rendering.HighDefinition /// public TargetBuffer targetDepthBuffer; + // The actual depth buffer has to follow some constraints, and thus may not be the same result as the target + // depth buffer that the user has requested. Apply these constraints and return a result. + internal TargetBuffer getConstrainedDepthBuffer() + { + TargetBuffer depth = targetDepthBuffer; + if (depth == TargetBuffer.Camera && + HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.dynamicResolutionSettings.enabled && + currentHDCamera.allowDynamicResolution && + injectionPoint == CustomPassInjectionPoint.AfterPostProcess) + { + // This custom pass is injected after postprocessing, and Dynamic Resolution Scaling is enabled, which + // means an upscaler is active. In this case, the camera color buffer is the full display resolution, + // but the camera depth buffer is a lower, pre-upscale resolution. So we cannot do depth testing here. + depth = TargetBuffer.None; + } + return depth; + } + /// /// What clear to apply when the color and depth buffer are bound /// @@ -314,7 +332,7 @@ namespace UnityEngine.Rendering.HighDefinition } // Set back the camera color buffer if we were using a custom buffer as target - if (customPass.targetDepthBuffer != TargetBuffer.Camera) + if (customPass.getConstrainedDepthBuffer() != TargetBuffer.Camera) CoreUtils.SetRenderTarget(ctx.cmd, outputColorBuffer); }); } @@ -351,16 +369,17 @@ namespace UnityEngine.Rendering.HighDefinition // This function must be only called from the ExecuteInternal method (requires current render target and current RT manager) void SetCustomPassTarget(CommandBuffer cmd) { + TargetBuffer depth = getConstrainedDepthBuffer(); // In case all the buffer are set to none, we can't bind anything - if (targetColorBuffer == TargetBuffer.None && targetDepthBuffer == TargetBuffer.None) + if (targetColorBuffer == TargetBuffer.None && depth == TargetBuffer.None) return; RTHandle colorBuffer = (targetColorBuffer == TargetBuffer.Custom) ? currentRenderTarget.customColorBuffer.Value : currentRenderTarget.colorBufferRG; - RTHandle depthBuffer = (targetDepthBuffer == TargetBuffer.Custom) ? currentRenderTarget.customDepthBuffer.Value : currentRenderTarget.depthBufferRG; + RTHandle depthBuffer = (depth == TargetBuffer.Custom) ? currentRenderTarget.customDepthBuffer.Value : currentRenderTarget.depthBufferRG; - if (targetColorBuffer == TargetBuffer.None && targetDepthBuffer != TargetBuffer.None) + if (targetColorBuffer == TargetBuffer.None && depth != TargetBuffer.None) CoreUtils.SetRenderTarget(cmd, depthBuffer, clearFlags); - else if (targetColorBuffer != TargetBuffer.None && targetDepthBuffer == TargetBuffer.None) + else if (targetColorBuffer != TargetBuffer.None && depth == TargetBuffer.None) CoreUtils.SetRenderTarget(cmd, colorBuffer, clearFlags); else { diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs index 3cd5604c..d1ccc6be 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs @@ -34,5 +34,8 @@ namespace UnityEngine.Rendering.HighDefinition Constant, FullScreenDebug, PBRSky, + FogVolumePreview, + FogVolumeVoxelization, + FogVolumeOverdrawDebug, } } diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl index 8ff52388..ee5aadc6 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl @@ -35,6 +35,9 @@ #define SHADERPASS_CONSTANT (25) #define SHADERPASS_FULL_SCREEN_DEBUG (26) #define SHADERPASS_PBRSKY (27) +#define SHADERPASS_FOG_VOLUME_PREVIEW (28) +#define SHADERPASS_FOG_VOLUME_VOXELIZATION (29) +#define SHADERPASS_FOG_VOLUME_OVERDRAW_DEBUG (30) #endif diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/HDRPDefaultVolumeProfileSetting.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/HDRPDefaultVolumeProfileSetting.cs index ff47063e..bf2110f1 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/HDRPDefaultVolumeProfileSetting.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/HDRPDefaultVolumeProfileSetting.cs @@ -69,9 +69,9 @@ namespace UnityEngine.Rendering.HighDefinition #if UNITY_EDITOR //Overriding "Reset" in menu that is not called at HDRPDefaultVolumeProfileSettings creation such Reset() - struct ResetImplementation : IRenderPipelineGraphicsSettingsContextMenu + struct ResetImplementation : IRenderPipelineGraphicsSettingsContextMenu2 { - public void PopulateContextMenu(HDRPDefaultVolumeProfileSettings setting, PropertyDrawer drawer, ref GenericMenu menu) + public void PopulateContextMenu(HDRPDefaultVolumeProfileSettings setting, SerializedProperty _, ref GenericMenu menu) { void Reset() { diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs index 0ca9f691..43059815 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs @@ -1236,41 +1236,49 @@ namespace UnityEngine.Rendering.HighDefinition m_CurrentDebugDisplaySettings = debugSettings; m_CurrentSunLight = sunLight; - SkyAmbientMode ambientMode = hdCamera.volumeStack.GetComponent().skyAmbientMode.value; + if (debugSettings.IsMatcapViewEnabled(hdCamera)) + { + HDRenderPipeline.SetGlobalTexture(renderGraph, HDShaderIDs._SkyTexture, m_BlackCubemapArray); + HDRenderPipeline.SetGlobalBuffer(renderGraph, HDShaderIDs._AmbientProbeData, m_BlackAmbientProbeBuffer); + } + else + { + SkyAmbientMode ambientMode = hdCamera.volumeStack.GetComponent().skyAmbientMode.value; - UpdateEnvironment(renderGraph, hdCamera, hdCamera.lightingSky, sunLight, m_UpdateRequired, ambientMode == SkyAmbientMode.Dynamic, false, ambientMode); + UpdateEnvironment(renderGraph, hdCamera, hdCamera.lightingSky, sunLight, m_UpdateRequired, ambientMode == SkyAmbientMode.Dynamic, false, ambientMode); - // Preview camera will have a different sun, therefore the hash for the static lighting sky will change and force a recomputation - // because we only maintain one static sky. Since we don't care that the static lighting may be a bit different in the preview we never recompute - // and we use the one from the main camera. - bool forceStaticUpdate = false; - m_ActiveStaticSky = m_StaticLightingSkies.GetValueOrDefault(SceneManager.GetActiveScene().GetHashCode(), null); + // Preview camera will have a different sun, therefore the hash for the static lighting sky will change and force a recomputation + // because we only maintain one static sky. Since we don't care that the static lighting may be a bit different in the preview we never recompute + // and we use the one from the main camera. + bool forceStaticUpdate = false; + m_ActiveStaticSky = m_StaticLightingSkies.GetValueOrDefault(SceneManager.GetActiveScene().GetHashCode(), null); #if UNITY_EDITOR - // In the editor, we might need the static sky ready for baking lightmaps/lightprobes regardless of the current ambient mode so we force it to update in this case if it's not been computed yet.. - // We always force an update of the static sky when we're in scene view mode. Previous behaviour was to prevent forced updates if the hash of the static sky was non-null, but this was preventing - // the lightmapper from updating in response to changes in environment. See GFXGI-237 for a better description of this issue. + // In the editor, we might need the static sky ready for baking lightmaps/lightprobes regardless of the current ambient mode so we force it to update in this case if it's not been computed yet.. + // We always force an update of the static sky when we're in scene view mode. Previous behaviour was to prevent forced updates if the hash of the static sky was non-null, but this was preventing + // the lightmapper from updating in response to changes in environment. See GFXGI-237 for a better description of this issue. - forceStaticUpdate = hdCamera.camera.cameraType == CameraType.SceneView; + forceStaticUpdate = hdCamera.camera.cameraType == CameraType.SceneView; #endif - if ((ambientMode == SkyAmbientMode.Static || forceStaticUpdate) && hdCamera.camera.cameraType != CameraType.Preview) - { - if (m_ActiveStaticSky != null) + if ((ambientMode == SkyAmbientMode.Static || forceStaticUpdate) && hdCamera.camera.cameraType != CameraType.Preview) { - m_StaticLightingSky.skySettings = m_ActiveStaticSky.skySettings; - m_StaticLightingSky.cloudSettings = m_ActiveStaticSky.cloudSettings; - m_StaticLightingSky.volumetricClouds = m_ActiveStaticSky.volumetricClouds; + if (m_ActiveStaticSky != null) + { + m_StaticLightingSky.skySettings = m_ActiveStaticSky.skySettings; + m_StaticLightingSky.cloudSettings = m_ActiveStaticSky.cloudSettings; + m_StaticLightingSky.volumetricClouds = m_ActiveStaticSky.volumetricClouds; + } + UpdateEnvironment(renderGraph, hdCamera, m_StaticLightingSky, sunLight, m_StaticSkyUpdateRequired || m_UpdateRequired, true, true, SkyAmbientMode.Static); + m_StaticSkyUpdateRequired = false; } - UpdateEnvironment(renderGraph, hdCamera, m_StaticLightingSky, sunLight, m_StaticSkyUpdateRequired || m_UpdateRequired, true, true, SkyAmbientMode.Static); - m_StaticSkyUpdateRequired = false; - } - m_UpdateRequired = false; + m_UpdateRequired = false; - SetGlobalSkyData(renderGraph, hdCamera.lightingSky, m_BuiltinParameters); + SetGlobalSkyData(renderGraph, hdCamera.lightingSky, m_BuiltinParameters); - // Keep global setter for now. We should probably remove it and set it explicitly where needed like any other resource. As is it breaks resource lifetime contract with render graph. - HDRenderPipeline.SetGlobalTexture(renderGraph, HDShaderIDs._SkyTexture, GetReflectionTexture(hdCamera.lightingSky)); - HDRenderPipeline.SetGlobalBuffer(renderGraph, HDShaderIDs._AmbientProbeData, GetDiffuseAmbientProbeBuffer(hdCamera)); + // Keep global setter for now. We should probably remove it and set it explicitly where needed like any other resource. As is it breaks resource lifetime contract with render graph. + HDRenderPipeline.SetGlobalTexture(renderGraph, HDShaderIDs._SkyTexture, GetReflectionTexture(hdCamera.lightingSky)); + HDRenderPipeline.SetGlobalBuffer(renderGraph, HDShaderIDs._AmbientProbeData, GetDiffuseAmbientProbeBuffer(hdCamera)); + } } static void UpdateBuiltinParameters(ref BuiltinSkyParameters builtinParameters, SkyUpdateContext skyContext, HDCamera hdCamera, Light sunLight, DebugDisplaySettings debugSettings) diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl deleted file mode 100644 index 19d9661e..00000000 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/VFXGraph/Shaders/VFXRayTracingCommon.hlsl +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef VFX_RAY_TRACING_COMMON_HLSL -#define VFX_RAY_TRACING_COMMON_HLSL - -// Object <-> primtive matrices -float4x4 ObjectToPrimitive(VFXAttributes attributes, float3 size3) -{ - float4x4 vfxToElement = GetVFXToElementMatrix(attributes.axisX, attributes.axisY, attributes.axisZ, - float3(attributes.angleX, attributes.angleY, attributes.angleZ), - float3(attributes.pivotX, attributes.pivotY, attributes.pivotZ), - size3, attributes.position); - #if VFX_WORLD_SPACE - float4x4 objToWorld = VFXGetObjectToWorldMatrix(); - objToWorld._m03_m13_m23 += GetAbsolutePositionWS(float3(0,0,0)); - return mul(vfxToElement, objToWorld); - #else - return vfxToElement; - #endif -} - -float4x4 PrimitiveToObject(VFXAttributes attributes, float3 size3) -{ - float4x4 elementToVFX = GetElementToVFXMatrix(attributes.axisX, attributes.axisY, attributes.axisZ, - float3(attributes.angleX,attributes.angleY,attributes.angleZ), - float3(attributes.pivotX,attributes.pivotY,attributes.pivotZ), - size3, attributes.position); - #if VFX_WORLD_SPACE - float4x4 worldToObj = VFXGetWorldToObjectMatrix(); - worldToObj = RevertCameraTranslationFromInverseMatrix(worldToObj); - return mul(worldToObj, elementToVFX); - #else - return elementToVFX; - #endif -} - -//World <-> Primitive matrix -float4x4 WorldToPrimitive(VFXAttributes attributes, float3 size3) -{ - float4x4 vfxToElement = GetVFXToElementMatrix(attributes.axisX, attributes.axisY, attributes.axisZ, - float3(attributes.angleX, attributes.angleY, attributes.angleZ), - float3(attributes.pivotX, attributes.pivotY, attributes.pivotZ), - size3, attributes.position); - #if VFX_WORLD_SPACE - return vfxToElement; - #else - float3x4 worldToObj3x4 = WorldToObject3x4(); - float4x4 worldToObj = float4x4( - worldToObj3x4._m00, worldToObj3x4._m01, worldToObj3x4._m02, worldToObj3x4._m03, - worldToObj3x4._m10, worldToObj3x4._m11, worldToObj3x4._m12, worldToObj3x4._m13, - worldToObj3x4._m20, worldToObj3x4._m21, worldToObj3x4._m22, worldToObj3x4._m23, - 0,0,0,1); - worldToObj = RevertCameraTranslationFromInverseMatrix(worldToObj); - return mul(vfxToElement, worldToObj); - #endif -} - -#if defined(VFX_PRIMITIVE_QUAD) - #define RAY_TRACING_QUAD_PRIMTIIVE - // structure that holds all we need for the intersection shader - struct RayTracingProceduralData - { - float4x4 objectToPrimitive; - float4x4 primitiveToObject; - float3 normal; - float3 position; - VFXAttributes attributes; - float3 size; - }; - - RayTracingProceduralData BuildRayTracingProceduralData(VFXAttributes attributes, float3 size3) - { - RayTracingProceduralData rtPrData; - rtPrData.objectToPrimitive = ObjectToPrimitive(attributes, size3); - rtPrData.primitiveToObject = PrimitiveToObject(attributes, size3); - rtPrData.position = rtPrData.primitiveToObject._m03_m13_m23; - rtPrData.normal = -rtPrData.primitiveToObject._m02_m12_m22; - rtPrData.attributes = attributes; - rtPrData.size = size3; - return rtPrData; - } -#endif - -#if defined(VFX_PRIMITIVE_TRIANGLE) - #define RAY_TRACING_TRIANGLE_PRIMTIIVE - - // structure that holds all we need for the intersection shader - struct RayTracingProceduralData - { - float4x4 objectToPrimitive; - float4x4 primitiveToObject; - float3 position; - float3 normal; - float2 p0; - float2 p1; - float2 p2; - VFXAttributes attributes; - float3 size; - }; - - RayTracingProceduralData BuildRayTracingProceduralData(VFXAttributes attributes, float3 size3) - { - RayTracingProceduralData rtPrData; - rtPrData.objectToPrimitive = ObjectToPrimitive(attributes, size3); - rtPrData.primitiveToObject = PrimitiveToObject(attributes, size3); - rtPrData.position = rtPrData.primitiveToObject._m03_m13_m23; - rtPrData.normal = -rtPrData.primitiveToObject._m02_m12_m22; - rtPrData.attributes = attributes; - rtPrData.size = size3; - - // Triangle coordinates - const float2 kOffsets[] = { - float2(-0.5f, -0.288675129413604736328125f), - float2(0.0f, 0.57735025882720947265625f), - float2(0.5f, -0.288675129413604736328125f), - }; - - const float kUVScale = 0.866025388240814208984375f; - - // Evaluate the three points of the triangle - rtPrData.p0 = (kOffsets[0] * kUVScale) + 0.5f; - rtPrData.p1 = (kOffsets[1] * kUVScale) + 0.5f; - rtPrData.p2 = (kOffsets[2] * kUVScale) + 0.5f; - return rtPrData; - } -#endif - -#if defined(VFX_PRIMITIVE_OCTAGON) - #define RAY_TRACING_DISK_PRIMTIIVE - // structure that holds all we need for the intersection shader - struct RayTracingProceduralData - { - float4x4 objectToPrimitive; - float4x4 primitiveToObject; - float3 normal; - float3 position; - VFXAttributes attributes; - float3 size; - }; - - RayTracingProceduralData BuildRayTracingProceduralData(VFXAttributes attributes, float3 size3) - { - RayTracingProceduralData rtPrData; - rtPrData.objectToPrimitive = ObjectToPrimitive(attributes, size3); - rtPrData.primitiveToObject = PrimitiveToObject(attributes, size3); - rtPrData.position = rtPrData.primitiveToObject._m03_m13_m23; - rtPrData.normal = -rtPrData.primitiveToObject._m02_m12_m22; - rtPrData.attributes = attributes; - rtPrData.size = size3; - return rtPrData; - } -#endif -#endif // VFX_RAY_TRACING_COMMON_HLSL diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/SampleWaterSurface.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/SampleWaterSurface.hlsl index 6321f779..082ba048 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/SampleWaterSurface.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/SampleWaterSurface.hlsl @@ -442,7 +442,11 @@ void EvaluateWaterAdditionalData(float3 positionOS, float3 transformedPosition, return; // Evaluate the pre-displaced absolute position +#if defined(WATER_DISPLACEMENT) + float3 positionRWS = positionOS; +#else float3 positionRWS = TransformObjectToWorld_Water(positionOS); +#endif // Evaluate the distance to the camera float distanceToCamera = length(positionRWS); // Get the world space transformed postion diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl index 4716bd7c..b197d5c0 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl @@ -40,7 +40,7 @@ float GetUnderWaterDistance(uint2 coord) uint xr = unity_StereoEyeIndex * _BufferStride; uint2 boundsX = uint2(0xFFFFFFFF - _WaterLine[0 + xr], _WaterLine[1 + xr]); uint posX = round(dot((float2)coord.xy, rightVector) - _BoundsSS.x); - posX = clamp(posX, boundsX.x, boundsX.y); + posX = clamp(posX, min(boundsX.y, boundsX.x), max(boundsX.x, boundsX.y)); // Decompress water line height float posY = dot((float2)coord.xy, upVector) - _BoundsSS.z; diff --git a/Packages/com.unity.render-pipelines.high-definition/package.json b/Packages/com.unity.render-pipelines.high-definition/package.json index d611f7ac..67eba72d 100644 --- a/Packages/com.unity.render-pipelines.high-definition/package.json +++ b/Packages/com.unity.render-pipelines.high-definition/package.json @@ -100,5 +100,5 @@ ] } ], - "_fingerprint": "4de1dfa20daa1dde4106c2c4990e5356c4cbf1e7" + "_fingerprint": "3672d1571dfd5f4d1627647460e0e4e2f11422ae" } diff --git a/Packages/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/AccelStructTests.cs b/Packages/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/AccelStructTests.cs index 510c6c2e..7113ff55 100644 --- a/Packages/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/AccelStructTests.cs +++ b/Packages/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/AccelStructTests.cs @@ -170,6 +170,7 @@ namespace UnityEngine.Rendering.UnifiedRayTracing.Tests } [Test] + [Timeout(360000)] public void AddInstance_MeshWith2GBWorthOfVertices_Throws() { var resources = new RayTracingResources(); diff --git a/Packages/com.unity.rendering.light-transport/package.json b/Packages/com.unity.rendering.light-transport/package.json index d7a62805..6c0b217f 100644 --- a/Packages/com.unity.rendering.light-transport/package.json +++ b/Packages/com.unity.rendering.light-transport/package.json @@ -15,5 +15,5 @@ "pathtracing", "monte-carlo" ], - "_fingerprint": "ec31b4120e30d44c7f702fa7bfa50d70b562cd4a" + "_fingerprint": "e647573c7d2ae78386ecb3f9f962738597f13fcf" } diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs b/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs deleted file mode 100644 index 719c800c..00000000 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEditor.ShaderGraph.Drawing; -using UnityEngine.Networking; -using UnityEngine.UIElements; - -class ResizeBorderFrame : VisualElement -{ - List m_ResizeSideHandles; - - bool m_MaintainApsectRatio; - - public bool maintainAspectRatio - { - get { return m_MaintainApsectRatio; } - set - { - m_MaintainApsectRatio = value; - foreach (ResizeSideHandle resizeHandle in m_ResizeSideHandles) - { - resizeHandle.maintainAspectRatio = value; - } - } - } - - public Action OnResizeFinished; - - public ResizeBorderFrame(VisualElement target) - { - InitializeResizeBorderFrame(target, target); - } - - public ResizeBorderFrame(VisualElement target, VisualElement container) - { - InitializeResizeBorderFrame(target, container); - } - - void InitializeResizeBorderFrame(VisualElement target, VisualElement container) - { - pickingMode = PickingMode.Ignore; - - AddToClassList("resizeBorderFrame"); - - m_ResizeSideHandles = new List(); - - // Add resize handles along the border - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.TopLeft)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Top)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.TopRight)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Right)); - m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.BottomRight)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Bottom)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.BottomLeft)); - // m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Left)); - - foreach (ResizeSideHandle resizeHandle in m_ResizeSideHandles) - { - resizeHandle.OnResizeFinished += HandleResizefinished; - Add(resizeHandle); - } - } - - void HandleResizefinished() - { - if (OnResizeFinished != null) - { - OnResizeFinished(); - } - } -} diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs.meta b/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs.meta deleted file mode 100644 index c7684fba..00000000 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeBorderFrame.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1128492b211994d46be4acd2ccac15fb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs b/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs deleted file mode 100644 index 2060816d..00000000 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs +++ /dev/null @@ -1,365 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.UIElements; -using UnityEngine.UIElements.StyleSheets; - -namespace UnityEditor.ShaderGraph.Drawing -{ - enum ResizeDirection - { - Any, - Vertical, - Horizontal - } - - enum ResizeHandleAnchor - { - Top, - TopRight, - Right, - BottomRight, - Bottom, - BottomLeft, - Left, - TopLeft - } - - class ResizeSideHandle : ImmediateModeElement - { - VisualElement m_ResizeTarget; - VisualElement m_Container; - - WindowDockingLayout m_WindowDockingLayout; - - bool m_MaintainAspectRatio; - - public bool maintainAspectRatio - { - get { return m_MaintainAspectRatio; } - set { m_MaintainAspectRatio = value; } - } - - public Action OnResizeFinished; - - bool m_Dragging; - - Rect m_ResizeBeginLayout; - Vector2 m_ResizeBeginMousePosition; - - private GUIStyle m_StyleWidget; - private GUIStyle m_StyleLabel; - private Texture image { get; set; } - - public ResizeSideHandle(VisualElement resizeTarget, VisualElement container, ResizeHandleAnchor anchor) - { - m_WindowDockingLayout = new WindowDockingLayout(); - - m_ResizeTarget = resizeTarget; - m_Container = container; - - AddToClassList("resize"); - - switch (anchor) - { - case ResizeHandleAnchor.Top: - { - AddToClassList("vertical"); - AddToClassList("top"); - RegisterCallback(HandleResizeFromTop); - break; - } - case ResizeHandleAnchor.TopRight: - { - AddToClassList("diagonal"); - AddToClassList("top-right"); - RegisterCallback(HandleResizeFromTopRight); - break; - } - case ResizeHandleAnchor.Right: - { - AddToClassList("horizontal"); - AddToClassList("right"); - RegisterCallback(HandleResizeFromRight); - break; - } - case ResizeHandleAnchor.BottomRight: - { - AddToClassList("diagonal"); - AddToClassList("bottom-right"); - RegisterCallback(HandleResizeFromBottomRight); - break; - } - case ResizeHandleAnchor.Bottom: - { - AddToClassList("vertical"); - AddToClassList("bottom"); - RegisterCallback(HandleResizeFromBottom); - break; - } - case ResizeHandleAnchor.BottomLeft: - { - AddToClassList("diagonal"); - AddToClassList("bottom-left"); - RegisterCallback(HandleResizeFromBottomLeft); - break; - } - case ResizeHandleAnchor.Left: - { - AddToClassList("horizontal"); - AddToClassList("left"); - RegisterCallback(HandleResizeFromLeft); - break; - } - case ResizeHandleAnchor.TopLeft: - { - AddToClassList("diagonal"); - AddToClassList("top-left"); - RegisterCallback(HandleResizeFromTopLeft); - break; - } - } - - RegisterCallback(HandleMouseDown); - RegisterCallback(HandleDraggableMouseUp); - - m_ResizeTarget.RegisterCallback(InitialLayoutSetup); - } - - void InitialLayoutSetup(GeometryChangedEvent evt) - { - m_ResizeTarget.UnregisterCallback(InitialLayoutSetup); - } - - void HandleResizeFromTop(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = float.NaN; - m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax; - - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y); - - m_ResizeTarget.style.height = newHeight; - - if (maintainAspectRatio) - m_ResizeTarget.style.width = newHeight; - - mouseMoveEvent.StopImmediatePropagation(); - } - - void HandleResizeFromTopRight(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = float.NaN; - m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax; - m_Container.style.left = m_Container.layout.xMin; - m_Container.style.right = float.NaN; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x); - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y); - - if (maintainAspectRatio) - newWidth = newHeight = Mathf.Min(newWidth, newHeight); - - m_ResizeTarget.style.width = newWidth; - m_ResizeTarget.style.height = newHeight; - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromRight(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.left = m_Container.layout.xMin; - m_Container.style.right = float.NaN; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x); - - m_ResizeTarget.style.width = newWidth; - - if (maintainAspectRatio) - { - m_ResizeTarget.style.height = newWidth; - } - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromBottomRight(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = m_Container.layout.yMin; - m_Container.style.bottom = float.NaN; - m_Container.style.left = m_Container.layout.xMin; - m_Container.style.right = float.NaN; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x); - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y); - - if (maintainAspectRatio) - newWidth = newHeight = Mathf.Min(newWidth, newHeight); - - m_ResizeTarget.style.width = newWidth; - m_ResizeTarget.style.height = newHeight; - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromBottom(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = m_Container.layout.yMin; - m_Container.style.bottom = float.NaN; - - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y); - - m_ResizeTarget.style.height = newHeight; - - if (maintainAspectRatio) - m_ResizeTarget.style.width = newHeight; - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromBottomLeft(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = m_Container.layout.yMin; - m_Container.style.bottom = float.NaN; - m_Container.style.left = float.NaN; - m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x); - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y); - - if (maintainAspectRatio) - newWidth = newHeight = Mathf.Min(newWidth, newHeight); - - m_ResizeTarget.style.width = newWidth; - m_ResizeTarget.style.height = newHeight; - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromLeft(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.left = float.NaN; - m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x); - - m_ResizeTarget.style.width = newWidth; - - if (maintainAspectRatio) - m_ResizeTarget.style.height = newWidth; - - mouseMoveEvent.StopPropagation(); - } - - void HandleResizeFromTopLeft(MouseMoveEvent mouseMoveEvent) - { - if (!m_Dragging) - return; - - Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition; - - // Set anchor points for positioning - m_Container.style.top = float.NaN; - m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax; - m_Container.style.left = float.NaN; - m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax; - - float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x); - float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y); - - if (maintainAspectRatio) - newWidth = newHeight = Mathf.Min(newWidth, newHeight); - - m_ResizeTarget.style.width = newWidth; - m_ResizeTarget.style.height = newHeight; - - mouseMoveEvent.StopPropagation(); - } - - void HandleMouseDown(MouseDownEvent mouseDownEvent) - { - // Get the docking settings for the window, as well as the - // layout and mouse position when resize begins. - m_WindowDockingLayout.CalculateDockingCornerAndOffset(m_Container.layout, m_Container.parent.layout); - m_WindowDockingLayout.ApplyPosition(m_Container); - - m_ResizeBeginLayout = m_ResizeTarget.layout; - m_ResizeBeginMousePosition = mouseDownEvent.mousePosition; - - m_Dragging = true; - this.CaptureMouse(); - mouseDownEvent.StopPropagation(); - } - - void HandleDraggableMouseUp(MouseUpEvent mouseUpEvent) - { - m_Dragging = false; - - if (this.HasMouseCapture()) - this.ReleaseMouse(); - - if (OnResizeFinished != null) - OnResizeFinished(); - - m_WindowDockingLayout.CalculateDockingCornerAndOffset(m_Container.layout, m_Container.parent.layout); - m_WindowDockingLayout.ApplyPosition(m_Container); - } - - protected override void ImmediateRepaint() - { - if (m_StyleWidget == null) - { - m_StyleWidget = new GUIStyle("WindowBottomResize") { fixedHeight = 0 }; - image = m_StyleWidget.normal.background; - } - - if (image == null) - { - Debug.LogWarning("null texture passed to GUI.DrawTexture"); - return; - } - - GUI.DrawTexture(contentRect, image, ScaleMode.ScaleAndCrop, true, 0, GUI.color, 0, 0); - } - } -} diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs.meta b/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs.meta deleted file mode 100644 index 56aa2002..00000000 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Manipulators/ResizeSideHandle.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b165bcb20989fa2428253e51bc4f440f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs b/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs index 9e60d1c4..cfbdd073 100644 --- a/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs +++ b/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs @@ -486,7 +486,7 @@ namespace UnityEditor.ShaderGraph.Drawing UpdateTitle(); } - public void UpdateTitle() + public void UpdateTitle(bool ignoreUnsavedChanges = false) { string assetPath = AssetDatabase.GUIDToAssetPath(selectedGuid); string shaderName = Path.GetFileNameWithoutExtension(assetPath); @@ -501,7 +501,7 @@ namespace UnityEditor.ShaderGraph.Drawing title = title + " (nothing loaded)"; else { - if (GraphHasChangedSinceLastSerialization()) + if (!ignoreUnsavedChanges && GraphHasChangedSinceLastSerialization()) { hasUnsavedChanges = true; // This is the message EditorWindow will show when prompting to close while dirty @@ -1260,6 +1260,7 @@ namespace UnityEditor.ShaderGraph.Drawing using (GraphLoadMarker.Auto()) { m_LastSerializedFileContents = File.ReadAllText(path, Encoding.UTF8); + graphObject = CreateInstance(); graphObject.hideFlags = HideFlags.HideAndDontSave; graphObject.graph = new GraphData @@ -1268,6 +1269,7 @@ namespace UnityEditor.ShaderGraph.Drawing isSubGraph = isSubGraph, messageManager = messageManager }; + MultiJson.Deserialize(graphObject.graph, m_LastSerializedFileContents); graphObject.graph.OnEnable(); graphObject.graph.ValidateGraph(); @@ -1281,8 +1283,7 @@ namespace UnityEditor.ShaderGraph.Drawing }; } - UpdateTitle(); - + UpdateTitle(ignoreUnsavedChanges: true); Repaint(); } catch (Exception) diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss b/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss deleted file mode 100644 index 9ad41828..00000000 --- a/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss +++ /dev/null @@ -1,65 +0,0 @@ -HelpBoxRow > #container{ - flex-grow: 1; - margin-left: 8px; - margin-right: 8px; - padding-left: 8px; - padding-right: 8px; - flex-direction: row; -} - -HelpBoxRow > #container > #label { - width : 20px; - height : 20px; - align-self: center; - margin-right: 8px; -} - -HelpBoxRow > #container > #content { - flex-grow: 1; - justify-content: center; -} - -HelpBoxRow -{ - white-space: normal; -} - -.help-box-row-style-info -{ - background-color: #474747; -} - -.help-box-row-style-info #label -{ - background-image : resource("console.infoicon"); -} - -.help-box-row-style-warning -{ -} - -.help-box-row-style-warning #label -{ - background-image : resource("console.warnicon"); -} - -.help-box-row-style-error -{ -} - -.help-box-row-style-error #label -{ - background-image : resource("console.erroricon"); -} - -#message-warn -{ - color:#584308; - white-space: normal; -} - -#message-info -{ - color:#d2d2d2; - white-space: normal; -} diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss.meta b/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss.meta deleted file mode 100644 index 68ff4595..00000000 --- a/Packages/com.unity.shadergraph/Editor/Resources/Styles/HelpBoxRow.uss.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c0c3b27af341d91469183b16b7d6638b -ScriptedImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 2 - userData: - assetBundleName: - assetBundleVariant: - script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} - disableValidation: 0 diff --git a/Packages/com.unity.shadergraph/package.json b/Packages/com.unity.shadergraph/package.json index d70f5596..68bd7d2a 100644 --- a/Packages/com.unity.shadergraph/package.json +++ b/Packages/com.unity.shadergraph/package.json @@ -43,5 +43,5 @@ "path": "Samples~/UGUIShaders" } ], - "_fingerprint": "26dc5ae27e7d8949b4e95045bf189b8733da88ee" + "_fingerprint": "8d13f365c6632c6b398ae9cd01d13bcee9880a3b" } diff --git a/Packages/com.unity.ugui/Editor/TMP/TMP_BaseShaderGUI.cs b/Packages/com.unity.ugui/Editor/TMP/TMP_BaseShaderGUI.cs index b712fc8d..bc0db5d6 100644 --- a/Packages/com.unity.ugui/Editor/TMP/TMP_BaseShaderGUI.cs +++ b/Packages/com.unity.ugui/Editor/TMP/TMP_BaseShaderGUI.cs @@ -438,7 +438,7 @@ namespace TMPro.EditorUtilities { MaterialProperty property = BeginProperty(name); s_TempLabel.text = label; - Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, false); + Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true); if (EndProperty()) { property.colorValue = value; diff --git a/Packages/com.unity.ugui/Editor/TMP/TMP_FontAssetEditor.cs b/Packages/com.unity.ugui/Editor/TMP/TMP_FontAssetEditor.cs index 03175494..4b670cfc 100644 --- a/Packages/com.unity.ugui/Editor/TMP/TMP_FontAssetEditor.cs +++ b/Packages/com.unity.ugui/Editor/TMP/TMP_FontAssetEditor.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEditor.TextCore.LowLevel; +using System; namespace TMPro.EditorUtilities @@ -255,7 +256,6 @@ namespace TMPro.EditorUtilities private System.DateTime timeStamp; - public void OnEnable() { m_FaceInfo_prop = serializedObject.FindProperty("m_FaceInfo"); @@ -533,7 +533,18 @@ namespace TMPro.EditorUtilities { EditorGUI.BeginChangeCheck(); // TODO: Switch shaders depending on GlyphRenderMode. - EditorGUILayout.PropertyField(m_AtlasRenderMode_prop); + var glyphRenderValues = (GlyphRenderMode[])Enum.GetValues(typeof(GlyphRenderMode)); + GlyphRenderMode currentValue = glyphRenderValues[m_AtlasRenderMode_prop.enumValueIndex]; + GlyphRenderModeUI selectedUI = (GlyphRenderModeUI)currentValue; + + selectedUI = (GlyphRenderModeUI)EditorGUILayout.EnumPopup("Render Mode", selectedUI); + GlyphRenderMode updatedValue = (GlyphRenderMode)selectedUI; + if (updatedValue != currentValue) + { + int updatedIndex = Array.IndexOf(glyphRenderValues, updatedValue); + m_AtlasRenderMode_prop.enumValueIndex = updatedIndex; + m_DisplayDestructiveChangeWarning = true; + } EditorGUILayout.PropertyField(m_SamplingPointSize_prop, new GUIContent("Sampling Point Size")); if (EditorGUI.EndChangeCheck()) { diff --git a/Packages/com.unity.ugui/Editor/TMP/TMPro_FontAssetCreatorWindow.cs b/Packages/com.unity.ugui/Editor/TMP/TMPro_FontAssetCreatorWindow.cs index 441d14cb..ad71747c 100644 --- a/Packages/com.unity.ugui/Editor/TMP/TMPro_FontAssetCreatorWindow.cs +++ b/Packages/com.unity.ugui/Editor/TMP/TMPro_FontAssetCreatorWindow.cs @@ -675,13 +675,13 @@ namespace TMPro.EditorUtilities //GUILayout.EndHorizontal(); // Render Mode Selection - CheckForLegacyGlyphRenderMode(); - + GlyphRenderModeUI selectedUIMode = (GlyphRenderModeUI)m_GlyphRenderMode; EditorGUI.BeginChangeCheck(); - m_GlyphRenderMode = (GlyphRenderMode)EditorGUILayout.EnumPopup("Render Mode", m_GlyphRenderMode); + selectedUIMode = (GlyphRenderModeUI)EditorGUILayout.EnumPopup("Render Mode", selectedUIMode); if (EditorGUI.EndChangeCheck()) { m_IsFontAtlasInvalid = true; + m_GlyphRenderMode = (GlyphRenderMode)selectedUIMode; } m_IncludeFontFeatures = EditorGUILayout.Toggle("Get Font Features", m_IncludeFontFeatures); @@ -1184,7 +1184,6 @@ namespace TMPro.EditorUtilities ClearGeneratedData(); } - /// /// Clear the previously generated data. /// diff --git a/Packages/com.unity.ugui/Runtime/TMP/TMP_InputField.cs b/Packages/com.unity.ugui/Runtime/TMP/TMP_InputField.cs index 6a8abaaf..0830328b 100644 --- a/Packages/com.unity.ugui/Runtime/TMP/TMP_InputField.cs +++ b/Packages/com.unity.ugui/Runtime/TMP/TMP_InputField.cs @@ -2395,17 +2395,16 @@ namespace TMPro } break; } - } - if (consumedEvent) + // We must also consume events when IME is active to prevent them from being passed to the text field. // UUM-100552 + if (consumedEvent || (m_IsCompositionActive && compositionLength > 0)) { UpdateLabel(); eventData.Use(); } } - /// /// /// @@ -4188,14 +4187,15 @@ namespace TMPro if (!cursorBeforeDash) { if (ch >= '0' && ch <= '9') return ch; - if (ch == '-' && (pos == 0 || selectionAtStart)) return ch; + if (ch == '-' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return ch; var separator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator; if (ch == Convert.ToChar(separator) && characterValidation == CharacterValidation.Decimal && !text.Contains(separator)) return ch; //Some keyboards including Samsung require double tapping a . to get a - this allows these keyboards to input negative integers - if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart)) return '-'; + if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return '-'; } + } else if (characterValidation == CharacterValidation.Digit) { diff --git a/Packages/com.unity.ugui/Runtime/TMP/TMP_SpriteAsset.cs b/Packages/com.unity.ugui/Runtime/TMP/TMP_SpriteAsset.cs index 7f123ba4..f798a3b3 100644 --- a/Packages/com.unity.ugui/Runtime/TMP/TMP_SpriteAsset.cs +++ b/Packages/com.unity.ugui/Runtime/TMP/TMP_SpriteAsset.cs @@ -237,7 +237,7 @@ namespace TMPro if (m_NameLookup == null) UpdateLookupTables(); - int hashCode = TMP_TextUtilities.GetSimpleHashCode(name); + int hashCode = TMP_TextUtilities.GetHashCode(name); return GetSpriteIndexFromHashcode(hashCode); } diff --git a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Image.cs b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Image.cs index abdb7060..3733bb80 100644 --- a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Image.cs +++ b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Image.cs @@ -935,15 +935,15 @@ namespace UnityEngine.UI internal SecondarySpriteTexture [] secondaryTextures => m_SecondaryTextures; // Internal for testing only - static void ClearArray(SecondarySpriteTexture[] array) + static void ClearArray(ref SecondarySpriteTexture[] array) { - Array.Resize(ref array, 0); + array = Array.Empty(); } bool CheckSecondaryTexturesChanged(Sprite sprite) { var changed = CheckSecondaryTexturesChanged(sprite, ref s_TempNewSecondaryTextures); - ClearArray(s_TempNewSecondaryTextures); + ClearArray(ref s_TempNewSecondaryTextures); return changed; } @@ -979,7 +979,7 @@ namespace UnityEngine.UI } else { - ClearArray(newSecondaryTextures); + ClearArray(ref newSecondaryTextures); } // If the list of secondary textures has not changed then return @@ -1017,7 +1017,7 @@ namespace UnityEngine.UI } } - ClearArray(s_TempNewSecondaryTextures); + ClearArray(ref s_TempNewSecondaryTextures); } /// @@ -1925,6 +1925,12 @@ namespace UnityEngine.UI float x = local.x / activeSprite.texture.width; float y = local.y / activeSprite.texture.height; + // Locations outside the image are always considered valid. + // This guarantees that the behavior remains consistent with the case where alphaHitTestMinimumThreshold <= 0. + // Without this check, we would continue to sample a pixel outside the texture. + if (x < 0 || x > 1 || y < 0 || y > 1) + return true; + try { return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold; diff --git a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/InputField.cs b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/InputField.cs index 827e0e2a..8033e843 100644 --- a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/InputField.cs +++ b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/InputField.cs @@ -3009,10 +3009,10 @@ namespace UnityEngine.UI if (!cursorBeforeDash || dashInSelection) { if (ch >= '0' && ch <= '9') return ch; - if (ch == '-' && (pos == 0 || selectionAtStart)) return ch; + if (ch == '-' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return ch; if ((ch == '.' || ch == ',') && characterValidation == CharacterValidation.Decimal && text.IndexOfAny(new[] { '.', ',' }) == -1) return ch; //Some keyboards including Samsung require double tapping a . to get a - this allows these keyboards to input negative integers - if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart)) return '-'; + if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return '-'; } } else if (characterValidation == CharacterValidation.Alphanumeric) diff --git a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Slider.cs b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Slider.cs index 99b97371..27c44ec1 100644 --- a/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Slider.cs +++ b/Packages/com.unity.ugui/Runtime/UGUI/UI/Core/Slider.cs @@ -533,6 +533,7 @@ namespace UnityEngine.UI return; m_Value = newValue; + MarkDirty(); UpdateVisuals(); if (sendCallback) { diff --git a/Packages/com.unity.ugui/package.json b/Packages/com.unity.ugui/package.json index b5d16449..caf198fa 100644 --- a/Packages/com.unity.ugui/package.json +++ b/Packages/com.unity.ugui/package.json @@ -19,5 +19,5 @@ "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0" }, - "_fingerprint": "5c9fd4989cdd8e57279d95edb174435a77f5c853" + "_fingerprint": "1a71b488df3ebf0445532ad09fea67fd1a895ab4" } diff --git a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXConvertSubgraph.cs b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXConvertSubgraph.cs index 5b723cfc..e0d33376 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXConvertSubgraph.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXConvertSubgraph.cs @@ -248,6 +248,8 @@ namespace UnityEditor.VFX.UI TransferEdges(); //TransferContextsFlowEdges(); UninitSmart(); + + m_TargetSubgraph.GetResource()?.WriteAssetWithSubAssets(); } public void ConvertToSubgraphOperator(VFXView sourceView, IEnumerable controllers, Rect rect, string path) @@ -282,6 +284,8 @@ namespace UnityEditor.VFX.UI var subGraphOperator = m_SourceNode as VFXSubgraphOperator; subGraphOperator.RecreateCopy(); subGraphOperator.ResyncSlots(true); + + m_TargetSubgraph.GetResource()?.WriteAssetWithSubAssets(); } List m_SourceBlockControllers; @@ -374,6 +378,8 @@ namespace UnityEditor.VFX.UI TransferEdges(); m_SourceControllers = m_SourceControllersWithBlocks.ToList(); UninitSmart(); + + m_TargetSubgraph.GetResource()?.WriteAssetWithSubAssets(); } bool CreateUniqueSubgraph(string typeName, string extension, Func createFunc) diff --git a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs index 0a16d099..d62810bf 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs @@ -1056,7 +1056,20 @@ namespace UnityEditor.VFX.UI m_ComponentBoard.RefreshInitializeErrors(); } + public void ToggleDebugPanels() + { + if (m_ProfilingBoard.parent != null && attachedComponent != null) + { + m_ProfilingBoard.TogglePanelsVisibility(); + } + } + public void ToggleProfilingBoard() + { + m_ToggleProfilingBoard.value = !m_ToggleProfilingBoard.value; + } + + void OnToggleProfilingBoard() { if (m_ProfilingBoard.parent == null) { @@ -1116,7 +1129,7 @@ namespace UnityEditor.VFX.UI Toggle m_ToggleProfilingBoard; void ToggleProfilingBoard(ChangeEvent e) { - ToggleProfilingBoard(); + OnToggleProfilingBoard(); } public void OnVisualEffectComponentChanged(IEnumerable visualEffects) @@ -1812,27 +1825,7 @@ namespace UnityEditor.VFX.UI { m_ComponentBoard?.DeactivateBoundsRecordingIfNeeded(); //Avoids saving the graph with unnecessary bounds computations - var graphToSave = new HashSet(); - GetGraphsRecursively(controller.graph, graphToSave); - foreach (var graph in graphToSave) - { - if (EditorUtility.IsDirty(graph) || UnityEngine.Object.ReferenceEquals(graph, controller.graph)) - { - graph.UpdateSubAssets(); - try - { - VFXGraph.compilingInEditMode = !m_IsRuntimeMode; - graph.visualEffectResource.WriteAsset(); - } - finally - { - VFXGraph.compilingInEditMode = false; - } - } - } - - // Only for testing purpose - //VFXAnalytics.GetInstance().OnSaveVFXAsset(this); + controller.graph.visualEffectResource.WriteAssetWithSubAssets(); } internal void SaveAs(string newPath) @@ -1848,46 +1841,6 @@ namespace UnityEditor.VFX.UI } } - void GetGraphsRecursively(VFXGraph start, HashSet graphs) - { - if (graphs.Contains(start)) - return; - graphs.Add(start); - foreach (var child in start.children) - { - if (child is VFXSubgraphOperator ope) - { - if (ope.subgraph != null) - { - var graph = ope.subgraph.GetResource().GetOrCreateGraph(); - GetGraphsRecursively(graph, graphs); - } - } - else if (child is VFXSubgraphContext subCtx) - { - if (subCtx.subgraph != null) - { - var graph = subCtx.subgraph.GetResource().GetOrCreateGraph(); - GetGraphsRecursively(graph, graphs); - } - } - else if (child is VFXContext ctx) - { - foreach (var block in ctx.children.Cast()) - { - if (block is VFXSubgraphBlock subBlock) - { - if (subBlock.subgraph != null) - { - var graph = subBlock.subgraph.GetResource().GetOrCreateGraph(); - GetGraphsRecursively(graph, graphs); - } - } - } - } - } - } - public EventPropagation OnCompile() { Compile(); @@ -1967,11 +1920,6 @@ namespace UnityEditor.VFX.UI } } - public void ToggleDebugPanels() - { - m_ProfilingBoard.TogglePanelsVisibility(); - } - public IEnumerable GetAllContexts() { foreach (var layer in contentViewContainer.Children()) diff --git a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXSlotContainerEditor.cs b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXSlotContainerEditor.cs index d8d93500..8d59a884 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXSlotContainerEditor.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXSlotContainerEditor.cs @@ -442,6 +442,7 @@ class VFXSlotContainerEditor : Editor { VFXValueType.Int32, new Color32(125, 110, 191, 255) }, { VFXValueType.Matrix4x4, new Color32(118, 118, 118, 255) }, { VFXValueType.Mesh, new Color32(130, 223, 226, 255) }, + { VFXValueType.SkinnedMeshRenderer, new Color32(130, 223, 226, 255) }, { VFXValueType.None, new Color32(118, 118, 118, 255) }, { VFXValueType.Spline, new Color32(130, 223, 226, 255) }, { VFXValueType.Texture2D, new Color32(250, 137, 137, 255) }, diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXErrorManager.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXErrorManager.cs index 69a07ce7..1eb13b51 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXErrorManager.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXErrorManager.cs @@ -158,7 +158,10 @@ namespace UnityEditor.VFX } finally { + // swap dirty and scheduled models + var tmp = m_DirtyModels; m_DirtyModels = m_ScheduledModels; + m_ScheduledModels = tmp; m_ScheduledModels.Clear(); m_IsGeneratingErrors = false; } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs index 6a8a7b60..3a369536 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs @@ -374,19 +374,7 @@ namespace UnityEditor.VFX AssetDatabase.StartAssetEditing(); } var vfxResource = VisualEffectResource.GetResourceAtPath(path); - if (vfxResource != null) - { - vfxResource.GetOrCreateGraph().UpdateSubAssets(); - try - { - VFXGraph.compilingInEditMode = vfxResource.GetOrCreateGraph().GetCompilationMode() == VFXCompilationMode.Edition; - vfxResource.WriteAsset(); // write asset as the AssetDatabase won't do it. - } - finally - { - VFXGraph.compilingInEditMode = false; - } - } + vfxResource?.WriteAssetWithSubAssets(); } } finally @@ -432,6 +420,13 @@ namespace UnityEditor.VFX resource.GetOrCreateGraph().UpdateSubAssets(); } + public static void WriteAssetWithSubAssets(this VisualEffectResource resource) + { + var graph = resource.GetOrCreateGraph(); + graph.UpdateSubAssets(); + resource.WriteAsset(); + } + public static bool IsAssetEditable(this VisualEffectResource resource) { return AssetDatabase.IsOpenForEdit((UnityEngine.Object)resource.asset ?? resource.subgraph, StatusQueryOptions.UseCachedIfPossible); @@ -488,15 +483,6 @@ namespace UnityEditor.VFX // 18: Change ProbabilitySampling m_IntegratedRandomDeprecated changed to m_Mode public static readonly int CurrentVersion = 18; - [NonSerialized] - internal static bool compilingInEditMode = false; - - public override void OnEnable() - { - base.OnEnable(); - m_ExpressionGraphDirty = true; - } - public override void OnSRPChanged() { m_GraphSanitized = false; @@ -1139,7 +1125,7 @@ namespace UnityEditor.VFX public void SetCompilationMode(VFXCompilationMode mode, bool reimport = true) { - if (m_CompilationMode != mode) + if (m_CompilationMode != mode && !GetResource().isSubgraph) { m_CompilationMode = mode; SetExpressionGraphDirty(); @@ -1400,11 +1386,10 @@ namespace UnityEditor.VFX public void CompileForImport() { - if (compilingInEditMode) - m_CompilationMode = VFXCompilationMode.Edition; + bool isSubgraph = GetResource().isSubgraph; SyncCustomAttributes(); - if (!GetResource().isSubgraph) + if (!isSubgraph) { // Check Graph Before Import can be needed to synchronize modified shaderGraph foreach (var child in children) @@ -1492,21 +1477,15 @@ namespace UnityEditor.VFX [SerializeField] private int m_ResourceVersion; - [NonSerialized] private bool m_GraphSanitized = false; - [NonSerialized] private bool m_ExpressionGraphDirty = true; - [NonSerialized] private bool m_ExpressionValuesDirty = true; - [NonSerialized] private bool m_DependentDirty = true; - [NonSerialized] private bool m_MaterialsDirty = false; - [NonSerialized] private bool m_CustomAttributesDirty = false; - [NonSerialized] private VFXGraphCompiledData m_CompiledData; + private VFXCompilationMode m_CompilationMode = VFXCompilationMode.Runtime; private bool m_ForceShaderDebugSymbols = false; private bool m_ForceShaderValidation = false; diff --git a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/PropertyRM.uss b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/PropertyRM.uss index 23b76860..5e83b2e7 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/PropertyRM.uss +++ b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/PropertyRM.uss @@ -107,9 +107,11 @@ .propertyrm #spacebutton.None { background-image : url("project:///Packages/com.unity.visualeffectgraph/Editor/UIResources/VFX/d_NoneSpace@2x.png"); } -.propertyrm VFXMatrix4x4Field Label { - width: 18px; - margin-left: 4px; + +.propertyrm #matrixContainer Label { + width: 20px; + margin-right: 2px; + -unity-text-align: middle-right; } .propertyrm #spacebutton:hover { diff --git a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXDataAnchor.uss b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXDataAnchor.uss index af340652..e1fd0396 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXDataAnchor.uss +++ b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXDataAnchor.uss @@ -175,6 +175,10 @@ VFXDataAnchor.Output #type -unity-text-align: middle-center; } +.VFXDataAnchor .propertyrm #matrixContainer FloatInput { + width: 30px; +} + .VFXOutputDataAnchor #icon { width: 13px; diff --git a/Packages/com.unity.visualeffectgraph/Shaders/VFXRayTracingCommon.hlsl.meta b/Packages/com.unity.visualeffectgraph/Shaders/VFXRayTracingCommon.hlsl.meta index e6c9b694..6799cecd 100644 --- a/Packages/com.unity.visualeffectgraph/Shaders/VFXRayTracingCommon.hlsl.meta +++ b/Packages/com.unity.visualeffectgraph/Shaders/VFXRayTracingCommon.hlsl.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c8d2f69646140a74bba957f46ba9419d +guid: 361d34e4d312a544890385da951dbd12 ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Packages/com.unity.visualeffectgraph/Shaders/VFXVolumetricFogUpdate.template b/Packages/com.unity.visualeffectgraph/Shaders/VFXVolumetricFogUpdate.template index 1fc57580..0cf1f175 100644 --- a/Packages/com.unity.visualeffectgraph/Shaders/VFXVolumetricFogUpdate.template +++ b/Packages/com.unity.visualeffectgraph/Shaders/VFXVolumetricFogUpdate.template @@ -10,6 +10,10 @@ ${VFXInclude("Shaders/VFXParticleCommon.template")} // Indirect draw is always enabled for volumetric fog output RWStructuredBuffer indirectBuffer; +#if HAS_STRIPS_DATA +StructuredBuffer stripDataBuffer; +#endif + #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" diff --git a/Packages/com.unity.visualeffectgraph/package.json b/Packages/com.unity.visualeffectgraph/package.json index 737f8f3a..9207001c 100644 --- a/Packages/com.unity.visualeffectgraph/package.json +++ b/Packages/com.unity.visualeffectgraph/package.json @@ -36,5 +36,5 @@ ] } ], - "_fingerprint": "a6f52bdb90a859536ffa7087989628316309dacc" + "_fingerprint": "1610c4dd914dcb46d906bfe48b8db559fd045331" }