This commit is contained in:
2021-06-13 10:28:03 +02:00
parent eb70603c85
commit df2d24cbd3
7487 changed files with 943244 additions and 0 deletions

View File

@@ -0,0 +1 @@
sub-package.*

View File

@@ -0,0 +1,999 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [11.0.0] - 2020-10-21
### Added
- Added real-time Point Light Shadows.
- Added support for custom additional (i.e punctual) light shadow resolutions.
- Added a supported MSAA samples count check, so the actual supported MSAA samples count value can be assigned to RenderTexture descriptors.
- Added the TerrainCompatible SubShader Tag. Use this Tag in your custom shader to tell Unity that the shader is compatible with the Terrain system.
- Added _CameraSortingLayerTexture global shader variable and related parameters
- Added preset shapes for creating a freeform light
- Added serialization of Freeform ShapeLight mesh to avoid CPU cost of generating them on the runtime.
- Added 2D Renderer Asset Preset for creating a Universal Renderer Asset
- Added an option to use faster, but less accurate approximation functions when converting between the sRGB and Linear color spaces.
- Added screen space shadow as renderer feature
- Added [DisallowMultipleRendererFeature] attribute for Renderer Features.
- Added support for the PlayStation 5 platform.
### Changed
- Optimized 2D Renderer performance on mobile GPUs by reducing the number of render target switches.
- Optimized 2D Renderer performance by rendering the normal buffer at the same lower resolution as the light buffers.
- Improved Light2D UI/UX
- Improved 2D Menu layout
- Deprecated Light2D Parametric Light
- Deprecated Light2D point light cookie
- Renamed Light2D point light to spot light
- 2D Renderer: The per Blend Style render texture scale setting was replaced by a global scale setting for all Blend Styles.
- Optimized 2D Renderer performance by using a tiny light texture for layer/blend style pairs for which no light is rendered.
- Reorgnized the settings in 2D Renderer Data Inspector.
- FallOff Lookup Texture is now part of 2D RenderData.
- Creating a Shadow Caster 2D will use try and use sprite and physics bounds as the default shape
- Deleting all points in a Shadow Caster will cause the shape to use the bounds.
- Improved Geometry for Smooth Falloff of 2D Shape Lights.
- Updated the tooltips for Light 2D Inspector.
- Removed the Custom blend Mode option from the Blend Styles.
- New default Blend Styles when a new 2D Renderer Data asset is created.
- Added a supported MSAA samples count check, so the actual supported MSAA samples count value can be assigned to RenderTexture descriptors.
- Bloom in Gamma color-space now more closely matches Linear color-space, this will mean project using Bloom and Gamma color-space may need to adjust Bloom Intensity to match previous look.
- Autodesk Interactive Shader Graph files and folders containing them were renamed. The new file paths do not have spaces.
- Changed shader keywords of main light shadow from toggling to enumerating.
- Always use "High" quality normals, which normalizes the normal in pixel shader. "Low" quality normals looked too much like a bug.
- Re-enabled implicit MSAA resolve to backbuffer on Metal MacOS.
- Changed Post Process Data to bool. When it is no enabled all post processing is stripped from build, when it is enabled you can still override resources there.
### Fixed
- Fixed an issue where Transparency Sort Mode value is taken from Graphics Settings instead of 2D Renderer [1310652](https://issuetracker.unity3d.com/issues/transparency-sort-mode-value-is-taken-from-graphics-settings-instead-of-2d-forward-renderer)
- Fixed an issue where Post Processing doesn't enable when PostProcessData reassigned from the asset selector window.
- Fixed an issue where the camera renders black screen when Post Processing is enabled in the 2D Renderer and in the Camera Component.
- Fixed an issue where ShadowCaster2D was generating garbage when running in the editor. [case 1304158](https://issuetracker.unity3d.com/product/unity/issues/guid/1304158/)
- Fixed an issue where the 2D Renderer was incorrectly rendering transparency with normal maps on an empty background.
- Fixed an issue where 2D lighting was incorrectly calculated when using a perspective camera.
- Fixed an issue where Light2D did not upgrade Shadow Strength, Volumetric Intensity, Volumetric Shadow Strength correctly [case 1317755](https://issuetracker.unity3d.com/issues/urp-lighting-missing-orange-tint-in-scene-background)
- Fixed an issue where normal-mapped Sprites could render differently depending on whether they're dynamically-batched. [case 1286186](https://issuetracker.unity3d.com/issues/urp-2d-2d-light-on-a-rotated-sprite-is-skewed-when-using-normal-map-and-sorting-layer-is-not-default)
- Fixed an issue where 2D Shadows were casting to the wrong layers [case 1300753][https://issuetracker.unity3d.com/product/unity/issues/guid/1300753/]
- Fixed an issue where the scene view camera was not correctly cleared for the 2D Renderer. [case 1311377](https://issuetracker.unity3d.com/product/unity/issues/guid/1311377/)
- Fixed an issue where Sprites on one Sorting Layer were fully lit even when there's no 2D light targeting that layer.
- Fixed an issue where null reference exception was thrown when creating a 2D Renderer Data asset while scripts are compiling. [case 1263040](https://issuetracker.unity3d.com/issues/urp-nullreferenceexception-error-is-thrown-on-creating-2d-renderer-asset)
- Fixed an issue where objects in motion might jitter when the Pixel Perfect Camera is used. [case 1300474](https://issuetracker.unity3d.com/issues/urp-characters-sprite-repeats-in-the-build-when-using-pixel-perfect-camera-and-2d-renderer)
- Fixed an issue where the letter box/pillar box areas were not properly cleared when the Pixel Perfect Camera is used. [case 1291224](https://issuetracker.unity3d.com/issues/pixel-perfect-image-artifact-appear-between-the-reference-resolution-and-screen-resolution-borders-when-strech-fill-is-enabled)
- Fixed an issue where the Cinemachine Pixel Perfect Extension might cause the Orthographic Size of the Camera to jump to 1 when the Scene is loaded. [case 1249076](https://issuetracker.unity3d.com/issues/cinemachine-pixel-perfect-camera-extension-causes-the-orthogonal-size-to-jump-to-1-when-the-scene-is-loaded)
- Fixed an issue where no preview would show for the lit sprite master node in shadergraph
- Fixed an issue where no shader was generated for unlit sprite shaders in shadergraph
- Fixed an issue where Sprite-Lit-Default shader's Normal Map property wasn't affected by Tiling or Offset. [case 1270850](https://issuetracker.unity3d.com/issues/sprite-lit-default-shaders-normal-map-and-mask-textures-are-not-affected-by-tiling-and-offset-values)
- Removed the warning about mis-matched vertex streams when creating a default Particle System. [case 1285272](https://issuetracker.unity3d.com/issues/particles-urp-default-material-shows-warning-in-inspector)
- Fixed latest mockHMD renderviewport scale doesn't fill whole view after scaling. [case 1286161] (https://issuetracker.unity3d.com/issues/xr-urp-renderviewportscale-doesnt-fill-whole-view-after-scaling)
- Fixed camera renders black in XR when user sets invalid MSAA value.
- Fixed an issue causing additional lights to stop working when set as the sun source. [case 1278768](https://issuetracker.unity3d.com/issues/urp-every-light-type-is-rendered-as-directional-light-if-it-is-set-as-sun-source-of-the-environment)
- Fixed an issue causing passthrough camera to not render. [case 1283894](https://issuetracker.unity3d.com/product/unity/issues/guid/1283894/)
- Fixed an issue that caused a null reference when Lift Gamma Gain was being displayed in the Inspector and URP was upgraded to a newer version. [case 1283588](https://issuetracker.unity3d.com/issues/argumentnullexception-is-thrown-when-upgrading-urp-package-and-volume-with-lift-gamma-gain-is-focused-in-inspector)
- Fixed an issue where soft particles were not rendered when depth texture was disabled in the URP Asset. [case 1162556](https://issuetracker.unity3d.com/issues/lwrp-unlit-particles-shader-is-not-rendered-when-soft-particles-are-enabled-on-built-application)
- Fixed an issue where soft particles were rendered opaque on OpenGL. [case 1226288](https://issuetracker.unity3d.com/issues/urp-objects-that-are-using-soft-particles-are-rendered-opaque-when-opengl-is-used)
- Fixed an issue where the depth texture sample node used an incorrect texture in some frames. [case 1268079](https://issuetracker.unity3d.com/issues/urp-depth-texture-sample-node-does-not-use-correct-texture-in-some-frames)
- Fixed a compiler error in BakedLit shader when using Hybrid Renderer.
- Fixed an issue with upgrading material set to cutout didn't properly set alpha clipping. [case 1235516](https://issuetracker.unity3d.com/issues/urp-upgrade-material-utility-does-not-set-the-alpha-clipping-when-material-was-using-a-shader-with-rendering-mode-set-to-cutout)
- Fixed XR camera fov can be changed through camera inspector.
- Fixed an issue where Universal Render Pipeline with disabled antiAliasing was overwriting QualitySettings.asset on frequent cases. [case 1219159](https://issuetracker.unity3d.com/issues/urp-qualitysettings-dot-asset-file-gets-overwritten-with-the-same-content-when-the-editor-is-closed)
- Fixed a case where overlay camera with output texture caused base camera not to render to screen. [case 1283225](https://issuetracker.unity3d.com/issues/game-view-renders-a-black-view-when-having-an-overlay-camera-which-had-output-texture-assigned-in-the-camera-stack)
- Fixed an issue where the scene view camera ignored the pipeline assets HDR setting. [case 1284369](https://issuetracker.unity3d.com/issues/urp-scene-view-camera-ignores-pipeline-assets-hdr-settings-when-main-camera-uses-pipeline-settings)
- Fixed an issue where the Camera inspector was grabbing the URP asset in Graphics Settings rather than the currently active.
- Fixed an issue where the Light Explorer was grabbing the URP asset in Graphics Settings rather than the currently active.
- Fixed an issue causing materials to be upgraded multiple times.
- Fixed bloom inconsistencies between Gamma and Linear color-spaces.
- Fixed an issue in where all the entries in the Renderer List wasn't selectable and couldn't be deleted.
- Fixed Deferred renderer on some Android devices by forcing accurate GBuffer normals. [case 1288042]
- Fixed an issue where MSAA did not work in Editor Game View on Windows with Vulkan.
- Fixed issue where selecting and deselecting Forward Renderer asset would leak memory [case 1290628](https://issuetracker.unity3d.com/issues/urp-scriptablerendererfeatureeditor-memory-leak-while-interacting-with-forward-renderer-in-the-project-window)
- Fixed an issue where render scale was breaking SSAO in scene view. [case 1296710](https://issuetracker.unity3d.com/issues/ssao-effect-floating-in-the-air-in-scene-view-when-2-objects-with-shadergraph-materials-are-on-top-of-each-other)
- Fixed an issue where the inspector of Renderer Data would break after adding RenderObjects renderer feature and then adding another renderer feature.
- Fixed material upgrader to run in batch mode [case 1305402]
- Removed Custom.meta which was causing warnings. [case 1314288](https://issuetracker.unity3d.com/issues/urp-warnings-about-missing-metadata-appear-after-installing)
- Fixed an issue such that it is now posible to enqueue render passes at runtime.
- Fixed a regression where the precision was changed. [case 1313942](https://issuetracker.unity3d.com/issues/urp-shader-precision-is-reduced-to-half-when-scriptablerenderfeature-class-is-in-the-project)
- Fixed the default background color for previews to use the original color.
- Fixed GC allocations from XR occlusion mesh when using multipass.
- Fixed wrong shader / properties assignement to materials created from 3DsMax 2021 Physical Material. (case 1293576)
- Fixed shadow cascade blend culling factor.
- Fixed an issue where having "Opaque Texture" and MSAA enabled would cause the opaque texture to be rendered black on old Apple GPUs [case 1247423](https://issuetracker.unity3d.com/issues/urp-metal-opaque-objects-are-rendered-black-when-msaa-is-enabled)
- Fixed an issue where bokeh dof is applied incorrectly when there is an overlay camera in the camera stack. [case 1303572](https://issuetracker.unity3d.com/issues/urp-bokeh-depth-of-field-is-applied-incorrectly-when-the-main-camera-has-an-overlay-camera-in-the-camera-stack)
- Fixed issue causing missing shaders on DirectX 11 feature level 10 GPUs. [case 1278390](https://issuetracker.unity3d.com/product/unity/issues/guid/1278390/)
- Fixed errors when the Profiler is used with XR multipass. [case 1322916](https://issuetracker.unity3d.com/issues/xr-urp-profiler-spams-errors-in-the-console-upon-entering-play-mode)
- Fixed an issue where changing camera's position in the BeginCameraRendering do not apply properly. [case 1318629] (https://issuetracker.unity3d.com/issues/camera-doesnt-move-when-changing-its-position-in-the-begincamerarendering-and-the-endcamerarendering-methods)
## [10.2.0] - 2020-10-19
### Changed
- Changed RenderObjectsFeature UI to only expose valid events. Previously, when selecting events before BeforeRenderingPrepasses objects would not be drawn correctly as stereo and camera setup only happens before rendering opaques objects.
- Transparent Lit ShaderGraph using Additive blending will now properly fade with alpha [1270344]
### Fixed
- Fixed the Unlit shader not being SRP Batcher compatible on OpenGLES/OpenGLCore. [case 1263720](https://issuetracker.unity3d.com/issues/urp-mobile-srp-batcher-is-not-visible-on-mobile-devices-in-frame-debugger)
- Fixed an issue with soft particles not rendering correctly for overlay cameras with post processing. [case 1241626](https://issuetracker.unity3d.com/issues/soft-particles-does-not-fade-out-near-the-opaque-surfaces-when-post-processing-is-enabled-on-a-stacked-camera)
- Fixed MSAA override on camera does not work in non-XR project if target eye is selected to both eye.
## [10.1.0] - 2020-10-12
- Added support for the Shadowmask Mixed Lighting Mode (Forward only), which supports up to four baked-shadow Lights.
- Added ComplexLit shader for advanced material features and deferred forward fallback.
- Added Clear Coat feature for ComplexLit shader and for shader graph.
- Added Parallax Mapping to the Lit shader (Lit.shader).
- Added the Detail Inputs setting group in the Lit shader (Lit.shader).
- Added Smooth shadow fading.
- The pipeline now outputs a warning in the console when trying to access camera color or depth texture when those are not valid. Those textures are only available in the context of `ScriptableRenderPass`.
- Added a property to access the renderer from the `CameraData`.
### Changed
- Shader functions SampleSH9, SampleSHPixel, SampleSHVertex are now gamma corrected in gamma space. As result LightProbes are gamma corrected too.
- The maximum number of visible lights when using OpenGL ES 3.x on Android now depends on the minimum OpenGL ES 3.x version as configured in PlayerSettings.
- The default value of the HDR property of a newly created Universal Render Pipeline Asset, is now set to true.
### Fixed
- Fixed an issue where the CapturePass would not capture the post processing effects.
- Fixed an issue were the filter window could not be defocused using the mouse. [case 1242032](https://issuetracker.unity3d.com/issues/urp-volume-override-window-doesnt-disappear-when-clicked-on-the-other-windows-in-the-editor)
- Fixed camera backgrounds not matching between editor and build when background is set to 'Uninitialized'. [case 1224369](https://issuetracker.unity3d.com/issues/urp-uninitialized-camera-background-type-does-not-match-between-the-build-and-game-view)
- Fixed a case where main light hard shadows would not work if any other light is present with soft shadows.[case 1250829](https://issuetracker.unity3d.com/issues/main-light-shadows-are-ignored-in-favor-of-additional-lights-shadows)
- Fixed issue that caused color grading to not work correctly with camera stacking. [case 1263193](https://issuetracker.unity3d.com/product/unity/issues/guid/1263193/)
- Fixed an issue that caused an infinite asset database reimport when running Unity in command line with -testResults argument.
- Fixed ParticlesUnlit shader to use fog color instead of always black. [case 1264585]
- Fixed issue that caused some properties in the camera to not be bolded and highlighted when edited in prefab mode. [case 1230082](https://issuetracker.unity3d.com/issues/urp-camera-prefab-fields-render-type-renderer-background-type-are-not-bolded-and-highlighted-when-edited-in-prefab-mode)
- Fixed issue where blur would sometimes flicker [case 1224915](https://issuetracker.unity3d.com/issues/urp-bloom-effect-flickers-when-using-integrated-post-processing-feature-set)
- Fixed an issue in where the camera inspector didn't refresh properly when changing pipeline in graphic settings. [case 1222668](https://issuetracker.unity3d.com/issues/urp-camera-properties-not-refreshing-on-adding-or-removing-urp-pipeline-in-the-graphics-setting)
- Fixed depth of field to work with dynamic resolution. [case 1225467](https://issuetracker.unity3d.com/issues/dynamic-resolution-rendering-error-when-using-depth-of-field-in-urp)
- Fixed FXAA, SSAO, Motion Blur to work with dynamic resolution.
- Fixed an issue where Pixel lighting variants were stripped in builds if another URP asset had Additional Lights set to Per Vertex [case 1263514](https://issuetracker.unity3d.com/issues/urp-all-pixel-lighting-variants-are-stripped-in-build-if-at-least-one-urp-asset-has-additional-lights-set-to-per-vertex)
- Fixed an issue where transparent meshes were rendered opaque when using custom render passes [case 1262887](https://issuetracker.unity3d.com/issues/urp-transparent-meshes-are-rendered-as-opaques-when-using-lit-shader-with-custom-render-pass)
- Fixed regression from 8.x.x that increased launch times on Android with GLES3. [case 1269119](https://issuetracker.unity3d.com/issues/android-launch-times-increased-x4-from-urp-8-dot-1-0-to-urp-10-dot-0-0-preview-dot-26)
- Fixed an issue with a render texture failing assertion when chosing an invalid format. [case 1222676](https://issuetracker.unity3d.com/issues/the-error-occurs-when-a-render-texture-which-has-a-certain-color-format-is-applied-to-the-cameras-output-target)
- Fixed an issue that caused the unity_CameraToWorld matrix to have z flipped values. [case 1257518](https://issuetracker.unity3d.com/issues/parameter-unity-cameratoworld-dot-13-23-33-is-inverted-when-using-universal-rp-7-dot-4-1-and-newer)
- Fixed not using the local skybox on the camera game object when the Skybox Material property in the Lighting window was set to null.
- Fixed an issue where, if URP was not in use, you would sometimes get errors about 2D Lights when going through the menus.
- Fixed GC when using XR single-pass automated tests.
- Fixed an issue that caused a null reference when deleting camera component in a prefab. [case 1244430](https://issuetracker.unity3d.com/issues/urp-argumentnullexception-error-is-thrown-on-removing-camera-component-from-camera-prefab)
- Fixed resolution of intermediate textures when rendering to part of a render texture. [case 1261287](https://issuetracker.unity3d.com/product/unity/issues/guid/1261287/)
- Fixed indirect albedo not working with shadergraph shaders in some rare setups. [case 1274967](https://issuetracker.unity3d.com/issues/gameobjects-with-custom-mesh-are-not-reflecting-the-light-when-using-the-shader-graph-shaders)
- Fixed XR mirroView sRGB issue when color space is gamma.
- Fixed an issue where XR eye textures are recreated multiple times per frame due to per camera MSAA change.
- Fixed an issue wehre XR mirror view selector stuck.
- Fixed LightProbes to have gamma correct when using gamma color space. [case 1268911](https://issuetracker.unity3d.com/issues/urp-has-no-gamma-correction-for-lightprobes)
- Fixed GLES2 shader compilation.
- Fixed useless mip maps on temporary RTs/PostProcessing inherited from Main RT descriptor.
- Fixed issue with lens distortion breaking rendering when enabled and its intensity is 0.
- Fixed mixed lighting subtractive and shadowmask modes for deferred renderer.
- Fixed issue that caused motion blur to not work in XR.
- Fixed 2D renderer when using Linear rendering on Android directly to backbuffer.
- Fixed issue where multiple cameras would cause GC each frame. [case 1259717](https://issuetracker.unity3d.com/issues/urp-scriptablerendercontext-dot-getcamera-array-dot-resize-creates-garbage-every-frame-when-more-than-one-camera-is-active)
- Fixed Missing camera cannot be removed after scene is saved by removing the Missing camera label. [case 1252255](https://issuetracker.unity3d.com/issues/universal-rp-missing-camera-cannot-be-removed-from-camera-stack-after-scene-is-saved)
- Fixed MissingReferenceException when removing Missing camera from camera stack by removing Missing camera label. [case 1252263](https://issuetracker.unity3d.com/issues/universal-rp-missingreferenceexception-errors-when-removing-missing-camera-from-stack)
- Fixed slow down in the editor when editing properties in the UI for renderer features. [case 1279804](https://issuetracker.unity3d.com/issues/a-short-freeze-occurs-in-the-editor-when-expanding-or-collapsing-with-the-arrow-the-renderer-feature-in-the-forward-renderer)
- Fixed test 130_UnityMatrixIVP on OpenGL ES 3
- Fixed MSAA on Metal MacOS and Editor.
## [10.0.0] - 2020-06-10
### Added
- Added the option to strip Terrain hole Shader variants.
- Added support for additional Directional Lights. The amount of additional Directional Lights is limited by the maximum Per-object Lights in the Render Pipeline Asset.
- Added default implementations of OnPreprocessMaterialDescription for FBX, Obj, Sketchup and 3DS file formats.
- Added Transparency Sort Mode and Transparency Sort Axis to 2DRendererData.
- Added support for a user defined default material to 2DRendererData.
- Added the option to toggle shadow receiving on transparent objects.
- Added XR multipass rendering. Multipass rendering is a requirement on many VR platforms and allows graceful fallback when single-pass rendering isn't available.
- Added support for Camera Stacking when using the Forward Renderer. This introduces the Camera `Render Type` property. A Base Camera can be initialized with either the Skybox or Solid Color, and can combine its output with that of one or more Overlay Cameras. An Overlay Camera is always initialized with the contents of the previous Camera that rendered in the Camera Stack.
- Added AssetPostprocessors and Shadergraphs to handle Arnold Standard Surface and 3DsMax Physical material import from FBX.
- Added `[MainTexture]` and `[MainColor]` shader property attributes to URP shader properties. These will link script material.mainTextureOffset and material.color to `_BaseMap` and `_BaseColor` shader properties.
- Added the option to specify the maximum number of visible lights. If you set a value, lights are sorted based on their distance from the Camera.
- Added the option to control the transparent layer separately in the Forward Renderer.
- Added the ability to set individual RendererFeatures to be active or not, use `ScriptableRendererFeature.SetActive(bool)` to set whether a Renderer Feature will execute, `ScriptableRendererFeature.isActive` can be used to check the current active state of the Renderer Feature.
additional steps to the 2D Renderer setup page for quality and platform settings.
- If Unity Editor Analytics are enabled, Universal collects anonymous data about usage of Universal. This helps the Universal team focus our efforts on the most common scenarios, and better understand the needs of our customers.
- Added a OnCameraSetup() function to the ScriptableRenderPass API, that gets called by the renderer before rendering each camera
- Added a OnCameraCleanup() function to the ScriptableRenderPass API, that gets called by the renderer after rendering each camera
- Added Default Material Type options to the 2D Renderer Data Asset property settings.
- Added additional steps to the 2D Renderer setup page for quality and platform settings.
- Added option to disable XR autotests on test settings.
- Shader Preprocessor strips gbuffer shader variants if DeferredRenderer is not in the list of renderers in any Scriptable Pipeline Assets.
- Added an option to enable/disable Adaptive Performance when the Adaptive Performance package is available in the project.
- Added support for 3DsMax's 2021 Simplified Physical Material from FBX files in the Model Importer.
- Added GI to SpeedTree
- Added support for DXT5nm-style normal maps on Android, iOS and tvOS
- Added stencil override support for deferred renderer.
- Added a warning message when a renderer is used with an unsupported graphics API, as the deferred renderer does not officially support GL-based platforms.
- Added option to skip a number of final bloom iterations.
- Added support for [Screen Space Ambient Occlusion](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@10.0/manual/post-processing-ssao.html) and a new shader variant _SCREEN_SPACE_OCCLUSION.
- Added support for Normal Texture being generated in a prepass.
- Added a ConfigureInput() function to ScriptableRenderPass, so it is possible for passes to ask that a Depth, Normal and/or Opaque textures to be generated by the forward renderer.
- Added a float2 normalizedScreenSpaceUV to the InputData Struct.
- Added new sections to documentation: [Writing custom shaders](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@10.0/manual/writing-custom-shaders-urp.html), and [Using the beginCameraRendering event](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@10.0/manual/using-begincamerarendering.html).
- Added support for GPU instanced mesh particles on supported platforms.
- Added API to check if a Camera or Light is compatible with Universal Render Pipeline.
### Changed
- Moved the icon that indicates the type of a Light 2D from the Inspector header to the Light Type field.
- Eliminated some GC allocations from the 2D Renderer.
- Added SceneSelection pass for TerrainLit shader.
- Remove final blit pass to force alpha to 1.0 on mobile platforms.
- Deprecated the CinemachineUniversalPixelPerfect extension. Use the one from Cinemachine v2.4 instead.
- Replaced PlayerSettings.virtualRealitySupported with XRGraphics.tryEnable.
- Blend Style in the 2DRendererData are now automatically enabled/disabled.
- When using the 2D Renderer, Sprites will render with a faster rendering path when no lights are present.
- Particle shaders now receive shadows
- The Scene view now mirrors the Volume Layer Mask set on the Main Camera.
- Drawing order of SRPDefaultUnlit is now the same as the Built-in Render Pipline.
- Made MaterialDescriptionPreprocessors private.
- UniversalRenderPipelineAsset no longer supports presets. [Case 1197020](https://issuetracker.unity3d.com/issues/urp-reset-functionality-does-not-work-on-preset-of-universalrenderpipelineassets).
- The number of maximum visible lights is now determined by whether the platform is mobile or not.
- Renderer Feature list is now redesigned to fit more closely to the Volume Profile UI, this vastly improves UX and reliability of the Renderer Features List.
- Default color values for Lit and SimpleLit shaders changed to white due to issues with texture based workflows.
- You can now subclass ForwardRenderer to create a custom renderer based on it.
- URP is now computing tangent space per fragment.
- Optimized the 2D Renderer to skip rendering into certain internal buffers when not necessary.
- You can now subclass ForwardRenderer to create a custom renderer based on it.
- URP shaders that contain a priority slider now no longer have an offset of 50 by default.
- The virtual ScriptableRenderer.FrameCleanup() function has been marked obsolete and replaced by ScriptableRenderer.OnCameraCleanup() to better describe when the function gets invoked by the renderer.
- DepthOnlyPass, CopyDepthPass and CopyColorPass now use OnCameraSetup() instead of Configure() to set up their passes before executing as they only need to get their rendertextures once per camera instead of once per eye.
- Updated shaders to be compatible with Microsoft's DXC.
- Mesh GPU Instancing option is now hidden from the particles system renderer as this feature is not supported by URP.
- The 2D Renderer now supports camera stacking.
- 2D shaders now use half-precision floats whenever precise results are not necessary.
- Removed the ETC1_EXTERNAL_ALPHA variant from Shader Graph Sprite shaders.
- Eliminated some unnecessary clearing of render targets when using the 2D Renderer.
- The rendering of 2D lights is more effient as sorting layers affected by the same set of lights are now batched.
- Removed the 8 renderer limit from URP Asset.
- Merged the deferred renderer into the forward renderer.
- Changing the default value of Skip Iterations to 1 in Bloom effect editor
- Use SystemInfo to check if multiview is supported instead of being platform hardcoded
- Default attachment setup behaviour for ScriptableRenderPasses that execute before rendering opaques is now set use current the active render target setup. This improves performance in some situations.
- Combine XR occlusion meshes into one when using single-pass (multiview or instancing) to reduce draw calls and state changes.
- Shaders included in the URP package now use local Material keywords instead of global keywords. This increases the amount of available global user-defined Material keywords.
### Fixed
- Fixed an issue that caused WebGL to render blank screen when Depth texture was enabled [case 1240228](https://issuetracker.unity3d.com/issues/webgl-urp-scene-is-rendered-black-in-webgl-build-when-depth-texture-is-enabled)
- Fixed NaNs in tonemap algorithms (neutral and ACES) on Nintendo Switch.
- Fixed a performance problem with ShaderPreprocessor with large amount of active shader variants in the project
- Fixed an issue where linear to sRGB conversion occurred twice on certain Android devices.
- Fixed an issue where there were 2 widgets showing the outer angle of a spot light.
- Fixed an issue where Unity rendered fullscreen quads with the pink error shader when you enabled the Stop NaN post-processing pass.
- Fixed an issue where Terrain hole Shader changes were missing. [Case 1179808](https://issuetracker.unity3d.com/issues/terrain-brush-tool-is-not-drawing-when-paint-holes-is-selected).
- Fixed an issue where the Shader Graph `SceneDepth` node didn't work with XR single-pass (double-wide) rendering. See [case 1123069](https://issuetracker.unity3d.com/issues/lwrp-vr-shadergraph-scenedepth-doesnt-work-in-single-pass-rendering).
- Fixed Unlit and BakedLit shader compilations in the meta pass.
- Fixed an issue where the Bokeh Depth of Field shader would fail to compile on PS4.
- Fixed an issue where the Scene lighting button didn't work when you used the 2D Renderer.
- Fixed a performance regression when you used the 2D Renderer.
- Fixed an issue where the Freeform 2D Light gizmo didn't correctly show the Falloff offset.
- Fixed an issue where the 2D Renderer rendered nothing when you used shadow-casting lights with incompatible Renderer2DData.
- Fixed an issue where errors were generated when the Physics2D module was not included in the project's manifest.
- Fixed an issue where Prefab previews were incorrectly lit when you used the 2D Renderer.
- Fixed an issue where the Light didn't update correctly when you deleted a Sprite that a Sprite 2D Light uses.
- Fixed an issue where 2D Lighting was broken for Perspective Cameras.
- Fixed an issue where resetting a Freeform 2D Light would throw null reference exceptions. [Case 1184536](https://issuetracker.unity3d.com/issues/lwrp-changing-light-type-to-freeform-after-clicking-on-reset-throws-multiple-arguementoutofrangeexception).
- Fixed an issue where Freeform 2D Lights were not culled correctly when there was a Falloff Offset.
- Fixed an issue where Tilemap palettes were invisible in the Tile Palette window when the 2D Renderer was in use. [Case 1162550](https://issuetracker.unity3d.com/issues/adding-tiles-in-the-tile-palette-makes-the-tiles-invisible).
- Fixed issue where black emission would cause unneccesary inspector UI repaints. [Case 1105661](https://issuetracker.unity3d.com/issues/lwrp-inspector-window-is-being-repainted-when-using-the-material-with-emission-enabled-and-set-to-black-00-0).
- Fixed user LUT sampling being done in Linear instead of sRGB.
- Fixed an issue when trying to get the Renderer via API on the first frame. [Case 1189196](https://issuetracker.unity3d.com/product/unity/issues/guid/1189196/).
- Fixed a material leak on domain reload.
- Fixed an issue where deleting an entry from the Renderer List and then undoing that change could cause a null reference. [Case 1191896](https://issuetracker.unity3d.com/issues/nullreferenceexception-when-attempting-to-remove-entry-from-renderer-features-list-after-it-has-been-removed-and-then-undone).
- Fixed an issue where the user would get an error if they removed the Additional Camera Data component. [Case 1189926](https://issuetracker.unity3d.com/issues/unable-to-remove-universal-slash-hd-additional-camera-data-component-serializedobject-target-destroyed-error-is-thrown).
- Fixed post-processing with XR single-pass rendering modes.
- Fixed an issue where Cinemachine v2.4 couldn't be used together with Universal RP due to a circular dependency between the two packages.
- Fixed an issue that caused shaders containing `HDRP` string in their path to be stripped from the build.
- Fixed an issue that caused only selected object to render in SceneView when Wireframe drawmode was selected.
- Fixed Renderer Features UI tooltips. [Case 1191901](https://issuetracker.unity3d.com/issues/forward-renderers-render-objects-layer-mask-tooltip-is-incorrect-and-contains-a-typo).
- Fixed multiple issues where Shader Graph shaders failed to build for XR in the Universal RP.
- Fixed an issue when using the 2D Renderer where some types of renderers would not be assigned the correct material.
- Fixed inconsistent lighting between the forward renderer and the deferred renderer, that was caused by a missing normalize operation on vertex normals on some speedtree shader variants.
- Fixed issue where XR Multiview failed to render when using URP Shader Graph Shaders
- Fixed lazy initialization with last version of ResourceReloader
- Fixed broken images in package documentation.
- Fixed an issue where viewport aspect ratio was wrong when using the Stretch Fill option of the Pixel Perfect Camera. [case 1188695](https://issuetracker.unity3d.com/issues/pixel-perfect-camera-component-does-not-maintain-the-aspect-ratio-when-the-stretch-fill-is-enabled)
- Fixed an issue where setting a Normal map on a newly created material would not update. [case 1197217](https://issuetracker.unity3d.com/product/unity/issues/guid/1197217/)
- Fixed an issue where post-processing was not applied for custom renderers set to run on the "After Rendering" event [case 1196219](https://issuetracker.unity3d.com/issues/urp-post-processing-is-not-applied-to-the-scene-when-render-ui-event-is-set-to-after-rendering)
- Fixed an issue that caused an extra blit when using custom renderers [case 1156741](https://issuetracker.unity3d.com/issues/lwrp-performance-decrease-when-using-a-scriptablerendererfeature)
- Fixed an issue with transparent objects not receiving shadows when using shadow cascades. [case 1116936](https://issuetracker.unity3d.com/issues/lwrp-cascaded-shadows-do-not-appear-on-alpha-blended-objects)
- Fixed issue where using a ForwardRendererData preset would cause a crash. [case 1201052](https://issuetracker.unity3d.com/product/unity/issues/guid/1201052/)
- Fixed an issue where particles had dark outlines when blended together [case 1199812](https://issuetracker.unity3d.com/issues/urp-soft-particles-create-dark-blending-artefacts-when-intersecting-with-scene-geometry)
- Fixed an issue with deleting shader passes in the custom renderer features list [case 1201664](https://issuetracker.unity3d.com/issues/urp-remove-button-is-not-activated-in-shader-passes-list-after-creating-objects-from-renderer-features-in-urpassets-renderer)
- Fixed camera inverse view-projection matrix in XR mode, depth-copy and color-copy passes.
- Fixed an issue with the null check when `UniversalRenderPipelineLightEditor.cs` tries to access `SceneView.lastActiveSceneView`.
- Fixed an issue where the 'Depth Texture' drop down was incorrectly disabled in the Camera Inspector.
- Fixed an issue that caused errors if you disabled the VR Module when building a project.
- Fixed an issue where the default TerrainLit Material was outdated, which caused the default Terrain to use per-vertex normals instead of per-pixel normals.
- Fixed shader errors and warnings in the default Universal RP Terrain Shader. [case 1185948](https://issuetracker.unity3d.com/issues/urp-terrain-slash-lit-base-pass-shader-does-not-compile)
- Fixed an issue where the URP Material Upgrader tried to upgrade standard Universal Shaders. [case 1144710](https://issuetracker.unity3d.com/issues/upgrading-to-lwrp-materials-is-trying-to-upgrade-lwrp-materials)
- Fixed an issue where some Materials threw errors when you upgraded them to Universal Shaders. [case 1200938](https://issuetracker.unity3d.com/issues/universal-some-materials-throw-errors-when-updated-to-universal-rp-through-update-materials-to-universal-rp)
- Fixed issue where normal maps on terrain appeared to have flipped X-components when compared to the same normal map on a mesh. [case 1181518](https://fogbugz.unity3d.com/f/cases/1181518/)
- Fixed an issue where the editor would sometimes crash when using additional lights [case 1176131](https://issuetracker.unity3d.com/issues/mac-crash-on-processshadowcasternodevisibilityandcullwithoutumbra-when-same-rp-asset-is-set-in-graphics-and-quality-settings)
- Fixed RemoveComponent on Camera contextual menu to not remove Camera while a component depend on it.
- Fixed an issue where right eye is not rendered to. [case 1170619](https://issuetracker.unity3d.com/issues/vr-lwrp-terrain-is-not-rendered-in-the-right-eye-of-an-hmd-when-using-single-pass-instanced-stereo-rendering-mode-with-lwrp)
- Fixed issue where TerrainDetailLit.shader fails to compile when XR is enabled.
- Fixed an issue that allowed height-based blending on Terrains with more than 4 materials, which is not supported.
- Fixed an issue where opaque objects were outputting incorrect alpha values [case 1168283](https://issuetracker.unity3d.com/issues/lwrp-alpha-clipping-material-makes-other-materials-look-like-alpha-clipping-when-gameobject-is-shown-in-render-texture)
- Fixed an issue where a depth texture was always created when post-processing was enabled, even if no effects made use of it.
- Fixed incorrect light attenuation on Nintendo Switch.
- Fixed an issue where the Volume System would not use the Cameras Transform when no `Volume Trigger` was set.
- Fixed an issue where post processing disappeared when using custom renderers and SMAA or no AA
- Fixed an issue where the 2D Renderer upgrader did not upgrade using the correct default material
- Fixed an issue with soft particles having dark blending when intersecting with scene geometry [case 1199812](https://issuetracker.unity3d.com/issues/urp-soft-particles-create-dark-blending-artefacts-when-intersecting-with-scene-geometry)
- Fixed an issue with additive particles blending incorrectly [case 1215713](https://issuetracker.unity3d.com/issues/universal-render-pipeline-additive-particles-not-using-vertex-alpha)
- Fixed an issue where camera preview window was missing in scene view. [case 1211971](https://issuetracker.unity3d.com/issues/scene-view-urp-camera-preview-window-is-missing-in-the-scene-view)
- Fixed an issue with shadow cascade values were not readable in the render pipeline asset [case 1219003](https://issuetracker.unity3d.com/issues/urp-cascade-values-truncated-on-selecting-two-or-four-cascades-in-shadows-under-universalrenderpipelineasset)
- Fixed an issue where MSAA isn't applied until eye textures are relocated by changing their resolution. [case 1197958](https://issuetracker.unity3d.com/issues/oculus-quest-oculus-go-urp-msaa-isnt-applied-until-eye-textures-are-relocated-by-changing-their-resolution)
- Fixed an issue where camera stacking didn't work properly inside prefab mode. [case 1220509](https://issuetracker.unity3d.com/issues/urp-cannot-assign-overlay-cameras-to-a-camera-stack-while-in-prefab-mode)
- Fixed the definition of `mad()` in SMAA shader for OpenGL.
- Fixed an issue where partical shaders failed to handle Single-Pass Stereo VR rendering with Double-Wide Textures. [case 1201208](https://issuetracker.unity3d.com/issues/urp-vr-each-eye-uses-the-cameraopaquetexture-of-both-eyes-for-rendering-when-using-single-pass-rendering-mode)
- Fixed an issue that caused assets to be reimported if player prefs were cleared. [case 1192259](https://issuetracker.unity3d.com/issues/lwrp-clearing-playerprefs-through-a-script-or-editor-causes-delay-and-console-errors-to-appear-when-entering-the-play-mode)
- Fixed missing Custom Render Features after Library deletion. [case 1196338](https://issuetracker.unity3d.com/product/unity/issues/guid/1196338/)
- Fixed not being able to remove a Renderer Feature due to tricky UI selection rects. [case 1208113](https://issuetracker.unity3d.com/product/unity/issues/guid/1208113/)
- Fixed an issue where the Camera Override on the Render Object Feature would not work with many Render Features in a row. [case 1205185](https://issuetracker.unity3d.com/product/unity/issues/guid/1205185/)
- Fixed UI clipping issue in Forward Renderer inspector. [case 1211954](https://issuetracker.unity3d.com/product/unity/issues/guid/1211954/)
- Fixed a Null ref when trying to remove a missing Renderer Feature from the Forward Renderer. [case 1196651](https://issuetracker.unity3d.com/product/unity/issues/guid/1196651/)
- Fixed data serialization issue when adding a Renderer Feature to teh Forward Renderer. [case 1214779](https://issuetracker.unity3d.com/product/unity/issues/guid/1214779/)
- Fixed issue with AssetPostprocessors dependencies causing models to be imported twice when upgrading the package version.
- Fixed an issue where NullReferenceException might be thrown when creating 2D Lights. [case 1219374](https://issuetracker.unity3d.com/issues/urp-nullreferenceexception-threw-on-adding-the-light-2d-experimental-component-when-2d-render-data-not-assigned)
- Fixed an issue with a blurry settings icon. [case 1201895](https://issuetracker.unity3d.com/issues/urp-setting-icon-blurred-in-universalrendererpipelineasset)
- Fixed issue that caused the QualitySettings anti-aliasing changing without user interaction. [case 1195272](https://issuetracker.unity3d.com/issues/lwrp-the-anti-alias-quality-settings-value-is-changing-without-user-interaction)
- Fixed an issue where Shader Graph shaders generate undeclared identifier 'GetWorldSpaceNormalizeViewDir' error.
- Fixed an issue where rendering into RenderTexture with Single Pass Instanced renders both eyes overlapping.
- Fixed an issue where Renderscale setting has no effect when using XRSDK.
- Fixed an issue where renderScale != 1 or Display.main.requiresBlitToBackbuffer forced an unnecessary blit on XR.
- Fixed an issue that causes double sRGB correction on Quest. [case 1209292](https://issuetracker.unity3d.com/product/unity/issues/guid/1209292)
- Fixed an issue where terrain DepthOnly pass does not work for XR.
- Fixed an issue that caused depth texture to be flipped when sampling from shaders [case 1225362](https://issuetracker.unity3d.com/issues/game-object-is-rendered-incorrectly-in-the-game-view-when-sampling-depth-texture)
- Fixed an issue with URP switching such that every avaiable URP makes a total set of supported features such that all URPs are taken into consideration. [case 1157420](https://issuetracker.unity3d.com/issues/lwrp-srp-switching-doesnt-work-even-with-manually-adding-shadervariants-per-scene)
- Fixed an issue where XR multipass repeatedly throws error messages "Multi pass stereo mode doesn't support Camera Stacking".
- Fixed an issue with shadows not appearing on terrains when no cascades were selected [case 1226530](https://issuetracker.unity3d.com/issues/urp-no-shadows-on-terrain-when-cascades-is-set-to-no-cascades-in-render-pipeline-asset-settings)
- Fixed a shader issue that caused the Color in Sprite Shape to work improperly.
- Fixed an issue with URP switching such that every available URP makes a total set of supported features such that all URPs are taken into consideration. [case 1157420](https://issuetracker.unity3d.com/issues/lwrp-srp-switching-doesnt-work-even-with-manually-adding-shadervariants-per-scene)
- Metallic slider on the Lit shader is now linear meaning correct values are used for PBR.
- Fixed an issue where Post-Processing caused nothing to render on GLES2.
- Fixed an issue that causes viewport to not work correctly when rendering to textures. [case 1225103](https://issuetracker.unity3d.com/issues/urp-the-viewport-rect-isnt-correctly-applied-when-the-camera-is-outputting-into-a-rendertexture)
- Fixed an issue that caused incorrect sampling of HDR reflection probe textures.
- Fixed UI text of RenderObjects feature to display LightMode tag instead of Shader Pass Name. [case 1201696](https://issuetracker.unity3d.com/issues/render-feature-slash-pass-ui-has-a-field-for-shader-pass-name-when-it-actually-expects-shader-pass-lightmode)
- Fixed an issue when Linear -> sRGB conversion would not happen on some Android devices. [case 1226208](https://issuetracker.unity3d.com/issues/no-srgb-conversion-on-some-android-devices-when-using-the-universal-render-pipeline)
- Fixed issue where using DOF at the same time as Dynamic Scaling, the depth buffer was smapled with incorrect UVs. [case 1225467](https://issuetracker.unity3d.com/product/unity/issues/guid/1225467/)
- Fixed an issue where an exception would be thrown when resetting the ShadowCaster2D component. [case 1225339](https://issuetracker.unity3d.com/issues/urp-unassignedreferenceexception-thrown-on-resetting-the-shadow-caster-2d-component)
- Fixe an issue where using a Subtractive Blend Style for your 2D Lights might cause artifacts in certain post-processing effects. [case 1215584](https://issuetracker.unity3d.com/issues/urp-incorrect-colors-in-scene-when-using-subtractive-and-multiply-blend-mode-in-gamma-color-space)
- Fixed an issue where Cinemachine Pixel Perfect Extension didn't work when CinemachineBrain Update Method is anything other than Late Update.
- Fixed an issue where Sprite Shader Graph shaders weren't double-sided by default.
- Fixed an issue where particles using Sprite Shader Graph shaders were invisible.
- Fixed an issue where Scene objects might be incorrectly affected by 2D Lights from a previous Sorting Layer.
- Fixed an issue where errors would appear in the Console when entering Play Mode with a 2D Light selected in the Hierarchy. [Case 1226918](https://issuetracker.unity3d.com/issues/errors-appear-in-the-console-when-global-2d-light-is-selected-in-hierarchy)
- Fixed an issue that caused Android GLES to render blank screen when Depth texture was enabled without Opaque texture [case 1219325](https://issuetracker.unity3d.com/issues/scene-is-not-rendered-on-android-8-and-9-when-depth-texture-is-enabled-in-urp-asset)
- Fixed an issue that caused transparent objects to always render over top of world space UI. [case 1219877](https://issuetracker.unity3d.com/product/unity/issues/guid/1219877/)
- Fixed issue causing sorting fudge to not work between shadergraph and urp particle shaders. [case 1222762](https://issuetracker.unity3d.com/product/unity/issues/guid/1222762/)
- Fixed shader compilation errors when using multiple lights in DX10 level GPU. [case 1222302](https://issuetracker.unity3d.com/issues/urp-no-materials-apart-from-ui-are-rendered-when-using-direct3d11-graphics-api-on-a-dx10-gpu)
- Fixed an issue with shadows not being correctly calculated in some shaders.
- Fixed invalid implementation of one function in LWRP -> URP backward compatibility support.
- Fixed issue on Nintendo Switch where maximum number of visible lights in C# code did not match maximum number in shader code.
- Fixed OpenGL ES 3.0 support for URP ShaderGraph. [case 1230890](https://issuetracker.unity3d.com/issues/urptemplate-gles3-android-custom-shader-fails-to-compile-on-adreno-306-gpu)
- Fixed an issue where multi edit camera properties didn't work. [case 1230080](https://issuetracker.unity3d.com/issues/urp-certain-settings-are-not-applied-to-all-cameras-when-multi-editing-in-the-inspector)
- Fixed an issue where the emission value in particle shaders would not update in the editor without entering the Play mode.
- Fixed issues with performance when importing fbx files.
- Fixed issues with NullReferenceException happening with URP shaders.
- Fixed an issue that caused memory allocations when sorting cameras. [case 1226448](https://issuetracker.unity3d.com/issues/2d-renderer-using-more-than-one-camera-that-renders-out-to-a-render-texture-creates-gc-alloc-every-frame)
- Fixed an issue where grid lines were drawn on top of opaque objects in the preview window. [Case 1240723](https://issuetracker.unity3d.com/issues/urp-grid-is-rendered-in-front-of-the-model-in-the-inspector-animation-preview-window-when-depth-or-opaque-texture-is-enabled).
- Fixed an issue where objects in the preview window were affected by layer mask settings in the default renderer. [Case 1204376](https://issuetracker.unity3d.com/issues/urp-prefab-preview-is-blank-when-a-custom-forward-renderer-data-and-default-layer-mask-is-mixed-are-used).
- Fixed an issue with reflections when using an orthographic camera [case 1209255](https://issuetracker.unity3d.com/issues/urp-weird-reflections-when-using-lit-material-and-a-camera-with-orthographic-projection)
- Fixed issue that caused unity_AmbientSky, unity_AmbientEquator and unity_AmbientGround variables to be unintialized.
- Fixed issue that caused `SHADERGRAPH_AMBIENT_SKY`, `SHADERGRAPH_AMBIENT_EQUATOR` and `SHADERGRAPH_AMBIENT_GROUND` variables to be uninitialized.
- Fixed SceneView Draw Modes not being properly updated after opening new scene view panels or changing the editor layout.
- Fixed GLES shaders compilation failing on Windows platform (not a mobile platform) due to uniform count limit.
- Fixed an issue that caused the inverse view and projection matrix to output wrong values in some platforms. [case 1243990](https://issuetracker.unity3d.com/issues/urp-8-dot-1-breaks-unity-matrix-i-vp)
- Fixed an issue where the Render Scale setting of the pipeline asset didn't properly change the resolution when using the 2D Renderer. [case 1241537](https://issuetracker.unity3d.com/issues/render-scale-is-not-applied-to-the-rendered-image-when-2d-renderer-is-used-and-hdr-option-is-disabled)
- Fixed an issue where 2D lights didn't respect the Camera's Culling Mask. [case 1239136](https://issuetracker.unity3d.com/issues/urp-2d-2d-lights-are-ignored-by-camera-culling-mask)
- Fixed broken documentation links for some 2D related components.
- Fixed an issue where Sprite shaders generated by Shader Graph weren't double-sided. [case 1261232](https://issuetracker.unity3d.com/product/unity/issues/guid/1261232/)
- Fixed an issue where the package would fail to compile if the Animation module was disabled. [case 1227068](https://issuetracker.unity3d.com/product/unity/issues/guid/1227068/)
- Fixed an issue where Stencil settings wasn't serialized properly in sub object [case 1241218](https://issuetracker.unity3d.com/issues/stencil-overrides-in-urp-7-dot-3-1-render-objects-does-not-save-or-apply)
- Fixed an issue with not being able to remove Light Mode Tags [case 1240895](https://issuetracker.unity3d.com/issues/urp-unable-to-remove-added-lightmode-tags-of-filters-property-in-render-object)
- Fixed an issue where preset button could still be used, when it is not supposed to. [case 1246261](https://issuetracker.unity3d.com/issues/urp-reset-functionality-does-not-work-for-renderobject-preset-asset)
- Fixed an issue where Model Importer Materials used the Standard Shader from the Built-in Render Pipeline instead of URP Lit shader when the import happened at Editor startup.
- Fixed an issue where only unique names of cameras could be added to the camera stack.
- Fixed issue that caused shaders to fail to compile in OpenGL 4.1 or below.
- Fixed an issue where camera stacking with MSAA on OpenGL resulted in a black screen. [case 1250602](https://issuetracker.unity3d.com/issues/urp-camera-stacking-results-in-black-screen-when-msaa-and-opengl-graphics-api-are-used)
- Optimized shader compilation times by compiling different variant sets for vertex and fragment shaders.
- Fixed shadows for additional lights by limiting MAX_VISIBLE_LIGHTS to 16 for OpenGL ES 2.0 and 3.0 on mobile platforms. [case 1244391](https://issuetracker.unity3d.com/issues/android-urp-spotlight-shadows-are-not-being-rendered-on-adreno-330-and-320-when-built)
- Fixed Lit/SimpleLit/ParticlesLit/ParticlesSimpleLit/ParticlesUnlit shaders emission color not to be converted from gamma to linear color space. [case 1249615]
- Fixed missing unity_MatrixInvP for shader code and shaderGraph.
- Fixed XR support for deferred renderer.
- Fixing RenderObject to reflect name changes done at CustomForwardRenderer asset in project view. [case 1246256](https://issuetracker.unity3d.com/issues/urp-renderobject-name-does-not-reflect-inside-customforwardrendererdata-asset-on-renaming-in-the-inspector)
- Fixing camera overlay stacking adding to respect unity general reference restrictions. [case 1240788](https://issuetracker.unity3d.com/issues/urp-overlay-camera-is-missing-in-stack-list-of-the-base-camera-prefab)
- Fixed profiler marker errors. [case 1240963](https://issuetracker.unity3d.com/issues/urp-errors-are-thrown-in-a-console-when-using-profiler-to-profile-editor)
- Fixed issue that caused the pipeline to not create _CameraColorTexture if a custom render pass is injected. [case 1232761](https://issuetracker.unity3d.com/issues/urp-the-intermediate-color-texture-is-no-longer-created-when-there-is-at-least-one-renderer-feature)
- Fixed target eye UI for XR rendering is missing from camera inspector. [case 1261612](https://issuetracker.unity3d.com/issues/xr-cameras-target-eye-property-is-missing-when-inspector-is-in-normal-mode)
- Fixed an issue where terrain and speedtree materials would not get upgraded by upgrade project materials. [case 1204189](https://fogbugz.unity3d.com/f/cases/1204189/)
- Fixed an issue that caused renderer feature to not render correctly if the pass was injected before rendering opaques and didn't implement `Configure` method. [case 1259750](https://issuetracker.unity3d.com/issues/urp-not-rendering-with-a-renderer-feature-before-rendering-shadows)
- Fixed an issue where postFX's temp texture is not released properly.
- Fixed an issue where ArgumentOutOfRangeException errors were thrown after removing Render feature [case 1268147](https://issuetracker.unity3d.com/issues/urp-argumentoutofrangeexception-errors-are-thrown-on-undoing-after-removing-render-feature)
- Fixed an issue where depth and depth/normal of grass isn't rendered to depth texture.
- Fixed an issue that impacted MSAA performance on iOS/Metal [case 1219054](https://issuetracker.unity3d.com/issues/urp-ios-msaa-has-a-bigger-negative-impact-on-performance-when-using-urp-compared-to-built-in-rp)
- Fixed an issue that caused a warning to be thrown about temporary render texture not found when user calls ConfigureTarget(0). [case 1220871](https://issuetracker.unity3d.com/issues/urp-scriptable-render-passes-which-dont-require-a-bound-render-target-triggers-render-target-warning)
- Fixed performance issues in the C# shader stripper.
## [7.1.1] - 2019-09-05
### Upgrade Guide
- The render pipeline now handles custom renderers differently. You must now set up renderers for the Camera on the Render Pipeline Asset.
- Render Pipeline Assets upgrades automatically and either creates a default forward renderer in your project or links the existing custom one that you've assigned.
- If you have custom renderers assigned to Cameras, you must now add them to the current Render Pipeline Asset. Then you can select which renderer to use on the Camera.
### Added
- Added shader function `GetMainLightShadowParams`. This returns a half4 for the main light that packs shadow strength in x component and shadow soft property in y component.
- Added shader function `GetAdditionalLightShadowParams`. This returns a half4 for an additional light that packs shadow strength in x component and shadow soft property in y component.
- Added a `Debug Level` option to the Render Pipeline Asset. With this, you can control the amount of debug information generated by the render pipeline.
- Added ability to set the `ScriptableRenderer` that the Camera renders with via C# using `UniversalAdditionalCameraData.SetRenderer(int index)`. This maps to the **Renderer List** on the Render Pipeline Asset.
- Added shadow support for the 2D Renderer.
- Added ShadowCaster2D, and CompositeShadowCaster2D components.
- Added shadow intensity and shadow volume intensity properties to Light2D.
- Added new Gizmos for Lights.
- Added CinemachineUniversalPixelPerfect, a Cinemachine Virtual Camera Extension that solves some compatibility issues between Cinemachine and Pixel Perfect Camera.
- Added an option that disables the depth/stencil buffer for the 2D Renderer.
- Added manipulation handles for the inner cone angle for spot lights.
- Added documentation for the built-in post-processing solution and Volumes framework (and removed incorrect mention of the PPv2 package).
### Changed
- Increased visible lights limit for the forward renderer. It now supports 256 visible lights except in mobile platforms. Mobile platforms support 32 visible lights.
- Increased per-object lights limit for the forward renderer. It now supports 8 per-object lights in all platforms except GLES2. GLES2 supports 4 per-object lights.
- The Sprite-Lit-Default shader and the Sprite Lit Shader Graph shaders now use the vertex tangents for tangent space calculations.
- Temporary render textures for cameras rendering to render textures now use the same format and multisampling configuration as camera's target texture.
- All platforms now use R11G11B10_UFloat format for HDR render textures if supported.
- There is now a list of `ScriptableRendererData` on the Render Pipeline Asset as opposed to a renderer type. These are available to all Cameras and are included in builds.
- The renderer override on the Camera is now an enum that maps to the list of `ScriptableRendererData` on the Render Pipeline Asset.
- Pixel Perfect Camera now allows rendering to a render texture.
- Light2D GameObjects that you've created now have a default position with z equal to 0.
- Documentation: Changed the "Getting Started" section into "Install and Configure". Re-arranged the Table of Content.
### Fixed
- Fixed LightProbe occlusion contribution. [case 1146667](https://issuetracker.unity3d.com/product/unity/issues/guid/1146667/)
- Fixed an issue that caused a log message to be printed in the console when creating a new Material. [case 1173160](https://issuetracker.unity3d.com/product/unity/issues/guid/1173160/)
- Fixed an issue where OnRenderObjectCallback was never invoked. [case 1122420](https://issuetracker.unity3d.com/issues/lwrp-gl-dot-lines-and-debug-dot-drawline-dont-render-when-scriptable-render-pipeline-settings-is-set-to-lwrp)
- Fixed an issue where Sprite Masks didn't function properly when using the 2D Renderer. [case 1163474](https://issuetracker.unity3d.com/issues/lwrp-sprite-renderer-ignores-sprite-mask-when-lightweight-render-pipeline-asset-data-is-set-to-2d-renderer-experimental)
- Fixed memory leaks when using the Frame Debugger with the 2D Renderer.
- Fixed an issue where materials using `_Time` did not animate in the scene. [1175396](https://issuetracker.unity3d.com/product/unity/issues/guid/1175396/)
- Fixed an issue where the Particle Lit shader had artifacts when both soft particles and HDR were enabled. [1136285](https://issuetracker.unity3d.com/product/unity/issues/guid/1136285/)
- Fixed an issue where the Area Lights were set to Realtime, which caused them to not bake. [1159838](https://issuetracker.unity3d.com/issues/lwrp-template-baked-area-lights-do-not-work-if-project-is-created-with-lightweight-rp-template)
- Fixed an issue where the Disc Light did not generate any light. [1175097](https://issuetracker.unity3d.com/issues/using-lwrp-area-light-does-not-generate-light-when-its-shape-is-set-to-disc)
- Fixed an issue where the alpha was killed when an opaque texture was requested on an offscreen camera with HDR enabled [case 1163320](https://issuetracker.unity3d.com/issues/lwrp-mobile-secondary-camera-background-alpha-value-is-lost-when-hdr-and-opaque-texture-are-enabled-in-lwrp-asset).
- Fixed an issue that caused Orthographic camera with far plane set to 0 to span Unity console with errors. [case 1172269](https://issuetracker.unity3d.com/issues/orthographic-camera-with-far-plane-set-to-0-results-in-assertions)
- Fixed an issue causing heap allocation in `RenderPipelineManager.DoRenderLoop` [case 1156241](https://issuetracker.unity3d.com/issues/lwrp-playerloop-renderpipelinemanager-dot-dorenderloop-internal-gc-dot-alloc-allocates-around-2-dot-6kb-for-every-camera-in-the-scene)
- Fixed an issue that caused shadow artifacts when using large spot angle values [case 1136165](https://issuetracker.unity3d.com/issues/lwrp-adjusting-spot-angle-on-a-spotlight-produces-shadowmap-artifacts)
- Fixed an issue that caused self-shadowing artifacts when adjusting shadow near-plane on spot lights.
- Fixed an issue that caused specular highlights to disappear when the smoothness value was set to 1.0. [case 1161827](https://issuetracker.unity3d.com/issues/lwrp-hdrp-lit-shader-max-smoothness-value-is-incosistent-between-pipelines)
- Fixed an issue in the Material upgrader that caused transparent Materials to not upgrade correctly to Universal RP. [case 1170419](https://issuetracker.unity3d.com/issues/shader-conversion-upgrading-project-materials-causes-standard-transparent-materials-to-flicker-when-moving-the-camera).
- Fixed an issue causing shadows to be incorrectly rendered when a light was close to the shadow caster.
- Fixed post-processing for the 2D Renderer.
- Fixed an issue in Light2D that caused a black line to appear for a 360 degree spotlight.
- Fixed a post-processing rendering issue with non-fullscreen viewport. [case 1177660](https://issuetracker.unity3d.com/issues/urp-render-scale-slider-value-modifies-viewport-coordinates-of-the-screen-instead-of-the-resolution)
- Fixed an issue where **Undo** would not undo the creation of Additional Camera Data. [case 1158861](https://issuetracker.unity3d.com/issues/lwrp-additional-camera-data-script-component-appears-on-camera-after-manually-re-picking-use-pipeline-settings)
- Fixed an issue where selecting the same drop-down menu item twice would trigger a change event. [case 1158861](https://issuetracker.unity3d.com/issues/lwrp-additional-camera-data-script-component-appears-on-camera-after-manually-re-picking-use-pipeline-settings)
- Fixed an issue where selecting certain objects that use instancing materials would throw console warnings. [case 1127324](https://issuetracker.unity3d.com/issues/console-warning-is-being-spammed-when-having-lwrp-enabled-and-shader-with-gpu-instancing-present-in-the-scene)
- Fixed a GUID conflict with LWRP. [case 1179895](https://issuetracker.unity3d.com/product/unity/issues/guid/1179895/)
- Fixed an issue where the Terrain shader generated NaNs.
- Fixed an issue that caused the `Opaque Color` pass to never render at half or quarter resolution.
- Fixed and issue where stencil state on a `ForwardRendererData` was reset each time rendering happened.
## [7.0.1] - 2019-07-25
### Changed
- Platform checks now provide more helpful feedback about supported features in the Inspectors.
### Fixed
- Fixed specular lighting related artifacts on Mobile [case 1143049](https://issuetracker.unity3d.com/issues/ios-lwrp-rounded-cubes-has-graphical-artifacts-when-setting-pbr-shaders-smoothness-about-to-0-dot-65-in-shadergraph) and [case 1164822](https://issuetracker.unity3d.com/issues/lwrp-specular-highlight-becomes-hard-edged-when-increasing-the-size-of-an-object).
- Post-processing is no longer enabled in the previews.
- Unity no longer force-enables post-processing on a camera by default.
- Fixed an issue that caused the Scene to render darker in GLES3 and linear color space. [case 1169789](https://issuetracker.unity3d.com/issues/lwrp-android-scene-is-rendered-darker-in-build-when-graphics-api-set-to-gles3-and-color-space-set-to-linear)
## [7.0.0] - 2019-07-17
### Universal Render Pipeline
- LWRP has been renamed to the "Universal Render Pipeline" (UniversalRP).
- UniversalRP is the same as LWRP in terms of features and scope.
- Classes have moved to the Universal namespace (from LWRP).
### Upgrade Guide
- Upgrading to UniversalRP is designed to be almost seamless from the user side.
- LWRP package still exists, this forwards includes and classes to the UniversalRP Package.
- Please see the more involved upgrade guide (https://docs.google.com/document/d/1Xd5bZa8pYZRHri-EnNkyhwrWEzSa15vtnpcg--xUCIs/).
### Added
- Initial Stadia platform support.
- Added a menu option to create a new `ScriptableRendererFeature` script. To do so in the Editor, click on Asset > Create > Rendering > Lightweight Render Pipeline > Renderer Feature.
- Added documentation for SpeedTree Shaders in LWRP.
- Added extended features to LWRP Terrain Shader, so terrain assets can be forward-compatible with HDRP.
- Enabled per-layer advanced or legacy-mode blending in LWRP Terrain Shader.
- Added the documentation page "Rendering in LWRP", which describes the forward rendering camera loop.
- Added documentation overview for how Post Processing Version 2 works in LWRP.
- Added documentation notes and FAQ entry on the 2D Renderer affecting the LWRP Asset.
### Changed
- Replaced beginCameraRendering callbacks by non obsolete implementation in Light2D
- Updated `ScriptableRendererFeature` and `ScriptableRenderPass` API docs.
- Shader type Real translates to FP16 precision on Nintendo Switch.
### Fixed
- Fixed a case where built-in Shader time values could be out of sync with actual time. [case 1142495](https://fogbugz.unity3d.com/f/cases/1142495/)
- Fixed an issue that caused forward renderer resources to not load properly when you upgraded LWRP from an older version to 7.0.0. [case 1154925](https://issuetracker.unity3d.com/issues/lwrp-upgrading-lwrp-package-to-7-dot-0-0-breaks-forwardrenderdata-asset-in-resource-files)
- Fixed GC spikes caused by LWRP allocating heap memory every frame.
- Fixed distortion effect on particle unlit shader.
- Fixed NullReference exception caused when trying to add a ScriptableRendererFeature.
- Fixed issue with certain LWRP shaders not showing when using forward/2D renderer.
- Fixed the shadow resolve pass and the final pass, so they're not consuming unnecessary bandwidth. [case 1152439](https://issuetracker.unity3d.com/issues/lwrp-mobile-increased-memory-usage-and-extra-rendering-steps)
- Added missing page for 2D Lights in LWRP.
- Tilemap tiles no longer appear black when you use the 2D renderer.
- Sprites in the preview window are no longer lit by 2D Scene lighting.
- Fixed warnings for unsupported shadow map formats for GLES2 API.
- Disabled shadows for devices that do not support shadow maps or depth textures.
- Fixed support for LWRP per-pixel terrain. [case 1110520](https://fogbugz.unity3d.com/f/cases/1110520)
- Fixed some basic UI/usability issues with LWRP terrain Materials (use of warnings and modal value changes).
- Fixed an issue where using LWRP and Sprite Shape together would produce meta file conflicts.
- Fixed fp16 overflow in Switch in specular calculation
- Fixed shader compilation errors for Android XR projects.
- Updated the pipeline Asset UI to cap the render scale at 2x so that it matches the render pipeline implementation limit.
## [6.7.0] - 2019-05-16
### Added
- Added SpeedTree Shaders.
- Added two Shader Graph master nodes: Lit Sprite and Unlit Sprite. They only work with the 2D renderer.
- Added documentation for the 2D renderer.
### Changed
- The 2D renderer and Light2D component received a number of improvements and are now ready to try as experimental features.
- Updated the [Feature Comparison Table](lwrp-builtin-feature-comparison.md) to reflect the current state of LWRP features.
### Fixed
- When in playmode, the error 'Non matching Profiler.EndSample' no longer appears. [case 1140750](https://fogbugz.unity3d.com/f/cases/1140750/)
- LWRP Particle Shaders now correctly render in stereo rendering modes. [case 1106699](https://fogbugz.unity3d.com/f/cases/1106699/)
- Shaders with 'debug' in the name are no longer stripped automatically. [case 1112983](https://fogbugz.unity3d.com/f/cases/1112983/)
- Fixed tiling issue with selection outline and baked cutout shadows.
- in the Shadergraph Unlit Master node, Premultiply no longer acts the same as Alpha. [case 1114708](https://fogbugz.unity3d.com/f/cases/1114708/)
- Fixed an issue where Lightprobe data was missing if it was needed per-pixel and GPU instancing was enabled.
- The Soft ScreenSpaceShadows Shader variant no longer gets stripped form builds. [case 1138236](https://fogbugz.unity3d.com/f/cases/1138236/)
- Fixed a typo in the Particle Unlit Shader, so Soft Particles now work correctly.
- Fixed emissive Materials not being baked for some meshes. [case 1145297](https://issuetracker.unity3d.com/issues/lwrp-emissive-materials-are-not-baked)
- Camera matrices are now correctly set up when you call rendering functions in EndCameraRendering. [case 1146586](https://issuetracker.unity3d.com/issues/lwrp-drawmeshnow-returns-wrong-positions-slash-scales-when-called-from-endcamerarendering-hook)
- Fixed GI not baking correctly while in gamma color space.
- Fixed a NullReference exception when adding a renderer feature that is contained in a global namespace. [case 1147068](https://issuetracker.unity3d.com/issues/scriptablerenderpipeline-inspector-ui-crashes-when-a-scriptablerenderfeature-is-not-in-a-namespace)
- Shaders are now set up for VR stereo instancing on Vulkan. [case 1142952](https://fogbugz.unity3d.com/f/cases/1142952/).
- VR stereo matrices and vertex inputs are now set up on Vulkan. [case 1142952](https://fogbugz.unity3d.com/f/cases/1142952/).
- Fixed the Material Upgrader so it's now run upon updating the LWRP package. [1148764](https://issuetracker.unity3d.com/product/unity/issues/guid/1148764/)
- Fixed a NullReference exception when you create a new Lightweight Render Pipeline Asset. [case 1153388](https://issuetracker.unity3d.com/product/unity/issues/guid/1153388/)
## [6.6.0] - 2019-04-01
### Added
- Added support for Baked Indirect mixed lighting.
- You can now use Light Probes for occlusion. This means that baked lights can now occlude dynamic objects.
- Added RenderObjects. You can add RenderObjects to a Renderer to perform custom rendering.
- (WIP) Added an experimental 2D renderer that implements a 2D lighting system.
- (WIP) Added a Light2D component that works with the 2D renderer to add lighting effects to 2D sprites.
### Fixed
- Fixed a project import issue in the LWRP template.
- Fixed the warnings that appear when you create new Unlit Shader Graphs using the Lightweight Render Pipeline.
- Fixed light attenuation precision on mobile platforms.
- Fixed split-screen rendering on mobile platforms.
- Fixed rendering when using an off-screen camera that renders to a depth texture.
- Fixed the exposed stencil render state in the renderer.
- Fixed the default layer mask so it's now applied to a depth pre-pass.
- Made several improvements and fixes to the render pass UI.
- Fixed artifacts that appeared due to precision errors in large scaled objects.
- Fixed an XR rendering issue where Unity required a depth texture.
- Fixed an issue that caused transparent objects to sort incorrectly.
## [6.5.0] - 2019-03-07
### Added
- You can now create a custom forward renderer by clicking on `Assets/Create/Rendering/Lightweight Render Pipeline/Forward Renderer`. This creates an Asset in your Project. You can add additional features to it and drag-n-drop the renderer to either the pipeline Asset or to a camera.
- You can now add `ScriptableRendererFeature` to the `ScriptableRenderer` to extend it with custom effects. A feature is an `ScriptableObject` that can be drag-n-dropped in the renderer and adds one or more `ScriptableRenderPass` to the renderer.
- `ScriptableRenderer` now exposes interface to configure lights. To do so, implement `SetupLights` when you create a new renderer.
- `ScriptableRenderer` now exposes interface to configure culling. To do so, implement `SetupCullingParameters` when you create a new renderer.
- `ScriptableRendererData` contains rendering resources for `ScriptableRenderer`. A renderer can be overridden globally for all cameras or on a per-camera basis.
- `ScriptableRenderPass` now has a `RenderPassEvents`. This controls where in the pipeline the render pass is added.
- `ScriptableRenderPass` now exposes `ConfigureTarget` and `ConfigureClear`. This allows the renderer to automatically figure out the currently active rendering targets.
- `ScriptableRenderPass` now exposes `Blit`. This performs a blit and sets the active render target in the renderer.
- `ScriptableRenderPass` now exposes `RenderPostProcessing`. This renders post-processing and sets the active render target in the renderer.
- `ScriptableRenderPass` now exposes `CreateDrawingSettings` as a helper for render passes that need to call `ScriptableRenderContext.DrawRenderers`.
### Changed
- Removed `RegisterShaderPassName` from `ScriptableRenderPass`. Instead, `CreateDrawingSettings` now takes one or a list of `ShaderTagId`.
- Removed remaining experimental namespace from LWRP. All APIrelated to `ScriptableRenderer`, `ScriptableRenderPass`, and render pass injection is now out of preview.
- Removed `SetRenderTarget` from `ScriptableRenderPass`. You should never call it. Instead, call `ConfigureTarget`, and the renderer automatically sets up targets for you.
- Removed `RenderFullscreenQuad` from `ScriptableRenderer`. Use `CommandBuffer.DrawMesh` and `RenderingUtils.fullscreenMesh` instead.
- Removed `RenderPostProcess` from `ScriptableRenderer`. Use `ScriptableRenderPass.RenderPostProcessing` instead.
- Removed `postProcessingContext` property from `ScriptableRenderer`. This is now exposed in `RenderingUtils.postProcessingContext`.
- Removed `GetCameraClearFlag` from `ScriptableRenderer`.
### Fixed
- Fixed y-flip in VR when post-processing is active.
- Fixed occlusion mesh for VR not rendering before rendering opaques.
- Enabling or disabling SRP Batcher in runtime works now.
- Fixed video player recorder when post-processing is enabled.
## [6.4.0] - 2019-02-21
## [6.3.0] - 2019-02-18
## [6.2.0] - 2019-02-15
### Changed
- Code refactor: all macros with ARGS have been swapped with macros with PARAM. This is because the ARGS macros were incorrectly named.
## [6.1.0] - 2019-02-13
## [6.0.0] - 2019-02-23
### Added
- You can now implement a custom renderer for LWRP. To do so, implement an `IRendererData` that contains all resources used in rendering. Then create an `IRendererSetup` that creates and queues `ScriptableRenderPass`. Change the renderer type either in the Pipeline Asset or in the Camera Inspector.
- LWRP now uses the Unity recorder extension. You can use this to capture the output of Cameras.
- You can now inject a custom render pass before LWRP renders opaque objects. To do so, implement an `IBeforeRender` interface.
- Distortion support in all Particle Shaders.
- An upgrade system for LWRP Materials with `MaterialPostprocessor`.
- An upgrade path for Unlit shaders
- Tooltips for Shaders.
- SRP Batcher support for Particle Shaders.
- Docs for these Shaders: Baked Lit, Particles Lit, Particles Simple Lit, and Particles Unlit.
- LWRP now supports dynamic resolution scaling. The target platform must also support it.
- LWRP now includes version defines for both C# and Shaders in the format of `LWRP_X_Y_Z_OR_NEWER`. For example, `LWRP_5_3_0_OR_NEWER` defines version 5.3.0.
- The Terrain Lit Shader now samples Spherical Harmonics if you haven't baked any lightmaps for terrain.
- Added a __Priority__ option, which you can use to tweak the rendering order. This is similar to render queue in the built-in render pipeline. These Shaders now have this option: Lit, Simple Lit, Baked Lit, Unlit, and all three Particle Shaders.
- Added support for overriding terrain detail rendering shaders, via the render pipeline editor resources asset.
### Changed
- You can now only initialize a camera by setting a Background Type. The supported options are Skybox, Solid Color, and Don't Care.
- LWRP now uses non-square shadowmap textures when it renders directional shadows with 2 shadow cascades.
- LWRP now uses RGB111110 as the HDR format on mobile devices, when this format is supported.
- Removed `IAfterDepthPrePass` interface.
- Weve redesigned the Shader GUI. For example, all property names in Shaders are now inline across the board
- The Simple Lit Shader now has Smoothness, which can be stored in the alpha of specular or albedo maps.
- The Simple Lit and Particles Simple Lit Shaders now take shininess from the length (brightness) of the specular map.
- The __Double sided__ property is now __Render Face__. This means you can also do front face culling.
- Changed the docs for Lit Shader, Simple Lit Shader and Unlit Shader according to Shader GUI changes.
- When you create a new LWRP Asset, it will now be initialized with settings that favor performance on mobile platforms.
- Updated the [FAQ](faq.md) and the [Built-in/LWRP feature comparison table](lwrp-builtin-feature-comparison.md).
### Fixed
- Several tweaks to reduce bandwidth consumption on mobile devices.
- The foldouts in the Lightweight Asset inspector UI now remember their state.
- Added missing meta file for GizmosRenderingPass.cs.
- Fixed artifacts when using multiple or Depth Only cameras. [Case 1072615](https://issuetracker.unity3d.com/issues/ios-using-multiple-cameras-in-the-scene-in-lightweight-render-pipeline-gives-corrupted-image-in-ios-device)
- Fixed a typo in ERROR_ON_UNSUPPORTED_FUNCTION() that was causing the shader compiler to run out of memory in GLES2. [Case 1104271](https://issuetracker.unity3d.com/issues/mobile-os-restarts-because-of-high-memory-usage-when-compiling-shaders-for-opengles2)
- LWRP now renders shadows on scaled objects correctly. [Case 1109017](https://issuetracker.unity3d.com/issues/scaled-objects-render-shadows-and-specularity-incorrectly-in-the-lwrp-on-device)
- LWRP now allows some Asset settings to be changed at runtime. [Case 1105552](https://issuetracker.unity3d.com/issues/lwrp-changing-render-scale-in-runtime-has-no-effect-since-lwrp-3-dot-3-0)
- Realtime shadows now work in GLES2. [Case 1087251](https://issuetracker.unity3d.com/issues/android-lwrp-no-real-time-light-and-shadows-using-gles2)
- Framedebugger now renders correctly when stepping through drawcalls.
- Cameras that request MSAA and Opaque Textures now use less frame bandwidth when they render.
- Fixed rendering in the gamma color space, so it doesn't appear darker.
- Particles SImple Lit and Particles Unlit Shaders now work correctly.
- __Soft Particles__ now work correctly.
- Camera fading for particles.
- Fixed a typo in the Unlit `IgnoreProjector` tag.
- Particles render in both eyes with stereo instancing
- Fixed specular issues on mobile. [case 1109017](https://issuetracker.unity3d.com/issues/scaled-objects-render-shadows-and-specularity-incorrectly-in-the-lwrp-on-device)
- Fixed issue causing LWRP to create MSAA framebuffer even when MSAA setting was disabled.
- Post-processing in mobile VR is now forced to be disabled. It was causing many rendering issues.
- Fixed Editor Previews breaking in Play Mode when VR is enabled. [Case 1109009](https://issuetracker.unity3d.com/issues/lwrp-editor-previews-break-in-play-mode-if-vr-is-enabled)
- A camera's HDR enable flag is now respected when rendering in XR.
- Terrain detail rendering now works correctly when LWRP is installed but inactive.
## [5.2.0] - 2018-11-27
### Added
- LWRP now handles blits that are required by the device when rendering to the backbuffer.
- You can now enable the SRP Batcher. To do so, go to the `Pipeline Asset`. Under `Advanced`, toggle `SRP Batcher`.
### Changed
- Renamed shader variable `unity_LightIndicesOffsetAndCount` to `unity_PerObjectLightData`.
- Shader variables `unity_4LightIndices0` and `unity_4LightIndices1` are now declared as `unity_PerObjectLightIndices` array.
## [5.1.0] - 2018-11-19
### Added
- The user documentation for LWRP is now in this GitHub repo, instead of in the separate GitHub wiki. You can find the most up-to-date pages in the [TableOfContents.md](TableOfCotents.md) file. Pages not listed in that file are still in progress.
### Changed
- The LWRP package is no longer in preview.
- LWRP built-in render passes are now internal.
- Changed namespace from `UnityEngine.Experimental.Rendering.LightweightPipeline` to `UnityEngine.Rendering.LWRP`.
- Changed namespace from `UnityEditor.Experimental.Rendering.LightweightPipeline` to `UnityEditor.Rendering.LWRP`.
### Fixed
- LWRP now respects the iOS Player setting **Force hard shadows**. When you enable this setting, hardware filtering of shadows is disabled.
- Scene view mode now renders baked lightmaps correctly. [Case 1092227](https://issuetracker.unity3d.com/issues/lwrp-scene-view-modes-render-objects-black)
- Shadow bias calculations are now correct for both Shader Graph and Terrain shaders.
- Blit shader now ignores culling.
- When you select __Per Vertex__ option for __Additional Lights__, the __Per Object Limit__ option is not greyed out anymore.
- When you change camera viewport height to values above 1.0, the Unity Editor doesn't freeze anymore. [Case 1097497](https://issuetracker.unity3d.com/issues/macos-lwrp-editor-freezes-after-changing-cameras-viewport-rect-values)
- When you use AR with LWRP, the following error message is not displayed in the console anymore: "The camera list passed to the render pipeline is either null or empty."
## [5.0.0-preview] - 2018-09-28
### Added
- Added occlusion mesh rendering/hookup for VR
- You can now configure default depth and normal shadow bias values in the pipeline asset.
- You can now add the `LWRPAdditionalLightData` component to a `Light` to override the default depth and normal shadow bias.
- You can now log the amount of shader variants in your build. To do so, go to the `Pipeline Asset`. Under `Advanced`, select and set the `Shader Variant Log Level`.
### Changed
- Removed the `supportedShaderFeatures` property from LWRP core. The shader stripper now figures out which variants to strip based on the current assigned pipeline Asset in the Graphics settings.
### Fixed
- The following error does not appear in console anymore: ("Begin/End Profiler section mismatch")
- When you select a material with the Lit shader, this no longer causes the following error in the console: ("Material doesn't have..."). [case 1092354](https://fogbugz.unity3d.com/f/cases/1092354/)
- In the Simple Lit shader, per-vertex additional lights are now shaded properly.
- Shader variant stripping now works when you're building a Project with Cloud Build. This greatly reduces build times from Cloud Build.
- Dynamic Objects now receive lighting when the light mode is set to mixed.
- MSAA now works on Desktop platforms.
- The shadow bias value is now computed correctly for shadow cascades and different shadow resolutions. [case 1076285](https://issuetracker.unity3d.com/issues/lwrp-realtime-directional-light-shadow-maps-exhibit-artifacts)
- When you use __Area Light__ with LWRP, __Cast Shadows__ no longer overlaps with other UI elements in the Inspector. [case 1085363](https://issuetracker.unity3d.com/issues/inspector-area-light-cast-shadows-ui-option-is-obscured-by-render-mode-for-lwrp-regression-in-2018-dot-3a3)
### Changed
Read/write XRGraphicsConfig -> Read-only XRGraphics interface to XRSettings.
## [4.0.0-preview] - 2018-09-28
### Added
- When you have enabled Gizmos, they now appear correctly in the Game view.
- Added requiresDepthPrepass field to RenderingData struct to tell if the runtime platform requires a depth prepass to generate a camera depth texture.
- The `RenderingData` struct now holds a reference to `CullResults`.
- When __HDR__ is enabled in the Camera but disabled in the Asset, an information box in the Camera Inspector informs you about it.
- When __MSAA__ is enabled in the Camera but disabled in the Asset, an information box in the Camera Inspector informs you about it.
- Enabled instancing on the terrain shader.
- Sorting of opaque objects now respects camera `opaqueSortMode` setting.
- Sorting of opaque objects disables front-to-back sorting flag, when camera settings allow that and the GPU has hidden surface removal.
- LWRP now has a Custom Light Explorer that suits its feature set.
- LWRP now supports Vertex Lit shaders for detail meshes on terrain.
- LWRP now has three interactive Autodesk shaders: Autodesk Interactive, Autodesk Interactive Masked and Autodesk Interactive Transparent.
- [Shader API] The `GetMainLight` and `GetAdditionalLight` functions can now compute shadow attenuation and store it in the new `shadowAttenuation` field in `LightData` struct.
- [Shader API] Added a `VertexPositionInputs` struct that contains vertex position in difference spaces (world, view, hclip).
- [Shader API] Added a `GetVertexPositionInputs` function to get an initialized `VertexPositionInputs`.
- [Shader API] Added a `GetPerObjectLightIndex` function to return the per-object index given a for-loop index.
- [Shader API] Added a `GetShadowCoord` function that takes a `VertexPositionInputs` as input.
- [ShaderLibrary] Added VertexNormalInputs struct that contains data for per-pixel normal computation.
- [ShaderLibrary] Added GetVertexNormalInputs function to return an initialized VertexNormalInputs.
### Changed
- The `RenderingData` struct is now read-only.
- `ScriptableRenderer`always performs a Clear before calling `IRendererSetup::Setup.`
- `ScriptableRenderPass::Execute` no longer takes `CullResults` as input. Instead, use `RenderingData`as input, since that references `CullResults`.
- `IRendererSetup_Setup` no longer takes `ScriptableRenderContext` and `CullResults` as input.
- Shader includes are now referenced via package relative paths instead of via the deprecated shader export path mechanism https://docs.unity3d.com/2018.3/Documentation/ScriptReference/ShaderIncludePathAttribute.html.
- The LWRP Asset settings were re-organized to be more clear.
- Vertex lighting now controls if additional lights should be shaded per-vertex or per-pixel.
- Renamed all `Local Lights` nomenclature to `Additional Lights`.
- Changed shader naming to conform to our SRP shader code convention.
- [Shader API] Renamed `SpotAttenuation` function to `AngleAttenuation`.
- [Shader API] Renamed `_SHADOWS_ENABLED` keyword to `_MAIN_LIGHT_SHADOWS`
- [Shader API] Renamed `_SHADOWS_CASCADE` keyword to `_MAIN_LIGHT_SHADOWS_CASCADE`
- [Shader API] Renamed `_VERTEX_LIGHTS` keyword to `_ADDITIONAL_LIGHTS_VERTEX`.
- [Shader API] Renamed `_LOCAL_SHADOWS_ENABLED` to `_ADDITIONAL_LIGHT_SHADOWS`
- [Shader API] Renamed `GetLight` function to `GetAdditionalLight`.
- [Shader API] Renamed `GetPixelLightCount` function to `GetAdditionalLightsCount`.
- [Shader API] Renamed `attenuation` to `distanceAttenuation` in `LightData`.
- [Shader API] Renamed `GetLocalLightShadowStrength` function to `GetAdditionalLightShadowStrength`.
- [Shader API] Renamed `SampleScreenSpaceShadowMap` functions to `SampleScreenSpaceShadowmap`.
- [Shader API] Renamed `MainLightRealtimeShadowAttenuation` function to `MainLightRealtimeShadow`.
- [Shader API] Renamed light constants from `Directional` and `Local` to `MainLight` and `AdditionalLights`.
- [Shader API] Renamed `GetLocalLightShadowSamplingData` function to `GetAdditionalLightShadowSamplingData`.
- [Shader API] Removed OUTPUT_NORMAL macro.
- [Shader API] Removed `lightIndex` and `substractiveAttenuation` from `LightData`.
- [Shader API] Removed `ComputeShadowCoord` function. `GetShadowCoord` is provided instead.
- All `LightweightPipeline` references in API and classes are now named `LightweightRenderPipeline`.
- Files no longer have the `Lightweight` prefix.
- Renamed Physically Based shaders to `Lit`, `ParticlesLit`, and `TerrainLit`.
- Renamed Simple Lighting shaders to `SimpleLit`, and `ParticlesSimpleLit`.
- [ShaderLibrary] Renamed `InputSurfacePBR.hlsl`, `InputSurfaceSimple.hlsl`, and `InputSurfaceUnlit` to `LitInput.hlsl`, `SimpleLitInput.hlsl`, and `UnlitInput.hlsl`. These files were moved from the `ShaderLibrary` folder to the`Shaders`.
- [ShaderLibrary] Renamed `LightweightPassLit.hlsl` and `LightweightPassLitSimple.hlsl` to `LitForwardPass.hlsl` and `SimpleLitForwardPass.hlsl`. These files were moved from the `ShaderLibrary` folder to `Shaders`.
- [ShaderLibrary] Renamed `LightweightPassMetaPBR.hlsl`, `LightweightPassMetaSimple.hlsl` and `LighweightPassMetaUnlit` to `LitMetaPass.hlsl`, `SimpleLitMetaPass.hlsl` and `UnlitMetaPass.hlsl`. These files were moved from the `ShaderLibrary` folder to `Shaders`.
- [ShaderLibrary] Renamed `LightweightPassShadow.hlsl` to `ShadowCasterPass.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `LightweightPassDepthOnly.hlsl` to `DepthOnlyPass.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `InputSurfaceTerrain.hlsl` to `TerrainLitInput.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `LightweightPassLitTerrain.hlsl` to `TerrainLitPases.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `ParticlesPBR.hlsl` to `ParticlesLitInput.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `InputSurfacePBR.hlsl` to `LitInput.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `InputSurfaceUnlit.hlsl` to `UnlitInput.hlsl`. This file was moved to the `Shaders` folder.
- [ShaderLibrary] Renamed `InputBuiltin.hlsl` to `UnityInput.hlsl`.
- [ShaderLibrary] Renamed `LightweightPassMetaCommon.hlsl` to `MetaInput.hlsl`.
- [ShaderLibrary] Renamed `InputSurfaceCommon.hlsl` to `SurfaceInput.hlsl`.
- [ShaderLibrary] Removed LightInput struct and GetLightDirectionAndAttenuation. Use GetAdditionalLight function instead.
- [ShaderLibrary] Removed ApplyFog and ApplyFogColor functions. Use MixFog and MixFogColor instead.
- [ShaderLibrary] Removed TangentWorldToNormal function. Use TransformTangentToWorld instead.
- [ShaderLibrary] Removed view direction normalization functions. View direction should always be normalized per pixel for accurate results.
- [ShaderLibrary] Renamed FragmentNormalWS function to NormalizeNormalPerPixel.
### Fixed
- If you have more than 16 lights in a scene, LWRP no longer causes random glitches while rendering lights.
- The Unlit shader now samples Global Illumination correctly.
- The Inspector window for the Unlit shader now displays correctly.
- Reduced GC pressure by removing several per-frame memory allocations.
- The tooltip for the the camera __MSAA__ property now appears correctly.
- Fixed multiple C# code analysis rule violations.
- The fullscreen mesh is no longer recreated upon every call to `ScriptableRenderer.fullscreenMesh`.
## [3.3.0-preview] - 2018-01-01
### Added
- Added callbacks to LWRP that can be attached to a camera (IBeforeCameraRender, IAfterDepthPrePass, IAfterOpaquePass, IAfterOpaquePostProcess, IAfterSkyboxPass, IAfterTransparentPass, IAfterRender)
###Changed
- Clean up LWRP creation of render textures. If we are not going straight to screen ensure that we create both depth and color targets.
- UNITY_DECLARE_FRAMEBUFFER_INPUT and UNITY_READ_FRAMEBUFFER_INPUT macros were added. They are necessary for reading transient attachments.
- UNITY_MATRIX_I_VP is now defined.
- Renamed LightweightForwardRenderer to ScriptableRenderer.
- Moved all light constants to _LightBuffer CBUFFER. Now _PerCamera CBUFFER contains all other per camera constants.
- Change real-time attenuation to inverse square.
- Change attenuation for baked GI to inverse square, to match real-time attenuation.
- Small optimization in light attenuation shader code.
### Fixed
- Lightweight Unlit shader UI doesn't throw an error about missing receive shadow property anymore.
## [3.2.0-preview] - 2018-01-01
### Changed
- Receive Shadows property is now exposed in the material instead of in the renderer.
- The UI for Lightweight asset has been updated with new categories. A more clean structure and foldouts has been added to keep things organized.
### Fixed
- Shadow casters are now properly culled per cascade. (case 1059142)
- Rendering no longer breaks when Android platform is selected in Build Settings. (case 1058812)
- Scriptable passes no longer have missing material references. Now they access cached materials in the renderer.(case 1061353)
- When you change a Shadow Cascade option in the Pipeline Asset, this no longer warns you that you've exceeded the array size for the _WorldToShadow property.
- Terrain shader optimizations.
## [3.1.0-preview] - 2018-01-01
### Fixed
- Fixed assert errors caused by multi spot lights
- Fixed LWRP-DirectionalShadowConstantBuffer params setting
## [3.0.0-preview] - 2018-01-01
### Added
- Added camera additional data component to control shadows, depth and color texture.
- pipeline now uses XRSEttings.eyeTextureResolutionScale as renderScale when in XR.
- New pass architecture. Allows for custom passes to be written and then used on a per camera basis in LWRP
### Changed
- Shadow rendering has been optimized for the Mali Utgard architecture by removing indexing and avoiding divisions for orthographic projections. This reduces the frame time by 25% on the Overdraw benchmark.
- Removed 7x7 tent filtering when using cascades.
- Screenspace shadow resolve is now only done when rendering shadow cascades.
- Updated the UI for the Lighweight pipeline asset.
- Update assembly definitions to output assemblies that match Unity naming convention (Unity.*).
### Fixed
- Post-processing now works with VR on PC.
- PS4 compiler error
- Fixed VR multiview rendering by forcing MSAA to be off. There's a current issue in engine that breaks MSAA and Texture2DArray.
- Fixed UnityPerDraw CB layout
- GLCore compute buffer compiler error
- Occlusion strength not being applied on LW standard shaders
- CopyDepth pass is being called even when a depth from prepass is available
- GLES2 shader compiler error in IntegrationTests
- Can't set RenderScale and ShadowDistance by script
- VR Single Pass Instancing shadows
- Fixed compilation errors on Nintendo Switch (limited XRSetting support).
## [2.0.0-preview] - 2018-01-01
### Added
- Explicit render target load/store actions were added to improve tile utilization
- Camera opaque color can be requested on the pipeline asset. It can be accessed in the shader by defining a _CameraOpaqueTexture. This can be used as an alternative to GrabPass.
- Dynamic Batching can be enabled in the pipeline asset
- Pipeline now strips unused or invalid variants and passes based on selected pipeline capabilities in the asset. This reduces build and memory consuption on target.
- Shader stripping settings were added to pipeline asset
### Changed
#### Pipeline
- Pipeline code is now more modular and extensible. A ForwardRenderer class is initialized by the pipeline with RenderingData and it's responsible for enqueueing and executing passes. In the future pluggable renderers will be supported.
- On mobile 1 directional light + up to 4 local lights (point or spot) are computed
- On other platforms 1 directional light + up to 8 local lights are computed
- Multiple shadow casting lights are supported. Currently only 1 directional + 4 spots light shadows.
#### Shading Framework
- Directional Lights are always considered a main light in shader. They have a fast shading path with no branching and no indexing.
- GetMainLight() is provided in shader to initialize Light struct with main light shading data.
- Directional lights have a dedicated shadowmap for performance reasons. Shadow coord always comes from interpolator.
- MainLigthRealtimeShadowAttenuation(float4 shadowCoord) is provided to compute main light realtime shadows.
- Spot and Point lights are always shaded in the light loop. Branching on uniform and indexing happens when shading them.
- GetLight(half index, float3 positionWS) is provided in shader to initialize Light struct for spot and point lights.
- Spot light shadows are baked into a single shadow atlas.
- Shadow coord for spot lights is always computed on fragment.
- Use LocalLightShadowAttenuation(int lightIndex, float3 positionWS) to comppute realtime shadows for spot lights.
### Fixed
- Issue that was causing VR on Android to render black
- Camera viewport issues
- UWP build issues
- Prevent nested camera rendering in the pipeline
## [1.1.4-preview] - 2018-01-01
### Added
- Terrain and grass shaders ported
- Updated materials and shader default albedo and specular color to midgrey.
- Exposed _ScaledScreenParams to shader. It works the same as _ScreenParams but takes pipeline RenderScale into consideration
- Performance Improvements in mobile
### Fixed
- SRP Shader library issue that was causing all constants to be highp in mobile
- shader error that prevented LWRP to build to UWP
- shader compilation errors in Linux due to case sensitive includes
- Rendering Texture flipping issue
- Standard Particles shader cutout and blending modes
- crash caused by using projectors
- issue that was causing Shadow Strength to not be computed on mobile
- Material Upgrader issue that caused editor to SoftLocks
- GI in Unlit shader
- Null reference in the Unlit material shader GUI
## [1.1.2-preview] - 2018-01-01
### Changed
- Performance improvements in mobile
### Fixed
- Shadows on GLES 2.0
- CPU performance regression in shadow rendering
- Alpha clip shadow issues
- Unmatched command buffer error message
- Null reference exception caused by missing resource in LWRP
- Issue that was causing Camera clear flags was being ignored in mobile
## [1.1.1-preview] - 2018-01-01
### Added
- Added Cascade Split selection UI
- Added SHADER_HINT_NICE_QUALITY. If user defines this to 1 in the shader Lightweight pipeline will favor quality even on mobile platforms.
### Changed
- Shadowmap uses 16bit format instead of 32bit.
- Small shader performance improvements
### Fixed
- Subtractive Mode
- Shadow Distance does not accept negative values anymore
## [0.1.24] - 2018-01-01
### Added
- Added Light abstraction layer on lightweight shader library.
- Added HDR global setting on pipeline asset.
- Added Soft Particles settings on pipeline asset.
- Ported particles shaders to SRP library
### Changed
- HDR RT now uses what format is configured in Tier settings.
- Refactored lightweight standard shaders and shader library to improve ease of use.
- Optimized tile LOAD op on mobile.
- Reduced GC pressure
- Reduced shader variant count by ~56% by improving fog and lightmap keywords
- Converted LW shader library files to use real/half when necessary.
### Fixed
- Realtime shadows on OpenGL
- Shader compiler errors in GLES 2.0
- Issue sorting issues when BeforeTransparent custom fx was enabled.
- VR single pass rendering.
- Viewport rendering issues when rendering to backbuffer.
- Viewport rendering issues when rendering to with MSAA turned off.
- Multi-camera rendering.
## [0.1.23] - 2018-01-01
### Added
- UI Improvements (Rendering features not supported by LW are hidden)
### Changed
- Shaders were ported to the new SRP shader library.
- Constant Buffer refactor to use new Batcher
- Shadow filtering and bias improved.
- Pipeline now updates color constants in gamma when in Gamma colorspace.
- Optimized ALU and CB usage on Shadows.
- Reduced shader variant count by ~33% by improving shadow and light classification keywords
- Default resources were removed from the pipeline asset.
### Fixed
- Fixed shader include path when using SRP from package manager.
- Fixed spot light attenuation to match Unity Built-in pipeline.
- Fixed depth pre-pass clearing issue.
## [0.1.12] - 2018-01-01
### Added
- Standard Unlit shader now has an option to sample GI.
- Added Material Upgrader for stock Unity Mobile and Legacy Shaders.
- UI improvements
### Changed
- Realtime shadow filtering was improved.
### Fixed
- Fixed an issue that was including unreferenced shaders in the build.
- Fixed a null reference caused by Particle System component lights.

View File

@@ -0,0 +1,11 @@
namespace UnityEditor.Experimental.Rendering.Universal
{
[CustomEditor(typeof(UnityEngine.Experimental.Rendering.Universal.CinemachineUniversalPixelPerfect)), CanEditMultipleObjects]
internal class CinemachineUniversalPixelPerfectEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("This Cinemachine extension is now deprecated and doesn't function properly. Instead, use the one from Cinemachine v2.4.0 or newer.", MessageType.Error);
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
[CustomEditor(typeof(CompositeShadowCaster2D))]
internal class CompositeShadowCaster2DEditor : Editor
{
public override void OnInspectorGUI()
{
}
}
}

View File

@@ -0,0 +1,68 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal
{
internal static class FreeformPathPresets
{
public static Vector3[] CreateSquare()
{
Vector3[] returnPath = new Vector3[4]
{
new Vector3(-0.5f, -0.5f),
new Vector3(0.5f, -0.5f),
new Vector3(0.5f, 0.5f),
new Vector3(-0.5f, 0.5f)
};
return returnPath;
}
public static Vector3[] CreateIsometricDiamond()
{
Vector3[] returnPath = new Vector3[4]
{
new Vector3(-0.5f, 0.0f),
new Vector3(0.0f, -0.25f),
new Vector3(0.5f, 0.0f),
new Vector3(0.0f, 0.25f)
};
return returnPath;
}
private static Vector3[] CreateShape(int vertices, float angleOffset)
{
Vector3[] returnPath = new Vector3[vertices];
const float kRadius = 0.5f;
for (int i = 0; i < vertices; i++)
{
float angle = ((float)i * 2 * Mathf.PI / (float)vertices) + angleOffset;
float x = kRadius * Mathf.Cos(angle);
float y = kRadius * Mathf.Sin(angle);
returnPath[i] = new Vector3(x, y);
}
return returnPath;
}
public static Vector3[] CreateCircle()
{
return CreateShape(32, 0);
}
public static Vector3[] CreateHexagonFlatTop()
{
return CreateShape(6, 0);
}
public static Vector3[] CreateHexagonPointedTop()
{
return CreateShape(6, 0.5f * Mathf.PI);
}
}
}

View File

@@ -0,0 +1,833 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.EditorTools;
using UnityEditor.Experimental.Rendering.Universal.Path2D;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;
using UnityEditor.AnimatedValues;
using UnityEditor.Rendering.Universal;
using UnityEditor.Rendering;
namespace UnityEditor.Experimental.Rendering.Universal
{
[CustomEditor(typeof(Light2D))]
[CanEditMultipleObjects]
internal class Light2DEditor : PathComponentEditor<ScriptablePath>
{
[EditorTool("Edit Freeform Shape", typeof(Light2D))]
class FreeformShapeTool : PathEditorTool<ScriptablePath>
{
const string k_ShapePath = "m_ShapePath";
public override bool IsAvailable()
{
var light = target as Light2D;
if (light == null)
return false;
else
return base.IsAvailable() && light.lightType == Light2D.LightType.Freeform;
}
protected override IShape GetShape(Object target)
{
return (target as Light2D).shapePath.ToPolygon(false);
}
protected override void SetShape(ScriptablePath shapeEditor, SerializedObject serializedObject)
{
serializedObject.Update();
var pointsProperty = serializedObject.FindProperty(k_ShapePath);
pointsProperty.arraySize = shapeEditor.pointCount;
for (var i = 0; i < shapeEditor.pointCount; ++i)
pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
((Light2D)(serializedObject.targetObject)).UpdateMesh(true);
// This is untracked right now...
serializedObject.ApplyModifiedProperties();
}
}
private static class Styles
{
public static readonly GUIContent InnerOuterSpotAngle = EditorGUIUtility.TrTextContent("Inner / Outer Spot Angle", "Adjusts the inner / outer angles of this light to change the angle ranges of this Spot Lights beam.");
public static Texture lightCapTopRight = Resources.Load<Texture>("LightCapTopRight");
public static Texture lightCapTopLeft = Resources.Load<Texture>("LightCapTopLeft");
public static Texture lightCapBottomLeft = Resources.Load<Texture>("LightCapBottomLeft");
public static Texture lightCapBottomRight = Resources.Load<Texture>("LightCapBottomRight");
public static Texture lightCapUp = Resources.Load<Texture>("LightCapUp");
public static Texture lightCapDown = Resources.Load<Texture>("LightCapDown");
public static GUIContent lightTypeFreeform = new GUIContent("Freeform", Resources.Load("InspectorIcons/FreeformLight") as Texture);
public static GUIContent lightTypeSprite = new GUIContent("Sprite", Resources.Load("InspectorIcons/SpriteLight") as Texture);
public static GUIContent lightTypePoint = new GUIContent("Spot", Resources.Load("InspectorIcons/PointLight") as Texture);
public static GUIContent lightTypeGlobal = new GUIContent("Global", Resources.Load("InspectorIcons/GlobalLight") as Texture);
public static GUIContent[] lightTypeOptions = new GUIContent[] { lightTypeFreeform, lightTypeSprite, lightTypePoint, lightTypeGlobal };
public static GUIContent blendingSettingsFoldout = EditorGUIUtility.TrTextContent("Blending", "Options used for blending");
public static GUIContent shadowsSettingsFoldout = EditorGUIUtility.TrTextContent("Shadows", "Options used for shadows");
public static GUIContent volumetricSettingsFoldout = EditorGUIUtility.TrTextContent("Volumetric", "Options used for volumetric lighting");
public static GUIContent normalMapsSettingsFoldout = EditorGUIUtility.TrTextContent("Normal Maps", "Options used for normal maps");
public static GUIContent generalLightType = EditorGUIUtility.TrTextContent("Light Type", "Select the light type. \n\nGlobal Light: For ambient light. \nSpot Light: For a spot light / point light. \nFreeform Light: For a custom shape light. \nSprite Light: For a custom light cookie using Sprites.");
public static GUIContent generalFalloffSize = EditorGUIUtility.TrTextContent("Falloff", "Adjusts the falloff area of this light. The higher the falloff value, the larger area the falloff spans.");
public static GUIContent generalFalloffIntensity = EditorGUIUtility.TrTextContent("Falloff Strength", "Adjusts the falloff curve to control the softness of this lights edges. The higher the falloff strength, the softer the edges of this light.");
public static GUIContent generalLightColor = EditorGUIUtility.TrTextContent("Color", "Adjusts this lights color.");
public static GUIContent generalLightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts this lights color intensity by using multiply to brighten the Sprite beyond its original color.");
public static GUIContent generalVolumeIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts the intensity of this additional light volume that's additively blended on top of this light. To enable the Volumetric Shadow Strength, increase this Intensity to be greater than 0.");
public static GUIContent generalBlendStyle = EditorGUIUtility.TrTextContent("Blend Style", "Adjusts how this light blends with the Sprites on the Target Sorting Layers. Different Blend Styles can be customized in the 2D Renderer Data Asset.");
public static GUIContent generalLightOverlapOperation = EditorGUIUtility.TrTextContent("Overlap Operation", "Determines how this light blends with the other lights either through additive or alpha blending.");
public static GUIContent generalLightOrder = EditorGUIUtility.TrTextContent("Light Order", "Determines the relative order in which lights of the same Blend Style get rendered. Lights with lower values are rendered first.");
public static GUIContent generalShadowIntensity = EditorGUIUtility.TrTextContent("Strength", "Adjusts the amount of light occlusion from the Shadow Caster 2D component(s) when blocking this light.The higher the value, the more opaque the shadow becomes.");
public static GUIContent generalShadowVolumeIntensity = EditorGUIUtility.TrTextContent("Shadow Strength", "Adjusts the amount of volume light occlusion from the Shadow Caster 2D component(s) when blocking this light.");
public static GUIContent generalSortingLayerPrefixLabel = EditorGUIUtility.TrTextContent("Target Sorting Layers", "Determines which layers this light affects. To optimize performance, minimize the number of layers this light affects.");
public static GUIContent generalLightNoLightEnabled = EditorGUIUtility.TrTextContentWithIcon("No valid blend styles are enabled.", MessageType.Error);
public static GUIContent generalNormalMapZDistance = EditorGUIUtility.TrTextContent("Distance", "Adjusts the z-axis distance of this light and the lit Sprite(s). Do note that this distance does not Transform the position of this light in the Scene.");
public static GUIContent generalNormalMapLightQuality = EditorGUIUtility.TrTextContent("Quality", "Determines the accuracy of the lighting calculations when normal map is used. To optimize for performance, select Fast.");
public static GUIContent pointLightRadius = EditorGUIUtility.TrTextContent("Radius", "Adjusts the inner / outer radius of this light to change the size of this light.");
public static GUIContent pointLightInner = EditorGUIUtility.TrTextContent("Inner", "Specify the inner radius of the light");
public static GUIContent pointLightOuter = EditorGUIUtility.TrTextContent("Outer", "Specify the outer radius of the light");
public static GUIContent pointLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Specify the sprite (deprecated)");
public static GUIContent shapeLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Assign a Sprite which acts as a mask to create a light cookie.");
public static GUIContent deprecatedParametricLightWarningSingle = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Light to a Freeform Light to enjoy similar light functionality.", MessageType.Warning);
public static GUIContent deprecatedParametricLightWarningMulti = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Lights to Freeform Lights to enjoy similar light functionality.", MessageType.Warning);
public static GUIContent deprecatedParametricLightInstructions = EditorGUIUtility.TrTextContent("Alternatively, you may choose to upgrade from the menu. Edit > Render Pipeline > Universal Render Pipeline > Upgrade Project/Scene Parametric Lights to Freeform");
public static GUIContent deprecatedParametricLightButtonSingle = EditorGUIUtility.TrTextContent("Upgrade Parametric Light");
public static GUIContent deprecatedParametricLightButtonMulti = EditorGUIUtility.TrTextContent("Upgrade Parametric Lights");
public static GUIContent renderPipelineUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("Universal scriptable renderpipeline asset must be assigned in Graphics Settings or Quality Settings.", MessageType.Warning);
public static GUIContent asset2DUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("2D renderer data must be assigned to your universal render pipeline asset or camera.", MessageType.Warning);
public static string deprecatedParametricLightDialogTextSingle = "The upgrade will convert the selected parametric light into a freeform light. You can't undo this operation.";
public static string deprecatedParametricLightDialogTextMulti = "The upgrade will convert the selected parametric lights into freeform lights. You can't undo this operation.";
public static string deprecatedParametricLightDialogTitle = "Parametric Light Upgrader";
public static string deprecatedParametricLightDialogProceed = "Proceed";
public static string deprecatedParametricLightDialogCancel = "Cancel";
}
const float k_GlobalLightGizmoSize = 1.2f;
const float k_AngleCapSize = 0.16f * k_GlobalLightGizmoSize;
const float k_AngleCapOffset = 0.08f * k_GlobalLightGizmoSize;
const float k_AngleCapOffsetSecondary = -0.05f;
const float k_RangeCapSize = 0.025f * k_GlobalLightGizmoSize;
const float k_InnerRangeCapSize = 0.08f * k_GlobalLightGizmoSize;
SerializedProperty m_LightType;
SerializedProperty m_LightColor;
SerializedProperty m_LightIntensity;
SerializedProperty m_UseNormalMap;
SerializedProperty m_ShadowIntensity;
SerializedProperty m_ShadowIntensityEnabled;
SerializedProperty m_ShadowVolumeIntensity;
SerializedProperty m_ShadowVolumeIntensityEnabled;
SerializedProperty m_ApplyToSortingLayers;
SerializedProperty m_VolumetricIntensity;
SerializedProperty m_VolumetricIntensityEnabled;
SerializedProperty m_BlendStyleIndex;
SerializedProperty m_FalloffIntensity;
SerializedProperty m_NormalMapZDistance;
SerializedProperty m_NormalMapQuality;
SerializedProperty m_LightOrder;
SerializedProperty m_OverlapOperation;
// Point Light Properties
SerializedProperty m_PointInnerAngle;
SerializedProperty m_PointOuterAngle;
SerializedProperty m_PointInnerRadius;
SerializedProperty m_PointOuterRadius;
SerializedProperty m_DeprecatedPointLightSprite;
// Shape Light Properties
SerializedProperty m_ShapeLightParametricRadius;
SerializedProperty m_ShapeLightFalloffSize;
SerializedProperty m_ShapeLightParametricSides;
SerializedProperty m_ShapeLightSprite;
SavedBool m_BlendingSettingsFoldout;
SavedBool m_ShadowsSettingsFoldout;
SavedBool m_VolumetricSettingsFoldout;
SavedBool m_NormalMapsSettingsFoldout;
int[] m_BlendStyleIndices;
GUIContent[] m_BlendStyleNames;
bool m_AnyBlendStyleEnabled = false;
SortingLayerDropDown m_SortingLayerDropDown;
Light2D lightObject => target as Light2D;
Analytics.Renderer2DAnalytics m_Analytics;
HashSet<Light2D> m_ModifiedLights;
private void AnalyticsTrackChanges(SerializedObject serializedObject)
{
if (serializedObject.hasModifiedProperties)
{
foreach (Object targetObj in serializedObject.targetObjects)
{
Light2D light2d = (Light2D)targetObj;
if (!m_ModifiedLights.Contains(light2d))
m_ModifiedLights.Add(light2d);
}
}
}
void OnEnable()
{
m_Analytics = Analytics.Renderer2DAnalytics.instance;
m_ModifiedLights = new HashSet<Light2D>();
m_SortingLayerDropDown = new SortingLayerDropDown();
m_BlendingSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPBlendingSettingsFoldout", false);
m_ShadowsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPShadowsSettingsFoldout", false);
m_VolumetricSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPVolumetricSettingsFoldout", false);
m_NormalMapsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPNormalMapsSettingsFoldout", false);
m_LightType = serializedObject.FindProperty("m_LightType");
m_LightColor = serializedObject.FindProperty("m_Color");
m_LightIntensity = serializedObject.FindProperty("m_Intensity");
m_UseNormalMap = serializedObject.FindProperty("m_UseNormalMap");
m_ShadowIntensity = serializedObject.FindProperty("m_ShadowIntensity");
m_ShadowIntensityEnabled = serializedObject.FindProperty("m_ShadowIntensityEnabled");
m_ShadowVolumeIntensity = serializedObject.FindProperty("m_ShadowVolumeIntensity");
m_ShadowVolumeIntensityEnabled = serializedObject.FindProperty("m_ShadowVolumeIntensityEnabled");
m_ApplyToSortingLayers = serializedObject.FindProperty("m_ApplyToSortingLayers");
m_VolumetricIntensity = serializedObject.FindProperty("m_LightVolumeIntensity");
m_VolumetricIntensityEnabled = serializedObject.FindProperty("m_LightVolumeIntensityEnabled");
m_BlendStyleIndex = serializedObject.FindProperty("m_BlendStyleIndex");
m_FalloffIntensity = serializedObject.FindProperty("m_FalloffIntensity");
m_NormalMapZDistance = serializedObject.FindProperty("m_NormalMapDistance");
m_NormalMapQuality = serializedObject.FindProperty("m_NormalMapQuality");
m_LightOrder = serializedObject.FindProperty("m_LightOrder");
m_OverlapOperation = serializedObject.FindProperty("m_OverlapOperation");
// Point Light
m_PointInnerAngle = serializedObject.FindProperty("m_PointLightInnerAngle");
m_PointOuterAngle = serializedObject.FindProperty("m_PointLightOuterAngle");
m_PointInnerRadius = serializedObject.FindProperty("m_PointLightInnerRadius");
m_PointOuterRadius = serializedObject.FindProperty("m_PointLightOuterRadius");
m_DeprecatedPointLightSprite = serializedObject.FindProperty("m_DeprecatedPointLightCookieSprite");
// Shape Light
m_ShapeLightParametricRadius = serializedObject.FindProperty("m_ShapeLightParametricRadius");
m_ShapeLightFalloffSize = serializedObject.FindProperty("m_ShapeLightFalloffSize");
m_ShapeLightParametricSides = serializedObject.FindProperty("m_ShapeLightParametricSides");
m_ShapeLightSprite = serializedObject.FindProperty("m_LightCookieSprite");
m_AnyBlendStyleEnabled = false;
var blendStyleIndices = new List<int>();
var blendStyleNames = new List<string>();
var rendererData = Light2DEditorUtility.GetRenderer2DData();
if (rendererData != null)
{
for (int i = 0; i < rendererData.lightBlendStyles.Length; ++i)
{
blendStyleIndices.Add(i);
ref var blendStyle = ref rendererData.lightBlendStyles[i];
if (blendStyle.maskTextureChannel == Light2DBlendStyle.TextureChannel.None)
blendStyleNames.Add(blendStyle.name);
else
{
var name = string.Format("{0} ({1})", blendStyle.name, blendStyle.maskTextureChannel);
blendStyleNames.Add(name);
}
m_AnyBlendStyleEnabled = true;
}
}
else
{
for (int i = 0; i < 4; ++i)
{
blendStyleIndices.Add(i);
blendStyleNames.Add("Operation" + i);
}
}
m_BlendStyleIndices = blendStyleIndices.ToArray();
m_BlendStyleNames = blendStyleNames.Select(x => new GUIContent(x)).ToArray();
m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
}
internal void SendModifiedAnalytics(Analytics.Renderer2DAnalytics analytics, Light2D light)
{
Analytics.Light2DData lightData = new Analytics.Light2DData();
lightData.was_create_event = false;
lightData.instance_id = light.GetInstanceID();
lightData.light_type = light.lightType;
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_LightDataString, lightData);
}
void OnDestroy()
{
if (m_ModifiedLights != null && m_ModifiedLights.Count > 0)
{
foreach (Light2D light in m_ModifiedLights)
{
SendModifiedAnalytics(m_Analytics, light);
}
}
}
void DrawBlendingGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_BlendingSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.blendingSettingsFoldout, m_BlendingSettingsFoldout.value);
if (m_BlendingSettingsFoldout.value)
{
if (!m_AnyBlendStyleEnabled)
EditorGUILayout.HelpBox(Styles.generalLightNoLightEnabled);
else
EditorGUILayout.IntPopup(m_BlendStyleIndex, m_BlendStyleNames, m_BlendStyleIndices, Styles.generalBlendStyle);
EditorGUILayout.PropertyField(m_LightOrder, Styles.generalLightOrder);
EditorGUILayout.PropertyField(m_OverlapOperation, Styles.generalLightOverlapOperation);
}
}
void DrawShadowsGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_ShadowsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.shadowsSettingsFoldout, m_ShadowsSettingsFoldout.value);
if (m_ShadowsSettingsFoldout.value)
{
DrawToggleProperty(Styles.generalShadowIntensity, m_ShadowIntensityEnabled, m_ShadowIntensity);
}
}
void DrawVolumetricGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_VolumetricSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.volumetricSettingsFoldout, m_VolumetricSettingsFoldout.value);
if (m_VolumetricSettingsFoldout.value)
{
DrawToggleProperty(Styles.generalVolumeIntensity, m_VolumetricIntensityEnabled, m_VolumetricIntensity);
if (m_VolumetricIntensity.floatValue < 0)
m_VolumetricIntensity.floatValue = 0;
EditorGUI.BeginDisabledGroup(!m_VolumetricIntensityEnabled.boolValue);
DrawToggleProperty(Styles.generalShadowVolumeIntensity, m_ShadowVolumeIntensityEnabled, m_ShadowVolumeIntensity);
EditorGUI.EndDisabledGroup();
}
}
void DrawNormalMapGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_NormalMapsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.normalMapsSettingsFoldout, m_NormalMapsSettingsFoldout.value);
if (m_NormalMapsSettingsFoldout.value)
{
EditorGUILayout.PropertyField(m_NormalMapQuality, Styles.generalNormalMapLightQuality);
EditorGUI.BeginDisabledGroup(m_NormalMapQuality.intValue == (int)Light2D.NormalMapQuality.Disabled);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_NormalMapZDistance, Styles.generalNormalMapZDistance);
if (EditorGUI.EndChangeCheck())
m_NormalMapZDistance.floatValue = Mathf.Max(0.0f, m_NormalMapZDistance.floatValue);
EditorGUI.EndDisabledGroup();
}
}
void DrawFoldouts()
{
DrawBlendingGroup();
DrawShadowsGroup();
DrawVolumetricGroup();
DrawNormalMapGroup();
}
void DrawRadiusProperties(GUIContent label, SerializedProperty innerRadius, GUIContent content1, SerializedProperty outerRadius, GUIContent content2)
{
GUIStyle style = GUI.skin.box;
float savedLabelWidth = EditorGUIUtility.labelWidth;
int savedIndentLevel = EditorGUI.indentLevel;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(label);
EditorGUILayout.BeginHorizontal();
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = style.CalcSize(content1).x;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(innerRadius, content1);
if (EditorGUI.EndChangeCheck())
{
if (innerRadius.floatValue > outerRadius.floatValue)
innerRadius.floatValue = outerRadius.floatValue;
else if (innerRadius.floatValue < 0)
innerRadius.floatValue = 0;
}
EditorGUIUtility.labelWidth = style.CalcSize(content2).x;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(outerRadius, content2);
if (EditorGUI.EndChangeCheck() && outerRadius.floatValue < innerRadius.floatValue)
outerRadius.floatValue = innerRadius.floatValue;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = savedLabelWidth;
EditorGUI.indentLevel = savedIndentLevel;
}
void DrawToggleProperty(GUIContent label, SerializedProperty boolProperty, SerializedProperty property)
{
int savedIndentLevel = EditorGUI.indentLevel;
float savedLabelWidth = EditorGUIUtility.labelWidth;
const int kCheckboxWidth = 20;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(boolProperty, GUIContent.none, GUILayout.MaxWidth(kCheckboxWidth));
EditorGUIUtility.labelWidth = EditorGUIUtility.labelWidth - kCheckboxWidth;
EditorGUI.BeginDisabledGroup(!boolProperty.boolValue);
EditorGUILayout.PropertyField(property, label);
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel = savedIndentLevel;
EditorGUIUtility.labelWidth = savedLabelWidth;
}
public void DrawInnerAndOuterSpotAngle(SerializedProperty minProperty, SerializedProperty maxProperty, GUIContent label)
{
float textFieldWidth = 45f;
float min = minProperty.floatValue;
float max = maxProperty.floatValue;
var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
// This widget is a little bit of a special case.
// The right hand side of the min max slider will control the reset of the max value
// The left hand side of the min max slider will control the reset of the min value
// The label itself will not have a right click and reset value.
rect = EditorGUI.PrefixLabel(rect, label);
EditorGUI.BeginProperty(new Rect(rect) { width = rect.width * 0.5f }, label, minProperty);
EditorGUI.BeginProperty(new Rect(rect) { xMin = rect.x + rect.width * 0.5f }, GUIContent.none, maxProperty);
var minRect = new Rect(rect) { width = textFieldWidth };
var maxRect = new Rect(rect) { xMin = rect.xMax - textFieldWidth };
var sliderRect = new Rect(rect) { xMin = minRect.xMax + 4, xMax = maxRect.xMin - 4 };
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedFloatField(minRect, minProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
if (minProperty.floatValue > maxProperty.floatValue)
minProperty.floatValue = maxProperty.floatValue;
else if (minProperty.floatValue < 0)
minProperty.floatValue = 0;
}
EditorGUI.BeginChangeCheck();
EditorGUI.MinMaxSlider(sliderRect, ref min, ref max, 0f, 360f);
if (EditorGUI.EndChangeCheck())
{
minProperty.floatValue = min;
maxProperty.floatValue = max;
}
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedFloatField(maxRect, m_PointOuterAngle, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
if (minProperty.floatValue > maxProperty.floatValue)
maxProperty.floatValue = minProperty.floatValue;
else if (maxProperty.floatValue > 360)
maxProperty.floatValue = 360;
}
EditorGUI.EndProperty();
EditorGUI.EndProperty();
}
void DrawGlobalLight(SerializedObject serializedObject)
{
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawBlendingGroup();
}
void DrawParametricDeprecated(SerializedObject serializedObject)
{
GUIContent buttonText = targets.Length > 1 ? Styles.deprecatedParametricLightButtonMulti : Styles.deprecatedParametricLightButtonSingle;
GUIContent helpText = targets.Length > 1 ? Styles.deprecatedParametricLightWarningMulti : Styles.deprecatedParametricLightWarningSingle;
string dialogText = targets.Length > 1 ? Styles.deprecatedParametricLightDialogTextMulti : Styles.deprecatedParametricLightDialogTextSingle;
EditorGUILayout.HelpBox(helpText);
EditorGUILayout.Space();
if (GUILayout.Button(buttonText))
{
if (EditorUtility.DisplayDialog(Styles.deprecatedParametricLightDialogTitle, dialogText, Styles.deprecatedParametricLightDialogProceed, Styles.deprecatedParametricLightDialogCancel))
{
for (int i = 0; i < targets.Length; i++)
{
Light2D light = (Light2D)targets[i];
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
Renderer2DUpgrader.UpgradeParametricLight(light);
}
}
}
EditorGUILayout.Space();
EditorGUILayout.HelpBox(Styles.deprecatedParametricLightInstructions);
}
bool DrawLightCommon()
{
var meshChanged = false;
Rect lightTypeRect = EditorGUILayout.GetControlRect();
EditorGUI.BeginProperty(lightTypeRect, GUIContent.none, m_LightType);
EditorGUI.BeginChangeCheck();
int newLightType = EditorGUI.Popup(lightTypeRect, Styles.generalLightType, m_LightType.intValue - 1, Styles.lightTypeOptions); // -1 is a bit hacky its to support compatibiltiy. We need something better.
if (EditorGUI.EndChangeCheck())
{
m_LightType.intValue = newLightType + 1; // -1 is a bit hacky its to support compatibiltiy. We need something better.
meshChanged = true;
}
EditorGUI.EndProperty();
// Color and intensity
EditorGUILayout.PropertyField(m_LightColor, Styles.generalLightColor);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_LightIntensity, Styles.generalLightIntensity);
if (EditorGUI.EndChangeCheck())
m_LightIntensity.floatValue = Mathf.Max(m_LightIntensity.floatValue, 0);
return meshChanged;
}
void DrawSpotLight(SerializedObject serializedObject)
{
DrawRadiusProperties(Styles.pointLightRadius, m_PointInnerRadius, Styles.pointLightInner, m_PointOuterRadius, Styles.pointLightOuter);
DrawInnerAndOuterSpotAngle(m_PointInnerAngle, m_PointOuterAngle, Styles.InnerOuterSpotAngle);
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
if (m_DeprecatedPointLightSprite.objectReferenceValue != null)
EditorGUILayout.PropertyField(m_DeprecatedPointLightSprite, Styles.pointLightSprite);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawFoldouts();
}
void DrawSpriteLight(SerializedObject serializedObject)
{
EditorGUILayout.PropertyField(m_ShapeLightSprite, Styles.shapeLightSprite);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawFoldouts();
}
void DrawShapeLight(SerializedObject serializedObject)
{
EditorGUILayout.PropertyField(m_ShapeLightFalloffSize, Styles.generalFalloffSize);
if (m_ShapeLightFalloffSize.floatValue < 0)
m_ShapeLightFalloffSize.floatValue = 0;
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
if (m_LightType.intValue == (int)Light2D.LightType.Freeform)
{
DoEditButton<FreeformShapeTool>(PathEditorToolContents.icon, "Edit Shape");
DoPathInspector<FreeformShapeTool>();
DoSnappingInspector<FreeformShapeTool>();
}
DrawFoldouts();
}
Vector3 DrawAngleSlider2D(Transform transform, Quaternion rotation, float radius, float offset, Handles.CapFunction capFunc, float capSize, bool leftAngle, bool drawLine, bool useCapOffset, ref float angle)
{
float oldAngle = angle;
float angleBy2 = (angle / 2) * (leftAngle ? -1.0f : 1.0f);
Vector3 trcwPos = Quaternion.AngleAxis(angleBy2, -transform.forward) * (transform.up);
Vector3 cwPos = transform.position + trcwPos * (radius + offset);
float direction = leftAngle ? 1 : -1;
// Offset the handle
float size = .25f * capSize;
Vector3 handleOffset = useCapOffset ? rotation * new Vector3(direction * size, 0, 0) : Vector3.zero;
EditorGUI.BeginChangeCheck();
var id = GUIUtility.GetControlID("AngleSlider".GetHashCode(), FocusType.Passive);
Vector3 cwHandle = Handles.Slider2D(id, cwPos, handleOffset, Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, capSize, capFunc, Vector3.zero);
if (EditorGUI.EndChangeCheck())
{
Vector3 toCwHandle = (transform.position - cwHandle).normalized;
angle = 360 - 2 * Quaternion.Angle(Quaternion.FromToRotation(transform.up, toCwHandle), Quaternion.identity);
angle = Mathf.Round(angle * 100) / 100f;
float side = Vector3.Dot(direction * transform.right, toCwHandle);
if (side < 0)
{
if (oldAngle < 180)
angle = 0;
else
angle = 360;
}
}
if (drawLine)
Handles.DrawLine(transform.position, cwHandle);
return cwHandle;
}
private float DrawAngleHandle(Transform transform, float radius, float offset, Handles.CapFunction capLeft, Handles.CapFunction capRight, ref float angle)
{
float old = angle;
float handleOffset = HandleUtility.GetHandleSize(transform.position) * offset;
float handleSize = HandleUtility.GetHandleSize(transform.position) * k_AngleCapSize;
Quaternion rotLt = Quaternion.AngleAxis(-angle / 2, -transform.forward) * transform.rotation;
DrawAngleSlider2D(transform, rotLt, radius, handleOffset, capLeft, handleSize, true, true, true, ref angle);
Quaternion rotRt = Quaternion.AngleAxis(angle / 2, -transform.forward) * transform.rotation;
DrawAngleSlider2D(transform, rotRt, radius, handleOffset, capRight, handleSize, false, true, true, ref angle);
return angle - old;
}
private void DrawRadiusArc(Transform transform, float radius, float angle, int steps, Handles.CapFunction capFunc, float capSize, bool even)
{
Handles.DrawWireArc(transform.position, transform.forward, Quaternion.AngleAxis(180 - angle / 2, transform.forward) * -transform.up, angle, radius);
}
Handles.CapFunction GetCapFunc(Texture texture, bool isAngleHandle)
{
return (controlID, position, rotation, size, eventType) => Light2DEditorUtility.GUITextureCap(controlID, texture, position, rotation, size, eventType, isAngleHandle);
}
private void DrawAngleHandles(Light2D light)
{
var oldColor = Handles.color;
Handles.color = Color.yellow;
float outerAngle = light.pointLightOuterAngle;
float diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, k_AngleCapOffset, GetCapFunc(Styles.lightCapTopRight, true), GetCapFunc(Styles.lightCapBottomRight, true), ref outerAngle);
light.pointLightOuterAngle = outerAngle;
if (diff != 0.0f)
light.pointLightInnerAngle = Mathf.Max(0.0f, light.pointLightInnerAngle + diff);
float innerAngle = light.pointLightInnerAngle;
diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, -k_AngleCapOffset, GetCapFunc(Styles.lightCapTopLeft, true), GetCapFunc(Styles.lightCapBottomLeft, true), ref innerAngle);
light.pointLightInnerAngle = innerAngle;
if (diff != 0.0f)
light.pointLightInnerAngle = light.pointLightInnerAngle < light.pointLightOuterAngle ? light.pointLightInnerAngle : light.pointLightOuterAngle;
light.pointLightInnerAngle = Mathf.Min(light.pointLightInnerAngle, light.pointLightOuterAngle);
Handles.color = oldColor;
}
private void DrawRangeHandles(Light2D light)
{
var dummy = 0.0f;
bool radiusChanged = false;
Vector3 handlePos = Vector3.zero;
Quaternion rotLeft = Quaternion.AngleAxis(0, -light.transform.forward) * light.transform.rotation;
float handleOffset = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapOffsetSecondary;
float handleSize = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapSize;
var oldColor = Handles.color;
Handles.color = Color.yellow;
float outerRadius = light.pointLightOuterRadius;
EditorGUI.BeginChangeCheck();
Vector3 returnPos = DrawAngleSlider2D(light.transform, rotLeft, outerRadius, -handleOffset, GetCapFunc(Styles.lightCapUp, false), handleSize, false, false, false, ref dummy);
if (EditorGUI.EndChangeCheck())
{
var vec = (returnPos - light.transform.position).normalized;
light.transform.up = new Vector3(vec.x, vec.y, 0);
outerRadius = (returnPos - light.transform.position).magnitude;
outerRadius = outerRadius + handleOffset;
radiusChanged = true;
}
DrawRadiusArc(light.transform, light.pointLightOuterRadius, light.pointLightOuterAngle, 0, Handles.DotHandleCap, k_RangeCapSize, false);
Handles.color = Color.gray;
float innerRadius = light.pointLightInnerRadius;
EditorGUI.BeginChangeCheck();
returnPos = DrawAngleSlider2D(light.transform, rotLeft, innerRadius, handleOffset, GetCapFunc(Styles.lightCapDown, false), handleSize, true, false, false, ref dummy);
if (EditorGUI.EndChangeCheck())
{
innerRadius = (returnPos - light.transform.position).magnitude;
innerRadius = innerRadius - handleOffset;
radiusChanged = true;
}
DrawRadiusArc(light.transform, light.pointLightInnerRadius, light.pointLightOuterAngle, 0, Handles.SphereHandleCap, k_InnerRangeCapSize, false);
Handles.color = oldColor;
if (radiusChanged)
{
light.pointLightInnerRadius = (outerRadius < innerRadius) ? outerRadius : innerRadius;
light.pointLightOuterRadius = (innerRadius > outerRadius) ? innerRadius : outerRadius;
}
}
void OnSceneGUI()
{
var light = target as Light2D;
if (light == null)
return;
Transform t = light.transform;
switch (light.lightType)
{
case Light2D.LightType.Point:
{
Undo.RecordObject(light.transform, "Edit Point Light Transform");
Undo.RecordObject(light, "Edit Point Light");
DrawRangeHandles(light);
DrawAngleHandles(light);
if (GUI.changed)
EditorUtility.SetDirty(light);
}
break;
case Light2D.LightType.Sprite:
{
var cookieSprite = light.lightCookieSprite;
if (cookieSprite != null)
{
Vector3 min = cookieSprite.bounds.min;
Vector3 max = cookieSprite.bounds.max;
Vector3 v0 = t.TransformPoint(new Vector3(min.x, min.y));
Vector3 v1 = t.TransformPoint(new Vector3(max.x, min.y));
Vector3 v2 = t.TransformPoint(new Vector3(max.x, max.y));
Vector3 v3 = t.TransformPoint(new Vector3(min.x, max.y));
Handles.DrawLine(v0, v1);
Handles.DrawLine(v1, v2);
Handles.DrawLine(v2, v3);
Handles.DrawLine(v3, v0);
}
}
break;
case Light2D.LightType.Freeform:
{
// Draw the falloff shape's outline
List<Vector2> falloffShape = light.GetFalloffShape();
Handles.color = Color.white;
for (int i = 0; i < falloffShape.Count - 1; ++i)
{
Handles.DrawLine(t.TransformPoint(falloffShape[i]), t.TransformPoint(falloffShape[i + 1]));
}
Handles.DrawLine(t.TransformPoint(falloffShape[falloffShape.Count - 1]), t.TransformPoint(falloffShape[0]));
for (int i = 0; i < light.shapePath.Length - 1; ++i)
{
Handles.DrawLine(t.TransformPoint(light.shapePath[i]),
t.TransformPoint(light.shapePath[i + 1]));
}
Handles.DrawLine(t.TransformPoint(light.shapePath[light.shapePath.Length - 1]), t.TransformPoint(light.shapePath[0]));
}
break;
}
}
public override void OnInspectorGUI()
{
var meshChanged = false;
serializedObject.Update();
UniversalRenderPipelineAsset asset = UniversalRenderPipeline.asset;
if (asset != null)
{
if (!Light2DEditorUtility.IsUsing2DRenderer())
{
EditorGUILayout.HelpBox(Styles.asset2DUnassignedWarning);
}
else
{
if (m_LightType.intValue != (int)Light2D.DeprecatedLightType.Parametric)
meshChanged = DrawLightCommon();
switch (m_LightType.intValue)
{
case (int)Light2D.LightType.Point:
{
DrawSpotLight(serializedObject);
}
break;
case (int)Light2D.LightType.Freeform:
{
DrawShapeLight(serializedObject);
}
break;
case (int)Light2D.LightType.Sprite:
{
DrawSpriteLight(serializedObject);
}
break;
case (int)Light2D.LightType.Global:
{
DrawGlobalLight(serializedObject);
}
break;
case (int)Light2D.DeprecatedLightType.Parametric:
{
DrawParametricDeprecated(serializedObject);
}
break;
}
AnalyticsTrackChanges(serializedObject);
if (serializedObject.ApplyModifiedProperties())
{
if (meshChanged)
lightObject.UpdateMesh(true);
}
}
}
else
{
EditorGUILayout.HelpBox(Styles.renderPipelineUnassignedWarning);
if (meshChanged)
lightObject.UpdateMesh(true);
}
}
}
}

View File

@@ -0,0 +1,123 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
internal static class Light2DEditorUtility
{
static Material s_TexCapMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/Internal-GUITexture"));
static internal void GUITextureCap(int controlID, Texture texture, Vector3 position, Quaternion rotation, float size, EventType eventType, bool isAngleHandle)
{
switch (eventType)
{
case (EventType.Layout):
{
Vector2 size2 = Vector2.one * size * 0.5f;
if (isAngleHandle)
size2.x = 0.0f;
HandleUtility.AddControl(controlID, DistanceToRectangle(position, rotation, size2));
break;
}
case (EventType.Repaint):
{
s_TexCapMaterial.mainTexture = texture;
s_TexCapMaterial.SetPass(0);
float w = texture.width;
float h = texture.height;
float max = Mathf.Max(w, h);
Vector3 scale = new Vector2(w / max, h / max) * size * 0.5f;
if (Camera.current == null)
scale.y *= -1f;
Matrix4x4 matrix = new Matrix4x4();
matrix.SetTRS(position, rotation, scale);
Graphics.DrawMeshNow(RenderingUtils.fullscreenMesh, matrix);
}
break;
}
}
static float DistanceToRectangle(Vector3 position, Quaternion rotation, Vector2 size)
{
Vector3[] points = { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero };
Vector3 sideways = rotation * new Vector3(size.x, 0, 0);
Vector3 up = rotation * new Vector3(0, size.y, 0);
points[0] = HandleUtility.WorldToGUIPoint(position + sideways + up);
points[1] = HandleUtility.WorldToGUIPoint(position + sideways - up);
points[2] = HandleUtility.WorldToGUIPoint(position - sideways - up);
points[3] = HandleUtility.WorldToGUIPoint(position - sideways + up);
points[4] = points[0];
Vector2 pos = Event.current.mousePosition;
bool oddNodes = false;
int j = 4;
for (int i = 0; i < 5; ++i)
{
if ((points[i].y > pos.y) != (points[j].y > pos.y))
{
if (pos.x < (points[j].x - points[i].x) * (pos.y - points[i].y) / (points[j].y - points[i].y) + points[i].x)
oddNodes = !oddNodes;
}
j = i;
}
if (!oddNodes)
{
// Distance to closest edge (not so fast)
float dist, closestDist = -1f;
j = 1;
for (int i = 0; i < 4; ++i)
{
dist = HandleUtility.DistancePointToLineSegment(pos, points[i], points[j++]);
if (dist < closestDist || closestDist < 0)
closestDist = dist;
}
return closestDist;
}
else
return 0;
}
public static Renderer2DData GetRenderer2DData()
{
UniversalRenderPipelineAsset pipelineAsset = UniversalRenderPipeline.asset;
if (pipelineAsset == null)
return null;
// try get the default
Renderer2DData rendererData = pipelineAsset.scriptableRendererData as Renderer2DData;
if (rendererData == null)
{
foreach (Camera camera in Camera.allCameras)
{
UniversalAdditionalCameraData additionalCameraData = camera.GetComponent<UniversalAdditionalCameraData>();
ScriptableRenderer renderer = additionalCameraData?.scriptableRenderer;
Renderer2D renderer2D = renderer as Renderer2D;
if (renderer2D != null)
return renderer2D.GetRenderer2DData();
}
}
return rendererData;
}
public static bool IsUsing2DRenderer()
{
return GetRenderer2DData() != null;
}
}
}

View File

@@ -0,0 +1,280 @@
using System;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
[CustomEditor(typeof(PixelPerfectCamera))]
class PixelPerfectCameraEditor : Editor
{
private class Style
{
public GUIContent x = new GUIContent("X");
public GUIContent y = new GUIContent("Y");
public GUIContent assetsPPU = new GUIContent("Assets Pixels Per Unit", "The amount of pixels that make up one unit of the Scene. Set this value to match the PPU value of Sprites in the Scene.");
public GUIContent refRes = new GUIContent("Reference Resolution", "The original resolution the Assets are designed for.");
public GUIContent upscaleRT = new GUIContent("Upscale Render Texture", "If enabled, the Scene is rendered as close as possible to the Reference Resolution while maintaining the screen aspect ratio, then upscaled to fit the full screen.");
public GUIContent pixelSnapping = new GUIContent("Pixel Snapping", "If enabled, Sprite Renderers are snapped to a grid in world space at render-time. Grid size is based on the Assets Pixels Per Unit value. This does not affect GameObjects' Transform positions.");
public GUIContent cropFrame = new GUIContent("Crop Frame", "Crops the viewport to match the Reference Resolution, along the checked axis. Black bars will be added to fit the screen aspect ratio.");
public GUIContent stretchFill = new GUIContent("Stretch Fill", "If enabled, expands the viewport to fit the screen resolution while maintaining the viewport aspect ratio.");
public GUIContent currentPixelRatio = new GUIContent("Current Pixel Ratio", "Ratio of the rendered Sprites compared to their original size.");
public GUIContent runInEditMode = new GUIContent("Run In Edit Mode", "Enable this to preview Camera setting changes in Edit Mode. This will cause constant changes to the Scene while active.");
public const string cameraStackingWarning = "Pixel Perfect Camera won't function properly if stacked with another camera.";
public GUIStyle centeredLabel;
public Style()
{
centeredLabel = new GUIStyle(EditorStyles.label);
centeredLabel.alignment = TextAnchor.MiddleCenter;
}
}
private static Style m_Style;
private const float k_SingleLetterLabelWidth = 15.0f;
private const float k_DottedLineSpacing = 2.5f;
private SerializedProperty m_AssetsPPU;
private SerializedProperty m_RefResX;
private SerializedProperty m_RefResY;
private SerializedProperty m_UpscaleRT;
private SerializedProperty m_PixelSnapping;
private SerializedProperty m_CropFrameY;
private SerializedProperty m_CropFrameX;
private SerializedProperty m_StretchFill;
private Vector2 m_GameViewSize = Vector2.zero;
private GUIContent m_CurrentPixelRatioValue;
bool m_CameraStacking;
private void LazyInit()
{
if (m_Style == null)
m_Style = new Style();
if (m_CurrentPixelRatioValue == null)
m_CurrentPixelRatioValue = new GUIContent();
}
void CheckForCameraStacking()
{
m_CameraStacking = false;
PixelPerfectCamera obj = target as PixelPerfectCamera;
UniversalAdditionalCameraData cameraData = null;
obj?.TryGetComponent(out cameraData);
if (cameraData == null)
return;
if (cameraData.renderType == CameraRenderType.Base)
{
var cameraStack = cameraData.cameraStack;
m_CameraStacking = cameraStack != null ? cameraStack.Count > 0 : false;
}
else if (cameraData.renderType == CameraRenderType.Overlay)
m_CameraStacking = true;
}
public void OnEnable()
{
m_AssetsPPU = serializedObject.FindProperty("m_AssetsPPU");
m_RefResX = serializedObject.FindProperty("m_RefResolutionX");
m_RefResY = serializedObject.FindProperty("m_RefResolutionY");
m_UpscaleRT = serializedObject.FindProperty("m_UpscaleRT");
m_PixelSnapping = serializedObject.FindProperty("m_PixelSnapping");
m_CropFrameY = serializedObject.FindProperty("m_CropFrameY");
m_CropFrameX = serializedObject.FindProperty("m_CropFrameX");
m_StretchFill = serializedObject.FindProperty("m_StretchFill");
}
public override bool RequiresConstantRepaint()
{
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj == null || !obj.enabled)
return false;
// If game view size changes, we need to force a repaint of the inspector as the pixel ratio value may change accordingly.
Vector2 gameViewSize = Handles.GetMainGameViewSize();
if (gameViewSize != m_GameViewSize)
{
m_GameViewSize = gameViewSize;
return true;
}
else
return false;
}
public override void OnInspectorGUI()
{
LazyInit();
float originalLabelWidth = EditorGUIUtility.labelWidth;
serializedObject.Update();
if (Event.current.type == EventType.Layout)
CheckForCameraStacking();
if (m_CameraStacking)
EditorGUILayout.HelpBox(Style.cameraStackingWarning, MessageType.Warning);
EditorGUILayout.PropertyField(m_AssetsPPU, m_Style.assetsPPU);
if (m_AssetsPPU.intValue <= 0)
m_AssetsPPU.intValue = 1;
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel(m_Style.refRes);
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
EditorGUILayout.PropertyField(m_RefResX, m_Style.x);
if (m_RefResX.intValue <= 0)
m_RefResX.intValue = 1;
EditorGUILayout.PropertyField(m_RefResY, m_Style.y);
if (m_RefResY.intValue <= 0)
m_RefResY.intValue = 1;
EditorGUIUtility.labelWidth = originalLabelWidth;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_UpscaleRT, m_Style.upscaleRT);
if (!m_UpscaleRT.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_PixelSnapping, m_Style.pixelSnapping);
EditorGUI.indentLevel--;
}
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel(m_Style.cropFrame);
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
EditorGUILayout.PropertyField(m_CropFrameX, m_Style.x, GUILayout.MaxWidth(40.0f));
EditorGUILayout.PropertyField(m_CropFrameY, m_Style.y);
EditorGUIUtility.labelWidth = originalLabelWidth;
}
EditorGUILayout.EndHorizontal();
if (m_CropFrameY.boolValue && m_CropFrameX.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_StretchFill, m_Style.stretchFill);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj != null)
{
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying || !obj.isActiveAndEnabled);
EditorGUI.BeginChangeCheck();
bool runInEditMode = EditorGUILayout.Toggle(obj.runInEditMode, GUI.skin.button, GUILayout.Width(110.0f));
GUI.Label(GUILayoutUtility.GetLastRect(), m_Style.runInEditMode, m_Style.centeredLabel);
if (EditorGUI.EndChangeCheck())
{
obj.runInEditMode = runInEditMode;
if (runInEditMode)
obj.GetComponent<Camera>().Render();
else
obj.OnDisable();
}
EditorGUI.EndDisabledGroup();
if (obj.isActiveAndEnabled && (EditorApplication.isPlaying || obj.runInEditMode))
{
if (Event.current.type == EventType.Layout)
m_CurrentPixelRatioValue.text = string.Format("{0}:1", obj.pixelRatio);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.LabelField(m_Style.currentPixelRatio, m_CurrentPixelRatioValue);
EditorGUI.EndDisabledGroup();
}
}
}
void OnSceneGUI()
{
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj == null)
return;
Camera camera = obj.GetComponent<Camera>();
// Show a green rect in scene view that represents the visible area when the pixel perfect correction takes effect in play mode.
Vector2 gameViewSize = Handles.GetMainGameViewSize();
int gameViewWidth = (int)gameViewSize.x;
int gameViewHeight = (int)gameViewSize.y;
int zoom = Math.Max(1, Math.Min(gameViewHeight / obj.refResolutionY, gameViewWidth / obj.refResolutionX));
float verticalOrthoSize;
float horizontalOrthoSize;
if (obj.cropFrameY && obj.cropFrameX)
{
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
horizontalOrthoSize = verticalOrthoSize * ((float)obj.refResolutionX / obj.refResolutionY);
}
else if (obj.cropFrameY)
{
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
horizontalOrthoSize = verticalOrthoSize * ((float)gameViewWidth / (zoom * obj.refResolutionY));
}
else if (obj.cropFrameX)
{
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
verticalOrthoSize = horizontalOrthoSize / (zoom * obj.refResolutionX / (float)gameViewHeight);
}
else
{
verticalOrthoSize = gameViewHeight * 0.5f / (zoom * obj.assetsPPU);
horizontalOrthoSize = verticalOrthoSize * camera.aspect;
}
Handles.color = Color.green;
Vector3 cameraPosition = camera.transform.position;
Vector3 p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Vector3 p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawLine(p1, p2);
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawLine(p2, p1);
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawLine(p1, p2);
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawLine(p2, p1);
// Show a green dotted rect in scene view that represents the area defined by the reference resolution.
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using UnityEngine;
using UnityEngine.Analytics;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal.Analytics
{
struct AnalyticsDataTypes
{
public const string k_LightDataString = "u2drendererlights";
public const string k_Renderer2DDataString = "u2drendererdata";
}
internal interface IAnalyticsData {};
[Serializable]
internal struct Light2DData : IAnalyticsData
{
[SerializeField]
public bool was_create_event;
[SerializeField]
public int instance_id;
[SerializeField]
public Light2D.LightType light_type;
};
[Serializable]
internal struct RendererAssetData : IAnalyticsData
{
[SerializeField]
public bool was_create_event;
[SerializeField]
public int instance_id;
[SerializeField]
public int blending_layers_count;
[SerializeField]
public int blending_modes_used;
}
interface IAnalytics
{
AnalyticsResult SendData(string eventString, IAnalyticsData data);
}
[InitializeOnLoad]
internal class Renderer2DAnalytics : IAnalytics
{
const int k_MaxEventsPerHour = 1000;
const int k_MaxNumberOfElements = 1000;
const string k_VendorKey = "unity.renderpipelines.universal.editor";
const int k_Version = 1;
static Renderer2DAnalytics m_Instance = new Renderer2DAnalytics();
static bool s_Initialize = false;
public static Renderer2DAnalytics instance
{
get
{
if (m_Instance == null)
m_Instance = new Renderer2DAnalytics();
return m_Instance;
}
}
public AnalyticsResult SendData(string eventString, IAnalyticsData data)
{
//Debug.Log("Sent Data " + JsonUtility.ToJson(data));
if (false == s_Initialize)
{
EditorAnalytics.RegisterEventWithLimit(AnalyticsDataTypes.k_LightDataString, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey, k_Version);
EditorAnalytics.RegisterEventWithLimit(AnalyticsDataTypes.k_Renderer2DDataString, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey, k_Version);
s_Initialize = true;
}
return EditorAnalytics.SendEventWithLimit(eventString, data, k_Version);
}
}
}

View File

@@ -0,0 +1,275 @@
using UnityEditor.Rendering;
using UnityEditor.Rendering.Universal;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
[CustomEditor(typeof(Renderer2DData), true)]
internal class Renderer2DDataEditor : Editor
{
class Styles
{
public static readonly GUIContent generalHeader = EditorGUIUtility.TrTextContent("General");
public static readonly GUIContent lightRenderTexturesHeader = EditorGUIUtility.TrTextContent("Light Render Textures");
public static readonly GUIContent lightBlendStylesHeader = EditorGUIUtility.TrTextContent("Light Blend Styles", "A Light Blend Style is a collection of properties that describe a particular way of applying lighting.");
public static readonly GUIContent postProcessHeader = EditorGUIUtility.TrTextContent("Post-processing");
public static readonly GUIContent transparencySortMode = EditorGUIUtility.TrTextContent("Transparency Sort Mode", "Default sorting mode used for transparent objects");
public static readonly GUIContent transparencySortAxis = EditorGUIUtility.TrTextContent("Transparency Sort Axis", "Axis used for custom axis sorting mode");
public static readonly GUIContent hdrEmulationScale = EditorGUIUtility.TrTextContent("HDR Emulation Scale", "Describes the scaling used by lighting to remap dynamic range between LDR and HDR");
public static readonly GUIContent lightRTScale = EditorGUIUtility.TrTextContent("Render Scale", "The resolution of intermediate light render textures, in relation to the screen resolution. 1.0 means full-screen size.");
public static readonly GUIContent maxLightRTCount = EditorGUIUtility.TrTextContent("Max Light Render Textures", "How many intermediate light render textures can be created and utilized concurrently. Higher value usually leads to better performance on mobile hardware at the cost of more memory.");
public static readonly GUIContent maxShadowRTCount = EditorGUIUtility.TrTextContent("Max Shadow Render Textures", "How many intermediate shadow render textures can be created and utilized concurrently. Higher value usually leads to better performance on mobile hardware at the cost of more memory.");
public static readonly GUIContent defaultMaterialType = EditorGUIUtility.TrTextContent("Default Material Type", "Material to use when adding new objects to a scene");
public static readonly GUIContent defaultCustomMaterial = EditorGUIUtility.TrTextContent("Default Custom Material", "Material to use when adding new objects to a scene");
public static readonly GUIContent name = EditorGUIUtility.TrTextContent("Name");
public static readonly GUIContent maskTextureChannel = EditorGUIUtility.TrTextContent("Mask Texture Channel", "Which channel of the mask texture will affect this Light Blend Style.");
public static readonly GUIContent blendMode = EditorGUIUtility.TrTextContent("Blend Mode", "How the lighting should be blended with the main color of the objects.");
public static readonly GUIContent useDepthStencilBuffer = EditorGUIUtility.TrTextContent("Depth/Stencil Buffer", "Uncheck this when you are certain you don't use any feature that requires the depth/stencil buffer (e.g. Sprite Mask). Not using the depth/stencil buffer may improve performance, especially on mobile platforms.");
public static readonly GUIContent postProcessIncluded = EditorGUIUtility.TrTextContent("Enabled", "Turns post-processing on (check box selected) or off (check box cleared). If you clear this check box, Unity excludes post-processing render Passes, shaders, and textures from the build.");
public static readonly GUIContent postProcessData = EditorGUIUtility.TrTextContent("Data", "The asset containing references to shaders and Textures that the Renderer uses for post-processing.");
public static readonly GUIContent cameraSortingLayerTextureHeader = EditorGUIUtility.TrTextContent("Camera Sorting Layers Texture", "Layers from back most to selected bounds will be rendered to _CameraSortingLayersTexture");
public static readonly GUIContent cameraSortingLayerTextureBound = EditorGUIUtility.TrTextContent("Bound", "Layers from back most to selected bounds will be rendered to _CameraSortingLayersTexture");
public static readonly GUIContent cameraSortingLayerDownsampling = EditorGUIUtility.TrTextContent("Downsampling Method", "Method used to copy _CameraSortingLayersTexture");
}
struct LightBlendStyleProps
{
public SerializedProperty name;
public SerializedProperty maskTextureChannel;
public SerializedProperty blendMode;
public SerializedProperty blendFactorMultiplicative;
public SerializedProperty blendFactorAdditive;
}
SerializedProperty m_TransparencySortMode;
SerializedProperty m_TransparencySortAxis;
SerializedProperty m_HDREmulationScale;
SerializedProperty m_LightRenderTextureScale;
SerializedProperty m_LightBlendStyles;
LightBlendStyleProps[] m_LightBlendStylePropsArray;
SerializedProperty m_UseDepthStencilBuffer;
SerializedProperty m_DefaultMaterialType;
SerializedProperty m_DefaultCustomMaterial;
SerializedProperty m_MaxLightRenderTextureCount;
SerializedProperty m_MaxShadowRenderTextureCount;
SerializedProperty m_PostProcessData;
SerializedProperty m_UseCameraSortingLayersTexture;
SerializedProperty m_CameraSortingLayersTextureBound;
SerializedProperty m_CameraSortingLayerDownsamplingMethod;
SavedBool m_GeneralFoldout;
SavedBool m_LightRenderTexturesFoldout;
SavedBool m_LightBlendStylesFoldout;
SavedBool m_CameraSortingLayerTextureFoldout;
SavedBool m_PostProcessingFoldout;
Analytics.Renderer2DAnalytics m_Analytics = Analytics.Renderer2DAnalytics.instance;
Renderer2DData m_Renderer2DData;
bool m_WasModified;
void SendModifiedAnalytics(Analytics.IAnalytics analytics)
{
if (m_WasModified)
{
Analytics.RendererAssetData modifiedData = new Analytics.RendererAssetData();
modifiedData.instance_id = m_Renderer2DData.GetInstanceID();
modifiedData.was_create_event = false;
modifiedData.blending_layers_count = 0;
modifiedData.blending_modes_used = 0;
analytics.SendData(Analytics.AnalyticsDataTypes.k_Renderer2DDataString, modifiedData);
}
}
void OnEnable()
{
m_WasModified = false;
m_Renderer2DData = (Renderer2DData)serializedObject.targetObject;
m_TransparencySortMode = serializedObject.FindProperty("m_TransparencySortMode");
m_TransparencySortAxis = serializedObject.FindProperty("m_TransparencySortAxis");
m_HDREmulationScale = serializedObject.FindProperty("m_HDREmulationScale");
m_LightRenderTextureScale = serializedObject.FindProperty("m_LightRenderTextureScale");
m_LightBlendStyles = serializedObject.FindProperty("m_LightBlendStyles");
m_MaxLightRenderTextureCount = serializedObject.FindProperty("m_MaxLightRenderTextureCount");
m_MaxShadowRenderTextureCount = serializedObject.FindProperty("m_MaxShadowRenderTextureCount");
m_PostProcessData = serializedObject.FindProperty("m_PostProcessData");
m_CameraSortingLayersTextureBound = serializedObject.FindProperty("m_CameraSortingLayersTextureBound");
m_UseCameraSortingLayersTexture = serializedObject.FindProperty("m_UseCameraSortingLayersTexture");
m_CameraSortingLayerDownsamplingMethod = serializedObject.FindProperty("m_CameraSortingLayerDownsamplingMethod");
int numBlendStyles = m_LightBlendStyles.arraySize;
m_LightBlendStylePropsArray = new LightBlendStyleProps[numBlendStyles];
for (int i = 0; i < numBlendStyles; ++i)
{
SerializedProperty blendStyleProp = m_LightBlendStyles.GetArrayElementAtIndex(i);
ref LightBlendStyleProps props = ref m_LightBlendStylePropsArray[i];
props.name = blendStyleProp.FindPropertyRelative("name");
props.maskTextureChannel = blendStyleProp.FindPropertyRelative("maskTextureChannel");
props.blendMode = blendStyleProp.FindPropertyRelative("blendMode");
props.blendFactorMultiplicative = blendStyleProp.FindPropertyRelative("customBlendFactors.multiplicative");
props.blendFactorAdditive = blendStyleProp.FindPropertyRelative("customBlendFactors.additive");
if (props.blendFactorMultiplicative == null)
props.blendFactorMultiplicative = blendStyleProp.FindPropertyRelative("customBlendFactors.modulate");
if (props.blendFactorAdditive == null)
props.blendFactorAdditive = blendStyleProp.FindPropertyRelative("customBlendFactors.additve");
}
m_UseDepthStencilBuffer = serializedObject.FindProperty("m_UseDepthStencilBuffer");
m_DefaultMaterialType = serializedObject.FindProperty("m_DefaultMaterialType");
m_DefaultCustomMaterial = serializedObject.FindProperty("m_DefaultCustomMaterial");
m_GeneralFoldout = new SavedBool($"{target.GetType()}.GeneralFoldout", true);
m_LightRenderTexturesFoldout = new SavedBool($"{target.GetType()}.LightRenderTexturesFoldout", true);
m_LightBlendStylesFoldout = new SavedBool($"{target.GetType()}.LightBlendStylesFoldout", true);
m_CameraSortingLayerTextureFoldout = new SavedBool($"{target.GetType()}.CameraSortingLayerTextureFoldout", true);
m_PostProcessingFoldout = new SavedBool($"{target.GetType()}.PostProcessingFoldout", true);
}
private void OnDestroy()
{
SendModifiedAnalytics(m_Analytics);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawGeneral();
DrawLightRenderTextures();
DrawLightBlendStyles();
DrawCameraSortingLayerTexture();
DrawPostProcessing();
m_WasModified |= serializedObject.hasModifiedProperties;
serializedObject.ApplyModifiedProperties();
}
public void DrawCameraSortingLayerTexture()
{
CoreEditorUtils.DrawSplitter();
m_CameraSortingLayerTextureFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.cameraSortingLayerTextureHeader, m_CameraSortingLayerTextureFoldout.value);
if (!m_CameraSortingLayerTextureFoldout.value)
return;
SortingLayer[] sortingLayers = SortingLayer.layers;
string[] optionNames = new string[sortingLayers.Length + 1];
int[] optionIds = new int[sortingLayers.Length + 1];
optionNames[0] = "Disabled";
optionIds[0] = -1;
int currentOptionIndex = 0;
for (int i = 0; i < sortingLayers.Length; i++)
{
optionNames[i + 1] = sortingLayers[i].name;
optionIds[i + 1] = sortingLayers[i].id;
if (sortingLayers[i].id == m_CameraSortingLayersTextureBound.intValue)
currentOptionIndex = i + 1;
}
int selectedOptionIndex = !m_UseCameraSortingLayersTexture.boolValue ? 0 : currentOptionIndex;
selectedOptionIndex = EditorGUILayout.Popup(Styles.cameraSortingLayerTextureBound, selectedOptionIndex, optionNames);
m_UseCameraSortingLayersTexture.boolValue = selectedOptionIndex != 0;
m_CameraSortingLayersTextureBound.intValue = optionIds[selectedOptionIndex];
EditorGUI.BeginDisabledGroup(!m_UseCameraSortingLayersTexture.boolValue);
EditorGUILayout.PropertyField(m_CameraSortingLayerDownsamplingMethod, Styles.cameraSortingLayerDownsampling);
EditorGUI.EndDisabledGroup();
}
private void DrawGeneral()
{
CoreEditorUtils.DrawSplitter();
m_GeneralFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.generalHeader, m_GeneralFoldout.value);
if (!m_GeneralFoldout.value)
return;
EditorGUILayout.PropertyField(m_TransparencySortMode, Styles.transparencySortMode);
using (new EditorGUI.DisabledGroupScope(m_TransparencySortMode.intValue != (int)TransparencySortMode.CustomAxis))
EditorGUILayout.PropertyField(m_TransparencySortAxis, Styles.transparencySortAxis);
EditorGUILayout.PropertyField(m_DefaultMaterialType, Styles.defaultMaterialType);
if (m_DefaultMaterialType.intValue == (int)Renderer2DData.Renderer2DDefaultMaterialType.Custom)
EditorGUILayout.PropertyField(m_DefaultCustomMaterial, Styles.defaultCustomMaterial);
EditorGUILayout.PropertyField(m_UseDepthStencilBuffer, Styles.useDepthStencilBuffer);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_HDREmulationScale, Styles.hdrEmulationScale);
if (EditorGUI.EndChangeCheck() && m_HDREmulationScale.floatValue < 1.0f)
m_HDREmulationScale.floatValue = 1.0f;
EditorGUILayout.Space();
}
private void DrawLightRenderTextures()
{
CoreEditorUtils.DrawSplitter();
m_LightRenderTexturesFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.lightRenderTexturesHeader, m_LightRenderTexturesFoldout.value);
if (!m_LightRenderTexturesFoldout.value)
return;
EditorGUILayout.PropertyField(m_LightRenderTextureScale, Styles.lightRTScale);
EditorGUILayout.PropertyField(m_MaxLightRenderTextureCount, Styles.maxLightRTCount);
EditorGUILayout.PropertyField(m_MaxShadowRenderTextureCount, Styles.maxShadowRTCount);
EditorGUILayout.Space();
}
private void DrawLightBlendStyles()
{
CoreEditorUtils.DrawSplitter();
m_LightBlendStylesFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.lightBlendStylesHeader, m_LightBlendStylesFoldout.value);
if (!m_LightBlendStylesFoldout.value)
return;
int numBlendStyles = m_LightBlendStyles.arraySize;
for (int i = 0; i < numBlendStyles; ++i)
{
ref LightBlendStyleProps props = ref m_LightBlendStylePropsArray[i];
EditorGUILayout.PropertyField(props.name, Styles.name);
EditorGUILayout.PropertyField(props.maskTextureChannel, Styles.maskTextureChannel);
EditorGUILayout.PropertyField(props.blendMode, Styles.blendMode);
EditorGUILayout.Space();
EditorGUILayout.Space();
}
EditorGUILayout.Space();
}
private void DrawPostProcessing()
{
CoreEditorUtils.DrawSplitter();
m_PostProcessingFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.postProcessHeader, m_PostProcessingFoldout.value);
if (!m_PostProcessingFoldout.value)
return;
EditorGUI.BeginChangeCheck();
var postProcessIncluded = EditorGUILayout.Toggle(Styles.postProcessIncluded, m_PostProcessData.objectReferenceValue != null);
if (EditorGUI.EndChangeCheck())
{
m_PostProcessData.objectReferenceValue = postProcessIncluded ? UnityEngine.Rendering.Universal.PostProcessData.GetDefaultPostProcessData() : null;
}
// this field is no longer hidden by the checkbox. It is bad UX to begin with
// also, if the field is hidden, the user could still use Asset Selector to set the value, but it won't stick
// making it look like a bug(1307128)
EditorGUILayout.PropertyField(m_PostProcessData, Styles.postProcessData);
EditorGUILayout.Space();
}
}
}

View File

@@ -0,0 +1,206 @@
using System;
using System.IO;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Scripting.APIUpdating;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.ProjectWindowCallback;
using UnityEditorInternal;
namespace UnityEditor.Experimental.Rendering.Universal
{
static class Renderer2DMenus
{
const int k_MenuPriority = 50;
static void Create2DRendererData(Action<Renderer2DData> onCreatedCallback)
{
var instance = ScriptableObject.CreateInstance<Create2DRendererDataAsset>();
instance.onCreated += onCreatedCallback;
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, instance, "New 2D Renderer Data.asset", null, null);
}
class Create2DRendererDataAsset : EndNameEditAction
{
public event Action<Renderer2DData> onCreated;
public override void Action(int instanceId, string pathName, string resourceFile)
{
var instance = CreateInstance<Renderer2DData>();
instance.postProcessData = PostProcessData.GetDefaultPostProcessData();
AssetDatabase.CreateAsset(instance, pathName);
Selection.activeObject = instance;
onCreated?.Invoke(instance);
}
}
internal static void PlaceGameObjectInFrontOfSceneView(GameObject go)
{
var sceneViews = SceneView.sceneViews;
if (sceneViews.Count >= 1)
{
SceneView view = SceneView.lastActiveSceneView;
if (!view)
view = sceneViews[0] as SceneView;
if (view)
view.MoveToView(go.transform);
}
}
// This is from GOCreationCommands
internal static void Place(GameObject go, GameObject parent)
{
if (parent != null)
{
var transform = go.transform;
Undo.SetTransformParent(transform, parent.transform, "Reparenting");
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
go.layer = parent.layer;
if (parent.GetComponent<RectTransform>())
ObjectFactory.AddComponent<RectTransform>(go);
}
else
{
PlaceGameObjectInFrontOfSceneView(go);
StageUtility.PlaceGameObjectInCurrentStage(go); // may change parent
go.transform.position = new Vector3(go.transform.position.x, go.transform.position.y, 0);
}
// Only at this point do we know the actual parent of the object and can modify its name accordingly.
GameObjectUtility.EnsureUniqueNameForSibling(go);
Undo.SetCurrentGroupName("Create " + go.name);
//EditorWindow.FocusWindowIfItsOpen<SceneHierarchyWindow>();
Selection.activeGameObject = go;
}
static void CreateLight(MenuCommand menuCommand, Light2D.LightType type, Vector3[] shapePath = null)
{
GameObject go = ObjectFactory.CreateGameObject("Light 2D", typeof(Light2D));
Light2D light2D = go.GetComponent<Light2D>();
light2D.lightType = type;
if (shapePath != null && shapePath.Length > 0)
light2D.shapePath = shapePath;
var parent = menuCommand.context as GameObject;
Place(go, parent);
Analytics.Light2DData lightData = new Analytics.Light2DData();
lightData.was_create_event = true;
lightData.instance_id = light2D.GetInstanceID();
lightData.light_type = light2D.lightType;
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_LightDataString, lightData);
}
static bool CreateLightValidation()
{
return Light2DEditorUtility.IsUsing2DRenderer();
}
[MenuItem("GameObject/Light/Freeform Light 2D/Square", false, k_MenuPriority)]
static void CreateSquareFreeformLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateSquare());
}
[MenuItem("GameObject/Light/Freeform Light 2D/Circle", false, k_MenuPriority)]
static void CreateCircleFreeformLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateCircle());
}
[MenuItem("GameObject/Light/Freeform Light 2D/Isometric Diamond", false, k_MenuPriority)]
static void CreateIsometricDiamondFreeformLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateIsometricDiamond());
}
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Flat Top", false, k_MenuPriority)]
static void CreateHexagonFlatTopFreeformLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateHexagonFlatTop());
}
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Pointed Top", false, k_MenuPriority)]
static void CreateHexagonPointedTopFreeformLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Freeform, FreeformPathPresets.CreateHexagonPointedTop());
}
[MenuItem("GameObject/Light/Sprite Light 2D", false, k_MenuPriority)]
static void CreateSpriteLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Sprite);
}
[MenuItem("GameObject/Light/Spot Light 2D", false, k_MenuPriority)]
static void CreatePointLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Point);
}
[MenuItem("GameObject/Light/Global Light 2D", false, k_MenuPriority)]
static void CreateGlobalLight2D(MenuCommand menuCommand)
{
CreateLight(menuCommand, Light2D.LightType.Global);
}
[MenuItem("GameObject/Light/Freeform Light 2D/Isometric Diamond", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Freeform Light 2D/Square", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Freeform Light 2D/Circle", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Flat Top", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Freeform Light 2D/Hexagon Pointed Top", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Sprite Light 2D", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Spot Light 2D", true, k_MenuPriority)]
[MenuItem("GameObject/Light/Global Light 2D", true, k_MenuPriority)]
static bool CreateLight2DValidation()
{
return CreateLightValidation();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812")]
internal class CreateUniversalPipelineAsset : EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
//Create asset
AssetDatabase.CreateAsset(UniversalRenderPipelineAsset.Create(UniversalRenderPipelineAsset.CreateRendererAsset(pathName, RendererType._2DRenderer)), pathName);
}
}
[MenuItem("Assets/Create/Rendering/Universal Render Pipeline/Pipeline Asset (2D Renderer)", priority = CoreUtils.assetCreateMenuPriority1 + 1)]
static void CreateUniversalPipeline()
{
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, UniversalRenderPipelineAsset.CreateInstance<CreateUniversalPipelineAsset>(),
"UniversalRenderPipelineAsset.asset", null, null);
}
[MenuItem("Assets/Create/Rendering/Universal Render Pipeline/2D Renderer", priority = CoreUtils.assetCreateMenuPriority2 + 1)]
static void Create2DRendererData()
{
Renderer2DMenus.Create2DRendererData((instance) =>
{
Analytics.RendererAssetData modifiedData = new Analytics.RendererAssetData();
modifiedData.instance_id = instance.GetInstanceID();
modifiedData.was_create_event = true;
modifiedData.blending_layers_count = 1;
modifiedData.blending_modes_used = 2;
Analytics.Renderer2DAnalytics.instance.SendData(Analytics.AnalyticsDataTypes.k_Renderer2DDataString, modifiedData);
});
}
}
}

View File

@@ -0,0 +1,215 @@
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
internal static class Renderer2DUpgrader
{
delegate void Upgrader<T>(T toUpgrade) where T : Object;
static void ProcessAssetDatabaseObjects<T>(string searchString, Upgrader<T> upgrader) where T : Object
{
string[] prefabNames = AssetDatabase.FindAssets(searchString);
foreach (string prefabName in prefabNames)
{
string path = AssetDatabase.GUIDToAssetPath(prefabName);
if (path.StartsWith("Assets"))
{
T obj = AssetDatabase.LoadAssetAtPath<T>(path);
if (obj != null)
{
upgrader(obj);
}
}
}
}
public static void UpgradeObjectWithParametricLights(GameObject obj)
{
Light2D[] lights = obj.GetComponents<Light2D>();
if (lights.Length > 0)
{
foreach (var light in lights)
UpgradeParametricLight(light);
}
}
public static void UpgradeParametricLight(Light2D light)
{
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
{
light.lightType = Light2D.LightType.Freeform;
float radius = light.shapeLightParametricRadius;
float angle = light.shapeLightParametricAngleOffset;
int sides = light.shapeLightParametricSides;
var angleOffset = Mathf.PI / 2.0f + Mathf.Deg2Rad * angle;
if (sides < 3)
{
radius = 0.70710678118654752440084436210485f * radius;
sides = 4;
}
if (sides == 4)
{
angleOffset = Mathf.PI / 4.0f + Mathf.Deg2Rad * angle;
}
var radiansPerSide = 2 * Mathf.PI / sides;
var min = new Vector3(float.MaxValue, float.MaxValue, 0);
var max = new Vector3(float.MinValue, float.MinValue, 0);
Vector3[] shapePath = new Vector3[sides];
for (var i = 0; i < sides; i++)
{
var endAngle = (i + 1) * radiansPerSide;
var extrudeDir = new Vector3(Mathf.Cos(endAngle + angleOffset), Mathf.Sin(endAngle + angleOffset), 0);
var endPoint = radius * extrudeDir;
shapePath[i] = endPoint;
}
light.shapePath = shapePath;
light.UpdateMesh(true);
}
}
static void UpgradeGameObject(GameObject go)
{
Renderer[] spriteRenderers = go.GetComponentsInChildren<Renderer>(true);
Renderer2DData data = Light2DEditorUtility.GetRenderer2DData();
if (data != null)
{
Material defaultMat = data.GetDefaultMaterial(DefaultMaterialType.Sprite);
bool upgraded = false;
foreach (Renderer renderer in spriteRenderers)
{
int materialCount = renderer.sharedMaterials.Length;
Material[] newMaterials = new Material[materialCount];
for (int i = 0; i < materialCount; i++)
{
Material mat = renderer.sharedMaterials[i];
if (mat != null && mat.shader.name == "Sprites/Default")
{
newMaterials[i] = defaultMat;
upgraded = true;
}
else
{
newMaterials[i] = renderer.sharedMaterials[i];
}
}
if (upgraded)
renderer.sharedMaterials = newMaterials;
}
if (upgraded)
{
Debug.Log(go.name + " was upgraded.", go);
EditorSceneManager.MarkSceneDirty(go.scene);
}
}
}
static void UpgradeMaterial(Material mat)
{
Renderer2DData data = Light2DEditorUtility.GetRenderer2DData();
if (data != null)
{
Material defaultMat = data.GetDefaultMaterial(DefaultMaterialType.Sprite);
if (mat.shader.name == "Sprites/Default")
{
mat.shader = defaultMat.shader;
}
}
}
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Materials to 2D Renderer Materials", false)]
static void UpgradeProjectTo2DRenderer()
{
if (!EditorUtility.DisplayDialog("2D Renderer Upgrader", "The upgrade will search for all prefabs in your project that use Sprite Renderers and change the material references of those Sprite Renderers to a lit material. You can't undo this operation. It's highly recommended to backup your project before proceeding.", "Proceed", "Cancel"))
return;
ProcessAssetDatabaseObjects<GameObject>("t: Prefab", UpgradeGameObject);
AssetDatabase.SaveAssets();
Resources.UnloadUnusedAssets();
}
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Materials to 2D Renderer Materials", false)]
static void UpgradeSceneTo2DRenderer()
{
if (!EditorUtility.DisplayDialog("2D Renderer Upgrader", "The upgrade will change the material references of Sprite Renderers in currently open scene(s) to a lit material. You can't undo this operation. Make sure you save the scene(s) before proceeding.", "Proceed", "Cancel"))
return;
GameObject[] gameObjects = Object.FindObjectsOfType<GameObject>();
if (gameObjects != null && gameObjects.Length > 0)
{
foreach (GameObject go in gameObjects)
{
UpgradeGameObject(go);
}
}
}
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Materials to 2D Renderer Materials", true)]
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Materials to 2D Renderer Materials", true)]
static bool MenuValidation()
{
return Light2DEditorUtility.IsUsing2DRenderer();
}
public static void UpgradeParametricLightsInScene(bool prompt)
{
if (prompt)
{
if (!EditorUtility.DisplayDialog("Parametric Light Upgrader", "The upgrade will change all game objects which use Parametric Light2D to Freeform Light2D in currently open scene(s). You can't undo this operation. Make sure you save the scene(s) before proceeding.", "Proceed", "Cancel"))
return;
}
GameObject[] gameObjects = Object.FindObjectsOfType<GameObject>();
if (gameObjects != null && gameObjects.Length > 0)
{
foreach (GameObject go in gameObjects)
{
UpgradeObjectWithParametricLights(go);
}
}
}
public static void UpgradeParametricLightsInProject(bool prompt = true)
{
if (prompt)
{
if (!EditorUtility.DisplayDialog("Parametric Light Upgrader", "The upgrade will search for all prefabs in your project that use Parametric Light2D and change them to Freeform Light2D. You can't undo this operation. It's highly recommended to backup your project before proceeding.", "Proceed", "Cancel"))
return;
}
ProcessAssetDatabaseObjects<GameObject>("t: Prefab", UpgradeObjectWithParametricLights);
AssetDatabase.SaveAssets();
Resources.UnloadUnusedAssets();
}
// Set priority to get these to the bottom. Add test for pipeline enabled
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Project Parametric Lights to Freeform", false)]
public static void UpgradeParametricLightsInProject()
{
UpgradeParametricLightsInProject(true);
}
[MenuItem("Edit/Render Pipeline/Universal Render Pipeline/Upgrade Scene Parametric Lights to Freeform", false)]
public static void UpgradeParametricLightsInScene()
{
UpgradeParametricLightsInScene(true);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,147 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.Experimental.Rendering.Universal.Path2D;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
internal class ShadowCasterPath : ScriptablePath
{
internal Bounds GetBounds()
{
ShadowCaster2D shadowCaster = (ShadowCaster2D)owner;
Renderer m_Renderer = shadowCaster.GetComponent<Renderer>();
if (m_Renderer != null)
{
return m_Renderer.bounds;
}
else
{
Collider2D collider = shadowCaster.GetComponent<Collider2D>();
if (collider != null)
return collider.bounds;
}
return new Bounds(shadowCaster.transform.position, shadowCaster.transform.lossyScale);
}
public override void SetDefaultShape()
{
Clear();
Bounds bounds = GetBounds();
AddPoint(new ControlPoint(bounds.min));
AddPoint(new ControlPoint(new Vector3(bounds.min.x, bounds.max.y)));
AddPoint(new ControlPoint(bounds.max));
AddPoint(new ControlPoint(new Vector3(bounds.max.x, bounds.min.y)));
base.SetDefaultShape();
}
}
[CustomEditor(typeof(ShadowCaster2D))]
[CanEditMultipleObjects]
internal class ShadowCaster2DEditor : PathComponentEditor<ShadowCasterPath>
{
[EditorTool("Edit Shadow Caster Shape", typeof(ShadowCaster2D))]
class ShadowCaster2DShadowCasterShapeTool : ShadowCaster2DShapeTool {};
private static class Styles
{
public static GUIContent shadowMode = EditorGUIUtility.TrTextContent("Use Renderer Silhouette", "When this and Self Shadows are enabled, the Renderer's silhouette is considered part of the shadow. When this is enabled and Self Shadows disabled, the Renderer's silhouette is excluded from the shadow.");
public static GUIContent selfShadows = EditorGUIUtility.TrTextContent("Self Shadows", "When enabled, the Renderer casts shadows on itself.");
public static GUIContent castsShadows = EditorGUIUtility.TrTextContent("Casts Shadows", "Specifies if this renderer will cast shadows");
public static GUIContent sortingLayerPrefixLabel = EditorGUIUtility.TrTextContent("Target Sorting Layers", "Apply shadows to the specified sorting layers.");
}
SerializedProperty m_UseRendererSilhouette;
SerializedProperty m_CastsShadows;
SerializedProperty m_SelfShadows;
SerializedProperty m_ReceivesShadows;
SortingLayerDropDown m_SortingLayerDropDown;
public void OnEnable()
{
m_UseRendererSilhouette = serializedObject.FindProperty("m_UseRendererSilhouette");
m_SelfShadows = serializedObject.FindProperty("m_SelfShadows");
m_CastsShadows = serializedObject.FindProperty("m_CastsShadows");
m_SortingLayerDropDown = new SortingLayerDropDown();
m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
}
public void ShadowCaster2DSceneGUI()
{
ShadowCaster2D shadowCaster = target as ShadowCaster2D;
Transform t = shadowCaster.transform;
Vector3[] shape = shadowCaster.shapePath;
Handles.color = Color.white;
for (int i = 0; i < shape.Length - 1; ++i)
{
Handles.DrawAAPolyLine(4, new Vector3[] { t.TransformPoint(shape[i]), t.TransformPoint(shape[i + 1]) });
}
if (shape.Length > 1)
Handles.DrawAAPolyLine(4, new Vector3[] { t.TransformPoint(shape[shape.Length - 1]), t.TransformPoint(shape[0]) });
}
public void ShadowCaster2DInspectorGUI<T>() where T : ShadowCaster2DShapeTool
{
DoEditButton<T>(PathEditorToolContents.icon, "Edit Shape");
DoPathInspector<T>();
DoSnappingInspector<T>();
}
public void OnSceneGUI()
{
if (m_CastsShadows.boolValue)
ShadowCaster2DSceneGUI();
}
public bool HasRenderer()
{
if (targets != null)
{
for (int i = 0; i < targets.Length; i++)
{
ShadowCaster2D shadowCaster = (ShadowCaster2D)targets[i];
Renderer renderer = shadowCaster.GetComponent<Renderer>();
if (renderer != null)
return true;
}
}
return false;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
using (new EditorGUI.DisabledScope(!HasRenderer())) // Done to support multiedit
{
EditorGUILayout.PropertyField(m_UseRendererSilhouette, Styles.shadowMode);
}
EditorGUILayout.PropertyField(m_CastsShadows, Styles.castsShadows);
EditorGUILayout.PropertyField(m_SelfShadows, Styles.selfShadows);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.sortingLayerPrefixLabel, null);
if (m_CastsShadows.boolValue)
ShadowCaster2DInspectorGUI<ShadowCaster2DShadowCasterShapeTool>();
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.Rendering.Universal.Path2D;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
class ShadowCaster2DShapeTool : PathEditorTool<ShadowCasterPath>
{
const string k_ShapePath = "m_ShapePath";
protected override IShape GetShape(Object target)
{
return (target as ShadowCaster2D).shapePath.ToPolygon(false);
}
protected override void SetShape(ShadowCasterPath shapeEditor, SerializedObject serializedObject)
{
serializedObject.Update();
var pointsProperty = serializedObject.FindProperty(k_ShapePath);
pointsProperty.arraySize = shapeEditor.pointCount;
for (var i = 0; i < shapeEditor.pointCount; ++i)
pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
// This is untracked right now...
serializedObject.ApplyModifiedProperties();
ShadowCaster2D shadowCaster = target as ShadowCaster2D;
if (shadowCaster != null)
{
int hash = LightUtility.GetShapePathHash(shadowCaster.shapePath);
shadowCaster.shapePathHash = hash;
}
}
}
}

View File

@@ -0,0 +1,223 @@
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal static class BezierUtility
{
static Vector3[] s_TempPoints = new Vector3[3];
public static Vector3 BezierPoint(Vector3 startPosition, Vector3 startTangent, Vector3 endTangent, Vector3 endPosition, float t)
{
float s = 1.0f - t;
return startPosition * s * s * s + startTangent * s * s * t * 3.0f + endTangent * s * t * t * 3.0f + endPosition * t * t * t;
}
public static Vector3 ClosestPointOnCurve(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
{
Vector3 startToEnd = endPosition - startPosition;
Vector3 startToTangent = (startTangent - startPosition);
Vector3 endToTangent = (endTangent - endPosition);
float sqrError = 0.001f;
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
return ClosestPointToSegment(point, startPosition, endPosition, out t);
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
float leftStartT = 0f;
float leftEndT = 0.5f;
float rightStartT = 0.5f;
float rightEndT = 1f;
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
Vector3 pointLeft = ClosestPointOnCurveIterative(point, leftStartPosition, leftEndPosition, leftStartTangent, leftEndTangent, sqrError, ref leftStartT, ref leftEndT);
Vector3 pointRight = ClosestPointOnCurveIterative(point, rightStartPosition, rightEndPosition, rightStartTangent, rightEndTangent, sqrError, ref rightStartT, ref rightEndT);
if ((point - pointLeft).sqrMagnitude < (point - pointRight).sqrMagnitude)
{
t = leftStartT;
return pointLeft;
}
t = rightStartT;
return pointRight;
}
public static Vector3 ClosestPointOnCurveFast(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, out float t)
{
float sqrError = 0.001f;
float startT = 0f;
float endT = 1f;
Vector3 closestPoint = ClosestPointOnCurveIterative(point, startPosition, endPosition, startTangent, endTangent, sqrError, ref startT, ref endT);
t = startT;
return closestPoint;
}
private static Vector3 ClosestPointOnCurveIterative(Vector3 point, Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, float sqrError, ref float startT, ref float endT)
{
while ((startPosition - endPosition).sqrMagnitude > sqrError)
{
Vector3 startToEnd = endPosition - startPosition;
Vector3 startToTangent = (startTangent - startPosition);
Vector3 endToTangent = (endTangent - endPosition);
if (Colinear(startToTangent, startToEnd, sqrError) && Colinear(endToTangent, startToEnd, sqrError))
{
float t;
Vector3 closestPoint = ClosestPointToSegment(point, startPosition, endPosition, out t);
t *= (endT - startT);
startT += t;
endT -= t;
return closestPoint;
}
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
SplitBezier(0.5f, startPosition, endPosition, startTangent, endTangent,
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
s_TempPoints[0] = leftStartPosition;
s_TempPoints[1] = leftStartTangent;
s_TempPoints[2] = leftEndTangent;
float sqrDistanceLeft = SqrDistanceToPolyLine(point, s_TempPoints);
s_TempPoints[0] = rightEndPosition;
s_TempPoints[1] = rightEndTangent;
s_TempPoints[2] = rightStartTangent;
float sqrDistanceRight = SqrDistanceToPolyLine(point, s_TempPoints);
if (sqrDistanceLeft < sqrDistanceRight)
{
startPosition = leftStartPosition;
endPosition = leftEndPosition;
startTangent = leftStartTangent;
endTangent = leftEndTangent;
endT -= (endT - startT) * 0.5f;
}
else
{
startPosition = rightStartPosition;
endPosition = rightEndPosition;
startTangent = rightStartTangent;
endTangent = rightEndTangent;
startT += (endT - startT) * 0.5f;
}
}
return endPosition;
}
public static void SplitBezier(float t, Vector3 startPosition, Vector3 endPosition, Vector3 startRightTangent, Vector3 endLeftTangent,
out Vector3 leftStartPosition, out Vector3 leftEndPosition, out Vector3 leftStartTangent, out Vector3 leftEndTangent,
out Vector3 rightStartPosition, out Vector3 rightEndPosition, out Vector3 rightStartTangent, out Vector3 rightEndTangent)
{
Vector3 tangent0 = (startRightTangent - startPosition);
Vector3 tangent1 = (endLeftTangent - endPosition);
Vector3 tangentEdge = (endLeftTangent - startRightTangent);
Vector3 tangentPoint0 = startPosition + tangent0 * t;
Vector3 tangentPoint1 = endPosition + tangent1 * (1f - t);
Vector3 tangentEdgePoint = startRightTangent + tangentEdge * t;
Vector3 newTangent0 = tangentPoint0 + (tangentEdgePoint - tangentPoint0) * t;
Vector3 newTangent1 = tangentPoint1 + (tangentEdgePoint - tangentPoint1) * (1f - t);
Vector3 newTangentEdge = newTangent1 - newTangent0;
Vector3 bezierPoint = newTangent0 + newTangentEdge * t;
leftStartPosition = startPosition;
leftEndPosition = bezierPoint;
leftStartTangent = tangentPoint0;
leftEndTangent = newTangent0;
rightStartPosition = bezierPoint;
rightEndPosition = endPosition;
rightStartTangent = newTangent1;
rightEndTangent = tangentPoint1;
}
private static Vector3 ClosestPointToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd, out float t)
{
Vector3 relativePoint = point - segmentStart;
Vector3 segment = (segmentEnd - segmentStart);
Vector3 segmentDirection = segment.normalized;
float length = segment.magnitude;
float dot = Vector3.Dot(relativePoint, segmentDirection);
if (dot <= 0f)
dot = 0f;
else if (dot >= length)
dot = length;
t = dot / length;
return segmentStart + segment * t;
}
private static float SqrDistanceToPolyLine(Vector3 point, Vector3[] points)
{
float minDistance = float.MaxValue;
for (int i = 0; i < points.Length - 1; ++i)
{
float distance = SqrDistanceToSegment(point, points[i], points[i + 1]);
if (distance < minDistance)
minDistance = distance;
}
return minDistance;
}
private static float SqrDistanceToSegment(Vector3 point, Vector3 segmentStart, Vector3 segmentEnd)
{
Vector3 relativePoint = point - segmentStart;
Vector3 segment = (segmentEnd - segmentStart);
Vector3 segmentDirection = segment.normalized;
float length = segment.magnitude;
float dot = Vector3.Dot(relativePoint, segmentDirection);
if (dot <= 0f)
return (point - segmentStart).sqrMagnitude;
else if (dot >= length)
return (point - segmentEnd).sqrMagnitude;
return Vector3.Cross(relativePoint, segmentDirection).sqrMagnitude;
}
private static bool Colinear(Vector3 v1, Vector3 v2, float error = 0.0001f)
{
return Mathf.Abs(v1.x * v2.y - v1.y * v2.x + v1.x * v2.z - v1.z * v2.x + v1.y * v2.z - v1.z * v2.y) < error;
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal enum TangentMode
{
Linear = 0,
Continuous = 1,
Broken = 2
}
[Serializable]
internal struct TangentCache
{
public Vector3 leftTangent;
public Vector3 rightTangent;
}
[Serializable]
internal struct ControlPoint
{
public Vector3 position;
public Vector3 localLeftTangent;
public Vector3 localRightTangent;
public TangentMode tangentMode;
public TangentCache continuousCache;
public TangentCache brokenCache;
public bool mirrorLeft;
public ControlPoint(Vector3 pos)
{
position = pos;
localLeftTangent = Vector3.zero;
localRightTangent = Vector3.zero;
tangentMode = TangentMode.Linear;
continuousCache = default(TangentCache);
brokenCache = default(TangentCache);
mirrorLeft = false;
}
public Vector3 leftTangent
{
get { return localLeftTangent + position; }
set { localLeftTangent = value - position; }
}
public Vector3 rightTangent
{
get { return localRightTangent + position; }
set { localRightTangent = value - position; }
}
public void StoreTangents()
{
if (tangentMode == TangentMode.Continuous)
{
continuousCache.leftTangent = localLeftTangent;
continuousCache.rightTangent = localRightTangent;
}
else if (tangentMode == TangentMode.Broken)
{
brokenCache.leftTangent = localLeftTangent;
brokenCache.rightTangent = localRightTangent;
}
}
public void RestoreTangents()
{
if (tangentMode == TangentMode.Continuous)
{
localLeftTangent = continuousCache.leftTangent;
localRightTangent = continuousCache.rightTangent;
}
else if (tangentMode == TangentMode.Broken)
{
localLeftTangent = brokenCache.leftTangent;
localRightTangent = brokenCache.rightTangent;
}
}
}
}

View File

@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
[Serializable]
internal class EditablePath : IEditablePath
{
[SerializeField]
private ShapeType m_ShapeType;
[SerializeField]
private IndexedSelection m_Selection = new IndexedSelection();
[SerializeField]
private List<ControlPoint> m_ControlPoints = new List<ControlPoint>();
[SerializeField]
private bool m_IsOpenEnded;
private Matrix4x4 m_LocalToWorldMatrix = Matrix4x4.identity;
private Matrix4x4 m_WorldToLocalMatrix = Matrix4x4.identity;
private Vector3 m_Forward = Vector3.forward;
private Vector3 m_Up = Vector3.up;
private Vector3 m_Right = Vector3.right;
public ShapeType shapeType
{
get { return m_ShapeType; }
set { m_ShapeType = value; }
}
public IUndoObject undoObject { get; set; }
public Matrix4x4 localToWorldMatrix
{
get { return m_LocalToWorldMatrix; }
set
{
m_LocalToWorldMatrix = value;
m_WorldToLocalMatrix = value.inverse;
}
}
public Vector3 forward
{
get { return m_Forward; }
set { m_Forward = value; }
}
public Vector3 up
{
get { return m_Up; }
set { m_Up = value; }
}
public Vector3 right
{
get { return m_Right; }
set { m_Right = value; }
}
public Matrix4x4 worldToLocalMatrix
{
get { return m_WorldToLocalMatrix; }
}
public bool isOpenEnded
{
get
{
if (pointCount < 3)
return true;
return m_IsOpenEnded;
}
set { m_IsOpenEnded = value; }
}
public ISelection<int> selection
{
get { return m_Selection; }
}
public int pointCount
{
get { return m_ControlPoints.Count; }
}
public ControlPoint GetPoint(int index)
{
return TransformPoint(localToWorldMatrix, m_ControlPoints[index]);
}
public void SetPoint(int index, ControlPoint controlPoint)
{
m_ControlPoints[index] = TransformPoint(worldToLocalMatrix, controlPoint);
}
public void AddPoint(ControlPoint controlPoint)
{
m_ControlPoints.Insert(pointCount, TransformPoint(worldToLocalMatrix, controlPoint));
}
public void InsertPoint(int index, ControlPoint controlPoint)
{
m_ControlPoints.Insert(index, TransformPoint(worldToLocalMatrix, controlPoint));
}
public void RemovePoint(int index)
{
m_ControlPoints.RemoveAt(index);
}
public void Clear()
{
m_ControlPoints.Clear();
}
private ControlPoint TransformPoint(Matrix4x4 transformMatrix, ControlPoint controlPoint)
{
if (transformMatrix == Matrix4x4.identity)
return controlPoint;
var newControlPoint = new ControlPoint()
{
position = transformMatrix.MultiplyPoint3x4(controlPoint.position),
tangentMode = controlPoint.tangentMode,
continuousCache = controlPoint.continuousCache,
brokenCache = controlPoint.brokenCache,
mirrorLeft = controlPoint.mirrorLeft
};
newControlPoint.rightTangent = transformMatrix.MultiplyPoint3x4(controlPoint.rightTangent);
newControlPoint.leftTangent = transformMatrix.MultiplyPoint3x4(controlPoint.leftTangent);
return newControlPoint;
}
public bool Select(ISelector<Vector3> selector)
{
var changed = false;
for (var i = 0; i < pointCount; ++i)
changed |= selection.Select(i, selector.Select(GetPoint(i).position));
return changed;
}
public virtual void SetDefaultShape()
{
}
}
}

View File

@@ -0,0 +1,264 @@
using System;
using System.Linq;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class EditablePathController : IEditablePathController
{
private ISnapping<Vector3> m_Snapping = new Snapping();
public IEditablePath editablePath { get; set; }
public IEditablePath closestEditablePath { get { return editablePath; } }
public ISnapping<Vector3> snapping
{
get { return m_Snapping; }
set { m_Snapping = value; }
}
public bool enableSnapping { get; set; }
public void RegisterUndo(string name)
{
if (editablePath.undoObject != null)
editablePath.undoObject.RegisterUndo(name);
}
public void ClearSelection()
{
editablePath.selection.Clear();
}
public void SelectPoint(int index, bool select)
{
editablePath.selection.Select(index, select);
}
public void CreatePoint(int index, Vector3 position)
{
ClearSelection();
if (editablePath.shapeType == ShapeType.Polygon)
{
editablePath.InsertPoint(index + 1, new ControlPoint() { position = position });
}
else if (editablePath.shapeType == ShapeType.Spline)
{
var nextIndex = NextIndex(index);
var currentPoint = editablePath.GetPoint(index);
var nextPoint = editablePath.GetPoint(nextIndex);
float t;
var closestPoint = BezierUtility.ClosestPointOnCurve(
position,
currentPoint.position,
nextPoint.position,
GetRightTangentPosition(index),
GetLeftTangentPosition(nextIndex),
out t);
Vector3 leftStartPosition;
Vector3 leftEndPosition;
Vector3 leftStartTangent;
Vector3 leftEndTangent;
Vector3 rightStartPosition;
Vector3 rightEndPosition;
Vector3 rightStartTangent;
Vector3 rightEndTangent;
BezierUtility.SplitBezier(t, currentPoint.position, nextPoint.position, GetRightTangentPosition(index), GetLeftTangentPosition(nextIndex),
out leftStartPosition, out leftEndPosition, out leftStartTangent, out leftEndTangent,
out rightStartPosition, out rightEndPosition, out rightStartTangent, out rightEndTangent);
var newPointIndex = index + 1;
var newPoint = new ControlPoint()
{
position = closestPoint,
leftTangent = leftEndTangent,
rightTangent = rightStartTangent,
tangentMode = TangentMode.Continuous
};
currentPoint.rightTangent = leftStartTangent;
nextPoint.leftTangent = rightEndTangent;
if (currentPoint.tangentMode == TangentMode.Linear && nextPoint.tangentMode == TangentMode.Linear)
{
newPoint.tangentMode = TangentMode.Linear;
newPoint.localLeftTangent = Vector3.zero;
newPoint.localRightTangent = Vector3.zero;
currentPoint.localRightTangent = Vector3.zero;
nextPoint.localLeftTangent = Vector3.zero;
}
else
{
if (currentPoint.tangentMode == TangentMode.Linear)
currentPoint.tangentMode = TangentMode.Broken;
if (nextPoint.tangentMode == TangentMode.Linear)
nextPoint.tangentMode = TangentMode.Broken;
}
editablePath.SetPoint(index, currentPoint);
editablePath.SetPoint(nextIndex, nextPoint);
editablePath.InsertPoint(newPointIndex, newPoint);
}
}
public void RemoveSelectedPoints()
{
var minPointCount = editablePath.isOpenEnded ? 2 : 3;
int pointsCountToRemove = editablePath.selection.Count;
if (editablePath.pointCount != pointsCountToRemove)
{
var indices = editablePath.selection.elements.OrderByDescending(i => i);
foreach (var index in indices)
{
if (editablePath.pointCount > minPointCount)
{
editablePath.RemovePoint(index);
}
}
ClearSelection();
}
else
{
editablePath.SetDefaultShape();
ClearSelection();
}
}
public void MoveSelectedPoints(Vector3 delta)
{
delta = Vector3.ProjectOnPlane(delta, editablePath.forward);
for (var i = 0; i < editablePath.pointCount; ++i)
{
if (editablePath.selection.Contains(i))
{
var controlPoint = editablePath.GetPoint(i);
controlPoint.position += delta;
editablePath.SetPoint(i, controlPoint);
}
}
}
public void MoveEdge(int index, Vector3 delta)
{
if (editablePath.isOpenEnded && index == editablePath.pointCount - 1)
return;
var controlPoint = editablePath.GetPoint(index);
controlPoint.position += delta;
editablePath.SetPoint(index, controlPoint);
controlPoint = NextControlPoint(index);
controlPoint.position += delta;
editablePath.SetPoint(NextIndex(index), controlPoint);
}
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent)
{
var controlPoint = editablePath.GetPoint(index);
controlPoint.leftTangent = position;
controlPoint.mirrorLeft = false;
if (setToLinear)
{
controlPoint.leftTangent = controlPoint.position;
controlPoint.rightTangent = cachedRightTangent;
}
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
{
var magnitude = controlPoint.localRightTangent.magnitude;
if (mirror)
magnitude = controlPoint.localLeftTangent.magnitude;
controlPoint.localRightTangent = magnitude * -controlPoint.localLeftTangent.normalized;
}
editablePath.SetPoint(index, controlPoint);
}
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent)
{
var controlPoint = editablePath.GetPoint(index);
controlPoint.rightTangent = position;
controlPoint.mirrorLeft = true;
if (setToLinear)
{
controlPoint.rightTangent = controlPoint.position;
controlPoint.leftTangent = cachedLeftTangent;
}
else if (controlPoint.tangentMode == TangentMode.Continuous || mirror)
{
var magnitude = controlPoint.localLeftTangent.magnitude;
if (mirror)
magnitude = controlPoint.localRightTangent.magnitude;
controlPoint.localLeftTangent = magnitude * -controlPoint.localRightTangent.normalized;
}
editablePath.SetPoint(index, controlPoint);
}
public void ClearClosestPath() {}
public void AddClosestPath(float distance) {}
private Vector3 GetLeftTangentPosition(int index)
{
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localLeftTangent.sqrMagnitude, 0f);
if (isLinear)
{
var position = editablePath.GetPoint(index).position;
var prevPosition = PrevControlPoint(index).position;
return (1f / 3f) * (prevPosition - position) + position;
}
return editablePath.GetPoint(index).leftTangent;
}
private Vector3 GetRightTangentPosition(int index)
{
var isLinear = Mathf.Approximately(editablePath.GetPoint(index).localRightTangent.sqrMagnitude, 0f);
if (isLinear)
{
var position = editablePath.GetPoint(index).position;
var nextPosition = NextControlPoint(index).position;
return (1f / 3f) * (nextPosition - position) + position;
}
return editablePath.GetPoint(index).rightTangent;
}
private int NextIndex(int index)
{
return EditablePathUtility.Mod(index + 1, editablePath.pointCount);
}
private ControlPoint NextControlPoint(int index)
{
return editablePath.GetPoint(NextIndex(index));
}
private int PrevIndex(int index)
{
return EditablePathUtility.Mod(index - 1, editablePath.pointCount);
}
private ControlPoint PrevControlPoint(int index)
{
return editablePath.GetPoint(PrevIndex(index));
}
}
}

View File

@@ -0,0 +1,275 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal static class EditablePathExtensions
{
public static Polygon ToPolygon(this IEditablePath path)
{
var polygon = new Polygon()
{
isOpenEnded = path.isOpenEnded,
points = new Vector3[path.pointCount]
};
for (var i = 0; i < path.pointCount; ++i)
polygon.points[i] = path.GetPoint(i).position;
return polygon;
}
public static Spline ToSpline(this IEditablePath path)
{
var count = path.pointCount * 3;
if (path.isOpenEnded)
count -= 2;
var spline = new Spline()
{
isOpenEnded = path.isOpenEnded,
points = new Vector3[count]
};
for (var i = 0; i < path.pointCount; ++i)
{
var point = path.GetPoint(i);
spline.points[i * 3] = point.position;
if (i * 3 + 1 < count)
{
var nextIndex = EditablePathUtility.Mod(i + 1, path.pointCount);
spline.points[i * 3 + 1] = path.CalculateRightTangent(i);
spline.points[i * 3 + 2] = path.CalculateLeftTangent(nextIndex);
}
}
return spline;
}
public static Vector3 CalculateLocalLeftTangent(this IEditablePath path, int index)
{
return path.CalculateLeftTangent(index) - path.GetPoint(index).position;
}
public static Vector3 CalculateLeftTangent(this IEditablePath path, int index)
{
var point = path.GetPoint(index);
var isTangentLinear = point.localLeftTangent == Vector3.zero;
var isEndpoint = path.isOpenEnded && index == 0;
var tangent = point.leftTangent;
if (isEndpoint)
return point.position;
if (isTangentLinear)
{
var prevPoint = path.GetPrevPoint(index);
var v = prevPoint.position - point.position;
tangent = point.position + v.normalized * (v.magnitude / 3f);
}
return tangent;
}
public static Vector3 CalculateLocalRightTangent(this IEditablePath path, int index)
{
return path.CalculateRightTangent(index) - path.GetPoint(index).position;
}
public static Vector3 CalculateRightTangent(this IEditablePath path, int index)
{
var point = path.GetPoint(index);
var isTangentLinear = point.localRightTangent == Vector3.zero;
var isEndpoint = path.isOpenEnded && index == path.pointCount - 1;
var tangent = point.rightTangent;
if (isEndpoint)
return point.position;
if (isTangentLinear)
{
var nextPoint = path.GetNextPoint(index);
var v = nextPoint.position - point.position;
tangent = point.position + v.normalized * (v.magnitude / 3f);
}
return tangent;
}
public static ControlPoint GetPrevPoint(this IEditablePath path, int index)
{
return path.GetPoint(EditablePathUtility.Mod(index - 1, path.pointCount));
}
public static ControlPoint GetNextPoint(this IEditablePath path, int index)
{
return path.GetPoint(EditablePathUtility.Mod(index + 1, path.pointCount));
}
public static void UpdateTangentMode(this IEditablePath path, int index)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
var isLeftTangentLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightTangentLinear = controlPoint.localRightTangent == Vector3.zero;
if (isLeftTangentLinear && isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Linear;
else if (isLeftTangentLinear || isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Broken;
else if (controlPoint.tangentMode != TangentMode.Continuous)
controlPoint.tangentMode = TangentMode.Broken;
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
path.localToWorldMatrix = localToWorldMatrix;
}
public static void UpdateTangentsFromMode(this IEditablePath path)
{
const float kEpsilon = 0.001f;
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
for (var i = 0; i < path.pointCount; ++i)
{
var controlPoint = path.GetPoint(i);
if (controlPoint.tangentMode == TangentMode.Linear)
{
controlPoint.localLeftTangent = Vector3.zero;
controlPoint.localRightTangent = Vector3.zero;
}
else if (controlPoint.tangentMode == TangentMode.Broken)
{
var isLeftEndpoint = path.isOpenEnded && i == 0;
var prevPoint = path.GetPrevPoint(i);
var nextPoint = path.GetNextPoint(i);
var liniarLeftPosition = (prevPoint.position - controlPoint.position) / 3f;
var isLeftTangentLinear = isLeftEndpoint || (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < kEpsilon;
if (isLeftTangentLinear)
controlPoint.localLeftTangent = Vector3.zero;
var isRightEndpoint = path.isOpenEnded && i == path.pointCount - 1;
var liniarRightPosition = (nextPoint.position - controlPoint.position) / 3f;
var isRightTangentLinear = isRightEndpoint || (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < kEpsilon;
if (isRightTangentLinear)
controlPoint.localRightTangent = Vector3.zero;
if (isLeftTangentLinear && isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Linear;
}
else if (controlPoint.tangentMode == TangentMode.Continuous)
{
//TODO: ensure tangent continuity
}
controlPoint.StoreTangents();
path.SetPoint(i, controlPoint);
}
path.localToWorldMatrix = localToWorldMatrix;
}
public static void SetTangentMode(this IEditablePath path, int index, TangentMode tangentMode)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
var isEndpoint = path.isOpenEnded && (index == 0 || index == path.pointCount - 1);
var oldTangentMode = controlPoint.tangentMode;
controlPoint.tangentMode = tangentMode;
controlPoint.RestoreTangents();
if (tangentMode == TangentMode.Linear)
{
controlPoint.localLeftTangent = Vector3.zero;
controlPoint.localRightTangent = Vector3.zero;
}
else if (tangentMode == TangentMode.Continuous && !isEndpoint)
{
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) < 0.001f;
var isLinear = isLeftLinear && isRightLinear;
if ((isLinear || oldTangentMode == TangentMode.Broken) && !isContinous)
{
var prevPoint = path.GetPrevPoint(index);
var nextPoint = path.GetNextPoint(index);
var vLeft = prevPoint.position - controlPoint.position;
var vRight = nextPoint.position - controlPoint.position;
var rightDirection = Vector3.Cross(Vector3.Cross(vLeft, vRight), vLeft.normalized + vRight.normalized).normalized;
var scale = 1f / 3f;
if (isLeftLinear)
controlPoint.localLeftTangent = vLeft.magnitude * scale * -rightDirection;
else
controlPoint.localLeftTangent = controlPoint.localLeftTangent.magnitude * -rightDirection;
if (isRightLinear)
controlPoint.localRightTangent = vRight.magnitude * scale * rightDirection;
else
controlPoint.localRightTangent = controlPoint.localRightTangent.magnitude * rightDirection;
}
}
else
{
var isLeftLinear = controlPoint.localLeftTangent == Vector3.zero;
var isRightLinear = controlPoint.localRightTangent == Vector3.zero;
if (isLeftLinear || isRightLinear)
{
if (isLeftLinear)
controlPoint.localLeftTangent = path.CalculateLocalLeftTangent(index);
if (isRightLinear)
controlPoint.localRightTangent = path.CalculateLocalRightTangent(index);
}
}
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
path.localToWorldMatrix = localToWorldMatrix;
}
public static void MirrorTangent(this IEditablePath path, int index)
{
var localToWorldMatrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
var controlPoint = path.GetPoint(index);
if (controlPoint.tangentMode == TangentMode.Linear)
return;
if (!Mathf.Approximately((controlPoint.localLeftTangent + controlPoint.localRightTangent).sqrMagnitude, 0f))
{
if (controlPoint.mirrorLeft)
controlPoint.localLeftTangent = -controlPoint.localRightTangent;
else
controlPoint.localRightTangent = -controlPoint.localLeftTangent;
controlPoint.StoreTangents();
path.SetPoint(index, controlPoint);
}
path.localToWorldMatrix = localToWorldMatrix;
}
}
}

View File

@@ -0,0 +1,14 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class EditablePathUtility
{
public static int Mod(int x, int m)
{
int r = x % m;
return r < 0 ? r + m : r;
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface IEditablePath : ISelectable<Vector3>
{
ShapeType shapeType { get; set; }
IUndoObject undoObject { get; set; }
ISelection<int> selection { get; }
Matrix4x4 localToWorldMatrix { get; set; }
Vector3 forward { get; set; }
Vector3 up { get; set; }
Vector3 right { get; set; }
bool isOpenEnded { get; set; }
int pointCount { get; }
ControlPoint GetPoint(int index);
void SetPoint(int index, ControlPoint controlPoint);
void AddPoint(ControlPoint controlPoint);
void InsertPoint(int index, ControlPoint controlPoint);
void RemovePoint(int index);
void Clear();
void SetDefaultShape();
}
}

View File

@@ -0,0 +1,24 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface IEditablePathController
{
IEditablePath editablePath { get; set; }
IEditablePath closestEditablePath { get; }
ISnapping<Vector3> snapping { get; set; }
bool enableSnapping { get; set; }
void RegisterUndo(string name);
void ClearSelection();
void SelectPoint(int index, bool select);
void CreatePoint(int index, Vector3 position);
void RemoveSelectedPoints();
void MoveSelectedPoints(Vector3 delta);
void MoveEdge(int index, Vector3 delta);
void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent);
void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent);
void ClearClosestPath();
void AddClosestPath(float distance);
}
}

View File

@@ -0,0 +1,10 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface ISnapping<T>
{
T Snap(T value);
}
}

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface IUndoObject
{
void RegisterUndo(string name);
}
}

View File

@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class MultipleEditablePathController : IEditablePathController
{
private IEditablePathController m_Controller = new EditablePathController();
private List<IEditablePath> m_Paths = new List<IEditablePath>();
private float m_ClosestDistance = float.MaxValue;
private IEditablePath m_ClosestPath;
public IEditablePath editablePath
{
get { return m_Controller.editablePath; }
set { m_Controller.editablePath = value; }
}
public IEditablePath closestEditablePath { get; private set; }
public ISnapping<Vector3> snapping
{
get { return m_Controller.snapping; }
set { m_Controller.snapping = value; }
}
public bool enableSnapping
{
get { return m_Controller.enableSnapping; }
set { m_Controller.enableSnapping = value; }
}
public void ClearPaths()
{
m_Paths.Clear();
}
public void AddPath(IEditablePath path)
{
if (!m_Paths.Contains(path))
m_Paths.Add(path);
}
public void RemovePath(IEditablePath path)
{
m_Paths.Remove(path);
}
public void RegisterUndo(string name)
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.RegisterUndo(name);
});
editablePath = current;
}
public void ClearSelection()
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.ClearSelection();
});
editablePath = current;
}
public void SelectPoint(int index, bool select)
{
m_Controller.SelectPoint(index, select);
}
public void CreatePoint(int index, Vector3 position)
{
m_Controller.CreatePoint(index, position);
}
public void RemoveSelectedPoints()
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.RemoveSelectedPoints();
});
editablePath = current;
}
public void MoveSelectedPoints(Vector3 delta)
{
var current = editablePath;
ForEach((s) =>
{
editablePath = s;
m_Controller.MoveSelectedPoints(delta);
});
editablePath = current;
}
public void MoveEdge(int index, Vector3 delta)
{
m_Controller.MoveEdge(index, delta);
}
public void SetLeftTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedRightTangent)
{
m_Controller.SetLeftTangent(index, position, setToLinear, mirror, cachedRightTangent);
}
public void SetRightTangent(int index, Vector3 position, bool setToLinear, bool mirror, Vector3 cachedLeftTangent)
{
m_Controller.SetRightTangent(index, position, setToLinear, mirror, cachedLeftTangent);
}
public void ClearClosestPath()
{
m_ClosestDistance = float.MaxValue;
closestEditablePath = null;
}
public void AddClosestPath(float distance)
{
if (distance <= m_ClosestDistance)
{
m_ClosestDistance = distance;
closestEditablePath = editablePath;
}
}
private void ForEach(Action<IEditablePath> action)
{
foreach (var path in m_Paths)
{
if (path == null)
continue;
action(path);
}
}
}
}

View File

@@ -0,0 +1,21 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class Snapping : ISnapping<Vector3>
{
public Vector3 Snap(Vector3 position)
{
return new Vector3(
Snap(position.x, EditorPrefs.GetFloat("MoveSnapX", 1f)),
Snap(position.y, EditorPrefs.GetFloat("MoveSnapY", 1f)),
position.z);
}
private float Snap(float value, float snap)
{
return Mathf.Round(value / snap) * snap;
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class GenericScriptablePath<T> : ScriptablePath
{
[SerializeField]
private List<T> m_Data = new List<T>();
public T[] data
{
get { return m_Data.ToArray(); }
set
{
if (value.Length != pointCount)
throw new Exception("Custom data count does not match control point count");
m_Data.Clear();
m_Data.AddRange(value);
}
}
public override void Clear()
{
base.Clear();
m_Data.Clear();
}
public override void AddPoint(ControlPoint controlPoint)
{
base.AddPoint(controlPoint);
m_Data.Add(Create());
}
public override void InsertPoint(int index, ControlPoint controlPoint)
{
base.InsertPoint(index, controlPoint);
m_Data.Insert(index, Create());
}
public override void RemovePoint(int index)
{
base.RemovePoint(index);
Destroy(m_Data[index]);
m_Data.RemoveAt(index);
}
public T GetData(int index)
{
return m_Data[index];
}
public void SetData(int index, T data)
{
m_Data[index] = data;
}
protected virtual T Create()
{
return Activator.CreateInstance<T>();
}
protected virtual void Destroy(T data) {}
}
}

View File

@@ -0,0 +1,120 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class GenericScriptablePathInspector<U, T> : ScriptablePathInspector where U : ScriptableData<T>
{
private List<U> m_DataObjects = new List<U>();
private List<U> m_SelectedDataObjects = new List<U>();
private Editor m_CachedEditor = null;
private void OnEnable()
{
PrepareDataObjects();
}
private void OnDestroy()
{
DestroyDataObjects();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
DoCustomDataInspector();
}
protected void DoCustomDataInspector()
{
PrepareDataObjects();
if (m_SelectedDataObjects.Count > 0)
{
CreateCachedEditor(m_SelectedDataObjects.ToArray(), null, ref m_CachedEditor);
EditorGUI.BeginChangeCheck();
m_CachedEditor.OnInspectorGUI();
if (EditorGUI.EndChangeCheck())
SetDataObjects();
}
}
private void PrepareDataObjects()
{
var elementCount = 0;
m_SelectedDataObjects.Clear();
foreach (var path in paths)
elementCount += path.pointCount;
while (m_DataObjects.Count < elementCount)
CreateDataObject();
var index = 0;
foreach (var path in paths)
{
var genericPath = path as GenericScriptablePath<T>;
var customDataArray = genericPath.data;
var length = customDataArray.Length;
for (var i = 0; i < length; ++i)
{
var dataObject = m_DataObjects[index + i];
dataObject.data = customDataArray[i];
if (path.selection.Contains(i))
{
dataObject.owner = path.owner;
dataObject.index = i;
m_SelectedDataObjects.Add(dataObject);
}
}
index += length;
}
}
private void SetDataObjects()
{
var index = 0;
foreach (var path in paths)
{
var genericPath = path as GenericScriptablePath<T>;
var customDataArray = genericPath.data;
var length = customDataArray.Length;
for (var i = 0; i < length; ++i)
customDataArray[i] = m_DataObjects[index + i].data;
genericPath.data = customDataArray;
index += length;
}
}
private U CreateDataObject()
{
var dataObject = ScriptableObject.CreateInstance<U>();
m_DataObjects.Add(dataObject);
return dataObject;
}
private void DestroyDataObjects()
{
foreach (var customDataObject in m_DataObjects)
DestroyImmediate(customDataObject);
m_DataObjects.Clear();
m_SelectedDataObjects.Clear();
}
}
}

View File

@@ -0,0 +1,121 @@
#pragma warning disable 0618
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.Experimental.Rendering.Universal.Path2D;
#if !UNITY_2020_2_OR_NEWER
using ToolManager = UnityEditor.EditorTools.EditorTools;
#endif
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal abstract class PathComponentEditor<T> : Editor where T : ScriptablePath
{
private static class Contents
{
public static readonly GUIContent snappingLabel = new GUIContent("Snapping", "Snap points using the snap settings");
}
private Editor m_CachedEditor = null;
protected void DoEditButton<U>(GUIContent icon, string label) where U : PathEditorTool<T>
{
const float kButtonWidth = 33;
const float kButtonHeight = 23;
const float k_SpaceBetweenLabelAndButton = 5;
var buttonStyle = new GUIStyle("EditModeSingleButton");
var rect = EditorGUILayout.GetControlRect(true, kButtonHeight, buttonStyle);
var buttonRect = new Rect(rect.xMin + EditorGUIUtility.labelWidth, rect.yMin, kButtonWidth, kButtonHeight);
var labelContent = new GUIContent(label);
var labelSize = GUI.skin.label.CalcSize(labelContent);
var labelRect = new Rect(
buttonRect.xMax + k_SpaceBetweenLabelAndButton,
rect.yMin + (rect.height - labelSize.y) * .5f,
labelSize.x,
rect.height);
using (new EditorGUI.DisabledGroupScope(!EditorToolManager.IsAvailable<U>()))
{
using (var check = new EditorGUI.ChangeCheckScope())
{
var isActive = GUI.Toggle(buttonRect, EditorToolManager.IsActiveTool<U>(), icon, buttonStyle);
GUI.Label(labelRect, label);
if (check.changed)
{
if (isActive)
ToolManager.SetActiveTool<U>();
else
ToolManager.RestorePreviousTool();
}
}
}
}
protected void DoPathInspector<U>() where U : PathEditorTool<T>
{
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
{
var paths = EditorToolManager.GetEditorTool<U>().paths;
CreateCachedEditor(paths, null, ref m_CachedEditor);
if (m_CachedEditor == null) //Needed to avoid a nullref on exiting playmode
return;
using (var check = new EditorGUI.ChangeCheckScope())
{
m_CachedEditor.OnInspectorGUI();
if (check.changed)
EditorToolManager.GetEditorTool<U>().SetShapes();
}
}
}
protected void DoSnappingInspector<U>() where U : PathEditorTool<T>
{
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
{
var tool = EditorToolManager.GetEditorTool<U>();
tool.enableSnapping = EditorGUILayout.Toggle(Contents.snappingLabel, tool.enableSnapping);
}
}
protected void DoOpenEndedInspector<U>(SerializedProperty isOpenEndedProperty) where U : PathEditorTool<T>
{
serializedObject.Update();
using (var check = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.PropertyField(isOpenEndedProperty);
if (check.changed)
{
if (EditorToolManager.IsActiveTool<U>() && EditorToolManager.IsAvailable<U>())
{
var paths = EditorToolManager.GetEditorTool<U>().paths;
foreach (var path in paths)
{
path.undoObject.RegisterUndo("Set Open Ended");
path.isOpenEnded = isOpenEndedProperty.boolValue;
}
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
}
#pragma warning restore 0618

View File

@@ -0,0 +1,499 @@
#pragma warning disable 0618
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
using UnityObject = UnityEngine.Object;
#if !UNITY_2020_2_OR_NEWER
using ToolManager = UnityEditor.EditorTools.EditorTools;
#endif
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal static class PathEditorToolContents
{
internal static readonly GUIContent shapeToolIcon = IconContent("ShapeTool", "Unlocks the shape to allow editing in the Scene View.");
internal static readonly GUIContent shapeToolPro = IconContent("ShapeToolPro", "Unlocks the shape to allow editing in the Scene View.");
internal static GUIContent IconContent(string name, string tooltip = null)
{
return new GUIContent(Resources.Load<Texture>(name), tooltip);
}
public static GUIContent icon
{
get
{
if (EditorGUIUtility.isProSkin)
return shapeToolPro;
return shapeToolIcon;
}
}
}
internal interface IDuringSceneGuiTool
{
void DuringSceneGui(SceneView sceneView);
bool IsAvailable();
}
[InitializeOnLoad]
internal class EditorToolManager
{
private static List<IDuringSceneGuiTool> m_Tools = new List<IDuringSceneGuiTool>();
static EditorToolManager()
{
SceneView.duringSceneGui += DuringSceneGui;
}
internal static void Add(IDuringSceneGuiTool tool)
{
if (!m_Tools.Contains(tool) && tool is EditorTool)
m_Tools.Add(tool);
}
internal static void Remove(IDuringSceneGuiTool tool)
{
if (m_Tools.Contains(tool))
m_Tools.Remove(tool);
}
internal static bool IsActiveTool<T>() where T : EditorTool
{
return ToolManager.activeToolType.Equals(typeof(T));
}
internal static bool IsAvailable<T>() where T : EditorTool
{
var tool = GetEditorTool<T>();
if (tool != null)
return tool.IsAvailable();
return false;
}
internal static T GetEditorTool<T>() where T : EditorTool
{
foreach (var tool in m_Tools)
{
if (tool.GetType().Equals(typeof(T)))
return tool as T;
}
return null;
}
private static void DuringSceneGui(SceneView sceneView)
{
foreach (var tool in m_Tools)
{
if (tool.IsAvailable() && ToolManager.IsActiveTool(tool as EditorTool))
tool.DuringSceneGui(sceneView);
}
}
}
internal abstract class PathEditorTool<T> : EditorTool, IDuringSceneGuiTool where T : ScriptablePath
{
private Dictionary<UnityObject, T> m_Paths = new Dictionary<UnityObject, T>();
private IGUIState m_GUIState = new GUIState();
private Dictionary<UnityObject, GUISystem> m_GUISystems = new Dictionary<UnityObject, GUISystem>();
private Dictionary<UnityObject, SerializedObject> m_SerializedObjects = new Dictionary<UnityObject, SerializedObject>();
private MultipleEditablePathController m_Controller = new MultipleEditablePathController();
private PointRectSelector m_RectSelector = new PointRectSelector();
private bool m_IsActive = false;
internal T[] paths
{
get { return m_Paths.Values.ToArray(); }
}
public bool enableSnapping
{
get { return m_Controller.enableSnapping; }
set { m_Controller.enableSnapping = value; }
}
public override GUIContent toolbarIcon
{
get { return PathEditorToolContents.icon; }
}
public override bool IsAvailable()
{
return targets.Count() > 0;
}
public T GetPath(UnityObject targetObject)
{
var path = default(T);
m_Paths.TryGetValue(targetObject, out path);
return path;
}
public void SetPath(UnityObject target)
{
var path = GetPath(target);
path.localToWorldMatrix = Matrix4x4.identity;
var undoName = Undo.GetCurrentGroupName();
var serializedObject = GetSerializedObject(target);
serializedObject.UpdateIfRequiredOrScript();
SetShape(path, serializedObject);
Undo.SetCurrentGroupName(undoName);
}
private void RepaintInspectors()
{
var editorWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
foreach (var editorWindow in editorWindows)
{
if (editorWindow.titleContent.text == "Inspector")
editorWindow.Repaint();
}
}
private void OnEnable()
{
m_IsActive = false;
EditorToolManager.Add(this);
SetupRectSelector();
HandleActivation();
ToolManager.activeToolChanged += HandleActivation;
}
private void OnDestroy()
{
EditorToolManager.Remove(this);
ToolManager.activeToolChanged -= HandleActivation;
UnregisterCallbacks();
}
private void HandleActivation()
{
if (m_IsActive == false && ToolManager.IsActiveTool(this))
Activate();
else if (m_IsActive)
Deactivate();
}
private void Activate()
{
m_IsActive = true;
RegisterCallbacks();
InitializeCache();
OnActivate();
}
private void Deactivate()
{
OnDeactivate();
DestroyCache();
UnregisterCallbacks();
m_IsActive = false;
}
private void RegisterCallbacks()
{
UnregisterCallbacks();
Selection.selectionChanged += SelectionChanged;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
Undo.undoRedoPerformed += UndoRedoPerformed;
}
private void UnregisterCallbacks()
{
Selection.selectionChanged -= SelectionChanged;
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
Undo.undoRedoPerformed -= UndoRedoPerformed;
}
private void DestroyCache()
{
foreach (var pair in m_Paths)
{
var path = pair.Value;
if (path != null)
{
Undo.ClearUndo(path);
UnityObject.DestroyImmediate(path);
}
}
m_Paths.Clear();
m_Controller.ClearPaths();
m_GUISystems.Clear();
m_SerializedObjects.Clear();
}
private void UndoRedoPerformed()
{
ForEachTarget((target) =>
{
var path = GetPath(target);
if (!path.modified)
InitializePath(target);
});
}
private void SelectionChanged()
{
InitializeCache();
}
private void PlayModeStateChanged(PlayModeStateChange stateChange)
{
if (stateChange == PlayModeStateChange.EnteredEditMode)
EditorApplication.delayCall += () => { InitializeCache(); }; //HACK: At this point target is null. Let's wait to next frame to refresh.
}
private void SetupRectSelector()
{
m_RectSelector.onSelectionBegin = BeginSelection;
m_RectSelector.onSelectionChanged = UpdateSelection;
m_RectSelector.onSelectionEnd = EndSelection;
}
private void ForEachTarget(Action<UnityObject> action)
{
foreach (var target in targets)
{
if (target == null)
continue;
action(target);
}
}
private void InitializeCache()
{
m_Controller.ClearPaths();
ForEachTarget((target) =>
{
var path = GetOrCreatePath(target);
var pointCount = path.pointCount;
InitializePath(target);
if (pointCount != path.pointCount)
path.selection.Clear();
CreateGUISystem(target);
m_Controller.AddPath(path);
});
}
private void InitializePath(UnityObject target)
{
IShape shape = null;
ControlPoint[] controlPoints = null;
try
{
shape = GetShape(target);
controlPoints = shape.ToControlPoints();
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
var path = GetPath(target);
path.Clear();
if (shape != null && controlPoints != null)
{
path.localToWorldMatrix = Matrix4x4.identity;
path.shapeType = shape.type;
path.isOpenEnded = shape.isOpenEnded;
foreach (var controlPoint in controlPoints)
path.AddPoint(controlPoint);
}
Initialize(path, GetSerializedObject(target));
}
private T GetOrCreatePath(UnityObject targetObject)
{
var path = GetPath(targetObject);
if (path == null)
{
path = ScriptableObject.CreateInstance<T>();
path.owner = targetObject;
m_Paths[targetObject] = path;
}
return path;
}
private GUISystem GetGUISystem(UnityObject target)
{
GUISystem guiSystem;
m_GUISystems.TryGetValue(target, out guiSystem);
return guiSystem;
}
private void CreateGUISystem(UnityObject target)
{
var guiSystem = new GUISystem(m_GUIState);
var view = new EditablePathView();
view.controller = m_Controller;
view.Install(guiSystem);
m_GUISystems[target] = guiSystem;
}
private SerializedObject GetSerializedObject(UnityObject target)
{
var serializedObject = default(SerializedObject);
if (!m_SerializedObjects.TryGetValue(target, out serializedObject))
{
serializedObject = new SerializedObject(target);
m_SerializedObjects[target] = serializedObject;
}
return serializedObject;
}
void IDuringSceneGuiTool.DuringSceneGui(SceneView sceneView)
{
if (m_GUIState.eventType == EventType.Layout)
m_Controller.ClearClosestPath();
m_RectSelector.OnGUI();
bool changed = false;
ForEachTarget((target) =>
{
var path = GetPath(target);
if (path != null)
{
path.localToWorldMatrix = GetLocalToWorldMatrix(target);
path.forward = GetForward(target);
path.up = GetUp(target);
path.right = GetRight(target);
m_Controller.editablePath = path;
using (var check = new EditorGUI.ChangeCheckScope())
{
GetGUISystem(target).OnGUI();
OnCustomGUI(path);
changed |= check.changed;
}
}
});
if (changed)
{
SetShapes();
RepaintInspectors();
}
}
private void BeginSelection(ISelector<Vector3> selector, bool isAdditive)
{
m_Controller.RegisterUndo("Selection");
if (isAdditive)
{
ForEachTarget((target) =>
{
var path = GetPath(target);
path.selection.BeginSelection();
});
}
else
{
UpdateSelection(selector);
}
}
private void UpdateSelection(ISelector<Vector3> selector)
{
var repaintInspectors = false;
ForEachTarget((target) =>
{
var path = GetPath(target);
repaintInspectors |= path.Select(selector);
});
if (repaintInspectors)
RepaintInspectors();
}
private void EndSelection(ISelector<Vector3> selector)
{
ForEachTarget((target) =>
{
var path = GetPath(target);
path.selection.EndSelection(true);
});
}
internal void SetShapes()
{
ForEachTarget((target) =>
{
SetPath(target);
});
}
private Transform GetTransform(UnityObject target)
{
return (target as Component).transform;
}
private Matrix4x4 GetLocalToWorldMatrix(UnityObject target)
{
return GetTransform(target).localToWorldMatrix;
}
private Vector3 GetForward(UnityObject target)
{
return GetTransform(target).forward;
}
private Vector3 GetUp(UnityObject target)
{
return GetTransform(target).up;
}
private Vector3 GetRight(UnityObject target)
{
return GetTransform(target).right;
}
protected abstract IShape GetShape(UnityObject target);
protected virtual void Initialize(T path, SerializedObject serializedObject) {}
protected abstract void SetShape(T path, SerializedObject serializedObject);
protected virtual void OnActivate() {}
protected virtual void OnDeactivate() {}
protected virtual void OnCustomGUI(T path) {}
}
}
#pragma warning restore 0618

View File

@@ -0,0 +1,107 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal static class PathEditorToolExtensions
{
public static void CycleTangentMode<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
{
var first = true;
var mixed = false;
var tangentMode = TangentMode.Linear;
var targets = pathEditorTool.targets;
foreach (var target in targets)
{
var path = pathEditorTool.GetPath(target);
if (path.selection.Count == 0)
continue;
for (var i = 0; i < path.pointCount; ++i)
{
if (!path.selection.Contains(i))
continue;
var point = path.GetPoint(i);
if (first)
{
first = false;
tangentMode = point.tangentMode;
}
else if (point.tangentMode != tangentMode)
{
mixed = true;
break;
}
}
if (mixed)
break;
}
if (mixed)
tangentMode = TangentMode.Linear;
else
tangentMode = GetNextTangentMode(tangentMode);
foreach (var target in targets)
{
var path = pathEditorTool.GetPath(target);
if (path.selection.Count == 0)
continue;
path.undoObject.RegisterUndo("Cycle Tangent Mode");
for (var i = 0; i < path.pointCount; ++i)
{
if (!path.selection.Contains(i))
continue;
path.SetTangentMode(i, tangentMode);
}
pathEditorTool.SetPath(target);
}
}
public static void MirrorTangent<T>(this PathEditorTool<T> pathEditorTool) where T : ScriptablePath
{
var targets = pathEditorTool.targets;
foreach (var target in targets)
{
var path = pathEditorTool.GetPath(target);
if (path.selection.Count == 0)
continue;
path.undoObject.RegisterUndo("Mirror Tangents");
for (var i = 0; i < path.pointCount; ++i)
{
if (!path.selection.Contains(i))
continue;
path.MirrorTangent(i);
}
pathEditorTool.SetPath(target);
}
}
private static TangentMode GetNextTangentMode(TangentMode tangentMode)
{
return (TangentMode)((((int)tangentMode) + 1) % Enum.GetValues(typeof(TangentMode)).Length);
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class ScriptableData<T> : ScriptableObject
{
[SerializeField]
private T m_Data;
public UnityObject owner { get; set; }
public int index { get; set; }
public T data
{
get { return m_Data; }
set { m_Data = value; }
}
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class ScriptablePath : ScriptableObject, IEditablePath, IUndoObject
{
[SerializeField]
private EditablePath m_EditablePath = new EditablePath();
[SerializeField]
private bool m_Modified = false;
internal bool modified
{
get { return m_Modified; }
}
internal UnityObject owner { get; set; }
public ShapeType shapeType
{
get { return m_EditablePath.shapeType; }
set { m_EditablePath.shapeType = value; }
}
public IUndoObject undoObject
{
get { return this; }
set {}
}
public ISelection<int> selection
{
get { return m_EditablePath.selection; }
}
public Matrix4x4 localToWorldMatrix
{
get { return m_EditablePath.localToWorldMatrix; }
set { m_EditablePath.localToWorldMatrix = value; }
}
public Vector3 forward
{
get { return m_EditablePath.forward; }
set { m_EditablePath.forward = value; }
}
public Vector3 up
{
get { return m_EditablePath.up; }
set { m_EditablePath.up = value; }
}
public Vector3 right
{
get { return m_EditablePath.right; }
set { m_EditablePath.right = value; }
}
public bool isOpenEnded
{
get { return m_EditablePath.isOpenEnded; }
set { m_EditablePath.isOpenEnded = value; }
}
public int pointCount
{
get { return m_EditablePath.pointCount; }
}
public bool Select(ISelector<Vector3> selector)
{
return m_EditablePath.Select(selector);
}
public virtual void Clear()
{
m_EditablePath.Clear();
}
public virtual ControlPoint GetPoint(int index)
{
return m_EditablePath.GetPoint(index);
}
public virtual void SetPoint(int index, ControlPoint controlPoint)
{
m_EditablePath.SetPoint(index, controlPoint);
}
public virtual void AddPoint(ControlPoint controlPoint)
{
m_EditablePath.AddPoint(controlPoint);
}
public virtual void InsertPoint(int index, ControlPoint controlPoint)
{
m_EditablePath.InsertPoint(index, controlPoint);
}
public virtual void RemovePoint(int index)
{
m_EditablePath.RemovePoint(index);
}
void IUndoObject.RegisterUndo(string name)
{
Undo.RegisterCompleteObjectUndo(this, name);
m_Modified = true;
}
public virtual void SetDefaultShape()
{
m_Modified = true;
}
}
}

View File

@@ -0,0 +1,233 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
[CanEditMultipleObjects]
[CustomEditor(typeof(ScriptablePath), true)]
internal class ScriptablePathInspector : Editor
{
private static class Contents
{
public static readonly GUIContent linearIcon = IconContent("TangentLinear", "TangentLinearPro", "Linear");
public static readonly GUIContent continuousIcon = IconContent("TangentContinuous", "TangentContinuousPro", "Continuous");
public static readonly GUIContent brokenIcon = IconContent("TangentBroken", "TangentBrokenPro", "Broken");
public static readonly GUIContent positionLabel = new GUIContent("Position", "Position of the Control Point");
public static readonly GUIContent enableSnapLabel = new GUIContent("Snapping", "Snap points using the snap settings");
public static readonly GUIContent tangentModeLabel = new GUIContent("Tangent Mode");
public static readonly GUIContent pointLabel = new GUIContent("Point");
private static GUIContent IconContent(string name, string tooltip = null)
{
return new GUIContent(Resources.Load<Texture>(name), tooltip);
}
private static GUIContent IconContent(string personal, string pro, string tooltip)
{
if (EditorGUIUtility.isProSkin)
return IconContent(pro, tooltip);
return IconContent(personal, tooltip);
}
}
private List<ScriptablePath> m_Paths = null;
private bool m_Dragged = false;
protected List<ScriptablePath> paths
{
get
{
if (m_Paths == null)
m_Paths = targets.Select(t => t as ScriptablePath).ToList();
return m_Paths;
}
}
public override void OnInspectorGUI()
{
DoTangentModeInspector();
DoPositionInspector();
}
protected void DoTangentModeInspector()
{
if (!IsAnyShapeType(ShapeType.Spline))
return;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(Contents.tangentModeLabel);
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
{
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Linear), Contents.linearIcon))
SetMixedTangentMode(TangentMode.Linear);
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Continuous), Contents.continuousIcon))
SetMixedTangentMode(TangentMode.Continuous);
if (DoToggle(GetToggleStateFromTangentMode(TangentMode.Broken), Contents.brokenIcon))
SetMixedTangentMode(TangentMode.Broken);
}
EditorGUILayout.EndHorizontal();
}
protected void DoPositionInspector()
{
var showMixedValue = EditorGUI.showMixedValue;
var wideMode = EditorGUIUtility.wideMode;
var position = Vector3.zero;
var isMixed = GetMixedPosition(out position);
EditorGUI.showMixedValue = isMixed;
EditorGUIUtility.wideMode = true;
using (new EditorGUI.DisabledGroupScope(!IsAnyPointSelected()))
{
if (GUIUtility.hotControl == 0)
m_Dragged = false;
EditorGUI.BeginChangeCheck();
var delta = EditorGUILayout.Vector2Field(Contents.positionLabel, position) - (Vector2)position;
if (EditorGUI.EndChangeCheck())
{
if (m_Dragged == false)
{
foreach (var path in paths)
path.undoObject.RegisterUndo("Point Position");
m_Dragged = true;
}
SetMixedDeltaPosition(delta);
}
}
EditorGUI.showMixedValue = showMixedValue;
EditorGUIUtility.wideMode = wideMode;
}
private bool DoToggle(bool value, GUIContent icon)
{
const float kButtonWidth = 33f;
const float kButtonHeight = 23f;
var buttonStyle = new GUIStyle("EditModeSingleButton");
var changed = false;
using (var check = new EditorGUI.ChangeCheckScope())
{
value = GUILayout.Toggle(value, icon, buttonStyle, GUILayout.Width(kButtonWidth), GUILayout.Height(kButtonHeight));
changed = check.changed;
}
return value && changed;
}
private bool GetToggleStateFromTangentMode(TangentMode mode)
{
foreach (var path in paths)
{
var selection = path.selection;
foreach (var index in selection.elements)
if (path.GetPoint(index).tangentMode != mode)
return false;
}
return true;
}
private void SetMixedTangentMode(TangentMode tangentMode)
{
foreach (var path in paths)
{
path.undoObject.RegisterUndo("Tangent Mode");
foreach (var index in path.selection.elements)
path.SetTangentMode(index, tangentMode);
}
SceneView.RepaintAll();
}
private bool GetMixedPosition(out Vector3 position)
{
var first = true;
position = Vector3.zero;
foreach (var path in paths)
{
var selection = path.selection;
var matrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
foreach (var index in selection.elements)
{
var controlPoint = path.GetPoint(index);
if (first)
{
position = controlPoint.position;
first = false;
}
else if (position != controlPoint.position)
{
return true;
}
}
path.localToWorldMatrix = matrix;
}
return false;
}
private void SetMixedDeltaPosition(Vector3 delta)
{
foreach (var path in paths)
{
var selection = path.selection;
var matrix = path.localToWorldMatrix;
path.localToWorldMatrix = Matrix4x4.identity;
foreach (var index in selection.elements)
{
var controlPoint = path.GetPoint(index);
controlPoint.position += delta;
path.SetPoint(index, controlPoint);
}
path.localToWorldMatrix = matrix;
}
}
private bool IsAnyShapeType(ShapeType shapeType)
{
foreach (var path in paths)
if (path.shapeType == shapeType)
return true;
return false;
}
protected bool IsAnyPointSelected()
{
foreach (var path in paths)
if (path.selection.Count > 0)
return true;
return false;
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class ClickAction : HoveredControlAction
{
private int m_Button;
private bool m_UseEvent;
public Action<IGUIState, Control> onClick;
public ClickAction(Control control, int button, bool useEvent = true) : base(control)
{
m_Button = button;
m_UseEvent = useEvent;
}
protected override bool GetTriggerContidtion(IGUIState guiState)
{
return guiState.mouseButton == m_Button && guiState.eventType == EventType.MouseDown;
}
protected override void OnTrigger(IGUIState guiState)
{
base.OnTrigger(guiState);
if (onClick != null)
onClick(guiState, hoveredControl);
if (m_UseEvent)
guiState.UseCurrentEvent();
}
protected override bool GetFinishContidtion(IGUIState guiState)
{
return true;
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class CommandAction : GUIAction
{
private string m_CommandName;
public Action<IGUIState> onCommand;
public CommandAction(string commandName)
{
m_CommandName = commandName;
}
protected override bool GetTriggerContidtion(IGUIState guiState)
{
if (guiState.eventType == EventType.ValidateCommand && guiState.commandName == m_CommandName)
{
guiState.UseCurrentEvent();
return true;
}
return false;
}
protected override bool GetFinishContidtion(IGUIState guiState)
{
if (guiState.eventType == EventType.ExecuteCommand && guiState.commandName == m_CommandName)
{
guiState.UseCurrentEvent();
return true;
}
return false;
}
protected override void OnFinish(IGUIState guiState)
{
if (onCommand != null)
onCommand(guiState);
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal abstract class Control
{
private string m_Name;
private int m_NameHashCode;
private int m_ID;
private LayoutData m_LayoutData;
private int m_ActionID = -1;
private LayoutData m_HotLayoutData;
public string name
{
get { return m_Name; }
}
public int ID
{
get { return m_ID; }
}
public int actionID
{
get { return m_ActionID; }
}
public LayoutData layoutData
{
get { return m_LayoutData; }
set { m_LayoutData = value; }
}
public LayoutData hotLayoutData
{
get { return m_HotLayoutData; }
}
public Control(string name)
{
m_Name = name;
m_NameHashCode = name.GetHashCode();
}
public void GetControl(IGUIState guiState)
{
m_ID = guiState.GetControlID(m_NameHashCode, FocusType.Passive);
}
internal void SetActionID(int actionID)
{
m_ActionID = actionID;
m_HotLayoutData = m_LayoutData;
}
public void BeginLayout(IGUIState guiState)
{
Debug.Assert(guiState.eventType == EventType.Layout);
m_LayoutData = OnBeginLayout(LayoutData.zero, guiState);
}
public void Layout(IGUIState guiState)
{
Debug.Assert(guiState.eventType == EventType.Layout);
for (var i = 0; i < GetCount(); ++i)
{
if (guiState.hotControl == actionID && hotLayoutData.index == i)
continue;
var layoutData = new LayoutData()
{
index = i,
position = GetPosition(guiState, i),
distance = GetDistance(guiState, i),
forward = GetForward(guiState, i),
up = GetUp(guiState, i),
right = GetRight(guiState, i),
userData = GetUserData(guiState, i)
};
m_LayoutData = LayoutData.Nearest(m_LayoutData, layoutData);
}
}
public void EndLayout(IGUIState guiState)
{
Debug.Assert(guiState.eventType == EventType.Layout);
OnEndLayout(guiState);
}
public void Repaint(IGUIState guiState)
{
for (var i = 0; i < GetCount(); ++i)
OnRepaint(guiState, i);
}
protected virtual LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
{
return data;
}
protected virtual void OnEndLayout(IGUIState guiState)
{
}
protected virtual void OnRepaint(IGUIState guiState, int index)
{
}
protected virtual int GetCount()
{
return 1;
}
protected virtual Vector3 GetPosition(IGUIState guiState, int index)
{
return Vector3.zero;
}
protected virtual Vector3 GetForward(IGUIState guiState, int index)
{
return Vector3.forward;
}
protected virtual Vector3 GetUp(IGUIState guiState, int index)
{
return Vector3.up;
}
protected virtual Vector3 GetRight(IGUIState guiState, int index)
{
return Vector3.right;
}
protected virtual float GetDistance(IGUIState guiState, int index)
{
return layoutData.distance;
}
protected virtual object GetUserData(IGUIState guiState, int index)
{
return null;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal abstract class DefaultControl : Control
{
public static readonly float kPickDistance = 5f;
public DefaultControl(string name) : base(name)
{
}
protected override LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
{
data.distance = kPickDistance;
return data;
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal abstract class GUIAction
{
private int m_ID = -1;
public Func<IGUIState, GUIAction, bool> enable;
public Func<IGUIState, GUIAction, bool> enableRepaint;
public Func<IGUIState, GUIAction, bool> repaintOnMouseMove;
public Action<IGUIState, GUIAction> onPreRepaint;
public Action<IGUIState, GUIAction> onRepaint;
public int ID
{
get { return m_ID; }
}
public void OnGUI(IGUIState guiState)
{
m_ID = guiState.GetControlID(GetType().GetHashCode(), FocusType.Passive);
if (guiState.hotControl == 0 && IsEnabled(guiState) && CanTrigger(guiState) && GetTriggerContidtion(guiState))
{
guiState.hotControl = ID;
OnTrigger(guiState);
}
if (guiState.hotControl == ID)
{
if (GetFinishContidtion(guiState))
{
OnFinish(guiState);
guiState.hotControl = 0;
}
else
{
OnPerform(guiState);
}
}
if (guiState.eventType == EventType.Repaint && IsRepaintEnabled(guiState))
Repaint(guiState);
}
public bool IsEnabled(IGUIState guiState)
{
if (enable != null)
return enable(guiState, this);
return true;
}
public bool IsRepaintEnabled(IGUIState guiState)
{
if (!IsEnabled(guiState))
return false;
if (enableRepaint != null)
return enableRepaint(guiState, this);
return true;
}
public void PreRepaint(IGUIState guiState)
{
Debug.Assert(guiState.eventType == EventType.Repaint);
if (IsEnabled(guiState) && onPreRepaint != null)
onPreRepaint(guiState, this);
}
private void Repaint(IGUIState guiState)
{
Debug.Assert(guiState.eventType == EventType.Repaint);
if (onRepaint != null)
onRepaint(guiState, this);
}
internal bool IsRepaintOnMouseMoveEnabled(IGUIState guiState)
{
if (!IsEnabled(guiState) || !IsRepaintEnabled(guiState))
return false;
if (repaintOnMouseMove != null)
return repaintOnMouseMove(guiState, this);
return false;
}
protected abstract bool GetFinishContidtion(IGUIState guiState);
protected abstract bool GetTriggerContidtion(IGUIState guiState);
protected virtual bool CanTrigger(IGUIState guiState) { return true; }
protected virtual void OnTrigger(IGUIState guiState)
{
}
protected virtual void OnPerform(IGUIState guiState)
{
}
protected virtual void OnFinish(IGUIState guiState)
{
}
}
}

View File

@@ -0,0 +1,165 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class GUIState : IGUIState
{
private Handles.CapFunction nullCap = (int c, Vector3 p , Quaternion r, float s, EventType ev) => {};
public Vector2 mousePosition
{
get { return Event.current.mousePosition; }
}
public int mouseButton
{
get { return Event.current.button; }
}
public int clickCount
{
get { return Event.current.clickCount; }
}
public bool isShiftDown
{
get { return Event.current.shift; }
}
public bool isAltDown
{
get { return Event.current.alt; }
}
public bool isActionKeyDown
{
get { return EditorGUI.actionKey; }
}
public KeyCode keyCode
{
get { return Event.current.keyCode; }
}
public EventType eventType
{
get { return Event.current.type; }
}
public string commandName
{
get { return Event.current.commandName; }
}
public int nearestControl
{
get { return HandleUtility.nearestControl; }
set { HandleUtility.nearestControl = value; }
}
public int hotControl
{
get { return GUIUtility.hotControl; }
set { GUIUtility.hotControl = value; }
}
public bool changed
{
get { return GUI.changed; }
set { GUI.changed = value; }
}
public int GetControlID(int hint, FocusType focusType)
{
return GUIUtility.GetControlID(hint, focusType);
}
public void AddControl(int controlID, float distance)
{
HandleUtility.AddControl(controlID, distance);
}
public bool Slider(int id, SliderData sliderData, out Vector3 newPosition)
{
if (mouseButton == 0 && eventType == EventType.MouseDown)
{
hotControl = 0;
nearestControl = id;
}
EditorGUI.BeginChangeCheck();
newPosition = Handles.Slider2D(id, sliderData.position, sliderData.forward, sliderData.right, sliderData.up, 1f, nullCap, Vector2.zero);
return EditorGUI.EndChangeCheck();
}
public void UseCurrentEvent()
{
Event.current.Use();
}
public void Repaint()
{
HandleUtility.Repaint();
}
public bool IsEventOutsideWindow()
{
return Event.current.type == EventType.Ignore;
}
public bool IsViewToolActive()
{
return UnityEditor.Tools.current == Tool.View || isAltDown || mouseButton == 1 || mouseButton == 2;
}
public bool HasCurrentCamera()
{
return Camera.current != null;
}
public float GetHandleSize(Vector3 position)
{
var scale = HasCurrentCamera() ? 0.01f : 0.05f;
return HandleUtility.GetHandleSize(position) * scale;
}
public float DistanceToSegment(Vector3 p1, Vector3 p2)
{
p1 = HandleUtility.WorldToGUIPoint(p1);
p2 = HandleUtility.WorldToGUIPoint(p2);
return HandleUtility.DistancePointToLineSegment(Event.current.mousePosition, p1, p2);
}
public float DistanceToCircle(Vector3 center, float radius)
{
return HandleUtility.DistanceToCircle(center, radius);
}
public Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePos)
{
Vector3 worldPos = Handles.inverseMatrix.MultiplyPoint(guiPosition);
if (Camera.current)
{
Ray ray = HandleUtility.GUIPointToWorldRay(guiPosition);
planeNormal = Handles.matrix.MultiplyVector(planeNormal);
planePos = Handles.matrix.MultiplyPoint(planePos);
Plane plane = new Plane(planeNormal, planePos);
float distance = 0f;
if (plane.Raycast(ray, out distance))
{
worldPos = Handles.inverseMatrix.MultiplyPoint(ray.GetPoint(distance));
}
}
return worldPos;
}
}
}

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class GUISystem
{
private readonly int kControlIDCheckHashCode = "ControlIDCheckHashCode".GetHashCode();
private List<Control> m_Controls = new List<Control>();
private List<GUIAction> m_Actions = new List<GUIAction>();
private IGUIState m_GUIState;
private int m_PrevNearestControl = -1;
private LayoutData m_PrevNearestLayoutData = LayoutData.zero;
private int m_ControlIDCheck = -1;
public GUISystem(IGUIState guiState)
{
m_GUIState = guiState;
}
public void AddControl(Control control)
{
if (control == null)
throw new NullReferenceException("Control is null");
m_Controls.Add(control);
}
public void RemoveControl(Control control)
{
m_Controls.Remove(control);
}
public void AddAction(GUIAction action)
{
if (action == null)
throw new NullReferenceException("Action is null");
m_Actions.Add(action);
}
public void RemoveAction(GUIAction action)
{
m_Actions.Remove(action);
}
public void OnGUI()
{
var controlIDCheck = m_GUIState.GetControlID(kControlIDCheckHashCode, FocusType.Passive);
if (m_GUIState.eventType == EventType.Layout)
m_ControlIDCheck = controlIDCheck;
else if (m_GUIState.eventType != EventType.Used && m_ControlIDCheck != controlIDCheck)
Debug.LogWarning("GetControlID at event " + m_GUIState.eventType + " returns a controlID different from the one in Layout event");
var nearestLayoutData = LayoutData.zero;
foreach (var control in m_Controls)
control.GetControl(m_GUIState);
if (m_GUIState.eventType == EventType.Layout)
{
foreach (var control in m_Controls)
control.BeginLayout(m_GUIState);
foreach (var control in m_Controls)
{
control.Layout(m_GUIState);
nearestLayoutData = LayoutData.Nearest(nearestLayoutData, control.layoutData);
}
foreach (var control in m_Controls)
m_GUIState.AddControl(control.ID, control.layoutData.distance);
foreach (var control in m_Controls)
control.EndLayout(m_GUIState);
if (m_PrevNearestControl == m_GUIState.nearestControl)
{
if (nearestLayoutData.index != m_PrevNearestLayoutData.index)
m_GUIState.Repaint();
}
else
{
m_PrevNearestControl = m_GUIState.nearestControl;
m_GUIState.Repaint();
}
m_PrevNearestLayoutData = nearestLayoutData;
}
if (m_GUIState.eventType == EventType.Repaint)
{
foreach (var action in m_Actions)
if (action.IsRepaintEnabled(m_GUIState))
action.PreRepaint(m_GUIState);
foreach (var control in m_Controls)
control.Repaint(m_GUIState);
}
var repaintOnMouseMove = false;
foreach (var action in m_Actions)
{
if (IsMouseMoveEvent())
repaintOnMouseMove |= action.IsRepaintOnMouseMoveEnabled(m_GUIState);
action.OnGUI(m_GUIState);
}
if (repaintOnMouseMove)
m_GUIState.UseCurrentEvent();
}
private bool IsMouseMoveEvent()
{
return m_GUIState.eventType == EventType.MouseMove || m_GUIState.eventType == EventType.MouseDrag;
}
}
}

View File

@@ -0,0 +1,99 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class GenericControl : Control
{
public Func<IGUIState, LayoutData> onBeginLayout = null;
public Action<IGUIState> onEndLayout = null;
public Action<IGUIState, Control, int> onRepaint;
public Func<int> count;
public Func<int, Vector3> position;
public Func<IGUIState, int, float> distance;
public Func<int, Vector3> forward;
public Func<int, Vector3> up;
public Func<int, Vector3> right;
public Func<int, object> userData = null;
public GenericControl(string name) : base(name)
{
}
protected override int GetCount()
{
if (count != null)
return count();
return base.GetCount();
}
protected override void OnEndLayout(IGUIState guiState)
{
if (onEndLayout != null)
onEndLayout(guiState);
}
protected override void OnRepaint(IGUIState guiState, int index)
{
if (onRepaint != null)
onRepaint(guiState, this, index);
}
protected override LayoutData OnBeginLayout(LayoutData data, IGUIState guiState)
{
if (onBeginLayout != null)
return onBeginLayout(guiState);
return data;
}
protected override Vector3 GetPosition(IGUIState guiState, int index)
{
if (position != null)
return position(index);
return base.GetPosition(guiState, index);
}
protected override float GetDistance(IGUIState guiState, int index)
{
if (distance != null)
return distance(guiState, index);
return base.GetDistance(guiState, index);
}
protected override Vector3 GetForward(IGUIState guiState, int index)
{
if (forward != null)
return forward(index);
return base.GetForward(guiState, index);
}
protected override Vector3 GetUp(IGUIState guiState, int index)
{
if (up != null)
return up(index);
return base.GetUp(guiState, index);
}
protected override Vector3 GetRight(IGUIState guiState, int index)
{
if (right != null)
return right(index);
return base.GetRight(guiState, index);
}
protected override object GetUserData(IGUIState guiState, int index)
{
if (userData != null)
return userData(index);
return base.GetUserData(guiState, index);
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class GenericDefaultControl : DefaultControl
{
public Func<IGUIState, Vector3> position;
public Func<IGUIState, Vector3> forward;
public Func<IGUIState, Vector3> up;
public Func<IGUIState, Vector3> right;
public Func<IGUIState, object> userData = null;
public GenericDefaultControl(string name) : base(name)
{
}
protected override Vector3 GetPosition(IGUIState guiState, int index)
{
if (position != null)
return position(guiState);
return base.GetPosition(guiState, index);
}
protected override Vector3 GetForward(IGUIState guiState, int index)
{
if (forward != null)
return forward(guiState);
return base.GetForward(guiState, index);
}
protected override Vector3 GetUp(IGUIState guiState, int index)
{
if (up != null)
return up(guiState);
return base.GetUp(guiState, index);
}
protected override Vector3 GetRight(IGUIState guiState, int index)
{
if (right != null)
return right(guiState);
return base.GetRight(guiState, index);
}
protected override object GetUserData(IGUIState guiState, int index)
{
if (userData != null)
return userData(guiState);
return base.GetUserData(guiState, index);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal abstract class HoveredControlAction : GUIAction
{
private Control m_HoveredControl;
public Control hoveredControl
{
get { return m_HoveredControl; }
}
public HoveredControlAction(Control control)
{
m_HoveredControl = control;
}
protected override bool CanTrigger(IGUIState guiState)
{
return guiState.nearestControl == hoveredControl.ID;
}
protected override void OnTrigger(IGUIState guiState)
{
m_HoveredControl.SetActionID(ID);
}
}
}

View File

@@ -0,0 +1,43 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal struct SliderData
{
public Vector3 position;
public Vector3 forward;
public Vector3 up;
public Vector3 right;
public static readonly SliderData zero = new SliderData() { position = Vector3.zero, forward = Vector3.forward, up = Vector3.up, right = Vector3.right };
}
internal interface IGUIState
{
Vector2 mousePosition { get; }
int mouseButton { get; }
int clickCount { get; }
bool isShiftDown { get; }
bool isAltDown { get; }
bool isActionKeyDown { get; }
KeyCode keyCode { get; }
EventType eventType { get; }
string commandName { get; }
int nearestControl { get; set; }
int hotControl { get; set; }
bool changed { get; set; }
int GetControlID(int hint, FocusType focusType);
void AddControl(int controlID, float distance);
bool Slider(int id, SliderData sliderData, out Vector3 newPosition);
void UseCurrentEvent();
void Repaint();
bool IsEventOutsideWindow();
bool IsViewToolActive();
bool HasCurrentCamera();
float GetHandleSize(Vector3 position);
float DistanceToSegment(Vector3 p1, Vector3 p2);
float DistanceToCircle(Vector3 center, float radius);
Vector3 GUIToWorld(Vector2 guiPosition, Vector3 planeNormal, Vector3 planePos);
}
}

View File

@@ -0,0 +1,25 @@
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal struct LayoutData
{
public int index;
public float distance;
public Vector3 position;
public Vector3 forward;
public Vector3 up;
public Vector3 right;
public object userData;
public static readonly LayoutData zero = new LayoutData() { index = 0, distance = float.MaxValue, position = Vector3.zero, forward = Vector3.forward, up = Vector3.up, right = Vector3.right };
public static LayoutData Nearest(LayoutData currentData, LayoutData newData)
{
if (newData.distance <= currentData.distance)
return newData;
return currentData;
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework
{
internal class SliderAction : ClickAction
{
private SliderData m_SliderData;
public Action<IGUIState, Control, Vector3> onSliderBegin;
public Action<IGUIState, Control, Vector3> onSliderChanged;
public Action<IGUIState, Control, Vector3> onSliderEnd;
public SliderAction(Control control) : base(control, 0, false)
{
}
protected override bool GetFinishContidtion(IGUIState guiState)
{
return guiState.eventType == EventType.MouseUp && guiState.mouseButton == 0;
}
protected override void OnTrigger(IGUIState guiState)
{
base.OnTrigger(guiState);
m_SliderData.position = hoveredControl.hotLayoutData.position;
m_SliderData.forward = hoveredControl.hotLayoutData.forward;
m_SliderData.right = hoveredControl.hotLayoutData.right;
m_SliderData.up = hoveredControl.hotLayoutData.up;
if (onSliderBegin != null)
onSliderBegin(guiState, hoveredControl, m_SliderData.position);
}
protected override void OnFinish(IGUIState guiState)
{
if (onSliderEnd != null)
onSliderEnd(guiState, hoveredControl, m_SliderData.position);
guiState.UseCurrentEvent();
guiState.Repaint();
}
protected override void OnPerform(IGUIState guiState)
{
Vector3 newPosition;
var changed = guiState.Slider(ID, m_SliderData, out newPosition);
if (changed)
{
m_SliderData.position = newPosition;
if (onSliderChanged != null)
onSliderChanged(guiState, hoveredControl, newPosition);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

View File

@@ -0,0 +1,7 @@
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface ISelectable<T>
{
bool Select(ISelector<T> selector);
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface ISelection<T>
{
int Count { get; }
T activeElement { get; set; }
T[] elements { get; set; }
void Clear();
void BeginSelection();
void EndSelection(bool select);
bool Select(T element, bool select);
bool Contains(T element);
}
}

View File

@@ -0,0 +1,7 @@
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface ISelector<T>
{
bool Select(T element);
}
}

View File

@@ -0,0 +1,13 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
[Serializable]
internal class IndexedSelection : SerializableSelection<int>
{
protected override int GetInvalidElement() { return -1; }
}
}

View File

@@ -0,0 +1,13 @@
using UnityEngine;
using UnityEditor;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class PointRectSelector : RectSelector<Vector3>
{
protected override bool Select(Vector3 element)
{
return guiRect.Contains(HandleUtility.WorldToGUIPoint(element), true);
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal abstract class RectSelector<T> : ISelector<T>
{
public Action<ISelector<T>, bool> onSelectionBegin;
public Action<ISelector<T>> onSelectionChanged;
public Action<ISelector<T>> onSelectionEnd = null;
private GUISystem m_GUISystem;
private Control m_RectSelectorControl;
private GUIAction m_RectSelectAction;
private Vector3 m_RectStart;
private Vector3 m_RectEnd;
private Rect m_GUIRect;
private IDrawer m_Drawer = new Drawer();
public Rect guiRect
{
get { return m_GUIRect; }
}
public RectSelector() : this(new GUISystem(new GUIState())) {}
public RectSelector(GUISystem guiSystem)
{
m_GUISystem = guiSystem;
m_RectSelectorControl = new GenericDefaultControl("RectSelector")
{
position = (guiState) =>
{
return GUIToWorld(guiState, guiState.mousePosition);
},
forward = (guiState) =>
{
if (Camera.current)
return Camera.current.transform.forward;
return Vector3.forward;
},
right = (guiState) =>
{
if (Camera.current)
return Camera.current.transform.right;
return Vector3.right;
},
up = (guiState) =>
{
if (Camera.current)
return Camera.current.transform.up;
return Vector3.up;
}
};
m_RectSelectAction = new SliderAction(m_RectSelectorControl)
{
enableRepaint = (guiState, action) =>
{
var size = m_RectStart - m_RectEnd;
return size != Vector3.zero && guiState.hotControl == action.ID;
},
onClick = (guiState, control) =>
{
m_RectStart = GUIToWorld(guiState, guiState.mousePosition);
m_RectEnd = m_RectStart;
m_GUIRect = CalculateGUIRect();
},
onSliderBegin = (guiState, control, position) =>
{
m_RectEnd = position;
m_GUIRect = CalculateGUIRect();
if (onSelectionBegin != null)
onSelectionBegin(this, guiState.isShiftDown);
},
onSliderChanged = (guiState, control, position) =>
{
m_RectEnd = position;
m_GUIRect = CalculateGUIRect();
if (onSelectionChanged != null)
onSelectionChanged(this);
},
onSliderEnd = (guiState, control, position) =>
{
if (onSelectionEnd != null)
onSelectionEnd(this);
},
onRepaint = (guiState, action) =>
{
m_Drawer.DrawSelectionRect(m_GUIRect);
}
};
m_GUISystem.AddControl(m_RectSelectorControl);
m_GUISystem.AddAction(m_RectSelectAction);
}
private Vector3 GUIToWorld(IGUIState guiState, Vector2 guiPosition)
{
var forward = Vector3.forward;
if (guiState.HasCurrentCamera())
forward = Camera.current.transform.forward;
return guiState.GUIToWorld(guiPosition, forward, Vector3.zero);
}
private Rect CalculateGUIRect()
{
return FromToRect(HandleUtility.WorldToGUIPoint(m_RectStart), HandleUtility.WorldToGUIPoint(m_RectEnd));
}
private Rect FromToRect(Vector2 start, Vector2 end)
{
Rect r = new Rect(start.x, start.y, end.x - start.x, end.y - start.y);
if (r.width < 0)
{
r.x += r.width;
r.width = -r.width;
}
if (r.height < 0)
{
r.y += r.height;
r.height = -r.height;
}
return r;
}
public void OnGUI()
{
m_GUISystem.OnGUI();
}
bool ISelector<T>.Select(T element)
{
return Select(element);
}
protected abstract bool Select(T element);
}
}

View File

@@ -0,0 +1,143 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
[Serializable]
internal abstract class SerializableSelection<T> : ISelection<T>, ISerializationCallbackReceiver
{
internal readonly static int kInvalidID = -1;
[SerializeField]
private T[] m_Keys = new T[0];
private HashSet<T> m_Selection = new HashSet<T>();
private HashSet<T> m_TemporalSelection = new HashSet<T>();
private bool m_SelectionInProgress = false;
public int Count
{
get { return m_Selection.Count + m_TemporalSelection.Count; }
}
public T activeElement
{
get { return First(); }
set
{
Clear();
Select(value, true);
}
}
public T[] elements
{
get
{
var set = m_Selection;
if (m_SelectionInProgress)
{
var union = new HashSet<T>(m_Selection);
union.UnionWith(m_TemporalSelection);
set = union;
}
return new List<T>(set).ToArray();
}
set
{
Clear();
foreach (var element in value)
Select(element, true);
}
}
protected abstract T GetInvalidElement();
public void Clear()
{
GetSelection().Clear();
}
public void BeginSelection()
{
m_SelectionInProgress = true;
Clear();
}
public void EndSelection(bool select)
{
m_SelectionInProgress = false;
if (select)
m_Selection.UnionWith(m_TemporalSelection);
else
m_Selection.ExceptWith(m_TemporalSelection);
m_TemporalSelection.Clear();
}
public bool Select(T element, bool select)
{
var changed = false;
if (EqualityComparer<T>.Default.Equals(element, GetInvalidElement()))
return changed;
if (select)
changed = GetSelection().Add(element);
else if (Contains(element))
changed = GetSelection().Remove(element);
return changed;
}
public bool Contains(T element)
{
return m_Selection.Contains(element) || m_TemporalSelection.Contains(element);
}
private HashSet<T> GetSelection()
{
if (m_SelectionInProgress)
return m_TemporalSelection;
return m_Selection;
}
private T First()
{
T element = First(m_Selection);
if (EqualityComparer<T>.Default.Equals(element, GetInvalidElement()))
element = First(m_TemporalSelection);
return element;
}
private T First(HashSet<T> set)
{
if (set.Count == 0)
return GetInvalidElement();
using (var enumerator = set.GetEnumerator())
{
Debug.Assert(enumerator.MoveNext());
return enumerator.Current;
}
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
m_Keys = new List<T>(m_Selection).ToArray();
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
elements = m_Keys;
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal enum ShapeType
{
Polygon,
Spline
}
internal interface IShape
{
ShapeType type { get; }
bool isOpenEnded { get; }
ControlPoint[] ToControlPoints();
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal struct Polygon : IShape
{
public bool isOpenEnded;
public Vector3[] points;
ShapeType IShape.type => ShapeType.Polygon;
bool IShape.isOpenEnded => isOpenEnded;
ControlPoint[] IShape.ToControlPoints()
{
if (points == null)
throw new NullReferenceException("Points array is null");
var controlPoints = new List<ControlPoint>();
foreach (var point in points)
{
controlPoints.Add(new ControlPoint() { position = point });
}
return controlPoints.ToArray();
}
public static Polygon empty = new Polygon() { isOpenEnded = true, points = new Vector3[0] };
}
}

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal static class ShapeExtensions
{
public static Polygon ToPolygon(this Vector3[] points, bool isOpenEnded)
{
return new Polygon()
{
isOpenEnded = isOpenEnded,
points = points
};
}
public static Spline ToSpline(this Vector3[] points, bool isOpenEnded)
{
if (!points.IsSpline(isOpenEnded) && points.IsSpline(!isOpenEnded))
{
var pointList = new List<Vector3>(points);
if (isOpenEnded)
{
while (pointList.Count % 3 != 1)
pointList.RemoveAt(pointList.Count - 1);
points = pointList.ToArray();
}
else
{
var last = pointList[pointList.Count - 1];
var first = pointList[0];
var v = first - last;
pointList.Add(last + v.normalized * (v.magnitude / 3f));
pointList.Add(first - v.normalized * (v.magnitude / 3f));
points = pointList.ToArray();
}
}
if (!points.IsSpline(isOpenEnded))
throw new Exception("The provided control point array can't conform a Spline.");
return new Spline()
{
isOpenEnded = isOpenEnded,
points = points
};
}
public static bool IsSpline(this Vector3[] points, bool isOpenEnded)
{
if (points.Length < 4)
return false;
if (isOpenEnded && points.Length % 3 != 1)
return false;
if (!isOpenEnded && points.Length % 3 != 0)
return false;
return true;
}
public static Spline ToSpline(this Polygon polygon)
{
var newPointCount = polygon.points.Length * 3;
if (polygon.isOpenEnded)
newPointCount = (polygon.points.Length - 1) * 3 + 1;
var newPoints = new Vector3[newPointCount];
var controlPoints = polygon.points;
var pointCount = controlPoints.Length;
for (var i = 0; i < pointCount; ++i)
{
var nextIndex = (i + 1) % pointCount;
var point = controlPoints[i];
var v = controlPoints[nextIndex] - point;
newPoints[i * 3] = point;
if (i * 3 + 2 < newPointCount)
{
newPoints[i * 3 + 1] = point + v / 3f;
newPoints[i * 3 + 2] = point + v * 2f / 3f;
}
}
return new Spline()
{
isOpenEnded = polygon.isOpenEnded,
points = newPoints
};
}
}
}

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal struct Spline : IShape
{
public bool isOpenEnded;
public Vector3[] points;
ShapeType IShape.type => ShapeType.Spline;
bool IShape.isOpenEnded => isOpenEnded;
ControlPoint[] IShape.ToControlPoints()
{
if (points == null)
throw new NullReferenceException("Points array is null");
if (!points.IsSpline(isOpenEnded))
throw new Exception("The provided control point array can't conform a Spline.");
var controlPoints = new List<ControlPoint>();
var leftTangent = Vector3.zero;
var rightTangent = Vector3.zero;
var pointCount = points.Length;
for (var i = 0; i < pointCount; i += 3)
{
if (i == 0)
{
if (isOpenEnded)
leftTangent = points[0];
else
leftTangent = points[EditablePathUtility.Mod(-1, pointCount)];
}
if (i == pointCount - 1 && isOpenEnded)
rightTangent = points[i];
else
rightTangent = points[i + 1];
controlPoints.Add(
new ControlPoint()
{
position = points[i],
leftTangent = leftTangent,
rightTangent = rightTangent,
tangentMode = TangentMode.Broken
});
if (i == pointCount - 1 && isOpenEnded)
leftTangent = Vector3.zero;
else
leftTangent = points[i + 2];
}
pointCount = controlPoints.Count;
for (var i = 0; i < pointCount; ++i)
{
var prevIndex = EditablePathUtility.Mod(i - 1, pointCount);
var nextIndex = EditablePathUtility.Mod(i + 1, pointCount);
var controlPoint = controlPoints[i];
var prevControlPoint = controlPoints[prevIndex];
var nextControlPoint = controlPoints[nextIndex];
var liniarLeftPosition = (prevControlPoint.position - controlPoint.position) / 3f;
var isLeftTangentLinear = (controlPoint.localLeftTangent - liniarLeftPosition).sqrMagnitude < 0.001f;
if (isLeftTangentLinear)
controlPoint.localLeftTangent = Vector3.zero;
var liniarRightPosition = (nextControlPoint.position - controlPoint.position) / 3f;
var isRightTangentLinear = (controlPoint.localRightTangent - liniarRightPosition).sqrMagnitude < 0.001f;
if (isRightTangentLinear)
controlPoint.localRightTangent = Vector3.zero;
var tangentDotProduct = Vector3.Dot(controlPoint.localLeftTangent.normalized, controlPoint.localRightTangent.normalized);
var isContinous = tangentDotProduct < 0f && (tangentDotProduct + 1) * (tangentDotProduct + 1) < 0.001f;
if (isLeftTangentLinear && isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Linear;
else if (isLeftTangentLinear || isRightTangentLinear)
controlPoint.tangentMode = TangentMode.Broken;
else if (isContinous)
controlPoint.tangentMode = TangentMode.Continuous;
controlPoints[i] = controlPoint;
}
return controlPoints.ToArray();
}
public static Spline empty = new Spline() { isOpenEnded = true, points = new Vector3[0] };
}
}

View File

@@ -0,0 +1,48 @@
using System;
using UnityEngine;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class CreatePointAction : ClickAction
{
private Control m_PointControl;
public Func<IGUIState, Vector2, Vector3> guiToWorld;
public Action<int, Vector3> onCreatePoint;
public CreatePointAction(Control pointControl, Control edgeControl) : base(edgeControl, 0, false)
{
m_PointControl = pointControl;
}
protected override void OnTrigger(IGUIState guiState)
{
base.OnTrigger(guiState);
var index = hoveredControl.layoutData.index;
var position = GetMousePositionWorld(guiState);
if (onCreatePoint != null)
onCreatePoint(index, position);
guiState.nearestControl = m_PointControl.ID;
var data = m_PointControl.layoutData;
data.index = index + 1;
data.position = position;
data.distance = 0f;
m_PointControl.layoutData = data;
guiState.changed = true;
}
private Vector3 GetMousePositionWorld(IGUIState guiState)
{
if (guiToWorld != null)
return guiToWorld(guiState, guiState.mousePosition);
return guiState.GUIToWorld(guiState.mousePosition, hoveredControl.layoutData.forward, hoveredControl.layoutData.position);
}
}
}

View File

@@ -0,0 +1,141 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class DefaultStyles
{
public readonly GUIStyle pointNormalStyle;
public readonly GUIStyle pointHoveredStyle;
public readonly GUIStyle pointSelectedStyle;
public readonly GUIStyle pointPreviewStyle;
public readonly GUIStyle pointRemovePreviewStyle;
public readonly GUIStyle tangentNormalStyle;
public readonly GUIStyle tangentHoveredStyle;
public readonly GUIStyle selectionRectStyle;
public DefaultStyles()
{
var pointNormal = Resources.Load<Texture2D>("Path/pointNormal");
var pointHovered = Resources.Load<Texture2D>("Path/pointHovered");
var pointSelected = Resources.Load<Texture2D>("Path/pointSelected");
var pointPreview = Resources.Load<Texture2D>("Path/pointPreview");
var pointRemovePreview = Resources.Load<Texture2D>("Path/pointRemovePreview");
var tangentNormal = Resources.Load<Texture2D>("Path/tangentNormal");
pointNormalStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointNormal"), Vector2.one * 12f);
pointHoveredStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointHovered"), Vector2.one * 12f);
pointSelectedStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointSelected"), Vector2.one * 12f);
pointPreviewStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointPreview"), Vector2.one * 12f);
pointRemovePreviewStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointRemovePreview"), Vector2.one * 12f);
tangentNormalStyle = CreateStyle(Resources.Load<Texture2D>("Path/tangentNormal"), Vector2.one * 8f);
tangentHoveredStyle = CreateStyle(Resources.Load<Texture2D>("Path/pointHovered"), Vector2.one * 10f);
selectionRectStyle = GUI.skin.FindStyle("selectionRect");
}
private GUIStyle CreateStyle(Texture2D texture, Vector2 size)
{
var guiStyle = new GUIStyle();
guiStyle.normal.background = texture;
guiStyle.fixedWidth = size.x;
guiStyle.fixedHeight = size.y;
return guiStyle;
}
}
internal class Drawer : IDrawer
{
private IGUIState m_GUIState = new GUIState();
private DefaultStyles m_Styles;
protected DefaultStyles styles
{
get
{
if (m_Styles == null)
m_Styles = new DefaultStyles();
return m_Styles;
}
}
public void DrawSelectionRect(Rect rect)
{
Handles.BeginGUI();
styles.selectionRectStyle.Draw(rect, GUIContent.none, false, false, false, false);
Handles.EndGUI();
}
public void DrawCreatePointPreview(Vector3 position)
{
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointPreviewStyle);
}
public void DrawRemovePointPreview(Vector3 position)
{
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointRemovePreviewStyle);
}
public void DrawPoint(Vector3 position)
{
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointNormalStyle);
}
public void DrawPointHovered(Vector3 position)
{
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointHoveredStyle);
}
public void DrawPointSelected(Vector3 position)
{
DrawGUIStyleCap(0, position, Quaternion.identity, m_GUIState.GetHandleSize(position), styles.pointSelectedStyle);
}
public void DrawLine(Vector3 p1, Vector3 p2, float width, Color color)
{
Handles.color = color;
Handles.DrawAAPolyLine(width, new Vector3[] { p1, p2 });
}
public void DrawDottedLine(Vector3 p1, Vector3 p2, float width, Color color)
{
Handles.color = color;
Handles.DrawDottedLine(p1, p2, width);
}
public void DrawBezier(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, float width, Color color)
{
Handles.color = color;
Handles.DrawBezier(p1, p4, p2, p3, color, null, width);
}
public void DrawTangent(Vector3 position, Vector3 tangent)
{
DrawLine(position, tangent, 3f, Color.yellow);
DrawGUIStyleCap(0, tangent, Quaternion.identity, m_GUIState.GetHandleSize(tangent), styles.tangentNormalStyle);
}
private void DrawGUIStyleCap(int controlID, Vector3 position, Quaternion rotation, float size, GUIStyle guiStyle)
{
if (Camera.current && Vector3.Dot(position - Camera.current.transform.position, Camera.current.transform.forward) < 0f)
return;
Handles.BeginGUI();
guiStyle.Draw(GetGUIStyleRect(guiStyle, position), GUIContent.none, controlID);
Handles.EndGUI();
}
private Rect GetGUIStyleRect(GUIStyle style, Vector3 position)
{
Vector2 vector = HandleUtility.WorldToGUIPoint(position);
float fixedWidth = style.fixedWidth;
float fixedHeight = style.fixedHeight;
return new Rect(vector.x - fixedWidth / 2f, vector.y - fixedHeight / 2f, fixedWidth, fixedHeight);
}
}
}

View File

@@ -0,0 +1,489 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal class EditablePathView : IEditablePathView
{
const float kSnappingDistance = 15f;
const string kDeleteCommandName = "Delete";
const string kSoftDeleteCommandName = "SoftDelete";
public IEditablePathController controller { get; set; }
private Control m_PointControl;
private Control m_EdgeControl;
private Control m_LeftTangentControl;
private Control m_RightTangentControl;
private GUIAction m_MovePointAction;
private GUIAction m_MoveEdgeAction;
private GUIAction m_CreatePointAction;
private GUIAction m_RemovePointAction1;
private GUIAction m_RemovePointAction2;
private GUIAction m_MoveLeftTangentAction;
private GUIAction m_MoveRightTangentAction;
private IDrawer m_Drawer;
public EditablePathView() : this(new Drawer()) {}
public EditablePathView(IDrawer drawer)
{
m_Drawer = drawer;
m_PointControl = new GenericControl("Point")
{
count = GetPointCount,
distance = (guiState, i) =>
{
var position = GetPoint(i).position;
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
},
position = (i) => { return GetPoint(i).position; },
forward = (i) => { return GetForward(); },
up = (i) => { return GetUp(); },
right = (i) => { return GetRight(); },
onRepaint = DrawPoint
};
m_EdgeControl = new GenericControl("Edge")
{
onEndLayout = (guiState) => { controller.AddClosestPath(m_EdgeControl.layoutData.distance); },
count = GetEdgeCount,
distance = DistanceToEdge,
position = (i) => { return GetPoint(i).position; },
forward = (i) => { return GetForward(); },
up = (i) => { return GetUp(); },
right = (i) => { return GetRight(); },
onRepaint = DrawEdge
};
m_LeftTangentControl = new GenericControl("LeftTangent")
{
count = () =>
{
if (GetShapeType() != ShapeType.Spline)
return 0;
return GetPointCount();
},
distance = (guiState, i) =>
{
if (!IsSelected(i) || IsOpenEnded() && i == 0)
return float.MaxValue;
var position = GetLeftTangent(i);
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
},
position = (i) => { return GetLeftTangent(i); },
forward = (i) => { return GetForward(); },
up = (i) => { return GetUp(); },
right = (i) => { return GetRight(); },
onRepaint = (guiState, control, i) =>
{
if (!IsSelected(i) || IsOpenEnded() && i == 0)
return;
var position = GetPoint(i).position;
var leftTangent = GetLeftTangent(i);
m_Drawer.DrawTangent(position, leftTangent);
}
};
m_RightTangentControl = new GenericControl("RightTangent")
{
count = () =>
{
if (GetShapeType() != ShapeType.Spline)
return 0;
return GetPointCount();
},
distance = (guiState, i) =>
{
if (!IsSelected(i) || IsOpenEnded() && i == GetPointCount() - 1)
return float.MaxValue;
var position = GetRightTangent(i);
return guiState.DistanceToCircle(position, guiState.GetHandleSize(position) * 10f);
},
position = (i) => { return GetRightTangent(i); },
forward = (i) => { return GetForward(); },
up = (i) => { return GetUp(); },
right = (i) => { return GetRight(); },
onRepaint = (guiState, control, i) =>
{
if (!IsSelected(i) || IsOpenEnded() && i == GetPointCount() - 1)
return;
var position = GetPoint(i).position;
var rightTangent = GetRightTangent(i);
m_Drawer.DrawTangent(position, rightTangent);
}
};
m_CreatePointAction = new CreatePointAction(m_PointControl, m_EdgeControl)
{
enable = (guiState, action) => { return !guiState.isShiftDown && controller.closestEditablePath == controller.editablePath; },
enableRepaint = EnableCreatePointRepaint,
repaintOnMouseMove = (guiState, action) => { return true; },
guiToWorld = GUIToWorld,
onCreatePoint = (index, position) =>
{
controller.RegisterUndo("Create Point");
controller.CreatePoint(index, position);
},
onPreRepaint = (guiState, action) =>
{
if (GetPointCount() > 0)
{
var position = ClosestPointInEdge(guiState, guiState.mousePosition, m_EdgeControl.layoutData.index);
m_Drawer.DrawCreatePointPreview(position);
}
}
};
Action<IGUIState> removePoints = (guiState) =>
{
controller.RegisterUndo("Remove Point");
controller.RemoveSelectedPoints();
guiState.changed = true;
};
m_RemovePointAction1 = new CommandAction(kDeleteCommandName)
{
enable = (guiState, action) => { return GetSelectedPointCount() > 0; },
onCommand = removePoints
};
m_RemovePointAction2 = new CommandAction(kSoftDeleteCommandName)
{
enable = (guiState, action) => { return GetSelectedPointCount() > 0; },
onCommand = removePoints
};
m_MovePointAction = new SliderAction(m_PointControl)
{
onClick = (guiState, control) =>
{
var index = control.layoutData.index;
if (!guiState.isActionKeyDown && !IsSelected(index))
controller.ClearSelection();
controller.SelectPoint(index, true);
guiState.changed = true;
},
onSliderBegin = (guiState, control, position) =>
{
controller.RegisterUndo("Move Point");
},
onSliderChanged = (guiState, control, position) =>
{
var index = control.hotLayoutData.index;
var delta = SnapIfNeeded(position) - GetPoint(index).position;
controller.MoveSelectedPoints(delta);
}
};
m_MoveEdgeAction = new SliderAction(m_EdgeControl)
{
enable = (guiState, action) => { return guiState.isShiftDown; },
onSliderBegin = (guiState, control, position) =>
{
controller.RegisterUndo("Move Edge");
},
onSliderChanged = (guiState, control, position) =>
{
var index = control.hotLayoutData.index;
var delta = position - GetPoint(index).position;
controller.MoveEdge(index, delta);
}
};
var cachedRightTangent = Vector3.zero;
var cachedLeftTangent = Vector3.zero;
m_MoveLeftTangentAction = new SliderAction(m_LeftTangentControl)
{
onSliderBegin = (guiState, control, position) =>
{
controller.RegisterUndo("Move Tangent");
cachedRightTangent = GetPoint(control.hotLayoutData.index).rightTangent;
},
onSliderChanged = (guiState, control, position) =>
{
var index = control.hotLayoutData.index;
var setToLinear = guiState.nearestControl == m_PointControl.ID && m_PointControl.layoutData.index == index;
controller.SetLeftTangent(index, position, setToLinear, guiState.isShiftDown, cachedRightTangent);
},
onSliderEnd = (guiState, control, position) =>
{
controller.editablePath.UpdateTangentMode(control.hotLayoutData.index);
guiState.changed = true;
}
};
m_MoveRightTangentAction = new SliderAction(m_RightTangentControl)
{
onSliderBegin = (guiState, control, position) =>
{
controller.RegisterUndo("Move Tangent");
cachedLeftTangent = GetPoint(control.hotLayoutData.index).leftTangent;
},
onSliderChanged = (guiState, control, position) =>
{
var index = control.hotLayoutData.index;
var setToLinear = guiState.nearestControl == m_PointControl.ID && m_PointControl.layoutData.index == index;
controller.SetRightTangent(index, position, setToLinear, guiState.isShiftDown, cachedLeftTangent);
},
onSliderEnd = (guiState, control, position) =>
{
controller.editablePath.UpdateTangentMode(control.hotLayoutData.index);
guiState.changed = true;
}
};
}
public void Install(GUISystem guiSystem)
{
guiSystem.AddControl(m_EdgeControl);
guiSystem.AddControl(m_PointControl);
guiSystem.AddControl(m_LeftTangentControl);
guiSystem.AddControl(m_RightTangentControl);
guiSystem.AddAction(m_CreatePointAction);
guiSystem.AddAction(m_RemovePointAction1);
guiSystem.AddAction(m_RemovePointAction2);
guiSystem.AddAction(m_MovePointAction);
guiSystem.AddAction(m_MoveEdgeAction);
guiSystem.AddAction(m_MoveLeftTangentAction);
guiSystem.AddAction(m_MoveRightTangentAction);
}
public void Uninstall(GUISystem guiSystem)
{
guiSystem.RemoveControl(m_EdgeControl);
guiSystem.RemoveControl(m_PointControl);
guiSystem.RemoveControl(m_LeftTangentControl);
guiSystem.RemoveControl(m_RightTangentControl);
guiSystem.RemoveAction(m_CreatePointAction);
guiSystem.RemoveAction(m_RemovePointAction1);
guiSystem.RemoveAction(m_RemovePointAction2);
guiSystem.RemoveAction(m_MovePointAction);
guiSystem.RemoveAction(m_MoveEdgeAction);
guiSystem.RemoveAction(m_MoveLeftTangentAction);
guiSystem.RemoveAction(m_MoveRightTangentAction);
}
private ControlPoint GetPoint(int index)
{
return controller.editablePath.GetPoint(index);
}
private int GetPointCount()
{
return controller.editablePath.pointCount;
}
private int GetEdgeCount()
{
if (controller.editablePath.isOpenEnded)
return controller.editablePath.pointCount - 1;
return controller.editablePath.pointCount;
}
private int GetSelectedPointCount()
{
return controller.editablePath.selection.Count;
}
private bool IsSelected(int index)
{
return controller.editablePath.selection.Contains(index);
}
private Vector3 GetForward()
{
return controller.editablePath.forward;
}
private Vector3 GetUp()
{
return controller.editablePath.up;
}
private Vector3 GetRight()
{
return controller.editablePath.right;
}
private Matrix4x4 GetLocalToWorldMatrix()
{
return controller.editablePath.localToWorldMatrix;
}
private ShapeType GetShapeType()
{
return controller.editablePath.shapeType;
}
private bool IsOpenEnded()
{
return controller.editablePath.isOpenEnded;
}
private Vector3 GetLeftTangent(int index)
{
return controller.editablePath.CalculateLeftTangent(index);
}
private Vector3 GetRightTangent(int index)
{
return controller.editablePath.CalculateRightTangent(index);
}
private int NextIndex(int index)
{
return EditablePathUtility.Mod(index + 1, GetPointCount());
}
private ControlPoint NextControlPoint(int index)
{
return GetPoint(NextIndex(index));
}
private int PrevIndex(int index)
{
return EditablePathUtility.Mod(index - 1, GetPointCount());
}
private ControlPoint PrevControlPoint(int index)
{
return GetPoint(PrevIndex(index));
}
private Vector3 ClosestPointInEdge(IGUIState guiState, Vector2 mousePosition, int index)
{
if (GetShapeType() == ShapeType.Polygon)
{
var p0 = GetPoint(index).position;
var p1 = NextControlPoint(index).position;
var mouseWorldPosition = GUIToWorld(guiState, mousePosition);
var dir1 = (mouseWorldPosition - p0);
var dir2 = (p1 - p0);
return Mathf.Clamp01(Vector3.Dot(dir1, dir2.normalized) / dir2.magnitude) * dir2 + p0;
}
else if (GetShapeType() == ShapeType.Spline)
{
var nextIndex = NextIndex(index);
float t;
return BezierUtility.ClosestPointOnCurve(
GUIToWorld(guiState, mousePosition),
GetPoint(index).position,
GetPoint(nextIndex).position,
GetRightTangent(index),
GetLeftTangent(nextIndex),
out t);
}
return Vector3.zero;
}
private float DistanceToEdge(IGUIState guiState, int index)
{
if (GetShapeType() == ShapeType.Polygon)
{
return guiState.DistanceToSegment(GetPoint(index).position, NextControlPoint(index).position);
}
else if (GetShapeType() == ShapeType.Spline)
{
var closestPoint = ClosestPointInEdge(guiState, guiState.mousePosition, index);
var closestPoint2 = HandleUtility.WorldToGUIPoint(closestPoint);
return (closestPoint2 - guiState.mousePosition).magnitude;
}
return float.MaxValue;
}
private Vector3 GUIToWorld(IGUIState guiState, Vector2 position)
{
return guiState.GUIToWorld(position, GetForward(), GetLocalToWorldMatrix().MultiplyPoint3x4(Vector3.zero));
}
private void DrawPoint(IGUIState guiState, Control control, int index)
{
var position = GetPoint(index).position;
if (guiState.hotControl == control.actionID && control.hotLayoutData.index == index || IsSelected(index))
m_Drawer.DrawPointSelected(position);
else if (guiState.hotControl == 0 && guiState.nearestControl == control.ID && control.layoutData.index == index)
m_Drawer.DrawPointHovered(position);
else
m_Drawer.DrawPoint(position);
}
private void DrawEdge(IGUIState guiState, Control control, int index)
{
if (GetShapeType() == ShapeType.Polygon)
{
var nextIndex = NextIndex(index);
var color = Color.white;
if (guiState.nearestControl == control.ID && control.layoutData.index == index && guiState.hotControl == 0)
color = Color.yellow;
m_Drawer.DrawLine(GetPoint(index).position, GetPoint(nextIndex).position, 5f, color);
}
else if (GetShapeType() == ShapeType.Spline)
{
var nextIndex = NextIndex(index);
var color = Color.white;
if (guiState.nearestControl == control.ID && control.layoutData.index == index && guiState.hotControl == 0)
color = Color.yellow;
m_Drawer.DrawBezier(
GetPoint(index).position,
GetRightTangent(index),
GetLeftTangent(nextIndex),
GetPoint(nextIndex).position,
5f,
color);
}
}
private bool EnableCreatePointRepaint(IGUIState guiState, GUIAction action)
{
return guiState.nearestControl != m_PointControl.ID &&
guiState.hotControl == 0 &&
(guiState.nearestControl != m_LeftTangentControl.ID) &&
(guiState.nearestControl != m_RightTangentControl.ID);
}
private Vector3 SnapIfNeeded(Vector3 position)
{
if (!controller.enableSnapping || controller.snapping == null)
return position;
var guiPosition = HandleUtility.WorldToGUIPoint(position);
var snappedGuiPosition = HandleUtility.WorldToGUIPoint(controller.snapping.Snap(position));
var sqrDistance = (guiPosition - snappedGuiPosition).sqrMagnitude;
if (sqrDistance < kSnappingDistance * kSnappingDistance)
position = controller.snapping.Snap(position);
return position;
}
}
}

View File

@@ -0,0 +1,18 @@
using UnityEngine;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface IDrawer
{
void DrawSelectionRect(Rect rect);
void DrawCreatePointPreview(Vector3 position);
void DrawRemovePointPreview(Vector3 position);
void DrawPoint(Vector3 position);
void DrawPointHovered(Vector3 position);
void DrawPointSelected(Vector3 position);
void DrawLine(Vector3 p1, Vector3 p2, float width, Color color);
void DrawDottedLine(Vector3 p1, Vector3 p2, float width, Color color);
void DrawBezier(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, float width, Color color);
void DrawTangent(Vector3 position, Vector3 tangent);
}
}

View File

@@ -0,0 +1,13 @@
using System;
using UnityEngine;
using UnityEditor.Experimental.Rendering.Universal.Path2D.GUIFramework;
namespace UnityEditor.Experimental.Rendering.Universal.Path2D
{
internal interface IEditablePathView
{
IEditablePathController controller { get; set; }
void Install(GUISystem guiSystem);
void Uninstall(GUISystem guiSystem);
}
}

View File

@@ -0,0 +1,148 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
namespace UnityEditor.Experimental.Rendering.Universal
{
internal class SortingLayerDropDown
{
private class LayerSelectionData
{
public SerializedObject serializedObject;
public Object[] targets;
public int layerID;
public System.Action<SerializedObject> onSelectionChanged;
public LayerSelectionData(SerializedObject so, int lid, Object[] tgts, System.Action<SerializedObject> selectionChangedCallback)
{
serializedObject = so;
layerID = lid;
targets = tgts;
onSelectionChanged = selectionChangedCallback;
}
}
private static class Styles
{
public static GUIContent sortingLayerAll = EditorGUIUtility.TrTextContent("All");
public static GUIContent sortingLayerNone = EditorGUIUtility.TrTextContent("None");
public static GUIContent sortingLayerMixed = EditorGUIUtility.TrTextContent("Mixed...");
}
SortingLayer[] m_AllSortingLayers;
GUIContent[] m_AllSortingLayerNames;
List<int> m_ApplyToSortingLayersList;
SerializedProperty m_ApplyToSortingLayers;
public void OnEnable(SerializedObject serializedObject, string propertyName)
{
m_ApplyToSortingLayers = serializedObject.FindProperty(propertyName);
m_ApplyToSortingLayersList = new List<int>(m_ApplyToSortingLayers.arraySize);
m_AllSortingLayers = SortingLayer.layers;
m_AllSortingLayerNames = m_AllSortingLayers.Select(x => new GUIContent(x.name)).ToArray();
}
void UpdateApplyToSortingLayersArray(object layerSelectionDataObject)
{
LayerSelectionData layerSelectionData = (LayerSelectionData)layerSelectionDataObject;
m_ApplyToSortingLayers.ClearArray();
for (int i = 0; i < m_ApplyToSortingLayersList.Count; ++i)
{
m_ApplyToSortingLayers.InsertArrayElementAtIndex(i);
m_ApplyToSortingLayers.GetArrayElementAtIndex(i).intValue = m_ApplyToSortingLayersList[i];
}
if (layerSelectionData.onSelectionChanged != null)
layerSelectionData.onSelectionChanged(layerSelectionData.serializedObject);
layerSelectionData.serializedObject.ApplyModifiedProperties();
if (layerSelectionData.targets is Light2D[])
{
foreach (Light2D light in layerSelectionData.targets)
{
if (light != null && light.lightType == Light2D.LightType.Global)
Light2DManager.ErrorIfDuplicateGlobalLight(light);
}
}
}
void OnNoSortingLayerSelected(object selectionData)
{
m_ApplyToSortingLayersList.Clear();
UpdateApplyToSortingLayersArray(selectionData);
}
void OnAllSortingLayersSelected(object selectionData)
{
m_ApplyToSortingLayersList.Clear();
m_ApplyToSortingLayersList.AddRange(m_AllSortingLayers.Select(x => x.id));
UpdateApplyToSortingLayersArray(selectionData);
}
void OnSortingLayerSelected(object layerSelectionDataObject)
{
LayerSelectionData layerSelectionData = (LayerSelectionData)layerSelectionDataObject;
int layerID = (int)layerSelectionData.layerID;
if (m_ApplyToSortingLayersList.Contains(layerID))
m_ApplyToSortingLayersList.RemoveAll(id => id == layerID);
else
m_ApplyToSortingLayersList.Add(layerID);
UpdateApplyToSortingLayersArray(layerSelectionDataObject);
}
public void OnTargetSortingLayers(SerializedObject serializedObject, Object[] targets, GUIContent labelContent, System.Action<SerializedObject> selectionChangedCallback)
{
Rect totalPosition = EditorGUILayout.GetControlRect();
GUIContent actualLabel = EditorGUI.BeginProperty(totalPosition, labelContent, m_ApplyToSortingLayers);
Rect position = EditorGUI.PrefixLabel(totalPosition, actualLabel);
m_ApplyToSortingLayersList.Clear();
int applyToSortingLayersSize = m_ApplyToSortingLayers.arraySize;
for (int i = 0; i < applyToSortingLayersSize; ++i)
{
int layerID = m_ApplyToSortingLayers.GetArrayElementAtIndex(i).intValue;
if (SortingLayer.IsValid(layerID))
m_ApplyToSortingLayersList.Add(layerID);
}
GUIContent selectedLayers;
if (m_ApplyToSortingLayersList.Count == 1)
selectedLayers = new GUIContent(SortingLayer.IDToName(m_ApplyToSortingLayersList[0]));
else if (m_ApplyToSortingLayersList.Count == m_AllSortingLayers.Length)
selectedLayers = Styles.sortingLayerAll;
else if (m_ApplyToSortingLayersList.Count == 0)
selectedLayers = Styles.sortingLayerNone;
else
selectedLayers = Styles.sortingLayerMixed;
if (EditorGUI.DropdownButton(position, selectedLayers, FocusType.Keyboard, EditorStyles.popup))
{
GenericMenu menu = new GenericMenu();
menu.allowDuplicateNames = true;
LayerSelectionData layerSelectionData = new LayerSelectionData(serializedObject, 0, targets, selectionChangedCallback);
menu.AddItem(Styles.sortingLayerNone, m_ApplyToSortingLayersList.Count == 0, OnNoSortingLayerSelected, layerSelectionData);
menu.AddItem(Styles.sortingLayerAll, m_ApplyToSortingLayersList.Count == m_AllSortingLayers.Length, OnAllSortingLayersSelected, layerSelectionData);
menu.AddSeparator("");
for (int i = 0; i < m_AllSortingLayers.Length; ++i)
{
var sortingLayer = m_AllSortingLayers[i];
layerSelectionData = new LayerSelectionData(serializedObject, sortingLayer.id, targets, selectionChangedCallback);
menu.AddItem(m_AllSortingLayerNames[i], m_ApplyToSortingLayersList.Contains(sortingLayer.id), OnSortingLayerSelected, layerSelectionData);
}
menu.DropDown(position);
}
EditorGUI.EndProperty();
}
}
}

View File

@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("UniversalGraphicsTests")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.GraphicTests.Performance.Universal.Editor")]

View File

@@ -0,0 +1,342 @@
using UnityEngine;
using UnityEditor.AssetImporters;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace UnityEditor.Rendering.Universal
{
class AutodeskInteractiveMaterialImport : AssetPostprocessor
{
static readonly uint k_Version = 1;
static readonly int k_Order = 3;
public override uint GetVersion()
{
return k_Version;
}
public override int GetPostprocessOrder()
{
return k_Order;
}
public void OnPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] clips)
{
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
if (!pipelineAsset || pipelineAsset.GetType() != typeof(UniversalRenderPipelineAsset))
return;
if (IsAutodeskInteractiveMaterial(description))
{
float floatProperty;
Vector4 vectorProperty;
TexturePropertyDescription textureProperty;
bool isMasked = description.TryGetProperty("mask_threshold", out floatProperty);
bool isTransparent = description.TryGetProperty("opacity", out floatProperty);
Shader shader;
if (isMasked)
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveMaskedShader;
else if (isTransparent)
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveTransparentShader;
else
shader = GraphicsSettings.currentRenderPipeline.autodeskInteractiveShader;
if (shader == null)
return;
material.shader = shader;
foreach (var clip in clips)
{
clip.ClearCurves();
}
if (description.TryGetProperty("base_color", out vectorProperty))
material.SetColor("_Color", vectorProperty);
if (description.TryGetProperty("emissive", out vectorProperty))
material.SetColor("_EmissionColor", vectorProperty);
if (description.TryGetProperty("roughness", out floatProperty))
material.SetFloat("_Roughness", floatProperty);
if (description.TryGetProperty("metallic", out floatProperty))
material.SetFloat("_Metallic", floatProperty);
if (description.TryGetProperty("uvTransform", out vectorProperty))
{
material.SetVector("_UvOffset", new Vector4(vectorProperty.x, vectorProperty.y, .0f, .0f));
material.SetVector("_UvTiling", new Vector4(vectorProperty.w, vectorProperty.z, .0f, .0f));
}
if (description.TryGetProperty("TEX_color_map", out textureProperty))
{
material.SetTexture("_MainTex", textureProperty.texture);
material.SetFloat("_UseColorMap", 1.0f);
}
else
{
material.SetFloat("_UseColorMap", 0.0f);
}
if (description.TryGetProperty("TEX_normal_map", out textureProperty))
{
material.SetTexture("_BumpMap", textureProperty.texture);
material.SetFloat("_UseNormalMap", 1.0f);
}
else
{
material.SetFloat("_UseNormalMap", 0.0f);
}
if (description.TryGetProperty("TEX_roughness_map", out textureProperty))
{
material.SetTexture("RoughnessMap", textureProperty.texture);
material.SetFloat("_UseRoughnessMap", 1.0f);
}
else
{
material.SetFloat("_UseRoughnessMap", 0.0f);
}
if (description.TryGetProperty("TEX_metallic_map", out textureProperty))
{
material.SetTexture("_MetallicMap", textureProperty.texture);
material.SetFloat("_UseMetallicMap", 1.0f);
}
else
{
material.SetFloat("_UseMetallicMap", 0.0f);
}
if (description.TryGetProperty("TEX_emissive_map", out textureProperty))
{
material.SetTexture("_EmissionMap", textureProperty.texture);
material.SetFloat("_UseEmissiveMap", 1.0f);
}
else
{
material.SetFloat("_UseEmissiveMap", 0.0f);
}
if (description.TryGetProperty("hasTransparencyTexture", out floatProperty))
material.SetFloat("_UseOpacityMap", floatProperty);
if (description.TryGetProperty("transparencyMaskThreshold", out floatProperty))
material.SetFloat("_OpacityThreshold", floatProperty);
if (description.TryGetProperty("TEX_ao_map", out textureProperty))
{
var tex = AssetDatabase.LoadAssetAtPath<Texture>(textureProperty.relativePath);
material.SetTexture("AoMap", tex);
material.SetFloat("UseAoMap", 1.0f);
}
else
{
material.SetFloat("UseAoMap", 0.0f);
}
RemapColorCurves(description, clips, "base_color", "_Color");
RemapCurve(description, clips, "mask_threshold", "_Cutoff");
RemapCurve(description, clips, "metallic", "_Metallic");
RemapCurve(description, clips, "roughness", "_Glossiness");
for (int i = 0; i < clips.Length; i++)
{
if (description.HasAnimationCurveInClip(clips[i].name, "uv_scale.x") || description.HasAnimationCurveInClip(clips[i].name, "uv_scale.y"))
{
AnimationCurve curve;
if (description.TryGetAnimationCurve(clips[i].name, "uv_scale.x", out curve))
clips[i].SetCurve("", typeof(Material), "_UvTiling.x", curve);
else
clips[i].SetCurve("", typeof(Material), "_UvTiling.x", AnimationCurve.Constant(0.0f, 1.0f, 1.0f));
if (description.TryGetAnimationCurve(clips[i].name, "uv_scale.y", out curve))
clips[i].SetCurve("", typeof(Material), "_UvTiling.y", curve);
else
clips[i].SetCurve("", typeof(Material), "_UvTiling.y", AnimationCurve.Constant(0.0f, 1.0f, 1.0f));
}
if (description.HasAnimationCurveInClip(clips[i].name, "uv_offset.x") || description.HasAnimationCurveInClip(clips[i].name, "uv_offset.y"))
{
AnimationCurve curve;
if (description.TryGetAnimationCurve(clips[i].name, "uv_offset.x", out curve))
clips[i].SetCurve("", typeof(Material), "_UvOffset.x", curve);
else
clips[i].SetCurve("", typeof(Material), "_UvOffset.x", AnimationCurve.Constant(0.0f, 1.0f, 0.0f));
if (description.TryGetAnimationCurve(clips[i].name, "uv_offset.y", out curve))
{
ConvertKeys(curve, ConvertFloatNegate);
clips[i].SetCurve("", typeof(Material), "_UvOffset.y", curve);
}
else
clips[i].SetCurve("", typeof(Material), "_UvOffset.y", AnimationCurve.Constant(0.0f, 1.0f, 0.0f));
}
}
if (description.HasAnimationCurve("emissive_intensity"))
{
Vector4 emissiveColor;
description.TryGetProperty("emissive", out emissiveColor);
for (int i = 0; i < clips.Length; i++)
{
AnimationCurve curve;
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out curve);
// remap emissive intensity to emission color
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curve);
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curve);
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curve);
}
}
else if (description.TryGetProperty("emissive", out vectorProperty))
{
if (vectorProperty.x > 0.0f || vectorProperty.y > 0.0f || vectorProperty.z > 0.0f)
{
material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.RealtimeEmissive;
material.EnableKeyword("_EMISSION");
}
if (description.TryGetProperty("emissive_intensity", out floatProperty))
{
vectorProperty *= floatProperty;
}
material.SetColor("_EmissionColor", vectorProperty);
if (description.HasAnimationCurve("emissive.x"))
{
if (description.HasAnimationCurve("emissive_intensity"))
{
// combine color and intensity.
for (int i = 0; i < clips.Length; i++)
{
AnimationCurve curve;
AnimationCurve intensityCurve;
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out intensityCurve);
description.TryGetAnimationCurve(clips[i].name, "emissive.x", out curve);
MultiplyCurves(curve, intensityCurve);
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curve);
description.TryGetAnimationCurve(clips[i].name, "emissive.y", out curve);
MultiplyCurves(curve, intensityCurve);
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curve);
description.TryGetAnimationCurve(clips[i].name, "emissive.z", out curve);
MultiplyCurves(curve, intensityCurve);
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curve);
}
}
else
{
RemapColorCurves(description, clips, "emissive", "_EmissionColor");
}
}
else if (description.HasAnimationCurve("emissive_intensity"))
{
Vector4 emissiveColor;
description.TryGetProperty("emissive", out emissiveColor);
for (int i = 0; i < clips.Length; i++)
{
AnimationCurve curve;
description.TryGetAnimationCurve(clips[i].name, "emissive_intensity", out curve);
// remap emissive intensity to emission color
AnimationCurve curveR = new AnimationCurve();
ConvertAndCopyKeys(curveR, curve, value => ConvertFloatMultiply(emissiveColor.x, value));
clips[i].SetCurve("", typeof(Material), "_EmissionColor.r", curveR);
AnimationCurve curveG = new AnimationCurve();
ConvertAndCopyKeys(curveG, curve, value => ConvertFloatMultiply(emissiveColor.y, value));
clips[i].SetCurve("", typeof(Material), "_EmissionColor.g", curveG);
AnimationCurve curveB = new AnimationCurve();
ConvertAndCopyKeys(curveB, curve, value => ConvertFloatMultiply(emissiveColor.z, value));
clips[i].SetCurve("", typeof(Material), "_EmissionColor.b", curveB);
}
}
}
}
}
static bool IsAutodeskInteractiveMaterial(MaterialDescription description)
{
return description.TryGetProperty("renderAPI", out string stringValue) && stringValue == "SFX_PBS_SHADER";
}
static void ConvertKeys(AnimationCurve curve, System.Func<float, float> convertionDelegate)
{
Keyframe[] keyframes = curve.keys;
for (int i = 0; i < keyframes.Length; i++)
{
keyframes[i].value = convertionDelegate(keyframes[i].value);
}
curve.keys = keyframes;
}
static void ConvertAndCopyKeys(AnimationCurve curveDest, AnimationCurve curveSource, System.Func<float, float> convertionDelegate)
{
for (int i = 0; i < curveSource.keys.Length; i++)
{
var sourceKey = curveSource.keys[i];
curveDest.AddKey(new Keyframe(sourceKey.time, convertionDelegate(sourceKey.value), sourceKey.inTangent, sourceKey.outTangent, sourceKey.inWeight, sourceKey.outWeight));
}
}
static float ConvertFloatNegate(float value)
{
return -value;
}
static float ConvertFloatMultiply(float value, float multiplier)
{
return value * multiplier;
}
static void MultiplyCurves(AnimationCurve curve, AnimationCurve curveMultiplier)
{
Keyframe[] keyframes = curve.keys;
for (int i = 0; i < keyframes.Length; i++)
{
keyframes[i].value *= curveMultiplier.Evaluate(keyframes[i].time);
}
curve.keys = keyframes;
}
static void RemapCurve(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName)
{
AnimationCurve curve;
for (int i = 0; i < clips.Length; i++)
{
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName, out curve))
{
clips[i].SetCurve("", typeof(Material), newPropertyName, curve);
}
}
}
static void RemapColorCurves(MaterialDescription description, AnimationClip[] clips, string originalPropertyName, string newPropertyName)
{
AnimationCurve curve;
for (int i = 0; i < clips.Length; i++)
{
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".x", out curve))
{
clips[i].SetCurve("", typeof(Material), newPropertyName + ".r", curve);
}
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".y", out curve))
{
clips[i].SetCurve("", typeof(Material), newPropertyName + ".g", curve);
}
if (description.TryGetAnimationCurve(clips[i].name, originalPropertyName + ".z", out curve))
{
clips[i].SetCurve("", typeof(Material), newPropertyName + ".b", curve);
}
}
}
}
}

View File

@@ -0,0 +1,317 @@
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
namespace UnityEditor.Rendering.Universal
{
class FBXArnoldSurfaceMaterialDescriptionPreprocessor : AssetPostprocessor
{
static readonly uint k_Version = 2;
static readonly int k_Order = 4;
static readonly string k_ShaderPath = "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ArnoldStandardSurface/ArnoldStandardSurface.shadergraph";
static readonly string k_ShaderTransparentPath = "Packages/com.unity.render-pipelines.universal/Runtime/Materials/ArnoldStandardSurface/ArnoldStandardSurfaceTransparent.shadergraph";
public override uint GetVersion()
{
return k_Version;
}
public override int GetPostprocessOrder()
{
return k_Order;
}
static bool IsMayaArnoldStandardSurfaceMaterial(MaterialDescription description)
{
float typeId;
description.TryGetProperty("TypeId", out typeId);
return typeId == 1138001;
}
static bool Is3DsMaxArnoldStandardSurfaceMaterial(MaterialDescription description)
{
float classIdA;
float classIdB;
string originalMtl;
description.TryGetProperty("ClassIDa", out classIdA);
description.TryGetProperty("ClassIDb", out classIdB);
description.TryGetProperty("ORIGINAL_MTL", out originalMtl);
return classIdA == 2121471519 && classIdB == 1660373836 && originalMtl != "PHYSICAL_MTL";
}
public void OnPreprocessMaterialDescription(MaterialDescription description, Material material,
AnimationClip[] clips)
{
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
if (!pipelineAsset || pipelineAsset.GetType() != typeof(UniversalRenderPipelineAsset))
return;
var lowerCasePath = Path.GetExtension(assetPath).ToLower();
if (lowerCasePath == ".fbx")
{
if (IsMayaArnoldStandardSurfaceMaterial(description))
CreateFromMayaArnoldStandardSurfaceMaterial(description, material, clips);
else if (Is3DsMaxArnoldStandardSurfaceMaterial(description))
CreateFrom3DsMaxArnoldStandardSurfaceMaterial(description, material, clips);
}
}
void CreateFromMayaArnoldStandardSurfaceMaterial(MaterialDescription description, Material material,
AnimationClip[] clips)
{
float floatProperty;
Vector4 vectorProperty;
TexturePropertyDescription textureProperty;
Shader shader;
float opacity = 1.0f;
Vector4 opacityColor;
TexturePropertyDescription opacityMap;
description.TryGetProperty("opacity", out opacityColor);
bool hasOpacityMap = description.TryGetProperty("opacity", out opacityMap);
opacity = Mathf.Min(Mathf.Min(opacityColor.x, opacityColor.y), opacityColor.z);
float transmission;
description.TryGetProperty("transmission", out transmission);
if (opacity == 1.0f && !hasOpacityMap)
{
opacity = 1.0f - transmission;
}
if (opacity < 1.0f || hasOpacityMap)
{
shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderTransparentPath);
if (shader == null)
return;
material.shader = shader;
if (hasOpacityMap)
{
material.SetTexture("_OPACITY_MAP", opacityMap.texture);
material.SetFloat("_OPACITY", 1.0f);
}
else
{
material.SetFloat("_OPACITY", opacity);
}
}
else
{
shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderPath);
if (shader == null)
return;
material.shader = shader;
}
foreach (var clip in clips)
{
clip.ClearCurves();
}
description.TryGetProperty("base", out floatProperty);
if (description.TryGetProperty("baseColor", out textureProperty))
{
SetMaterialTextureProperty("_BASE_COLOR_MAP", material, textureProperty);
material.SetColor("_BASE_COLOR", Color.white * floatProperty);
}
else if (description.TryGetProperty("baseColor", out vectorProperty))
{
if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
{
vectorProperty.x = Mathf.LinearToGammaSpace(vectorProperty.x);
vectorProperty.y = Mathf.LinearToGammaSpace(vectorProperty.y);
vectorProperty.z = Mathf.LinearToGammaSpace(vectorProperty.z);
vectorProperty *= floatProperty;
}
material.SetColor("_BASE_COLOR", vectorProperty * floatProperty);
}
if (description.TryGetProperty("emission", out floatProperty) && floatProperty > 0.0f)
{
remapPropertyColorOrTexture(description, material, "emissionColor", "_EMISSION_COLOR", floatProperty);
}
remapPropertyFloatOrTexture(description, material, "metalness", "_METALNESS");
description.TryGetProperty("specular", out floatProperty);
remapPropertyColorOrTexture(description, material, "specularColor", "_SPECULAR_COLOR", floatProperty);
remapPropertyFloatOrTexture(description, material, "specularRoughness", "_SPECULAR_ROUGHNESS");
remapPropertyFloatOrTexture(description, material, "specularIOR", "_SPECULAR_IOR");
remapPropertyTexture(description, material, "normalCamera", "_NORMAL_MAP");
}
void CreateFrom3DsMaxArnoldStandardSurfaceMaterial(MaterialDescription description, Material material,
AnimationClip[] clips)
{
float floatProperty;
Vector4 vectorProperty;
TexturePropertyDescription textureProperty;
var shader = AssetDatabase.LoadAssetAtPath<Shader>(k_ShaderPath);
if (shader == null)
return;
material.shader = shader;
foreach (var clip in clips)
{
clip.ClearCurves();
}
float opacity = 1.0f;
Vector4 opacityColor;
TexturePropertyDescription opacityMap;
description.TryGetProperty("opacity", out opacityColor);
bool hasOpacityMap = description.TryGetProperty("opacity", out opacityMap);
opacity = Mathf.Min(Mathf.Min(opacityColor.x, opacityColor.y), opacityColor.z);
if (opacity < 1.0f || hasOpacityMap)
{
if (hasOpacityMap)
{
material.SetTexture("_OPACITY_MAP", opacityMap.texture);
material.SetFloat("_OPACITY", 1.0f);
}
else
{
material.SetFloat("_OPACITY", opacity);
}
}
description.TryGetProperty("base", out floatProperty);
if (description.TryGetProperty("base_color.shader", out textureProperty))
{
SetMaterialTextureProperty("_BASE_COLOR_MAP", material, textureProperty);
material.SetColor("_BASE_COLOR", Color.white * floatProperty);
}
else if (description.TryGetProperty("base_color", out vectorProperty))
{
if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
{
vectorProperty.x = Mathf.LinearToGammaSpace(vectorProperty.x);
vectorProperty.y = Mathf.LinearToGammaSpace(vectorProperty.y);
vectorProperty.z = Mathf.LinearToGammaSpace(vectorProperty.z);
}
material.SetColor("_BASE_COLOR", vectorProperty * floatProperty);
}
if (description.TryGetProperty("emission", out floatProperty) && floatProperty > 0.0f)
{
remapPropertyColorOrTexture3DsMax(description, material, "emission_color", "_EMISSION_COLOR",
floatProperty);
}
remapPropertyFloatOrTexture3DsMax(description, material, "metalness", "_METALNESS");
description.TryGetProperty("specular", out float specularFactor);
remapPropertyColorOrTexture3DsMax(description, material, "specular_color", "_SPECULAR_COLOR",
specularFactor);
remapPropertyFloatOrTexture3DsMax(description, material, "specular_roughness", "_SPECULAR_ROUGHNESS");
remapPropertyFloatOrTexture3DsMax(description, material, "specular_IOR", "_SPECULAR_IOR");
remapPropertyTexture(description, material, "normal_camera", "_NORMAL_MAP");
}
static void SetMaterialTextureProperty(string propertyName, Material material,
TexturePropertyDescription textureProperty)
{
material.SetTexture(propertyName, textureProperty.texture);
material.SetTextureOffset(propertyName, textureProperty.offset);
material.SetTextureScale(propertyName, textureProperty.scale);
}
static void remapPropertyFloat(MaterialDescription description, Material material, string inPropName,
string outPropName)
{
if (description.TryGetProperty(inPropName, out float floatProperty))
{
material.SetFloat(outPropName, floatProperty);
}
}
static void remapPropertyTexture(MaterialDescription description, Material material, string inPropName,
string outPropName)
{
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
{
material.SetTexture(outPropName, textureProperty.texture);
}
}
static void remapPropertyColorOrTexture3DsMax(MaterialDescription description, Material material,
string inPropName, string outPropName, float multiplier = 1.0f)
{
if (description.TryGetProperty(inPropName + ".shader", out TexturePropertyDescription textureProperty))
{
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
material.SetColor(outPropName, Color.white * multiplier);
}
else
{
description.TryGetProperty(inPropName, out Vector4 vectorProperty);
material.SetColor(outPropName, vectorProperty * multiplier);
}
}
static void remapPropertyFloatOrTexture3DsMax(MaterialDescription description, Material material,
string inPropName, string outPropName)
{
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
{
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
material.SetFloat(outPropName, 1.0f);
}
else
{
description.TryGetProperty(inPropName, out float floatProperty);
material.SetFloat(outPropName, floatProperty);
}
}
static void remapPropertyColorOrTexture(MaterialDescription description, Material material, string inPropName,
string outPropName, float multiplier = 1.0f)
{
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
{
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
material.SetColor(outPropName, Color.white * multiplier);
}
else
{
description.TryGetProperty(inPropName, out Vector4 vectorProperty);
material.SetColor(outPropName, vectorProperty * multiplier);
}
}
static void remapPropertyFloatOrTexture(MaterialDescription description, Material material, string inPropName,
string outPropName)
{
if (description.TryGetProperty(inPropName, out TexturePropertyDescription textureProperty))
{
material.SetTexture(outPropName + "_MAP", textureProperty.texture);
material.SetFloat(outPropName, 1.0f);
}
else
{
description.TryGetProperty(inPropName, out float floatProperty);
material.SetFloat(outPropName, floatProperty);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More