|
@ -8,7 +8,7 @@ ms.localizationpriority: high
|
|||
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
|
||||
---
|
||||
|
||||
# Clipping Primitives
|
||||
# Clipping primitives
|
||||
|
||||
Clipping primitives can be used to dynamically slice away a mesh and peer inside the geometry. This is useful whenever you need to inspect the inner workings of a complex model.
|
||||
|
||||
|
@ -24,25 +24,25 @@ Graphics Tools contains a few clipping primitive types for developers to pick fr
|
|||
> [!NOTE]
|
||||
> By default, Graphics Tools only transfers the data of one clipping primitive per type per level to materials (i.e., only one `MF_GTClippingSphere`, `MF_GTClippingBox`, etc. has an effect within a level.) To utilize more than one clipping primitive per type per level please see the [advanced usage](#Advanced-usage) section.
|
||||
|
||||
### Clipping Plane
|
||||
### Clipping plane
|
||||
|
||||
The `UGTClippingPlane` component (or actor) represents an infinite plane. The plane's position is controlled by the component's transform location and plane normal is determined by transform's x axis direction. Use the `MF_GTClippingPlane` material function to calculate a point's signed distance from the primitive.
|
||||
|
||||
![ClippingPrimitivesPlane](Images/ClippingPrimitives/ClippingPrimitivesPlane.png)
|
||||
|
||||
### Clipping Sphere
|
||||
### Clipping sphere
|
||||
|
||||
The `UGTClippingSphere` component (or actor) represents a sphere (or [ellipsoid](https://en.wikipedia.org/wiki/Ellipsoid)). The sphere's location, rotation, and scale is controlled by the component's transform. Non-uniform scales will turn the sphere into a ellipsoid. Use the `MF_GTClippingSphere` material function to calculate a point's signed distance from the primitive.
|
||||
|
||||
![ClippingPrimitivesSphere](Images/ClippingPrimitives/ClippingPrimitivesSphere.png)
|
||||
|
||||
### Clipping Box
|
||||
### Clipping box
|
||||
|
||||
The `UGTClippingBox` component (or actor) represents a box (i.e., cube). The box's location, rotation, and scale are controlled by the component's transform. Use the `MF_GTClippingBox` material function to calculate a point's signed distance from the primitive.
|
||||
|
||||
![ClippingPrimitivesCube](Images/ClippingPrimitives/ClippingPrimitivesCube.png)
|
||||
|
||||
### Clipping Cone
|
||||
### Clipping cone
|
||||
|
||||
The `UGTClippingCone` component (or actor) represents a cone or more exactly a resizable capped cylinder. The cone's location and rotation are controlled by the component's transform. The component transform's scale controls the cone's height, bottom cap radius, and top cap radius with the x, y, and z values respectively. Use the `MF_GTClippingCone` material function to calculate a point's signed distance from the primitive.
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ To create a rim lit material use Unreal's built in `Fresnel` material node (1) a
|
|||
|
||||
![Rim Lit Material](Images/Effects/EffectsRimLitMaterial.png)
|
||||
|
||||
# Procedural Normal
|
||||
# Procedural normal
|
||||
|
||||
Surface normals are used extensively in computer graphics to determine which way a surface is facing and how light bounces off a surface. Normally surface normals are calculated for a mesh at creation or import time, but with procedural meshes this calculation may not occur. Fortunately, there is a way to automatically generate surface normals in a pixel shader.
|
||||
|
||||
|
@ -73,7 +73,7 @@ An example material graph of procedural normal calculation turned into a color c
|
|||
|
||||
![Procedural Normal](Images/Effects/EffectsProceduralNormal.png)
|
||||
|
||||
# Biplanar Mapping
|
||||
# Biplanar mapping
|
||||
|
||||
Biplanar mapping is a technique that projects a texture map onto a 3D surface based on vertex positions and normals. Textures are normally mapped onto a 3D surface using texture coordinates (or UVs) but occasionally a mesh does not contain texture coordinates or it is desirable to procedurally generate texture coordinates based on a the position of a mesh. This effect is commonly used for terrain or a [spatial mesh](SpatialPerception.md).
|
||||
|
||||
|
|
После Ширина: | Высота: | Размер: 101 KiB |
После Ширина: | Высота: | Размер: 256 KiB |
После Ширина: | Высота: | Размер: 275 KiB |
После Ширина: | Высота: | Размер: 231 KiB |
После Ширина: | Высота: | Размер: 481 KiB |
После Ширина: | Высота: | Размер: 270 KiB |
После Ширина: | Высота: | Размер: 100 KiB |
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
title: MeshOutlines
|
||||
description: Guide to graphic resources and techniques in Graphics Tools.
|
||||
author: Cameron-Micka
|
||||
ms.author: thmicka
|
||||
ms.date: 07/27/2021
|
||||
ms.localizationpriority: high
|
||||
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
|
||||
---
|
||||
|
||||
# Mesh outlines
|
||||
|
||||
Outlines, sometimes referred to as stroke or silhouette effect, are a common design pattern to demonstrate when an object is selected or to draw attention to an object. Many modern mesh outline techniques are done using [post processing](https://docs.unrealengine.com/4.26/en-US/RenderingAndGraphics/PostProcessEffects/) effects. Post processing provides flexible and great quality outlines but can be prohibitively expensive on mobile mixed reality devices. To avoid performance issues, we can rely on methods used in video games of the past, such as an "inverted hull" method.
|
||||
|
||||
![MeshOutlines](Images/FeatureCards/MeshOutlines.png)
|
||||
|
||||
The inverted hull method requires that the object being outlined is rendered twice, but for most meshes this is cheaper than introducing an entire post processing pipeline. An outlined mesh is first rendered normally, then it is rendered a second time flipped inside out and slightly larger. Normally a shader will dynamically make the mesh larger by extruding vertices along a vertex normal. Graphics Tools has the `GTMeshOutline` component and `M_GTDefaultOutline` material to help with rendering a mesh outline and with outline mesh preparation. Details on how to use these components can be found in the [example usage](#Example-usage) section.
|
||||
|
||||
Note, the inverted hull technique has a few caveats that are worth mentioning before continuing:
|
||||
|
||||
* The outline mesh must be watertight hull (and not double sided) else you may see split edges, holes, or other artifacts. (Graphics Tools contains a process to help generate outline meshes.)
|
||||
* Mesh concavities can intersect each other when the outline is thick. This is due to a limitation in the Unreal renderer for controlling sort order and depth writes of opaque materials.
|
||||
* Outlines will not render though occluding objects.
|
||||
* Translucent objects may show the outline mesh in areas you would expect to be occluded.
|
||||
|
||||
## Example level
|
||||
|
||||
There are some demonstrations of mesh outlines within the `\GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\MeshOutlines\MeshOutlines.umap` level.
|
||||
|
||||
## Example usage
|
||||
|
||||
In the following steps we will create a static mesh in a level and have it outlined.
|
||||
|
||||
1. First let's add a cube to an empty level.
|
||||
* Create a new level (File > New Level) and select "Empty Level."
|
||||
* From the "Place Actors" panel drop a Cube actor into the level.
|
||||
* It's good practice to keep our materials simple (in other words keep the number of shader instructions low) when authoring materials for mixed reality. With the Cube actor selected change the material to the `M_GTDefaultLit` material.
|
||||
* Our Cube may look a little dark. That's because we need a light to illuminate it. From the "Place Actors" panel drop a `GTDirectionalLight` actor into the level.
|
||||
* Set the `GTDirectionalLight`'s "Intensity" to 2. The default value represents a "sunny day" and may wash out our material.
|
||||
* Lastly scale the Cube down so that it is a acceptable size for mixed reality. A uniform scale of 0.2 is a good scale to try.
|
||||
|
||||
![Create Cube Actor](Images/MeshOutlines/CreateCubeActor.png)
|
||||
|
||||
2. Next we are going to add a `GTMeshOutline` component to our Cube.
|
||||
* Add a `GTMeshOutline` component as a child of the Cube's `StaticMesh` component (1). Note, we are adding the component as a child of the static mesh since we will want the outline component to remain fixed to the static mesh, in other words translate, rotate, and scale whenever the mesh is transformed.
|
||||
* You will notice immediately that the `GTMeshOutline` component has a `Cube` static mesh and `M_GTDefaultOutline` material, but the outline doesn't look correct. There are obvious discontinuities in the outline (2) and it occludes the Cube. To fix this we need to make an "outline mesh" that represents a cube. We will do this in the next step.
|
||||
|
||||
![Add Outline Component](Images/MeshOutlines/AddOutlineComponent.png)
|
||||
|
||||
2. Normally you would generate an outline mesh in a digital content creation tool outside of Unreal. This would entail flipping the normals of the mesh and smoothing any vertex normals that would cause discontinuities, or holes, to open in the mesh when it is extruded along the surface normal. Not all of us are skilled artists, so fortunately for us Graphics Tools contains tooling to generate an outline mesh automatically.
|
||||
* Select the `GTMeshOutline` component and ensure that the same mesh used by the parent `StaticMesh` component is specified in the `GTMeshOutline` component's static mesh (1). Normally this is populated automatically for you.
|
||||
* Next scroll down to the "Mesh Outline" properties and select "Create Outline Static Mesh" (2).
|
||||
* Choose a project directory to save the outline mesh to, and optionally a name, and click "OK" (3). If the process is successful a new `CubeOutline` mesh will be generated and automatically applied to the `GTMeshOutline` component's static mesh.
|
||||
|
||||
![Create Outline Static Mesh](Images/MeshOutlines/CreateOutlineStaticMesh.png)
|
||||
|
||||
4. You cube should now have a red outline around your cube.
|
||||
* You can modify the color of the outline by adjusting the `Outline Color` property (1).
|
||||
* You may also modify the thickness of the outline, in Unreal units, by adjusting the `Outline Thickness` property (2). Try bumping the thickness up to "1.5"
|
||||
as seen in the image below. It is up to the material to decide how to use this value, but in the case of the `M_GTDefaultOutline` material it is used to determine the distance to offset the vertex position along the vertex normal.
|
||||
|
||||
![Modify Outline](Images/MeshOutlines/ModifyOutline.png)
|
||||
|
||||
You now have a cube that is outlined using the `GTMeshOutline` component. You can enable and disable rendering of the outline just like any other static mesh component. Or try animating the outline thickness and color in a blueprint or C++.
|
||||
|
||||
## Advanced usage
|
||||
|
||||
Outlines can be represented by more than a solid unlit color. Because an outline has a unique material, it can take advantage of almost any effect available to the material graph. The below image is an example of using the `GTMeshOutline` component with a custom additive material that strobes light across the outline.
|
||||
|
||||
![Custom Outline Material](Images/MeshOutlines/CustomOutlineMaterial.png)
|
||||
|
||||
When creating custom material outlines make sure to extrude the mesh's vertices with the "World Position Offset." If the outline mesh is not extruded it will be completely occluded by the normal static mesh. Vertex extrusion is done by scaling the world space vertex normal by the `Outline Thickness` parameter and sending that value into the "World Position Offset." An example of this is in the below material graph:
|
||||
|
||||
![Vertex Extrusion Material Graph](Images/MeshOutlines/VertexExtrusionMaterialGraph.png)
|
||||
|
||||
## See also
|
||||
|
||||
- [Lighting](Lighting.md)
|
||||
- [Effects](Effects.md)
|
|
@ -8,7 +8,7 @@ ms.localizationpriority: high
|
|||
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
|
||||
---
|
||||
|
||||
# Proximity Lights
|
||||
# Proximity lights
|
||||
|
||||
A proximity light is a [Fluent Design System](https://www.microsoft.com/design/fluent) paradigm that mimics a [point light](https://docs.unrealengine.com/en-US/BuildingWorlds/LightingAndShadows/LightTypes/Point/index.html) projected onto a surface with a color gradient. Often used for near interactions, an app can control the location and properties of a proximity light via the `GTProximityLight` component (or actor). To utilize proximity lights on a [material](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/index.html), place the `MF_GTProximityLights` [material function](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/Functions/index.html) within a material and assign the output to the material's emissive color.
|
||||
|
||||
|
|
|
@ -18,14 +18,20 @@ The third release of the Graphics Tools supports Windows, HoloLens 2, and Androi
|
|||
|
||||
## What's new
|
||||
|
||||
### Android Support
|
||||
### Android support
|
||||
|
||||
Graphics Tools can now be used with on Android platforms. Testing is limited, but current tests pass on a Samsung Galaxy S20 5G.
|
||||
|
||||
### OpenXR Support
|
||||
### OpenXR support
|
||||
|
||||
Graphics Tools now defaults to the OpenXR runtime. The Windows MR plugins is still supported. See the [OpenXR plugin](https://github.com/microsoft/Microsoft-OpenXR-Unreal) to learn more.
|
||||
|
||||
### Mesh outlines
|
||||
|
||||
Editor and runtime components to render an outline around a static mesh.
|
||||
|
||||
[![MeshOutlines](Images/FeatureCards/MeshOutlines.png)](Docs/MeshOutlines.md)
|
||||
|
||||
## Breaking changes
|
||||
|
||||
None
|
||||
|
|
|
@ -8,7 +8,7 @@ ms.localizationpriority: high
|
|||
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
|
||||
---
|
||||
|
||||
# Spatial Perception
|
||||
# Spatial perception
|
||||
|
||||
Mixed Reality devices can generate meshes of the real world via a unique blend of hardware and software. Unlike traditional geometry these meshes only contain vertex positions and not other vertex attributes like normals and texture coordinates. Without vertex normals and texture coordinates it can be difficult to create visually compelling materials. Below we demonstrate a few techniques to help solve this issue.
|
||||
|
||||
|
@ -22,19 +22,19 @@ To try all of the materials outlined below run the `\GraphicsToolsProject\Plugin
|
|||
|
||||
The `ARTrackableNotify` actor component is used to subscribe to any events which add, update, and remove spatial meshes. Spatial meshes are geometric surfaces which come from surfaces such as a wall, table, or even your hands. Because these meshes get regenerated in real time, they normally don’t contain vertex normals or texture coordinates which are needed for even the most basic effects. To solve this issue, we can try a few different techniques.
|
||||
|
||||
### Wireframe Spatial Mesh
|
||||
### Wireframe spatial mesh
|
||||
|
||||
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\Common\Materials\M_Wireframe.uasset` material demonstrates a simple wireframe reconstruction of a mesh. The wireframe material uses a special material checkbox that is available in all Unreal materials. Note, only the wire color can be controlled. If you app needs to render a color for the triangle faces it will need to render the mesh a second time with a different material.
|
||||
|
||||
![Wireframe](Images/SpatialPerception/SpatialPerceptionWireframe.png)
|
||||
|
||||
### Surface Normal Spatial Mesh
|
||||
### Surface normal spatial mesh
|
||||
|
||||
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\Materials\M_SpatialNormals.uasset` material shows how to procedurally generate a surface normal and translate the normal vector into a color. Please see [procedural normals](Effects.md#Procedural-Normal) for more information about normal generation.
|
||||
|
||||
![Normals](Images/SpatialPerception/SpatialPerceptionNormals.png)
|
||||
|
||||
### Textured Spatial Mesh
|
||||
### Textured spatial mesh
|
||||
|
||||
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\Materials\M_SpatialCheckerLit.uasset` material demonstrates how to procedurally generate a surface normal and texture coordinate to sample a texture. With normals and texture coordinates generated it's possible to texture and light a spatial mesh. Please see [biplanar mapping](Effects.md#Biplanar-Mapping) for more information about texture coordinate generation.
|
||||
|
||||
|
|
|
@ -17,5 +17,7 @@
|
|||
href: Profiling.md
|
||||
- name: Spatial Perception
|
||||
href: SpatialPerception.md
|
||||
- name: Mesh Outlines
|
||||
href: MeshOutlines.md
|
||||
- name: Contributing
|
||||
href: CONTRIBUTING.md
|
||||
|
|
|
@ -82,4 +82,5 @@ bSkipMovies=False
|
|||
+MapsToCook=(FilePath="/GraphicsToolsExamples/ClippingPrimitives/ClippingPrimitives")
|
||||
+MapsToCook=(FilePath="/GraphicsToolsExamples/Profiling/Profiling")
|
||||
+MapsToCook=(FilePath="/GraphicsToolsExamples/SpatialPerception/SpatialPerception")
|
||||
+MapsToCook=(FilePath="/GraphicsToolsExamples/MeshOutlines/MeshOutlines")
|
||||
|
||||
|
|
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsTools/Content/Materials/M_GTDefaultOutline.uasset
Normal file
|
@ -9,48 +9,20 @@ public class GraphicsTools : ModuleRules
|
|||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core"
|
||||
});
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"Projects",
|
||||
"RenderCore",
|
||||
"RHI"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"Projects",
|
||||
"RenderCore",
|
||||
"RHI"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "GTMeshOutlineComponent.h"
|
||||
|
||||
#include "GraphicsTools.h"
|
||||
|
||||
UGTMeshOutlineComponent::UGTMeshOutlineComponent()
|
||||
{
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> OutlineMaterialFinder(TEXT("/GraphicsTools/Materials/M_GTDefaultOutline"));
|
||||
check(OutlineMaterialFinder.Object);
|
||||
OutlineMaterial = OutlineMaterialFinder.Object;
|
||||
|
||||
// Disable collision on outline meshes.
|
||||
SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
}
|
||||
|
||||
void UGTMeshOutlineComponent::SetOutlineColor(FColor Color)
|
||||
{
|
||||
if (OutlineColor != Color)
|
||||
{
|
||||
OutlineColor = Color;
|
||||
|
||||
UpdateMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
void UGTMeshOutlineComponent::SetOutlineThickness(float Thickness)
|
||||
{
|
||||
if (OutlineThickness != Thickness)
|
||||
{
|
||||
OutlineThickness = Thickness;
|
||||
|
||||
UpdateMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
void UGTMeshOutlineComponent::SetComputeSmoothNormals(bool Compute)
|
||||
{
|
||||
if (bComputeSmoothNormals != Compute)
|
||||
{
|
||||
bComputeSmoothNormals = Compute;
|
||||
|
||||
UE_LOG(
|
||||
GraphicsTools, Warning,
|
||||
TEXT("It is recomended that the outline static mesh is re-created after altering the smooth normal's property."));
|
||||
}
|
||||
}
|
||||
|
||||
void UGTMeshOutlineComponent::OnRegister()
|
||||
{
|
||||
Super::OnRegister();
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
|
||||
if (GetStaticMesh() == nullptr)
|
||||
{
|
||||
// If a static mesh isn't specified, try grabbing one from the parent.
|
||||
if (UStaticMeshComponent* Parent = Cast<UStaticMeshComponent>(GetAttachParent()))
|
||||
{
|
||||
SetStaticMesh(Parent->GetStaticMesh());
|
||||
SetMaterial(0, OutlineMaterial);
|
||||
UpdateMaterial();
|
||||
SetRelativeScale3D(FVector::OneVector);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMaterial(0) == nullptr)
|
||||
{
|
||||
// Use the default outline material if one isn't specified.
|
||||
SetMaterial(0, OutlineMaterial);
|
||||
UpdateMaterial();
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
void UGTMeshOutlineComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTMeshOutlineComponent, OutlineColor))
|
||||
{
|
||||
UpdateMaterial();
|
||||
}
|
||||
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTMeshOutlineComponent, OutlineThickness))
|
||||
{
|
||||
UpdateMaterial();
|
||||
}
|
||||
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTMeshOutlineComponent, bComputeSmoothNormals))
|
||||
{
|
||||
UE_LOG(
|
||||
GraphicsTools, Warning,
|
||||
TEXT("It is recomended that the outline static mesh is re-created after altering the smooth normal's property."));
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
void UGTMeshOutlineComponent::UpdateMaterial()
|
||||
{
|
||||
UMaterialInterface* Material = GetMaterial(0);
|
||||
|
||||
if (Material == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FName OutlineInstanceMaterialName = NAME_None;
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (Cast<UMaterialInstanceDynamic>(Material) == nullptr)
|
||||
{
|
||||
OutlineInstanceMaterialName = *Material->GetName().Append("Instance");
|
||||
}
|
||||
#endif
|
||||
|
||||
UMaterialInstanceDynamic* MaterialInstance = CreateDynamicMaterialInstance(0, Material, OutlineInstanceMaterialName);
|
||||
|
||||
static const FName OutlineColorName = "BaseColor";
|
||||
MaterialInstance->SetVectorParameterValue(OutlineColorName, OutlineColor);
|
||||
|
||||
static const FName OutlineThicknessName = "OutlineThickness";
|
||||
MaterialInstance->SetScalarParameterValue(OutlineThicknessName, OutlineThickness);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
|
||||
#include "GTMeshOutlineComponent.generated.h"
|
||||
|
||||
/**
|
||||
Component which can be used to render an outline around a static mesh. Using this component introduces an additional render pass of
|
||||
the object being outlined, but is designed to run performantly on mobile mixed reality devices and does not utilize any post processes.
|
||||
Because this effect happens during the default render pass there are a few limitations of this effect which include:
|
||||
- The outline mesh must be watertight hull (and not double sided) else you may see split edges, holes, or other artifacts.
|
||||
- Mesh concavities can intersect each other when the outline is thick. This could be solved if the sort order of opaque objects could
|
||||
be controlled so that the outline renders first and depth write disabled.
|
||||
- Outlines will not render though occluding objects.
|
||||
- Translucent objects may show the outline mesh in areas you would expect to be occluded.
|
||||
Note, this component assumes a material is being used which has a `BaseColor` and `OutlineThickness` parameter to function correctly. The
|
||||
`OutlineThickness` should scale the vertex position offset along the vertex normal.
|
||||
*/
|
||||
UCLASS(ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent), HideCategories = (Physics, Collision))
|
||||
class GRAPHICSTOOLS_API UGTMeshOutlineComponent : public UStaticMeshComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGTMeshOutlineComponent();
|
||||
|
||||
/** Accessor to the outline color. */
|
||||
UFUNCTION(BlueprintGetter, Category = "Mesh Outline")
|
||||
FColor GetOutlineColor() const { return OutlineColor; }
|
||||
|
||||
/** Sets the outline color and updates the material instance. */
|
||||
UFUNCTION(BlueprintSetter, Category = "Mesh Outline")
|
||||
void SetOutlineColor(FColor Color);
|
||||
|
||||
/** Accessor to the outline thickness (in Unreal units). */
|
||||
UFUNCTION(BlueprintGetter, Category = "Mesh Outline")
|
||||
float GetOutlineThickness() const { return OutlineThickness; }
|
||||
|
||||
/** Sets the outline thickness (in Unreal units) and updates the material instance. */
|
||||
UFUNCTION(BlueprintSetter, Category = "Mesh Outline")
|
||||
void SetOutlineThickness(float Thickness);
|
||||
|
||||
/** Sets if the mesh outline generation algorithm should smooth normals. */
|
||||
UFUNCTION(BlueprintGetter, Category = "Mesh Outline")
|
||||
bool GetComputeSmoothNormals() const { return bComputeSmoothNormals; }
|
||||
|
||||
/** Accessor for if the mesh outline generation algorithm should smooth normals. */
|
||||
UFUNCTION(BlueprintSetter, Category = "Mesh Outline")
|
||||
void SetComputeSmoothNormals(bool Compute);
|
||||
|
||||
//
|
||||
// UObject interface
|
||||
|
||||
/** Sets up the components default state when registered. */
|
||||
virtual void OnRegister() override;
|
||||
|
||||
protected:
|
||||
#if WITH_EDITOR
|
||||
/** Responds to details panel updates. */
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
private:
|
||||
/** Updates the `BaseColor` and `OutlineThickness` parameter on a material instance. */
|
||||
void UpdateMaterial();
|
||||
|
||||
/** The color of the mesh outline. Passes this value into the `BaseColor` parameter of the material instance. */
|
||||
UPROPERTY(EditAnywhere, BlueprintGetter = "GetOutlineColor", BlueprintSetter = "SetOutlineColor", Category = "Mesh Outline")
|
||||
FColor OutlineColor = FColor(255, 0, 0, 255);
|
||||
|
||||
/** The thickness (in Unreal units) of the mesh outline. Passes this value into the `OutlineThickness` parameter of the material
|
||||
* instance. It is up to the material to choose what to do with this value, but normally it is used to determine the distance to offset
|
||||
* the vertex position along the vertex normal. */
|
||||
UPROPERTY(
|
||||
EditAnywhere, BlueprintGetter = "GetOutlineThickness", BlueprintSetter = "SetOutlineThickness", Category = "Mesh Outline",
|
||||
Category = "Mesh Outline", meta = (UIMin = "0.0", UIMax = "10.0"))
|
||||
float OutlineThickness = 0.5f;
|
||||
|
||||
/** This setting is optional for some meshes. Outline extrusion occurs by moving a vertex along a vertex normal, on some meshes
|
||||
* extruding along the default normals will cause discontinuities in the outline. To fix these discontinuities, you can check this box
|
||||
* to generate a smooth normal set during outline mesh generation. */
|
||||
UPROPERTY(
|
||||
EditAnywhere, BlueprintGetter = "GetComputeSmoothNormals", BlueprintSetter = "SetComputeSmoothNormals", Category = "Mesh Outline")
|
||||
bool bComputeSmoothNormals = true;
|
||||
|
||||
/** The default outline material. */
|
||||
UPROPERTY(Transient)
|
||||
UMaterialInterface* OutlineMaterial = nullptr;
|
||||
};
|
|
@ -9,9 +9,30 @@ public class GraphicsToolsEditor : ModuleRules
|
|||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "Engine", "Core", "CoreUObject", "GraphicsTools" });
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"Engine",
|
||||
"UnrealEd",
|
||||
"PropertyEditor",
|
||||
"RenderCore",
|
||||
"RHI",
|
||||
"ProceduralMeshComponent",
|
||||
"MeshDescription",
|
||||
"StaticMeshDescription",
|
||||
"AssetTools",
|
||||
"AssetRegistry",
|
||||
"GraphicsTools"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd" });
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"UnrealEd",
|
||||
"ProceduralMeshComponent"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "GTMeshOutlineComponentDetails.h"
|
||||
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "GTMeshOutlineComponent.h"
|
||||
#include "GraphicsToolsEditor.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "IDetailsView.h"
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "ProceduralMeshConversion.h"
|
||||
#include "StaticMeshAttributes.h"
|
||||
|
||||
#include "Application/SlateWindowHelper.h"
|
||||
#include "Dialogs/DlgPickAssetPath.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "PhysicsEngine/BodySetup.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/SNullWidget.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
|
||||
TSharedRef<IDetailCustomization> FGTMeshOutlineComponentDetails::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FGTMeshOutlineComponentDetails);
|
||||
}
|
||||
|
||||
void FGTMeshOutlineComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
IDetailCategoryBuilder& ProcMeshCategory = DetailBuilder.EditCategory("Mesh Outline");
|
||||
|
||||
const FText ConvertToStaticMeshText = FText::AsCultureInvariant("Create Outline Static Mesh");
|
||||
|
||||
// Cache the selected objects.
|
||||
SelectedObjectsList = DetailBuilder.GetSelectedObjects();
|
||||
|
||||
ProcMeshCategory.AddCustomRow(ConvertToStaticMeshText, false)
|
||||
.NameContent()[SNullWidget::NullWidget]
|
||||
.ValueContent()
|
||||
.VAlign(VAlign_Center)
|
||||
.MaxDesiredWidth(250)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.ToolTipText(FText::AsCultureInvariant("Create a new StaticMesh asset based on the current StaticMesh "
|
||||
"modified to work with mesh outlines. Does not modify instance."))
|
||||
.OnClicked(this, &FGTMeshOutlineComponentDetails::ClickedOnConvertToStaticMesh)
|
||||
.IsEnabled(this, &FGTMeshOutlineComponentDetails::ConvertToStaticMeshEnabled)
|
||||
.Content()[SNew(STextBlock).Text(ConvertToStaticMeshText)]];
|
||||
}
|
||||
|
||||
UGTMeshOutlineComponent* FGTMeshOutlineComponentDetails::GetFirstSelectedMeshOutlineComponent() const
|
||||
{
|
||||
UGTMeshOutlineComponent* OutlineMeshComponent = nullptr;
|
||||
|
||||
for (const TWeakObjectPtr<UObject>& Object : SelectedObjectsList)
|
||||
{
|
||||
UGTMeshOutlineComponent* TestOutlineMeshComponent = Cast<UGTMeshOutlineComponent>(Object.Get());
|
||||
|
||||
if (TestOutlineMeshComponent != nullptr && !TestOutlineMeshComponent->IsTemplate() &&
|
||||
TestOutlineMeshComponent->GetStaticMesh() != nullptr)
|
||||
{
|
||||
OutlineMeshComponent = TestOutlineMeshComponent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return OutlineMeshComponent;
|
||||
}
|
||||
|
||||
bool FGTMeshOutlineComponentDetails::ConvertToStaticMeshEnabled() const
|
||||
{
|
||||
return GetFirstSelectedMeshOutlineComponent() != nullptr;
|
||||
}
|
||||
|
||||
typedef TPair<int32, FVector> IndexVertex;
|
||||
|
||||
void SmoothNormals(const TArray<FVector>& Vertices, TArray<FVector>& Normals, TArray<FProcMeshTangent>& Tangents)
|
||||
{
|
||||
TMap<FVector, TArray<IndexVertex>> GroupedVerticies;
|
||||
GroupedVerticies.Reserve(Vertices.Num()); // Reserve for the worse case, each vertex is at a unique location.
|
||||
|
||||
// Group all vertices that share the same location in space.
|
||||
{
|
||||
const int32 Count = Vertices.Num();
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
TArray<IndexVertex>& SmoothingGroup = GroupedVerticies.FindOrAdd(Vertices[Index]);
|
||||
SmoothingGroup.Add(IndexVertex(Index, Vertices[Index]));
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't hit the degenerate case of each vertex is its own group (no vertices shared a location), average the normals/tangents of
|
||||
// each group.
|
||||
if (GroupedVerticies.Num() != Vertices.Num())
|
||||
{
|
||||
TArray<FVector> SmoothNormals(Normals);
|
||||
TArray<FProcMeshTangent> SmoothTangents(Tangents);
|
||||
|
||||
for (auto& Pair : GroupedVerticies)
|
||||
{
|
||||
TArray<IndexVertex>& SmoothingGroup = Pair.Value;
|
||||
|
||||
// No need to smooth a group of one.
|
||||
if (SmoothingGroup.Num() != 1)
|
||||
{
|
||||
// Average the normals and tangents.
|
||||
FVector SmoothedNormal = FVector::ZeroVector;
|
||||
FVector SmoothedTangent = FVector::ZeroVector;
|
||||
|
||||
for (auto& Vertex : SmoothingGroup)
|
||||
{
|
||||
SmoothedNormal += Normals[Vertex.Key];
|
||||
SmoothedTangent += Tangents[Vertex.Key].TangentX;
|
||||
}
|
||||
|
||||
SmoothedNormal.Normalize();
|
||||
SmoothedTangent.Normalize();
|
||||
|
||||
for (auto& Vertex : SmoothingGroup)
|
||||
{
|
||||
SmoothNormals[Vertex.Key] = SmoothedNormal;
|
||||
SmoothTangents[Vertex.Key] = FProcMeshTangent(SmoothedTangent, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Normals = SmoothNormals;
|
||||
Tangents = SmoothTangents;
|
||||
}
|
||||
}
|
||||
|
||||
bool BuildOutlineMesh(UProceduralMeshComponent* ProceduralMesh, UGTMeshOutlineComponent* OutlineMesh)
|
||||
{
|
||||
FStaticMeshRenderData* RenderData = OutlineMesh->GetStaticMesh()->RenderData.Get();
|
||||
|
||||
if (RenderData == nullptr)
|
||||
{
|
||||
UE_LOG(GraphicsToolsEditor, Warning, TEXT("Failed to create the outline mesh becasue the source mesh has no render data."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RenderData->LODResources.Num() == 0)
|
||||
{
|
||||
UE_LOG(
|
||||
GraphicsToolsEditor, Warning, TEXT("Failed to build the outline mesh becasue the source mesh has nothing in the LOD chain."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FStaticMeshLODResources& LOD0 = RenderData->LODResources[0];
|
||||
|
||||
// Positions.
|
||||
TArray<FVector> Vertices;
|
||||
{
|
||||
const int32 Count = LOD0.VertexBuffers.PositionVertexBuffer.GetNumVertices();
|
||||
|
||||
Vertices.Reserve(Count);
|
||||
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
Vertices.Add(LOD0.VertexBuffers.PositionVertexBuffer.VertexPosition(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// Triangles (flip to emulate front face culling).
|
||||
TArray<int32> Triangles;
|
||||
{
|
||||
const int32 Count = LOD0.IndexBuffer.GetNumIndices();
|
||||
|
||||
Triangles.Reserve(Count);
|
||||
|
||||
for (int32 Index = 0; Index < Count; Index += 3)
|
||||
{
|
||||
int32 A = LOD0.IndexBuffer.GetIndex(Index + 0);
|
||||
int32 B = LOD0.IndexBuffer.GetIndex(Index + 1);
|
||||
int32 C = LOD0.IndexBuffer.GetIndex(Index + 2);
|
||||
Triangles.Add(C);
|
||||
Triangles.Add(B);
|
||||
Triangles.Add(A);
|
||||
}
|
||||
}
|
||||
|
||||
// Normals, UV0, and tangents.
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector2D> UV0;
|
||||
TArray<FProcMeshTangent> Tangents;
|
||||
{
|
||||
const int32 Count = LOD0.VertexBuffers.StaticMeshVertexBuffer.GetNumVertices();
|
||||
|
||||
Normals.Reserve(Count);
|
||||
UV0.Reserve(Count);
|
||||
Tangents.Reserve(Count);
|
||||
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
Normals.Add(LOD0.VertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(Index));
|
||||
UV0.Add(LOD0.VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(Index, 0));
|
||||
Tangents.Add(FProcMeshTangent(LOD0.VertexBuffers.StaticMeshVertexBuffer.VertexTangentX(Index), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Vertex colors.
|
||||
TArray<FColor> VertexColors;
|
||||
{
|
||||
const int32 Count = LOD0.VertexBuffers.ColorVertexBuffer.GetNumVertices();
|
||||
|
||||
VertexColors.Reserve(Count);
|
||||
|
||||
for (int32 Index = 0; Index < Count; ++Index)
|
||||
{
|
||||
VertexColors.Add(LOD0.VertexBuffers.ColorVertexBuffer.VertexColor(Index));
|
||||
}
|
||||
}
|
||||
|
||||
if (OutlineMesh->GetComputeSmoothNormals())
|
||||
{
|
||||
SmoothNormals(Vertices, Normals, Tangents);
|
||||
}
|
||||
|
||||
ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FReply FGTMeshOutlineComponentDetails::ClickedOnConvertToStaticMesh()
|
||||
{
|
||||
UGTMeshOutlineComponent* MeshOutlineComponent = GetFirstSelectedMeshOutlineComponent();
|
||||
|
||||
if (MeshOutlineComponent != nullptr)
|
||||
{
|
||||
FString NewNameSuggestion = MeshOutlineComponent->GetStaticMesh()->GetName() + FString(TEXT("Outline"));
|
||||
FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
|
||||
|
||||
FString Name;
|
||||
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
|
||||
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
|
||||
|
||||
TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget = SNew(SDlgPickAssetPath)
|
||||
.Title(FText::AsCultureInvariant("Choose a New Outline Mesh Location"))
|
||||
.DefaultAssetPath(FText::FromString(PackageName));
|
||||
|
||||
if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
|
||||
{
|
||||
// Get the full name of where we want to create the physics asset.
|
||||
FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
|
||||
FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
|
||||
|
||||
// Check if the user inputed a valid asset name, if they did not, give it the generated default name
|
||||
if (MeshName == NAME_None)
|
||||
{
|
||||
// Use the defaults that were already generated.
|
||||
UserPackageName = PackageName;
|
||||
MeshName = *Name;
|
||||
}
|
||||
|
||||
// Create a temporary ProceduralMeshComponent to populate.
|
||||
UProceduralMeshComponent* ProceduralMeshComponent =
|
||||
NewObject<UProceduralMeshComponent>(MeshOutlineComponent->GetOwner(), NAME_None, RF_Transactional | RF_TextExportTransient);
|
||||
ProceduralMeshComponent->SetupAttachment(MeshOutlineComponent);
|
||||
ProceduralMeshComponent->bIsEditorOnly = true;
|
||||
|
||||
if (BuildOutlineMesh(ProceduralMeshComponent, MeshOutlineComponent))
|
||||
{
|
||||
FMeshDescription MeshDescription = BuildMeshDescription(ProceduralMeshComponent);
|
||||
|
||||
if (MeshDescription.Polygons().Num() > 0)
|
||||
{
|
||||
UPackage* Package = CreatePackage(*UserPackageName);
|
||||
check(Package);
|
||||
|
||||
// Create StaticMesh object.
|
||||
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
|
||||
StaticMesh->InitResources();
|
||||
|
||||
StaticMesh->LightingGuid = FGuid::NewGuid();
|
||||
|
||||
// Add source to new StaticMesh.
|
||||
FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel();
|
||||
SrcModel.BuildSettings.bRecomputeNormals = false;
|
||||
SrcModel.BuildSettings.bRecomputeTangents = false;
|
||||
SrcModel.BuildSettings.bRemoveDegenerates = false;
|
||||
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
|
||||
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
|
||||
SrcModel.BuildSettings.bGenerateLightmapUVs = true;
|
||||
SrcModel.BuildSettings.SrcLightmapIndex = 0;
|
||||
SrcModel.BuildSettings.DstLightmapIndex = 1;
|
||||
StaticMesh->CreateMeshDescription(0, MoveTemp(MeshDescription));
|
||||
StaticMesh->CommitMeshDescription(0);
|
||||
|
||||
// Collision.
|
||||
if (!ProceduralMeshComponent->bUseComplexAsSimpleCollision)
|
||||
{
|
||||
StaticMesh->CreateBodySetup();
|
||||
UBodySetup* NewBodySetup = StaticMesh->BodySetup;
|
||||
NewBodySetup->BodySetupGuid = FGuid::NewGuid();
|
||||
NewBodySetup->AggGeom.ConvexElems = ProceduralMeshComponent->ProcMeshBodySetup->AggGeom.ConvexElems;
|
||||
NewBodySetup->bGenerateMirroredCollision = false;
|
||||
NewBodySetup->bDoubleSidedGeometry = true;
|
||||
NewBodySetup->CollisionTraceFlag = CTF_UseDefault;
|
||||
NewBodySetup->CreatePhysicsMeshes();
|
||||
}
|
||||
|
||||
// Materials.
|
||||
TSet<UMaterialInterface*> UniqueMaterials;
|
||||
const int32 NumSections = ProceduralMeshComponent->GetNumSections();
|
||||
|
||||
for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
|
||||
{
|
||||
FProcMeshSection* ProcSection = ProceduralMeshComponent->GetProcMeshSection(SectionIdx);
|
||||
UMaterialInterface* Material = ProceduralMeshComponent->GetMaterial(SectionIdx);
|
||||
UniqueMaterials.Add(Material);
|
||||
}
|
||||
|
||||
// Copy materials to new mesh.
|
||||
for (auto* Material : UniqueMaterials)
|
||||
{
|
||||
StaticMesh->StaticMaterials.Add(FStaticMaterial(Material));
|
||||
}
|
||||
|
||||
// Set the imported version before calling the build.
|
||||
StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
|
||||
|
||||
// Build mesh from source.
|
||||
StaticMesh->Build(false);
|
||||
StaticMesh->PostEditChange();
|
||||
|
||||
// Notify asset registry of new asset.
|
||||
FAssetRegistryModule::AssetCreated(StaticMesh);
|
||||
|
||||
// Apply the static mesh to the mesh outline component.
|
||||
MeshOutlineComponent->SetStaticMesh(StaticMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(
|
||||
GraphicsToolsEditor, Warning, TEXT("Failed to create the outline mesh becasue the source mesh has no polygons."));
|
||||
}
|
||||
}
|
||||
|
||||
// Release the procedural mesh component since it is no longer required.
|
||||
ProceduralMeshComponent->DestroyComponent();
|
||||
ProceduralMeshComponent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
|
@ -7,6 +7,8 @@
|
|||
#include "GTClippingConeComponentVisualizer.h"
|
||||
#include "GTClippingPlaneComponentVisualizer.h"
|
||||
#include "GTClippingSphereComponentVisualizer.h"
|
||||
#include "GTMeshOutlineComponent.h"
|
||||
#include "GTMeshOutlineComponentDetails.h"
|
||||
#include "GTProximityLightComponentVisualizer.h"
|
||||
#include "UnrealEdGlobals.h"
|
||||
|
||||
|
@ -68,11 +70,24 @@ void FGraphicsToolsEditorModule::StartupModule()
|
|||
Visualizer->OnRegister();
|
||||
}
|
||||
}
|
||||
|
||||
// Register customizations
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
PropertyModule.RegisterCustomClassLayout(
|
||||
UGTMeshOutlineComponent::StaticClass()->GetFName(),
|
||||
FOnGetDetailCustomizationInstance::CreateStatic(&FGTMeshOutlineComponentDetails::MakeInstance));
|
||||
}
|
||||
}
|
||||
|
||||
void FGraphicsToolsEditorModule::ShutdownModule()
|
||||
{
|
||||
if (UObjectInitialized() && FModuleManager::Get().IsModuleLoaded(TEXT("PropertyEditor")))
|
||||
{
|
||||
// Unregister customizations
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
PropertyModule.UnregisterCustomClassLayout(UGTMeshOutlineComponent::StaticClass()->GetFName());
|
||||
}
|
||||
|
||||
if (GUnrealEd)
|
||||
{
|
||||
// Unregister visualizers
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IDetailCustomization.h"
|
||||
|
||||
#include "Input/Reply.h"
|
||||
|
||||
class IDetailLayoutBuilder;
|
||||
class UGTMeshOutlineComponent;
|
||||
|
||||
/**
|
||||
* Details panel customization that displays a button to serialize a outline mesh to a static mesh asset.
|
||||
*/
|
||||
class FGTMeshOutlineComponentDetails : public IDetailCustomization
|
||||
{
|
||||
public:
|
||||
/** Makes a new instance of this detail layout class for a specific detail view requesting it. */
|
||||
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||
|
||||
/** IDetailCustomization interface. */
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||
|
||||
/** Handle clicking the convert button. */
|
||||
FReply ClickedOnConvertToStaticMesh();
|
||||
|
||||
/** Is the convert button enabled. */
|
||||
bool ConvertToStaticMeshEnabled() const;
|
||||
|
||||
/** Util to get the OutlineMeshComponent we want to convert. */
|
||||
UGTMeshOutlineComponent* GetFirstSelectedMeshOutlineComponent() const;
|
||||
|
||||
/** Cached array of selected objects. */
|
||||
TArray<TWeakObjectPtr<UObject>> SelectedObjectsList;
|
||||
};
|
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Materials/M_OutlineScanLine.uasset
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/MeshOutlines.umap
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Meshes/SM_ConeOutline.uasset
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Meshes/SM_CubeOutline.uasset
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Meshes/SM_CylinderOutline.uasset
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Meshes/SM_HumanHeartOutline.uasset
Normal file
Двоичные данные
GraphicsToolsProject/Plugins/GraphicsToolsExamples/Content/MeshOutlines/Meshes/SM_SphereOutline.uasset
Normal file
|
@ -30,7 +30,8 @@ If you're already familiar with Mixed Reality development in Unreal, you can fin
|
|||
| Physically based lighting technique optimized for Mixed Reality | A custom light type used to demonstrate distance to surface | Common graphics effects utilized in Mixed Reality |
|
||||
[ ![ClippingPrimitives](Docs/Images/FeatureCards/ClippingPrimitives.png)](Docs/ClippingPrimitives.md) [Clipping Primitives](Docs/ClippingPrimitives.md) | [![Profiling](Docs/Images/FeatureCards/Profiling.png)](Docs/Profiling.md) [Profiling](Docs/Profiling.md) | [![SpatialPerception](Docs/Images/FeatureCards/SpatialPerception.png)](Docs/SpatialPerception.md) [Spatial Perception](Docs/SpatialPerception.md) |
|
||||
| Tools to dynamically slice away a mesh and peer inside the geometry | Explore profiling techniques and tools useful for Mixed Reality development | Techniques that create visually compelling materials for real world geometry |
|
||||
| | | |
|
||||
| [![MeshOutlines](Docs/Images/FeatureCards/MeshOutlines.png)](Docs/MeshOutlines.md) [Mesh Outlines](Docs/MeshOutlines.md) | | |
|
||||
| Editor and runtime components to render an outline around a static mesh | | |
|
||||
|
||||
# Example levels
|
||||
|
||||
|
|