Merge branch '1.1' of https://github.com/FlaxEngine/FlaxEngine into convertactor

This commit is contained in:
Jean-Baptiste Perrier
2021-02-17 14:30:11 +01:00
422 changed files with 19469 additions and 9933 deletions

BIN
Content/Editor/Camera/M_Camera.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/DefaultFontMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Gizmo/Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialWire.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Highlight Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Icons/IconsMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@@ -108,15 +108,14 @@ float4 GetVertexColor(MaterialInput input)
return 1;
}
@8
// Get material properties function (for pixel shader)
Material GetMaterialPS(MaterialInput input)
{
@4
}
// Fix line for errors/warnings for shader code from template
#line 1000
// Input macro specified by the material: DECAL_BLEND_MODE
#define DECAL_BLEND_MODE_TRANSLUCENT 0
@@ -211,3 +210,5 @@ void PS_Decal(
#error "Invalid decal blending mode"
#endif
}
@9

View File

@@ -0,0 +1,377 @@
// File generated by Flax Materials Editor
// Version: @0
#define MATERIAL 1
@3
#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
@7
// Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data)
float4x4 ViewProjectionMatrix;
float4x4 WorldMatrix;
float4x4 LocalMatrix;
float4x4 ViewMatrix;
float3 ViewPos;
float ViewFar;
float3 ViewDir;
float TimeParam;
float4 ViewInfo;
float4 ScreenSize;
float3 Dummy0;
float WorldDeterminantSign;
float MeshMinZ;
float Segment;
float ChunksPerSegment;
float PerInstanceRandom;
float4 TemporalAAJitter;
float3 GeometrySize;
float MeshMaxZ;
@1META_CB_END
// Shader resources
@2
// The spline deformation buffer (stored as 4x3, 3 float4 behind each other)
Buffer<float4> SplineDeformation : register(t0);
// Geometry data passed though the graphics rendering stages up to the pixel shader
struct GeometryData
{
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
#if USE_VERTEX_COLOR
half4 VertexColor : COLOR;
#endif
float3 WorldNormal : TEXCOORD2;
float4 WorldTangent : TEXCOORD3;
};
// Interpolants passed from the vertex shader
struct VertexOutput
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
#if USE_TESSELLATION
float TessellationMultiplier : TESS;
#endif
};
// Interpolants passed to the pixel shader
struct PixelInput
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
bool IsFrontFace : SV_IsFrontFace;
};
// Material properties generation input
struct MaterialInput
{
float3 WorldPosition;
float TwoSidedSign;
float2 TexCoord;
#if USE_VERTEX_COLOR
half4 VertexColor;
#endif
float3x3 TBN;
float4 SvPosition;
float3 PreSkinnedPosition;
float3 PreSkinnedNormal;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT];
#endif
};
// Extracts geometry data to the material input
MaterialInput GetGeometryMaterialInput(GeometryData geometry)
{
MaterialInput output = (MaterialInput)0;
output.WorldPosition = geometry.WorldPosition;
output.TexCoord = geometry.TexCoord;
#if USE_VERTEX_COLOR
output.VertexColor = geometry.VertexColor;
#endif
output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent);
return output;
}
#if USE_TESSELLATION
// Interpolates the geometry positions data only (used by the tessallation when generating vertices)
#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset
// Offsets the geometry positions data only (used by the tessallation when generating vertices)
#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset
// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess)
#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \
float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \
float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \
float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \
geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW
// Interpolates the geometry data except positions (used by the tessallation when generating vertices)
GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2)
{
GeometryData output = (GeometryData)0;
output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2;
#if USE_VERTEX_COLOR
output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2;
#endif
output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2;
output.WorldNormal = normalize(output.WorldNormal);
output.WorldTangent = p0.WorldTangent * w0 + p1.WorldTangent * w1 + p2.WorldTangent * w2;
output.WorldTangent.xyz = normalize(output.WorldTangent.xyz);
return output;
}
#endif
MaterialInput GetMaterialInput(PixelInput input)
{
MaterialInput output = GetGeometryMaterialInput(input.Geometry);
output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0);
output.SvPosition = input.Position;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = input.CustomVSToPS;
#endif
return output;
}
// Removes the scale vector from the local to world transformation matrix
float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld)
{
// Extract per axis scales from localToWorld transform
float scaleX = length(localToWorld[0]);
float scaleY = length(localToWorld[1]);
float scaleZ = length(localToWorld[2]);
float3 invScale = float3(
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
localToWorld[0] *= invScale.x;
localToWorld[1] *= invScale.y;
localToWorld[2] *= invScale.z;
return localToWorld;
}
// Transforms a vector from tangent space to world space
float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector)
{
return mul(tangentVector, input.TBN);
}
// Transforms a vector from world space to tangent space
float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector)
{
return mul(input.TBN, worldVector);
}
// Transforms a vector from world space to view space
float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector)
{
return mul(worldVector, (float3x3)ViewMatrix);
}
// Transforms a vector from view space to world space
float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector)
{
return mul((float3x3)ViewMatrix, viewVector);
}
// Transforms a vector from local space to world space
float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector)
{
float3x3 localToWorld = (float3x3)WorldMatrix;
//localToWorld = RemoveScaleFromLocalToWorld(localToWorld);
return mul(localVector, localToWorld);
}
// Transforms a vector from local space to world space
float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector)
{
float3x3 localToWorld = (float3x3)WorldMatrix;
//localToWorld = RemoveScaleFromLocalToWorld(localToWorld);
return mul(localToWorld, worldVector);
}
// Gets the current object position
float3 GetObjectPosition(MaterialInput input)
{
return WorldMatrix[3].xyz;
}
// Gets the current object size
float3 GetObjectSize(MaterialInput input)
{
float4x4 world = WorldMatrix;
return GeometrySize * float3(world._m00, world._m11, world._m22);
}
// Get the current object random value
float GetPerInstanceRandom(MaterialInput input)
{
return PerInstanceRandom;
}
// Get the current object LOD transition dither factor
float GetLODDitherFactor(MaterialInput input)
{
return 0;
}
// Gets the interpolated vertex color (in linear space)
float4 GetVertexColor(MaterialInput input)
{
#if USE_VERTEX_COLOR
return input.VertexColor;
#else
return 1;
#endif
}
float3 SampleLightmap(Material material, MaterialInput materialInput)
{
return 0;
}
@8
// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@5
}
// Get material properties function (for domain shader)
Material GetMaterialDS(MaterialInput input)
{
@6
}
// Get material properties function (for pixel shader)
Material GetMaterialPS(MaterialInput input)
{
@4
}
// Calculates the transform matrix from mesh tangent space to local space
float3x3 CalcTangentToLocal(ModelInput input)
{
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
float3 normal = input.Normal.xyz * 2.0 - 1.0;
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
float3 bitangent = cross(normal, tangent) * bitangentSign;
return float3x3(tangent, bitangent, normal);
}
// Vertex Shader function for GBuffer Pass and Depth Pass (with full vertex data)
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR)
VertexOutput VS_SplineModel(ModelInput input)
{
VertexOutput output;
// Apply local transformation of the geometry before deformation
float3 position = mul(float4(input.Position, 1), LocalMatrix).xyz;
float4x4 world = LocalMatrix;
// Apply spline curve deformation
float splineAlpha = saturate((position.z - MeshMinZ) / (MeshMaxZ - MeshMinZ));
int splineIndex = (int)((Segment + splineAlpha) * ChunksPerSegment);
position.z = splineAlpha;
float3x4 splineMatrix = float3x4(SplineDeformation[splineIndex * 3], SplineDeformation[splineIndex * 3 + 1], SplineDeformation[splineIndex * 3 + 2]);
position = mul(splineMatrix, float4(position, 1));
float4x3 splineMatrixT = transpose(splineMatrix);
world = mul(world, float4x4(float4(splineMatrixT[0], 0), float4(splineMatrixT[1], 0), float4(splineMatrixT[2], 0), float4(splineMatrixT[3], 1)));
// Compute world space vertex position
output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz;
world = mul(world, WorldMatrix);
// Compute clip space position
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
// Pass vertex attributes
output.Geometry.TexCoord = input.TexCoord;
#if USE_VERTEX_COLOR
output.Geometry.VertexColor = input.Color;
#endif
// Calculate tanget space to world space transformation matrix for unit vectors
float3x3 tangentToLocal = CalcTangentToLocal(input);
float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world);
float3x3 tangentToWorld = mul(tangentToLocal, localToWorld);
output.Geometry.WorldNormal = tangentToWorld[2];
output.Geometry.WorldTangent.xyz = tangentToWorld[0];
output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f;
// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS
MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry);
materialInput.TwoSidedSign = WorldDeterminantSign;
materialInput.SvPosition = output.Position;
materialInput.PreSkinnedPosition = input.Position.xyz;
materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
Material material = GetMaterialVS(materialInput);
#endif
// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
output.Geometry.WorldPosition += material.PositionOffset;
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
#endif
// Get tessalation multiplier (per vertex)
#if USE_TESSELLATION
output.TessellationMultiplier = material.TessellationMultiplier;
#endif
// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = material.CustomVSToPS;
#endif
return output;
}
#if USE_DITHERED_LOD_TRANSITION
void ClipLODTransition(PixelInput input)
{
}
#endif
// Pixel Shader function for Depth Pass
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Depth(PixelInput input)
{
#if MATERIAL_MASKED || MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Perform per pixel clipping
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE
clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD);
#endif
#endif
}
@9

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Deferred Shading: Defines
@1// Deferred Shading: Includes
@2// Deferred Shading: Constants
@3// Deferred Shading: Resources
@4// Deferred Shading: Utilities
@5// Deferred Shading: Shaders
// Pixel Shader function for GBuffer Pass
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_LIGHTMAP=0)
META_PERMUTATION_1(USE_LIGHTMAP=1)
void PS_GBuffer(
in PixelInput input
,out float4 Light : SV_Target0
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
// GBuffer
,out float4 RT0 : SV_Target1
,out float4 RT1 : SV_Target2
,out float4 RT2 : SV_Target3
#if USE_GBUFFER_CUSTOM_DATA
,out float4 RT3 : SV_Target4
#endif
#endif
)
{
Light = 0;
#if USE_DITHERED_LOD_TRANSITION
// LOD masking
ClipLODTransition(input);
#endif
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if USE_LIGHTMAP
float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness);
float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness);
// Sample lightmap
float3 diffuseIndirectLighting = SampleLightmap(material, materialInput);
// Apply static indirect light
Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor);
#endif
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
// Pack material properties to GBuffer
RT0 = float4(material.Color, material.AO);
RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0));
RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0);
// Custom data
#if USE_GBUFFER_CUSTOM_DATA
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE
RT3 = float4(material.SubsurfaceColor, material.Opacity);
#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
RT3 = float4(material.SubsurfaceColor, material.Opacity);
#else
RT3 = float4(0, 0, 0, 0);
#endif
#endif
// Add light emission
#if USE_EMISSIVE
Light.rgb += material.Emissive;
#endif
#else
// Handle blending as faked forward pass (use Light buffer and skip GBuffer modification)
Light = float4(material.Emissive, material.Opacity);
#endif
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Distortion: Defines
@1// Distortion: Includes
@2// Distortion: Constants
@3// Distortion: Resources
@4// Distortion: Utilities
@5// Distortion: Shaders
#if USE_DISTORTION
// Pixel Shader function for Distortion Pass
META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2)
float4 PS_Distortion(PixelInput input) : SV_Target0
{
#if USE_DITHERED_LOD_TRANSITION
// LOD masking
ClipLODTransition(input);
#endif
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal));
float airIOR = 1.0f;
#if USE_PIXEL_NORMAL_OFFSET_REFRACTION
float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1)));
float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR);
#else
float2 distortion = viewNormal.xy * (material.Refraction - airIOR);
#endif
// Clip if the distortion distance (squared) is too small to be noticed
clip(dot(distortion, distortion) - 0.00001);
// Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction
distortion *= 4.0f;
// Use separate storage for positive and negative offsets
float2 addOffset = max(distortion, 0);
float2 subOffset = abs(min(distortion, 0));
return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y);
}
#endif

View File

@@ -0,0 +1,124 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Forward Shading: Defines
#define MAX_LOCAL_LIGHTS 4
@1// Forward Shading: Includes
#include "./Flax/LightingCommon.hlsl"
#if USE_REFLECTIONS
#include "./Flax/ReflectionsCommon.hlsl"
#endif
#include "./Flax/Lighting.hlsl"
#include "./Flax/ShadowsSampling.hlsl"
#include "./Flax/ExponentialHeightFog.hlsl"
@2// Forward Shading: Constants
LightData DirectionalLight;
LightShadowData DirectionalLightShadow;
LightData SkyLight;
ProbeData EnvironmentProbe;
ExponentialHeightFogData ExponentialHeightFog;
float3 Dummy2;
uint LocalLightsCount;
LightData LocalLights[MAX_LOCAL_LIGHTS];
@3// Forward Shading: Resources
TextureCube EnvProbe : register(t__SRV__);
TextureCube SkyLightTexture : register(t__SRV__);
Texture2DArray DirectionalLightShadowMap : register(t__SRV__);
@4// Forward Shading: Utilities
DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow);
@5// Forward Shading: Shaders
// Pixel Shader function for Forward Pass
META_PS(USE_FORWARD, FEATURE_LEVEL_ES2)
float4 PS_Forward(PixelInput input) : SV_Target0
{
float4 output = 0;
#if USE_DITHERED_LOD_TRANSITION
// LOD masking
ClipLODTransition(input);
#endif
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
// Add emissive light
output = float4(material.Emissive, material.Opacity);
#if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT
// Setup GBuffer data as proxy for lighting
GBufferSample gBuffer;
gBuffer.Normal = material.WorldNormal;
gBuffer.Roughness = material.Roughness;
gBuffer.Metalness = material.Metalness;
gBuffer.Color = material.Color;
gBuffer.Specular = material.Specular;
gBuffer.AO = material.AO;
gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz;
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE
gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity);
#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity);
#else
gBuffer.CustomData = float4(0, 0, 0, 0);
#endif
gBuffer.WorldPos = materialInput.WorldPosition;
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
if (DirectionalLight.CastShadows > 0)
{
LightShadowData directionalLightShadowData = GetDirectionalLightShadowData();
shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g);
}
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light
light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture);
// Calculate lighting from local lights
LOOP
for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++)
{
const LightData localLight = LocalLights[localLightIndex];
bool isSpotLight = localLight.SpotAngles.x > -2.0f;
shadowMask = 1.0f;
light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight);
}
#if USE_REFLECTIONS
// Calculate reflections
light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a;
#endif
// Add lighting (apply ambient occlusion)
output.rgb += light.rgb * gBuffer.AO;
#if USE_FOG
// Calculate exponential height fog
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0);
// Apply fog to the output color
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
output = float4(output.rgb * fog.a + fog.rgb, output.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT
output = float4(output.rgb * fog.a + fog.rgb, output.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE
output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY
output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a);
#endif
#endif
#endif
return output;
}

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Lightmap: Defines
#define CAN_USE_LIGHTMAP 1
@1// Lightmap: Includes
@2// Lightmap: Constants
float4 LightmapArea;
@3// Lightmap: Resources
#if USE_LIGHTMAP
// Irradiance and directionality prebaked lightmaps
Texture2D Lightmap0 : register(t__SRV__);
Texture2D Lightmap1 : register(t__SRV__);
Texture2D Lightmap2 : register(t__SRV__);
#endif
@4// Lightmap: Utilities
#if USE_LIGHTMAP
// Evaluates the H-Basis coefficients in the tangent space normal direction
float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3)
{
// Band 0
float3 color = h0 * (1.0f / sqrt(2.0f * PI));
// Band 1
color += h1 * -sqrt(1.5f / PI) * n.y;
color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f);
color += h3 * -sqrt(1.5f / PI) * n.x;
return color;
}
float3 SampleLightmap(Material material, MaterialInput materialInput)
{
// Sample lightmaps
float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV);
float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV);
float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV);
// Unpack H-basis
float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x);
float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y);
float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z);
float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w);
// Sample baked diffuse irradiance from the H-basis coefficients
float3 normal = material.TangentNormal;
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
normal *= material.TangentNormal;
#endif
return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI;
}
#endif
@5// Lightmap: Shaders

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Motion Vectors: Defines
@1// Motion Vectors: Includes
@2// Motion Vectors: Constants
@3// Motion Vectors: Resources
@4// Motion Vectors: Utilities
@5// Motion Vectors: Shaders
// Pixel Shader function for Motion Vectors Pass
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_MotionVectors(PixelInput input) : SV_Target0
{
#if USE_DITHERED_LOD_TRANSITION
// LOD masking
ClipLODTransition(input);
#endif
#if MATERIAL_MASKED
// Perform per pixel clipping if material requries it
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
// Calculate this and previosu frame pixel locations in clip space
float4 prevClipPos = mul(float4(input.Geometry.PrevWorldPosition, 1), PrevViewProjectionMatrix);
float4 curClipPos = mul(float4(input.Geometry.WorldPosition, 1), ViewProjectionMatrix);
float2 prevHPos = prevClipPos.xy / prevClipPos.w;
float2 curHPos = curClipPos.xy / curClipPos.w;
// Revert temporal jitter offset
prevHPos -= TemporalAAJitter.zw;
curHPos -= TemporalAAJitter.xy;
// Clip Space -> UV Space
float2 vPosPrev = prevHPos.xy * 0.5f + 0.5f;
float2 vPosCur = curHPos.xy * 0.5f + 0.5f;
vPosPrev.y = 1.0 - vPosPrev.y;
vPosCur.y = 1.0 - vPosCur.y;
// Calculate per-pixel motion vector
return float4(vPosCur - vPosPrev, 0, 1);
}

View File

@@ -0,0 +1,184 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
@0// Tessellation: Defines
#define TessalationProjectOntoPlane(planeNormal, planePosition, pointToProject) pointToProject - dot(pointToProject - planePosition, planeNormal) * planeNormal
@1// Tessellation: Includes
@2// Tessellation: Constants
@3// Tessellation: Resources
@4// Tessellation: Utilities
@5// Tessellation: Shaders
#if USE_TESSELLATION
// Interpolants passed from the hull shader to the domain shader
struct TessalationHSToDS
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
float TessellationMultiplier : TESS;
};
// Interpolants passed from the domain shader and to the pixel shader
struct TessalationDSToPS
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
};
MaterialInput GetMaterialInput(TessalationDSToPS input)
{
MaterialInput output = GetGeometryMaterialInput(input.Geometry);
output.SvPosition = input.Position;
output.TwoSidedSign = WorldDeterminantSign;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = input.CustomVSToPS;
#endif
return output;
}
struct TessalationPatch
{
float EdgeTessFactor[3] : SV_TessFactor;
float InsideTessFactor : SV_InsideTessFactor;
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
float3 B210 : POSITION4;
float3 B120 : POSITION5;
float3 B021 : POSITION6;
float3 B012 : POSITION7;
float3 B102 : POSITION8;
float3 B201 : POSITION9;
float3 B111 : CENTER;
#endif
};
TessalationPatch HS_PatchConstant(InputPatch<VertexOutput, 3> input)
{
TessalationPatch output;
// Average tess factors along edges, and pick an edge tess factor for the interior tessellation
float4 tessellationMultipliers;
tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier);
tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier);
tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier);
tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier);
tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR);
output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge
output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge
output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge
output.InsideTessFactor = tessellationMultipliers.w;
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
// Calculate PN Triangle control points
// Reference: [Vlachos 2001]
float3 p1 = input[0].Geometry.WorldPosition;
float3 p2 = input[1].Geometry.WorldPosition;
float3 p3 = input[2].Geometry.WorldPosition;
float3 n1 = input[0].Geometry.WorldNormal;
float3 n2 = input[1].Geometry.WorldNormal;
float3 n3 = input[2].Geometry.WorldNormal;
output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f;
output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f;
output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f;
output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f;
output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f;
output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f;
float3 e = (output.B210 + output.B120 + output.B021 + output.B012 + output.B102 + output.B201) / 6.0f;
float3 v = (p1 + p2 + p3) / 3.0f;
output.B111 = e + ((e - v) / 2.0f);
#endif
return output;
}
META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5)
META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS)
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[maxtessfactor(MAX_TESSELLATION_FACTOR)]
[outputcontrolpoints(3)]
[patchconstantfunc("HS_PatchConstant")]
TessalationHSToDS HS(InputPatch<VertexOutput, TESSELLATION_IN_CONTROL_POINTS> input, uint ControlPointID : SV_OutputControlPointID)
{
TessalationHSToDS output;
// Pass through shader
#define COPY(thing) output.thing = input[ControlPointID].thing;
COPY(Position);
COPY(Geometry);
COPY(TessellationMultiplier);
#if USE_CUSTOM_VERTEX_INTERPOLATORS
COPY(CustomVSToPS);
#endif
#undef COPY
return output;
}
META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5)
[domain("tri")]
TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch<TessalationHSToDS, 3> input)
{
TessalationDSToPS output;
// Get the barycentric coords
float U = barycentricCoords.x;
float V = barycentricCoords.y;
float W = barycentricCoords.z;
// Interpolate patch attributes to generated vertices
output.Geometry = InterpolateGeometry(input[0].Geometry, U, input[1].Geometry, V, input[2].Geometry, W);
#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing
INTERPOLATE(Position);
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
// Interpolate using barycentric coordinates and PN Triangle control points
float UU = U * U;
float VV = V * V;
float WW = W * W;
float UU3 = UU * 3.0f;
float VV3 = VV * 3.0f;
float WW3 = WW * 3.0f;
float3 offset =
constantData.B210 * UU3 * V +
constantData.B120 * VV3 * U +
constantData.B021 * VV3 * W +
constantData.B012 * WW3 * V +
constantData.B102 * WW3 * U +
constantData.B201 * UU3 * W +
constantData.B111 * 6.0f * W * U * V;
InterpolateGeometryPositions(output.Geometry, input[0].Geometry, UU * U, input[1].Geometry, VV * V, input[2].Geometry, WW * W, offset);
#else
InterpolateGeometryPositions(output.Geometry, input[0].Geometry, U, input[1].Geometry, V, input[2].Geometry, W, float3(0, 0, 0));
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
UNROLL
for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++)
INTERPOLATE(CustomVSToPS[i]);
#endif
#undef INTERPOLATE
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG
// Orthogonal projection in the tangent planes with interpolation
ApplyGeometryPositionsPhongTess(output.Geometry, input[0].Geometry, input[1].Geometry, input[2].Geometry, U, V, W);
#endif
// Perform displacement mapping
#if USE_DISPLACEMENT
MaterialInput materialInput = GetMaterialInput(output);
Material material = GetMaterialDS(materialInput);
OffsetGeometryPositions(output.Geometry, material.WorldDisplacement);
#endif
// Recalculate the clip space position
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
return output;
}
#endif

View File

@@ -3,7 +3,6 @@
#define MATERIAL 1
@3
#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
@@ -22,7 +21,7 @@ float4 ViewInfo;
float4 ScreenSize;
@1META_CB_END
// Material shader resources
// Shader resources
@2
// Interpolants passed from the vertex shader
struct VertexOutput
@@ -185,6 +184,8 @@ float4 GetVertexColor(MaterialInput input)
#endif
}
@8
// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@@ -197,9 +198,6 @@ Material GetMaterialPS(MaterialInput input)
@4
}
// Fix line for errors/warnings for shader code from template
#line 1000
// Vertex Shader function for GUI materials rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
@@ -257,3 +255,5 @@ float4 PS_GUI(PixelInput input) : SV_Target0
return float4(material.Emissive, material.Opacity);
}
@9

View File

@@ -2,19 +2,13 @@
// Version: @0
#define MATERIAL 1
#define MAX_LOCAL_LIGHTS 4
@3
// Ribbons don't use sorted indices so overlap the segment distances buffer on the slot
#define HAS_SORTED_INDICES (!defined(_VS_Ribbon))
#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
#include "./Flax/LightingCommon.hlsl"
#if USE_REFLECTIONS
#include "./Flax/ReflectionsCommon.hlsl"
#endif
#include "./Flax/Lighting.hlsl"
#include "./Flax/ShadowsSampling.hlsl"
#include "./Flax/ExponentialHeightFog.hlsl"
#include "./Flax/Matrix.hlsl"
@7
struct SpriteInput
@@ -55,44 +49,19 @@ uint RibbonSegmentCount;
float4x4 WorldMatrixInverseTransposed;
@1META_CB_END
// Secondary constantant buffer (for lighting)
META_CB_BEGIN(1, LightingData)
LightData DirectionalLight;
LightShadowData DirectionalLightShadow;
LightData SkyLight;
ProbeData EnvironmentProbe;
ExponentialHeightFogData ExponentialHeightFog;
float3 Dummy1;
uint LocalLightsCount;
LightData LocalLights[MAX_LOCAL_LIGHTS];
META_CB_END
DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow);
// Particles attributes buffer
ByteAddressBuffer ParticlesData : register(t0);
// Ribbons don't use sorted indices so overlap the segment distances buffer on the slot
#define HAS_SORTED_INDICES (!defined(_VS_Ribbon))
#if HAS_SORTED_INDICES
// Sorted particles indices
Buffer<uint> SortedIndices : register(t1);
#else
// Ribbon particles segments distances buffer
Buffer<float> SegmentDistances : register(t1);
#endif
// Shader resources
TextureCube EnvProbe : register(t2);
TextureCube SkyLightTexture : register(t3);
Texture2DArray DirectionalLightShadowMap : register(t4);
@2
// Interpolants passed from the vertex shader
struct VertexOutput
{
@@ -172,14 +141,11 @@ MaterialInput GetMaterialInput(PixelInput input)
}
// Gets the local to world transform matrix (supports instancing)
float4x4 GetInstanceTransform(ModelInput input)
{
return WorldMatrix;
}
float4x4 GetInstanceTransform(MaterialInput input)
{
return WorldMatrix;
}
#if USE_INSTANCING
#define GetInstanceTransform(input) float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f))
#else
#define GetInstanceTransform(input) WorldMatrix;
#endif
// Removes the scale vector from the local to world transformation matrix (supports instancing)
float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld)
@@ -312,6 +278,8 @@ float3 TransformParticleVector(float3 input)
return mul(float4(input, 0.0f), WorldMatrixInverseTransposed).xyz;
}
@8
// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@@ -330,9 +298,6 @@ Material GetMaterialPS(MaterialInput input)
@4
}
// Fix line for errors/warnings for shader code from template
#line 1000
// Calculates the transform matrix from mesh tangent space to local space
half3x3 CalcTangentToLocal(ModelInput input)
{
@@ -712,142 +677,9 @@ VertexOutput VS_Ribbon(uint vertexIndex : SV_VertexID)
return output;
}
// Pixel Shader function for Forward Pass
META_PS(USE_FORWARD, FEATURE_LEVEL_ES2)
float4 PS_Forward(PixelInput input) : SV_Target0
{
float4 output = 0;
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
// Add emissive light
output = float4(material.Emissive, material.Opacity);
#if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT
// Setup GBuffer data as proxy for lighting
GBufferSample gBuffer;
gBuffer.Normal = material.WorldNormal;
gBuffer.Roughness = material.Roughness;
gBuffer.Metalness = material.Metalness;
gBuffer.Color = material.Color;
gBuffer.Specular = material.Specular;
gBuffer.AO = material.AO;
gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz;
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE
gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity);
#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity);
#else
gBuffer.CustomData = float4(0, 0, 0, 0);
#endif
gBuffer.WorldPos = materialInput.WorldPosition;
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
if (DirectionalLight.CastShadows > 0)
{
LightShadowData directionalLightShadowData = GetDirectionalLightShadowData();
shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g);
}
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light
light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture);
// Calculate lighting from local lights
LOOP
for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++)
{
const LightData localLight = LocalLights[localLightIndex];
bool isSpotLight = localLight.SpotAngles.x > -2.0f;
shadowMask = 1.0f;
light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight);
}
#if USE_REFLECTIONS
// Calculate reflections
light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a;
#endif
// Add lighting (apply ambient occlusion)
output.rgb += light.rgb * gBuffer.AO;
#if USE_FOG
// Calculate exponential height fog
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0);
// Apply fog to the output color
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
output = float4(output.rgb * fog.a + fog.rgb, output.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT
output = float4(output.rgb * fog.a + fog.rgb, output.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE
output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a);
#elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY
output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a);
#endif
#endif
#endif
return output;
}
#if USE_DISTORTION
// Pixel Shader function for Distortion Pass
META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2)
float4 PS_Distortion(PixelInput input) : SV_Target0
{
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal));
float airIOR = 1.0f;
#if USE_PIXEL_NORMAL_OFFSET_REFRACTION
float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1)));
float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR);
#else
float2 distortion = viewNormal.xy * (material.Refraction - airIOR);
#endif
// Clip if the distortion distance (squared) is too small to be noticed
clip(dot(distortion, distortion) - 0.00001);
// Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction
distortion *= 4.0f;
// Use separate storage for positive and negative offsets
float2 addOffset = max(distortion, 0);
float2 subOffset = abs(min(distortion, 0));
return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y);
}
#endif
// Pixel Shader function for Depth Pass
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Depth(PixelInput input
#if GLSL
, out float4 OutColor : SV_Target0
#endif
)
void PS_Depth(PixelInput input)
{
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
@@ -860,8 +692,6 @@ void PS_Depth(PixelInput input
#if MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT
clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD);
#endif
#if GLSL
OutColor = 0;
#endif
}
@9

View File

@@ -20,7 +20,7 @@ float4 ScreenSize;
float4 TemporalAAJitter;
@1META_CB_END
// Material shader resources
// Shader resources
@2
// Interpolants passed to the pixel shader
struct PixelInput
@@ -128,15 +128,14 @@ float4 GetVertexColor(MaterialInput input)
return 1;
}
@8
// Get material properties function (for pixel shader)
Material GetMaterialPS(MaterialInput input)
{
@4
}
// Fix line for errors/warnings for shader code from template
#line 1000
// Pixel Shader function for PostFx materials rendering
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_PostFx(PixelInput input) : SV_Target0
@@ -147,3 +146,5 @@ float4 PS_PostFx(PixelInput input) : SV_Target0
return float4(material.Emissive, material.Opacity);
}
@9

View File

@@ -0,0 +1,620 @@
// File generated by Flax Materials Editor
// Version: @0
#define MATERIAL 1
@3
#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
@7
// Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data)
float4x4 ViewProjectionMatrix;
float4x4 WorldMatrix;
float4x4 ViewMatrix;
float4x4 PrevViewProjectionMatrix;
float4x4 PrevWorldMatrix;
float3 ViewPos;
float ViewFar;
float3 ViewDir;
float TimeParam;
float4 ViewInfo;
float4 ScreenSize;
float3 WorldInvScale;
float WorldDeterminantSign;
float2 Dummy0;
float LODDitherFactor;
float PerInstanceRandom;
float4 TemporalAAJitter;
float3 GeometrySize;
float Dummy1;
@1META_CB_END
// Shader resources
@2
// Geometry data passed though the graphics rendering stages up to the pixel shader
struct GeometryData
{
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
#if USE_VERTEX_COLOR
half4 VertexColor : COLOR;
#endif
float3 WorldNormal : TEXCOORD3;
float4 WorldTangent : TEXCOORD4;
float3 InstanceOrigin : TEXCOORD5;
float2 InstanceParams : TEXCOORD6; // x-PerInstanceRandom, y-LODDitherFactor
float3 PrevWorldPosition : TEXCOORD7;
};
// Interpolants passed from the vertex shader
struct VertexOutput
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
#if USE_TESSELLATION
float TessellationMultiplier : TESS;
#endif
};
// Interpolants passed to the pixel shader
struct PixelInput
{
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
bool IsFrontFace : SV_IsFrontFace;
};
// Material properties generation input
struct MaterialInput
{
float3 WorldPosition;
float TwoSidedSign;
float2 TexCoord;
#if USE_LIGHTMAP
float2 LightmapUV;
#endif
#if USE_VERTEX_COLOR
half4 VertexColor;
#endif
float3x3 TBN;
float4 SvPosition;
float3 PreSkinnedPosition;
float3 PreSkinnedNormal;
float3 InstanceOrigin;
float2 InstanceParams;
#if USE_INSTANCING
float3 InstanceTransform1;
float3 InstanceTransform2;
float3 InstanceTransform3;
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT];
#endif
};
// Extracts geometry data to the material input
MaterialInput GetGeometryMaterialInput(GeometryData geometry)
{
MaterialInput output = (MaterialInput)0;
output.WorldPosition = geometry.WorldPosition;
output.TexCoord = geometry.TexCoord;
#if USE_LIGHTMAP
output.LightmapUV = geometry.LightmapUV;
#endif
#if USE_VERTEX_COLOR
output.VertexColor = geometry.VertexColor;
#endif
output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent);
output.InstanceOrigin = geometry.InstanceOrigin;
output.InstanceParams = geometry.InstanceParams;
return output;
}
#if USE_TESSELLATION
// Interpolates the geometry positions data only (used by the tessallation when generating vertices)
#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset; output.PrevWorldPosition = p0.PrevWorldPosition * w0 + p1.PrevWorldPosition * w1 + p2.PrevWorldPosition * w2 + offset
// Offsets the geometry positions data only (used by the tessallation when generating vertices)
#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset; geometry.PrevWorldPosition += offset
// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess)
#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \
float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \
float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \
float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \
geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; \
posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.PrevWorldPosition, geometry.PrevWorldPosition); \
posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.PrevWorldPosition, geometry.PrevWorldPosition); \
posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.PrevWorldPosition, geometry.PrevWorldPosition); \
geometry.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW
// Interpolates the geometry data except positions (used by the tessallation when generating vertices)
GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2)
{
GeometryData output = (GeometryData)0;
output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2;
#if USE_LIGHTMAP
output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2;
#endif
#if USE_VERTEX_COLOR
output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2;
#endif
output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2;
output.WorldNormal = normalize(output.WorldNormal);
output.WorldTangent = p0.WorldTangent * w0 + p1.WorldTangent * w1 + p2.WorldTangent * w2;
output.WorldTangent.xyz = normalize(output.WorldTangent.xyz);
output.InstanceOrigin = p0.InstanceOrigin;
output.InstanceParams = p0.InstanceParams;
return output;
}
#endif
MaterialInput GetMaterialInput(PixelInput input)
{
MaterialInput output = GetGeometryMaterialInput(input.Geometry);
output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0);
output.SvPosition = input.Position;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = input.CustomVSToPS;
#endif
return output;
}
// Gets the local to world transform matrix (supports instancing)
#if USE_INSTANCING
#define GetInstanceTransform(input) float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f))
#else
#define GetInstanceTransform(input) WorldMatrix;
#endif
// Removes the scale vector from the local to world transformation matrix (supports instancing)
float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld)
{
#if USE_INSTANCING
// Extract per axis scales from localToWorld transform
float scaleX = length(localToWorld[0]);
float scaleY = length(localToWorld[1]);
float scaleZ = length(localToWorld[2]);
float3 invScale = float3(
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
#else
float3 invScale = WorldInvScale;
#endif
localToWorld[0] *= invScale.x;
localToWorld[1] *= invScale.y;
localToWorld[2] *= invScale.z;
return localToWorld;
}
// Transforms a vector from tangent space to world space
float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector)
{
return mul(tangentVector, input.TBN);
}
// Transforms a vector from world space to tangent space
float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector)
{
return mul(input.TBN, worldVector);
}
// Transforms a vector from world space to view space
float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector)
{
return mul(worldVector, (float3x3)ViewMatrix);
}
// Transforms a vector from view space to world space
float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector)
{
return mul((float3x3)ViewMatrix, viewVector);
}
// Transforms a vector from local space to world space
float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector)
{
float3x3 localToWorld = (float3x3)GetInstanceTransform(input);
//localToWorld = RemoveScaleFromLocalToWorld(localToWorld);
return mul(localVector, localToWorld);
}
// Transforms a vector from local space to world space
float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector)
{
float3x3 localToWorld = (float3x3)GetInstanceTransform(input);
//localToWorld = RemoveScaleFromLocalToWorld(localToWorld);
return mul(localToWorld, worldVector);
}
// Gets the current object position (supports instancing)
float3 GetObjectPosition(MaterialInput input)
{
return input.InstanceOrigin.xyz;
}
// Gets the current object size (supports instancing)
float3 GetObjectSize(MaterialInput input)
{
float4x4 world = GetInstanceTransform(input);
return GeometrySize * float3(world._m00, world._m11, world._m22);
}
// Get the current object random value (supports instancing)
float GetPerInstanceRandom(MaterialInput input)
{
return input.InstanceParams.x;
}
// Get the current object LOD transition dither factor (supports instancing)
float GetLODDitherFactor(MaterialInput input)
{
#if USE_DITHERED_LOD_TRANSITION
return input.InstanceParams.y;
#else
return 0;
#endif
}
// Gets the interpolated vertex color (in linear space)
float4 GetVertexColor(MaterialInput input)
{
#if USE_VERTEX_COLOR
return input.VertexColor;
#else
return 1;
#endif
}
@8
// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@5
}
// Get material properties function (for domain shader)
Material GetMaterialDS(MaterialInput input)
{
@6
}
// Get material properties function (for pixel shader)
Material GetMaterialPS(MaterialInput input)
{
@4
}
// Calculates the transform matrix from mesh tangent space to local space
float3x3 CalcTangentToLocal(ModelInput input)
{
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
float3 normal = input.Normal.xyz * 2.0 - 1.0;
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
float3 bitangent = cross(normal, tangent) * bitangentSign;
return float3x3(tangent, bitangent, normal);
}
float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal)
{
float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world);
return mul(tangentToLocal, localToWorld);
}
// Vertex Shader function for GBuffer Pass and Depth Pass (with full vertex data)
META_VS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_INSTANCING=0)
META_PERMUTATION_1(USE_INSTANCING=1)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR)
META_VS_IN_ELEMENT(ATTRIBUTE,0, R32G32B32A32_FLOAT,3, 0, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,1, R32G32B32A32_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,2, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,3, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,4, R16G16B16A16_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
VertexOutput VS(ModelInput input)
{
VertexOutput output;
// Compute world space vertex position
float4x4 world = GetInstanceTransform(input);
output.Geometry.WorldPosition = mul(float4(input.Position.xyz, 1), world).xyz;
output.Geometry.PrevWorldPosition = mul(float4(input.Position.xyz, 1), PrevWorldMatrix).xyz;
// Compute clip space position
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
// Pass vertex attributes
output.Geometry.TexCoord = input.TexCoord;
#if USE_VERTEX_COLOR
output.Geometry.VertexColor = input.Color;
#endif
output.Geometry.InstanceOrigin = world[3].xyz;
#if USE_INSTANCING
output.Geometry.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy;
output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w);
#else
#if CAN_USE_LIGHTMAP
output.Geometry.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy;
#else
output.Geometry.LightmapUV = input.LightmapUV;
#endif
output.Geometry.InstanceParams = float2(PerInstanceRandom, LODDitherFactor);
#endif
// Calculate tanget space to world space transformation matrix for unit vectors
float3x3 tangentToLocal = CalcTangentToLocal(input);
float3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal);
output.Geometry.WorldNormal = tangentToWorld[2];
output.Geometry.WorldTangent.xyz = tangentToWorld[0];
output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f;
// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS
MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry);
materialInput.TwoSidedSign = WorldDeterminantSign;
materialInput.SvPosition = output.Position;
materialInput.PreSkinnedPosition = input.Position.xyz;
materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
#if USE_INSTANCING
materialInput.InstanceTransform1 = input.InstanceTransform1.xyz;
materialInput.InstanceTransform2 = input.InstanceTransform2.xyz;
materialInput.InstanceTransform3 = input.InstanceTransform3.xyz;
#endif
Material material = GetMaterialVS(materialInput);
#endif
// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
output.Geometry.WorldPosition += material.PositionOffset;
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
#endif
// Get tessalation multiplier (per vertex)
#if USE_TESSELLATION
output.TessellationMultiplier = material.TessellationMultiplier;
#endif
// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = material.CustomVSToPS;
#endif
return output;
}
// Vertex Shader function for Depth Pass
META_VS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_INSTANCING=0)
META_PERMUTATION_1(USE_INSTANCING=1)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(ATTRIBUTE,0, R32G32B32A32_FLOAT,3, 0, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,1, R32G32B32A32_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,2, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,3, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
META_VS_IN_ELEMENT(ATTRIBUTE,4, R16G16B16A16_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING)
float4 VS_Depth(ModelInput_PosOnly input) : SV_Position
{
float4x4 world = GetInstanceTransform(input);
float3 worldPosition = mul(float4(input.Position.xyz, 1), world).xyz;
float4 position = mul(float4(worldPosition, 1), ViewProjectionMatrix);
return position;
}
#if USE_SKINNING
// The skeletal bones matrix buffer (stored as 4x3, 3 float4 behind each other)
Buffer<float4> BoneMatrices : register(t0);
#if PER_BONE_MOTION_BLUR
// The skeletal bones matrix buffer from the previous frame
Buffer<float4> PrevBoneMatrices : register(t1);
float3x4 GetPrevBoneMatrix(int index)
{
float4 a = PrevBoneMatrices[index * 3];
float4 b = PrevBoneMatrices[index * 3 + 1];
float4 c = PrevBoneMatrices[index * 3 + 2];
return float3x4(a, b, c);
}
float3 SkinPrevPosition(ModelInput_Skinned input)
{
float4 position = float4(input.Position.xyz, 1);
float3x4 boneMatrix = input.BlendWeights.x * GetPrevBoneMatrix(input.BlendIndices.x);
boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y);
boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z);
boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w);
return mul(boneMatrix, position);
}
#endif
// Cached skinning data to avoid multiple calculation
struct SkinningData
{
float3x4 BlendMatrix;
};
// Calculates the transposed transform matrix for the given bone index
float3x4 GetBoneMatrix(int index)
{
float4 a = BoneMatrices[index * 3];
float4 b = BoneMatrices[index * 3 + 1];
float4 c = BoneMatrices[index * 3 + 2];
return float3x4(a, b, c);
}
// Calculates the transposed transform matrix for the given vertex (uses blending)
float3x4 GetBoneMatrix(ModelInput_Skinned input)
{
float3x4 boneMatrix = input.BlendWeights.x * GetBoneMatrix(input.BlendIndices.x);
boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y);
boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z);
boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w);
return boneMatrix;
}
// Transforms the vertex position by weighted sum of the skinning matrices
float3 SkinPosition(ModelInput_Skinned input, SkinningData data)
{
return mul(data.BlendMatrix, float4(input.Position.xyz, 1));
}
// Transforms the vertex position by weighted sum of the skinning matrices
float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
{
// Unpack vertex tangent frame
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
float3 normal = input.Normal.xyz * 2.0 - 1.0;
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
// Apply skinning
tangent = mul(data.BlendMatrix, float4(tangent, 0));
normal = mul(data.BlendMatrix, float4(normal, 0));
float3 bitangent = cross(normal, tangent) * bitangentSign;
return float3x3(tangent, bitangent, normal);
}
// Vertex Shader function for GBuffers/Depth Pass (skinned mesh rendering)
META_VS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_SKINNING=1)
META_PERMUTATION_2(USE_SKINNING=1, PER_BONE_MOTION_BLUR=1)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(BLENDINDICES, 0, R8G8B8A8_UINT, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(BLENDWEIGHT, 0, R16G16B16A16_FLOAT,0, ALIGN, PER_VERTEX, 0, true)
VertexOutput VS_Skinned(ModelInput_Skinned input)
{
VertexOutput output;
// Perform skinning
SkinningData data;
data.BlendMatrix = GetBoneMatrix(input);
float3 position = SkinPosition(input, data);
float3x3 tangentToLocal = SkinTangents(input, data);
// Compute world space vertex position
float4x4 world = GetInstanceTransform(input);
output.Geometry.WorldPosition = mul(float4(position, 1), world).xyz;
#if PER_BONE_MOTION_BLUR
float3 prevPosition = SkinPrevPosition(input);
output.Geometry.PrevWorldPosition = mul(float4(prevPosition, 1), PrevWorldMatrix).xyz;
#else
output.Geometry.PrevWorldPosition = mul(float4(position, 1), PrevWorldMatrix).xyz;
#endif
// Compute clip space position
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
// Pass vertex attributes
output.Geometry.TexCoord = input.TexCoord;
#if USE_VERTEX_COLOR
output.Geometry.VertexColor = float4(0, 0, 0, 1);
#endif
output.Geometry.LightmapUV = float2(0, 0);
output.Geometry.InstanceOrigin = world[3].xyz;
#if USE_INSTANCING
output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w);
#else
output.Geometry.InstanceParams = float2(PerInstanceRandom, LODDitherFactor);
#endif
// Calculate tanget space to world space transformation matrix for unit vectors
float3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal);
output.Geometry.WorldNormal = tangentToWorld[2];
output.Geometry.WorldTangent.xyz = tangentToWorld[0];
output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f;
// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS
MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry);
materialInput.TwoSidedSign = WorldDeterminantSign;
materialInput.SvPosition = output.Position;
materialInput.PreSkinnedPosition = input.Position.xyz;
materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
Material material = GetMaterialVS(materialInput);
#endif
// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
output.Geometry.WorldPosition += material.PositionOffset;
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
#endif
// Get tessalation multiplier (per vertex)
#if USE_TESSELLATION
output.TessellationMultiplier = material.TessellationMultiplier;
#endif
// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
output.CustomVSToPS = material.CustomVSToPS;
#endif
return output;
}
#endif
#if USE_DITHERED_LOD_TRANSITION
void ClipLODTransition(PixelInput input)
{
float ditherFactor = input.InstanceParams.y;
if (abs(ditherFactor) > 0.001)
{
float randGrid = cos(dot(floor(input.Position.xy), float2(347.83452793, 3343.28371863)));
float randGridFrac = frac(randGrid * 1000.0);
half mask = (ditherFactor < 0.0) ? (ditherFactor + 1.0 > randGridFrac) : (ditherFactor < randGridFrac);
clip(mask - 0.001);
}
}
#endif
// Pixel Shader function for Depth Pass
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Depth(PixelInput input)
{
#if USE_DITHERED_LOD_TRANSITION
// LOD masking
ClipLODTransition(input);
#endif
#if MATERIAL_MASKED || MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Perform per pixel clipping
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE
clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD);
#endif
#endif
}
@9

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@
#define MATERIAL 1
@3
// Enables/disables smooth terrain chunks LOD transitions (with morphing higher LOD near edges to the lower LOD in the neighbour)
#define USE_SMOOTH_LOD_TRANSITION 1
@@ -26,7 +25,6 @@ float3 ViewDir;
float TimeParam;
float4 ViewInfo;
float4 ScreenSize;
float4 LightmapArea;
float3 WorldInvScale;
float WorldDeterminantSign;
float PerInstanceRandom;
@@ -39,35 +37,31 @@ float2 OffsetUV;
float2 Dummy0;
@1META_CB_END
#if CAN_USE_LIGHTMAP
// Irradiance and directionality prebaked lightmaps
Texture2D Lightmap0 : register(t0);
Texture2D Lightmap1 : register(t1);
Texture2D Lightmap2 : register(t2);
#endif
// Terrain data
Texture2D Heightmap : register(t3);
Texture2D Splatmap0 : register(t4);
Texture2D Splatmap1 : register(t5);
Texture2D Heightmap : register(t0);
Texture2D Splatmap0 : register(t1);
Texture2D Splatmap1 : register(t2);
// Material shader resources
// Shader resources
@2
// Geometry data passed though the graphics rendering stages up to the pixel shader
struct GeometryData
{
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
float3 WorldNormal : TEXCOORD3;
float HolesMask : TEXCOORD4;
#if USE_TERRAIN_LAYERS
float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5;
#endif
};
// Interpolants passed from the vertex shader
struct VertexOutput
{
float4 Position : SV_Position;
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
float3 WorldNormal : TEXCOORD3;
float HolesMask : TEXCOORD4;
#if USE_TERRAIN_LAYERS
float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5;
#endif
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
@@ -79,19 +73,12 @@ struct VertexOutput
// Interpolants passed to the pixel shader
struct PixelInput
{
float4 Position : SV_Position;
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
float3 WorldNormal : TEXCOORD3;
float HolesMask : TEXCOORD4;
#if USE_TERRAIN_LAYERS
float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5;
#endif
float4 Position : SV_Position;
GeometryData Geometry;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
bool IsFrontFace : SV_IsFrontFace;
bool IsFrontFace : SV_IsFrontFace;
};
// Material properties generation input
@@ -116,25 +103,66 @@ struct MaterialInput
#endif
};
// Extracts geometry data to the material input
MaterialInput GetGeometryMaterialInput(GeometryData geometry)
{
MaterialInput output = (MaterialInput)0;
output.WorldPosition = geometry.WorldPosition;
output.TexCoord = geometry.TexCoord;
#if USE_LIGHTMAP
output.LightmapUV = geometry.LightmapUV;
#endif
output.TBN = CalcTangentBasisFromWorldNormal(geometry.WorldNormal);
output.HolesMask = geometry.HolesMask;
#if USE_TERRAIN_LAYERS
output.Layers = geometry.Layers;
#endif
return output;
}
#if USE_TESSELLATION
// Interpolates the geometry positions data only (used by the tessallation when generating vertices)
#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset
// Offsets the geometry positions data only (used by the tessallation when generating vertices)
#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset
// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess)
#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \
float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \
float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \
float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \
geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW
// Interpolates the geometry data except positions (used by the tessallation when generating vertices)
GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2)
{
GeometryData output = (GeometryData)0;
output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2;
output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2;
output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2;
output.WorldNormal = normalize(output.WorldNormal);
output.HolesMask = p0.HolesMask * w0 + p1.HolesMask * w1 + p2.HolesMask * w2;
#if USE_TERRAIN_LAYERS
UNROLL
for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++)
output.Layers[i] = p0.Layers[i] * w0 + p1.Layers[i] * w1 + p2.Layers[i] * w2;
#endif
return output;
}
#endif
MaterialInput GetMaterialInput(PixelInput input)
{
MaterialInput result = (MaterialInput)0;
result.WorldPosition = input.WorldPosition;
result.TexCoord = input.TexCoord;
#if USE_LIGHTMAP
result.LightmapUV = input.LightmapUV;
#endif
result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal);
result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0);
result.SvPosition = input.Position;
result.HolesMask = input.HolesMask;
#if USE_TERRAIN_LAYERS
result.Layers = input.Layers;
#endif
MaterialInput output = GetGeometryMaterialInput(input.Geometry);
output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0);
output.SvPosition = input.Position;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
result.CustomVSToPS = input.CustomVSToPS;
output.CustomVSToPS = input.CustomVSToPS;
#endif
return result;
return output;
}
// Removes the scale vector from the local to world transformation matrix
@@ -216,6 +244,8 @@ float4 GetVertexColor(MaterialInput input)
return 1;
}
@8
// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@@ -234,9 +264,6 @@ Material GetMaterialPS(MaterialInput input)
@4
}
// Fix line for errors/warnings for shader code from template
#line 1000
// Calculates LOD value (with fractional part for blending)
float CalcLOD(float2 xy, float4 morph)
{
@@ -285,7 +312,7 @@ float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal)
struct TerrainVertexInput
{
float2 TexCoord : TEXCOORD0;
float4 Morph : TEXCOORD1;
float4 Morph : TEXCOORD1;
};
// Vertex Shader function for terrain rendering
@@ -336,7 +363,7 @@ VertexOutput VS(TerrainVertexInput input)
float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f;
float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y);
normal = normalize(normal);
output.HolesMask = isHole ? 0 : 1;
output.Geometry.HolesMask = isHole ? 0 : 1;
if (isHole)
{
normal = float3(0, 1, 0);
@@ -353,10 +380,10 @@ VertexOutput VS(TerrainVertexInput input)
float3 position = float3(positionXZ.x, height, positionXZ.y);
// Compute world space vertex position
output.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz;
output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz;
// Compute clip space position
output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
// Pass vertex attributes
#if USE_SMOOTH_LOD_TRANSITION
@@ -364,46 +391,46 @@ VertexOutput VS(TerrainVertexInput input)
#else
float2 texCoord = input.TexCoord;
#endif
output.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV;
output.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy;
output.Geometry.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV;
output.Geometry.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy;
// Extract terrain layers weights from the splatmap
#if USE_TERRAIN_LAYERS
output.Layers[0] = splatmap0Value;
output.Geometry.Layers[0] = splatmap0Value;
#if TERRAIN_LAYERS_DATA_SIZE > 1
output.Layers[1] = splatmap1Value;
output.Geometry.Layers[1] = splatmap1Value;
#endif
#endif
// Compute world space normal vector
float3x3 tangentToLocal = CalcTangentBasisFromWorldNormal(normal);
float3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal);
output.WorldNormal = tangentToWorld[2];
output.Geometry.WorldNormal = tangentToWorld[2];
// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS
MaterialInput materialInput = (MaterialInput)0;
materialInput.WorldPosition = output.WorldPosition;
materialInput.TexCoord = output.TexCoord;
materialInput.WorldPosition = output.Geometry.WorldPosition;
materialInput.TexCoord = output.Geometry.TexCoord;
#if USE_LIGHTMAP
materialInput.LightmapUV = output.LightmapUV;
materialInput.LightmapUV = output.Geometry.LightmapUV;
#endif
materialInput.TBN = CalcTangentBasisFromWorldNormal(output.WorldNormal);
materialInput.TBN = CalcTangentBasisFromWorldNormal(output.Geometry.WorldNormal);
materialInput.TwoSidedSign = WorldDeterminantSign;
materialInput.SvPosition = output.Position;
materialInput.PreSkinnedPosition = position;
materialInput.PreSkinnedNormal = normal;
materialInput.HolesMask = output.HolesMask;
materialInput.HolesMask = output.Geometry.HolesMask;
#if USE_TERRAIN_LAYERS
materialInput.Layers = output.Layers;
materialInput.Layers = output.Geometry.Layers;
#endif
Material material = GetMaterialVS(materialInput);
#endif
// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
output.WorldPosition += material.PositionOffset;
output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);
output.Geometry.WorldPosition += material.PositionOffset;
output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix);
#endif
// Get tessalation multiplier (per vertex)
@@ -419,356 +446,9 @@ VertexOutput VS(TerrainVertexInput input)
return output;
}
#if USE_TESSELLATION
// Interpolants passed from the hull shader to the domain shader
struct TessalationHSToDS
{
float4 Position : SV_Position;
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
float3 WorldNormal : TEXCOORD3;
float HolesMask : TEXCOORD4;
#if USE_TERRAIN_LAYERS
float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5;
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
float TessellationMultiplier : TESS;
};
// Interpolants passed from the domain shader and to the pixel shader
struct TessalationDSToPS
{
float4 Position : SV_Position;
float3 WorldPosition : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float2 LightmapUV : TEXCOORD2;
float3 WorldNormal : TEXCOORD3;
float HolesMask : TEXCOORD4;
#if USE_TERRAIN_LAYERS
float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5;
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
};
MaterialInput GetMaterialInput(TessalationDSToPS input)
{
MaterialInput result = (MaterialInput)0;
result.WorldPosition = input.WorldPosition;
result.TexCoord = input.TexCoord;
#if USE_LIGHTMAP
result.LightmapUV = input.LightmapUV;
#endif
result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal);
result.TwoSidedSign = WorldDeterminantSign;
result.SvPosition = input.Position;
result.HolesMask = input.HolesMask;
#if USE_TERRAIN_LAYERS
result.Layers = input.Layers;
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
result.CustomVSToPS = input.CustomVSToPS;
#endif
return result;
}
struct TessalationPatch
{
float EdgeTessFactor[3] : SV_TessFactor;
float InsideTessFactor : SV_InsideTessFactor;
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
float3 B210 : POSITION4;
float3 B120 : POSITION5;
float3 B021 : POSITION6;
float3 B012 : POSITION7;
float3 B102 : POSITION8;
float3 B201 : POSITION9;
float3 B111 : CENTER;
#endif
};
TessalationPatch HS_PatchConstant(InputPatch<VertexOutput, 3> input)
{
TessalationPatch output;
// Average tess factors along edges, and pick an edge tess factor for the interior tessellation
float4 TessellationMultipliers;
TessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier);
TessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier);
TessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier);
TessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier);
TessellationMultipliers = clamp(TessellationMultipliers, 1, MAX_TESSELLATION_FACTOR);
output.EdgeTessFactor[0] = TessellationMultipliers.x; // 1->2 edge
output.EdgeTessFactor[1] = TessellationMultipliers.y; // 2->0 edge
output.EdgeTessFactor[2] = TessellationMultipliers.z; // 0->1 edge
output.InsideTessFactor = TessellationMultipliers.w;
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
// Calculate PN-Triangle coefficients
// Refer to Vlachos 2001 for the original formula
float3 p1 = input[0].WorldPosition;
float3 p2 = input[1].WorldPosition;
float3 p3 = input[2].WorldPosition;
float3 n1 = input[0].WorldNormal;
float3 n2 = input[1].WorldNormal;
float3 n3 = input[2].WorldNormal;
// Calculate control points
output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f;
output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f;
output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f;
output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f;
output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f;
output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f;
float3 e = (output.B210 + output.B120 + output.B021 +
output.B012 + output.B102 + output.B201) / 6.0f;
float3 v = (p1 + p2 + p3) / 3.0f;
output.B111 = e + ((e - v) / 2.0f);
#endif
return output;
}
META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5)
META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS)
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[maxtessfactor(MAX_TESSELLATION_FACTOR)]
[outputcontrolpoints(3)]
[patchconstantfunc("HS_PatchConstant")]
TessalationHSToDS HS(InputPatch<VertexOutput, TESSELLATION_IN_CONTROL_POINTS> input, uint ControlPointID : SV_OutputControlPointID)
{
TessalationHSToDS output;
// Pass through shader
#define COPY(thing) output.thing = input[ControlPointID].thing;
COPY(Position);
COPY(WorldPosition);
COPY(TexCoord);
COPY(LightmapUV);
COPY(WorldNormal);
COPY(HolesMask);
COPY(TessellationMultiplier);
#if USE_TERRAIN_LAYERS
COPY(Layers);
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
COPY(CustomVSToPS);
#endif
#undef COPY
return output;
}
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG
// Orthogonal projection on to plane
float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject)
{
return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal;
}
#endif
META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5)
[domain("tri")]
TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch<TessalationHSToDS, 3> input)
{
TessalationDSToPS output;
// Get the barycentric coords
float U = barycentricCoords.x;
float V = barycentricCoords.y;
float W = barycentricCoords.z;
// Interpolate patch attributes to generated vertices
#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing
#define COPY(thing) output.thing = input[0].thing
INTERPOLATE(Position);
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN
float UU = U * U;
float VV = V * V;
float WW = W * W;
float UU3 = UU * 3.0f;
float VV3 = VV * 3.0f;
float WW3 = WW * 3.0f;
// Interpolate using barycentric coordinates and PN Triangle control points
output.WorldPosition =
input[0].WorldPosition * UU * U +
input[1].WorldPosition * VV * V +
input[2].WorldPosition * WW * W +
constantData.B210 * UU3 * V +
constantData.B120 * VV3 * U +
constantData.B021 * VV3 * W +
constantData.B012 * WW3 * V +
constantData.B102 * WW3 * U +
constantData.B201 * UU3 * W +
constantData.B111 * 6.0f * W * U * V;
#else
INTERPOLATE(WorldPosition);
#endif
INTERPOLATE(TexCoord);
INTERPOLATE(LightmapUV);
INTERPOLATE(WorldNormal);
INTERPOLATE(HolesMask);
#if USE_TERRAIN_LAYERS
UNROLL
for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++)
{
INTERPOLATE(Layers[i]);
}
#endif
#if USE_CUSTOM_VERTEX_INTERPOLATORS
UNROLL
for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++)
{
INTERPOLATE(CustomVSToPS[i]);
}
#endif
#undef INTERPOLATE
#undef COPY
// Interpolating normal can unnormalize it, so normalize it
output.WorldNormal = normalize(output.WorldNormal);
#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG
// Orthogonal projection in the tangent planes
float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition);
float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition);
float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition);
// Interpolate the projected points
output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW;
#endif
// Perform displacement mapping
#if USE_DISPLACEMENT
MaterialInput materialInput = GetMaterialInput(output);
Material material = GetMaterialDS(materialInput);
output.WorldPosition += material.WorldDisplacement;
#endif
// Recalculate the clip space position
output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix);
return output;
}
#endif
#if USE_LIGHTMAP
float3 SampleLightmap(Material material, MaterialInput materialInput)
{
// Sample lightmaps
float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV);
float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV);
float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV);
// Unpack H-basis
float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x);
float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y);
float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z);
float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w);
// Sample baked diffuse irradiance from the H-basis coefficients
float3 normal = material.TangentNormal;
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
normal *= material.TangentNormal;
#endif
return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI;
}
#endif
// Pixel Shader function for GBuffer Pass
META_PS(true, FEATURE_LEVEL_ES2)
META_PERMUTATION_1(USE_LIGHTMAP=0)
META_PERMUTATION_1(USE_LIGHTMAP=1)
void PS_GBuffer(
in PixelInput input
,out float4 Light : SV_Target0
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
,out float4 RT0 : SV_Target1
,out float4 RT1 : SV_Target2
,out float4 RT2 : SV_Target3
#if USE_GBUFFER_CUSTOM_DATA
,out float4 RT3 : SV_Target4
#endif
#endif
)
{
Light = 0;
// Get material parameters
MaterialInput materialInput = GetMaterialInput(input);
Material material = GetMaterialPS(materialInput);
// Masking
#if MATERIAL_MASKED
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if USE_LIGHTMAP
float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness);
float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness);
// Sample lightmap
float3 diffuseIndirectLighting = SampleLightmap(material, materialInput);
// Apply static indirect light
Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor);
#endif
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
// Pack material properties to GBuffer
RT0 = float4(material.Color, material.AO);
RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0));
RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0);
// Custom data
#if USE_GBUFFER_CUSTOM_DATA
#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE
RT3 = float4(material.SubsurfaceColor, material.Opacity);
#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE
RT3 = float4(material.SubsurfaceColor, material.Opacity);
#else
RT3 = float4(0, 0, 0, 0);
#endif
#endif
// Add light emission
#if USE_EMISSIVE
Light.rgb += material.Emissive;
#endif
#else
// Handle blending as faked forward pass (use Light buffer and skip GBuffer modification)
Light = float4(material.Emissive, material.Opacity);
#endif
}
// Pixel Shader function for Depth Pass
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Depth(PixelInput input
#if GLSL
, out float4 OutColor : SV_Target0
#endif
)
void PS_Depth(PixelInput input)
{
#if MATERIAL_MASKED
// Perform per pixel clipping if material requries it
@@ -776,8 +456,6 @@ void PS_Depth(PixelInput input
Material material = GetMaterialPS(materialInput);
clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if GLSL
OutColor = 0;
#endif
}
@9

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/TexturePreviewMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Wires Debug Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/DefaultDeformableMaterial.flax (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Engine/DefaultMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/DefaultTerrainMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SingleColorMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SkyboxMaterial.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -50,6 +50,9 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public bool IsMethod => false;
/// <inheritdoc />
public bool IsEvent => false;
/// <inheritdoc />
public bool HasGet => true;
@@ -174,6 +177,9 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public bool IsMethod => true;
/// <inheritdoc />
public bool IsEvent => false;
/// <inheritdoc />
public bool HasGet => false;

View File

@@ -1,30 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The Android platform settings asset archetype. Allows to edit asset via editor.
/// </summary>
public class AndroidPlatformSettings : SettingsBase
{
/// <summary>
/// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
/// </summary>
[EditorOrder(0), EditorDisplay("General"), Tooltip("The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.")]
public string PackageName = "com.${COMPANY_NAME}.${PROJECT_NAME}";
/// <summary>
/// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.
/// </summary>
[EditorOrder(100), EditorDisplay("General"), Tooltip("The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.")]
public string[] Permissions;
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
public Texture OverrideIcon;
}
}

View File

@@ -1,34 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The audio payback engine settings container. Allows to edit asset via editor.
/// </summary>
public sealed class AudioSettings : SettingsBase
{
/// <summary>
/// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.
/// </summary>
[DefaultValue(false)]
[EditorOrder(0), EditorDisplay("General"), Tooltip("If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.")]
public bool DisableAudio;
/// <summary>
/// The doppler doppler effect factor. Scale for source and listener velocities. Default is 1.
/// </summary>
[DefaultValue(1.0f)]
[EditorOrder(100), EditorDisplay("General"), Limit(0, 10.0f, 0.01f), Tooltip("The doppler doppler effect factor. Scale for source and listener velocities. Default is 1.")]
public float DopplerFactor = 1.0f;
/// <summary>
/// True if mute all audio playback when game has no use focus.
/// </summary>
[DefaultValue(true)]
[EditorOrder(200), EditorDisplay("General", "Mute On Focus Loss"), Tooltip("If checked, engine will mute all audio playback when game has no use focus.")]
public bool MuteOnFocusLoss = true;
}
}

View File

@@ -1,77 +1,12 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The game building settings container. Allows to edit asset via editor.
/// </summary>
public sealed class BuildSettings : SettingsBase
partial class BuildSettings
{
/// <summary>
/// The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to.
/// </summary>
[DefaultValue(4096)]
[EditorOrder(10), Limit(32, short.MaxValue), EditorDisplay("General", "Max assets per package"), Tooltip("The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to.")]
public int MaxAssetsPerPackage = 4096;
/// <summary>
/// The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to.
/// </summary>
[DefaultValue(1024)]
[EditorOrder(20), Limit(16, short.MaxValue), EditorDisplay("General", "Max package size (in MB)"), Tooltip("The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to.")]
public int MaxPackageSizeMB = 1024;
/// <summary>
/// The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.
/// </summary>
[DefaultValue(0)]
[EditorOrder(30), EditorDisplay("General"), Tooltip("The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.")]
public int ContentKey = 0;
/// <summary>
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).
/// </summary>
[DefaultValue(false)]
[EditorOrder(40), EditorDisplay("General"), Tooltip("If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).")]
public bool ForDistribution;
/// <summary>
/// If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.
/// </summary>
[DefaultValue(false)]
[EditorOrder(50), EditorDisplay("General"), Tooltip("If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.")]
public bool SkipPackaging;
/// <summary>
/// The additional assets to include into build (into root assets set).
/// </summary>
[EditorOrder(1000), EditorDisplay("Additional Data"), Tooltip("The additional assets to include into build (into root assets set).")]
public Asset[] AdditionalAssets;
/// <summary>
/// The additional folders with assets to include into build (into root assets set). List of paths relative to the project directory (or absolute).
/// </summary>
[EditorOrder(1010), EditorDisplay("Additional Data"), Tooltip("The additional folders with assets to include into build (to root assets set). List of paths relative to the project directory (or absolute).")]
public string[] AdditionalAssetFolders;
/// <summary>
/// Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
/// </summary>
[DefaultValue(false)]
[EditorOrder(2000), EditorDisplay("Content", "Shaders No Optimize"), Tooltip("Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.")]
public bool ShadersNoOptimize;
/// <summary>
/// Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).
/// </summary>
[DefaultValue(false)]
[EditorOrder(2010), EditorDisplay("Content"), Tooltip("Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).")]
public bool ShadersGenerateDebugData;
/// <summary>
/// The build presets.
/// </summary>
@@ -90,13 +25,6 @@ namespace FlaxEditor.Content.Settings
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Development,
},
new BuildTarget
{
Name = "Windows 32bit",
Output = "Output\\Win32",
Platform = BuildPlatform.Windows32,
Mode = BuildConfiguration.Development,
},
}
},
new BuildPreset
@@ -109,14 +37,7 @@ namespace FlaxEditor.Content.Settings
Name = "Windows 64bit",
Output = "Output\\Win64",
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Development,
},
new BuildTarget
{
Name = "Windows 32bit",
Output = "Output\\Win32",
Platform = BuildPlatform.Windows32,
Mode = BuildConfiguration.Development,
Mode = BuildConfiguration.Release,
},
}
},

View File

@@ -35,6 +35,12 @@ namespace FlaxEditor.Content.Settings
[EditorOrder(30), Tooltip("Configuration build mode")]
public BuildConfiguration Mode;
/// <summary>
/// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).
/// </summary>
[EditorOrder(90), Tooltip("The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).")]
public string[] CustomDefines;
/// <summary>
/// The pre-build action command line.
/// </summary>
@@ -46,11 +52,5 @@ namespace FlaxEditor.Content.Settings
/// </summary>
[EditorOrder(110)]
public string PostBuildAction;
/// <summary>
/// Gets the build options computed from the target configuration.
/// </summary>
[HideInEditor, NoSerialize]
public virtual BuildOptions Options => BuildOptions.None;
}
}

View File

@@ -7,32 +7,11 @@ using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The game settings asset archetype. Allows to edit asset via editor.
/// </summary>
public sealed class GameSettings : SettingsBase
partial class GameSettings
{
internal const string PS4PlatformSettingsTypename = "FlaxEditor.Content.Settings.PS4PlatformSettings";
internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings";
/// <summary>
/// The product full name.
/// </summary>
[EditorOrder(0), EditorDisplay("General"), Tooltip("The name of your product.")]
public string ProductName;
/// <summary>
/// The company full name.
/// </summary>
[EditorOrder(10), EditorDisplay("General"), Tooltip("The name of your company or organization.")]
public string CompanyName;
/// <summary>
/// The copyright note used for content signing (eg. source code header).
/// </summary>
[EditorOrder(15), EditorDisplay("General"), Tooltip("The copyright note used for content signing (eg. source code header).")]
public string CopyrightNotice;
/// <summary>
/// The default application icon.
/// </summary>

View File

@@ -1,61 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The graphics rendering settings container. Allows to edit asset via editor. To modify those settings at runtime use <see cref="GraphicsSettings"/>.
/// </summary>
/// <seealso cref="FlaxEngine.Graphics"/>
public sealed class GraphicsSettings : SettingsBase
{
/// <summary>
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
/// </summary>
[EditorOrder(20), EditorDisplay("General", "Use V-Sync"), Tooltip("Enables rendering synchronization with the refresh rate of the display device to avoid \"tearing\" artifacts.")]
public bool UseVSync = false;
/// <summary>
/// Anti Aliasing quality setting.
/// </summary>
[EditorOrder(1000), EditorDisplay("Quality", "AA Quality"), Tooltip("Anti Aliasing quality.")]
public Quality AAQuality = Quality.Medium;
/// <summary>
/// Screen Space Reflections quality.
/// </summary>
[EditorOrder(1100), EditorDisplay("Quality", "SSR Quality"), Tooltip("Screen Space Reflections quality.")]
public Quality SSRQuality = Quality.Medium;
/// <summary>
/// Screen Space Ambient Occlusion quality setting.
/// </summary>
[EditorOrder(1200), EditorDisplay("Quality", "SSAO Quality"), Tooltip("Screen Space Ambient Occlusion quality setting.")]
public Quality SSAOQuality = Quality.Medium;
/// <summary>
/// Volumetric Fog quality setting.
/// </summary>
[EditorOrder(1250), EditorDisplay("Quality", "Volumetric Fog Quality"), Tooltip("Volumetric Fog quality setting.")]
public Quality VolumetricFogQuality = Quality.High;
/// <summary>
/// The shadows quality.
/// </summary>
[EditorOrder(1300), EditorDisplay("Quality", "Shadows Quality"), Tooltip("The shadows quality.")]
public Quality ShadowsQuality = Quality.Medium;
/// <summary>
/// The shadow maps quality (textures resolution).
/// </summary>
[EditorOrder(1310), EditorDisplay("Quality", "Shadow Maps Quality"), Tooltip("The shadow maps quality (textures resolution).")]
public Quality ShadowMapsQuality = Quality.Medium;
/// <summary>
/// Enables cascades splits blending for directional light shadows.
/// </summary>
[EditorOrder(1320), EditorDisplay("Quality", "Allow CSM Blending"), Tooltip("Enables cascades splits blending for directional light shadows.")]
public bool AllowCSMBlending = false;
}
}

View File

@@ -4,10 +4,7 @@ using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The input settings container. Allows to edit asset via editor.
/// </summary>
public sealed class InputSettings : SettingsBase
partial class InputSettings
{
/// <summary>
/// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.

View File

@@ -6,10 +6,7 @@ using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The layers and objects tags settings. Allows to edit asset via editor.
/// </summary>
public sealed class LayersAndTagsSettings : SettingsBase
partial class LayersAndTagsSettings
{
/// <summary>
/// The tag names.

View File

@@ -1,60 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The Linux platform settings asset archetype. Allows to edit asset via editor.
/// </summary>
public class LinuxPlatformSettings : SettingsBase
{
/// <summary>
/// The default game window mode.
/// </summary>
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")]
public GameWindowMode WindowMode = GameWindowMode.Windowed;
/// <summary>
/// The default game window width (in pixels).
/// </summary>
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")]
public int ScreenWidth = 1280;
/// <summary>
/// The default game window height (in pixels).
/// </summary>
[EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")]
public int ScreenHeight = 720;
/// <summary>
/// Enables resizing the game window by the user.
/// </summary>
[EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")]
public bool ResizableWindow = false;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
[EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")]
public bool RunInBackground = false;
/// <summary>
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
/// </summary>
[EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")]
public bool ForceSingleInstance = false;
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
public Texture OverrideIcon;
/// <summary>
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
/// </summary>
[EditorOrder(2020), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")]
public bool SupportVulkan = true;
}
}

View File

@@ -1,106 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The navigation system settings container.
/// </summary>
public sealed class NavigationSettings : SettingsBase
{
/// <summary>
/// The height of a grid cell in the navigation mesh building steps using heightfields.
/// A lower number means higher precision on the vertical axis but longer build times.
/// </summary>
[DefaultValue(10.0f), Limit(1, 400)]
[EditorOrder(10), EditorDisplay("Nav Mesh Options"), Tooltip("The height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")]
public float CellHeight = 10.0f;
/// <summary>
/// The width/height of a grid cell in the navigation mesh building steps using heightfields.
/// A lower number means higher precision on the horizontal axes but longer build times.
/// </summary>
[DefaultValue(30.0f), Limit(1, 400)]
[EditorOrder(20), EditorDisplay("Nav Mesh Options"), Tooltip("The width/height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")]
public float CellSize = 30.0f;
/// <summary>
/// Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize.
/// </summary>
[DefaultValue(64), Limit(8, 4096)]
[EditorOrder(30), EditorDisplay("Nav Mesh Options"), Tooltip("Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize.")]
public int TileSize = 64;
/// <summary>
/// The minimum number of cells allowed to form isolated island areas.
/// </summary>
[DefaultValue(0), Limit(0, 100)]
[EditorOrder(40), EditorDisplay("Nav Mesh Options"), Tooltip("The minimum number of cells allowed to form isolated island areas.")]
public int MinRegionArea = 0;
/// <summary>
/// Any regions with a span count smaller than this value will, if possible, be merged with larger regions.
/// </summary>
[DefaultValue(20), Limit(0, 100)]
[EditorOrder(50), EditorDisplay("Nav Mesh Options"), Tooltip("Any regions with a span count smaller than this value will, if possible, be merged with larger regions.")]
public int MergeRegionArea = 20;
/// <summary>
/// The maximum allowed length for contour edges along the border of the mesh.
/// </summary>
[DefaultValue(1200.0f), Limit(100)]
[EditorOrder(60), EditorDisplay("Nav Mesh Options", "Max Edge Length"), Tooltip("The maximum allowed length for contour edges along the border of the mesh.")]
public float MaxEdgeLen = 1200.0f;
/// <summary>
/// The maximum distance a simplified contour's border edges should deviate from the original raw contour.
/// </summary>
[DefaultValue(1.3f), Limit(0.1f, 4)]
[EditorOrder(70), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance a simplified contour's border edges should deviate from the original raw contour.")]
public float MaxEdgeError = 1.3f;
/// <summary>
/// The sampling distance to use when generating the detail mesh. For height detail only.
/// </summary>
[DefaultValue(600.0f), Limit(1)]
[EditorOrder(80), EditorDisplay("Nav Mesh Options", "Detail Sampling Distance"), Tooltip("The sampling distance to use when generating the detail mesh.")]
public float DetailSamplingDist = 600.0f;
/// <summary>
/// The maximum distance the detail mesh surface should deviate from heightfield data. For height detail only.
/// </summary>
[DefaultValue(1.0f), Limit(0, 3)]
[EditorOrder(90), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance the detail mesh surface should deviate from heightfield data.")]
public float MaxDetailSamplingError = 1.0f;
/// <summary>
/// The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius.
/// </summary>
[DefaultValue(34.0f), Limit(0)]
[EditorOrder(1000), EditorDisplay("Agent Options"), Tooltip("The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius.")]
public float WalkableRadius = 34.0f;
/// <summary>
/// The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value.
/// </summary>
[DefaultValue(144.0f), Limit(0)]
[EditorOrder(1010), EditorDisplay("Agent Options"), Tooltip("The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value.")]
public float WalkableHeight = 144.0f;
/// <summary>
/// The maximum ledge height that is considered to still be traversable.
/// </summary>
[DefaultValue(35.0f), Limit(0)]
[EditorOrder(1020), EditorDisplay("Agent Options"), Tooltip("The maximum ledge height that is considered to still be traversable.")]
public float WalkableMaxClimb = 35.0f;
/// <summary>
/// The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value.
/// </summary>
[DefaultValue(60.0f), Limit(0, 89.0f)]
[EditorOrder(1030), EditorDisplay("Agent Options"), Tooltip("The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value.")]
public float WalkableMaxSlopeAngle = 60.0f;
}
}

View File

@@ -1,112 +1,17 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The physics simulation settings container. Allows to edit asset via editor.
/// </summary>
public sealed class PhysicsSettings : SettingsBase
partial class PhysicsSettings
{
/// <summary>
/// The default gravity force value (in cm^2/s).
/// </summary>
[DefaultValue(typeof(Vector3), "0,-981.0,0")]
[EditorOrder(0), EditorDisplay("Simulation"), Tooltip("The default gravity force value (in cm^2/s).")]
public Vector3 DefaultGravity = new Vector3(0, -981.0f, 0);
/// <summary>
/// If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.
/// </summary>
[DefaultValue(true)]
[EditorOrder(10), EditorDisplay("Simulation"), Tooltip("If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.")]
public bool QueriesHitTriggers = true;
/// <summary>
/// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.
/// </summary>
[DefaultValue(5.0f)]
[EditorOrder(20), EditorDisplay("Simulation"), Limit(-1, 10), Tooltip("Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.")]
public float TriangleMeshTriangleMinAreaThreshold = 5.0f;
/// <summary>
/// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity
/// </summary>
[DefaultValue(200.0f)]
[EditorOrder(30), EditorDisplay("Simulation"), Limit(0), Tooltip("Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity")]
public float BounceThresholdVelocity = 200.0f;
/// <summary>
/// Default friction combine mode, controls how friction is computed for multiple materials.
/// </summary>
[DefaultValue(PhysicsCombineMode.Average)]
[EditorOrder(40), EditorDisplay("Simulation"), Tooltip("Default friction combine mode, controls how friction is computed for multiple materials.")]
public PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode.Average;
/// <summary>
/// Default restitution combine mode, controls how restitution is computed for multiple materials.
/// </summary>
[DefaultValue(PhysicsCombineMode.Average)]
[EditorOrder(50), EditorDisplay("Simulation"), Tooltip("Default restitution combine mode, controls how restitution is computed for multiple materials.")]
public PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode.Average;
/// <summary>
/// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally.
/// </summary>
[DefaultValue(false)]
[EditorOrder(70), EditorDisplay("Simulation", "Disable CCD"), Tooltip("If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally.")]
public bool DisableCCD;
/// <summary>
/// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.
/// </summary>
[DefaultValue(false)]
[EditorOrder(80), EditorDisplay("Simulation"), Tooltip("Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.")]
public bool EnableAdaptiveForce;
/// <summary>
/// The maximum allowed delta time (in seconds) for the physics simulation step.
/// </summary>
[DefaultValue(1.0f / 10.0f)]
[EditorOrder(1000), EditorDisplay("Framerate"), Limit(0.0013f, 2.0f), Tooltip("The maximum allowed delta time (in seconds) for the physics simulation step.")]
public float MaxDeltaTime = 1.0f / 10.0f;
/// <summary>
/// Whether to substep the physics simulation.
/// </summary>
[DefaultValue(false)]
[EditorOrder(1005), EditorDisplay("Framerate"), Tooltip("Whether to substep the physics simulation.")]
public bool EnableSubstepping;
/// <summary>
/// Delta time (in seconds) for an individual simulation substep.
/// </summary>
[DefaultValue(1.0f / 120.0f)]
[EditorOrder(1010), EditorDisplay("Framerate"), Limit(0.0013f, 1.0f), Tooltip("Delta time (in seconds) for an individual simulation substep.")]
public float SubstepDeltaTime = 1.0f / 120.0f;
/// <summary>
/// The maximum number of substeps for physics simulation.
/// </summary>
[DefaultValue(5)]
[EditorOrder(1020), EditorDisplay("Framerate"), Limit(1, 16), Tooltip("The maximum number of substeps for physics simulation.")]
public int MaxSubsteps = 5;
/// <summary>
/// The collision layers masks. Used to define layer-based collision detection.
/// </summary>
[EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(FlaxEditor.CustomEditors.Dedicated.LayersMatrixEditor))]
public uint[] LayerMasks = new uint[32];
/// <summary>
/// Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.
/// </summary>
[DefaultValue(false)]
[EditorOrder(1100), EditorDisplay("Other", "Support Cooking At Runtime"), Tooltip("Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.")]
public bool SupportCookingAtRuntime;
/// <summary>
/// Initializes a new instance of the <see cref="PhysicsSettings"/> class.
/// </summary>

View File

@@ -1,48 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The time settings asset archetype. Allows to edit asset via editor.
/// </summary>
public sealed class TimeSettings : SettingsBase
{
/// <summary>
/// The target amount of the game logic updates per second (script updates frequency). Use 0 for infinity.
/// </summary>
[DefaultValue(30.0f)]
[EditorOrder(1), Limit(0, 1000), EditorDisplay(null, "Update FPS"), Tooltip("Target amount of the game logic updates per second (script updates frequency). Use 0 for infinity.")]
public float UpdateFPS = 30.0f;
/// <summary>
/// The target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity.
/// </summary>
[DefaultValue(60.0f)]
[EditorOrder(2), Limit(0, 1000), EditorDisplay(null, "Physics FPS"), Tooltip("Target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity.")]
public float PhysicsFPS = 60.0f;
/// <summary>
/// The target amount of the frames rendered per second (actual game FPS). Use 0 for infinity.
/// </summary>
[DefaultValue(60.0f)]
[EditorOrder(3), Limit(0, 1000), EditorDisplay(null, "Draw FPS"), Tooltip("Target amount of the frames rendered per second (actual game FPS). Use 0 for infinity.")]
public float DrawFPS = 60.0f;
/// <summary>
/// The game time scale factor. Default is 1.
/// </summary>
[DefaultValue(1.0f)]
[EditorOrder(10), Limit(0, 1000.0f, 0.1f), Tooltip("Game time scaling factor. Default is 1 for real-time simulation.")]
public float TimeScale = 1.0f;
/// <summary>
/// The maximum allowed delta time (in seconds) for the game logic update step.
/// </summary>
[DefaultValue(1.0f / 10.0f)]
[EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), Tooltip("The maximum allowed delta time (in seconds) for the game logic update step.")]
public float MaxUpdateDeltaTime = 1.0f / 10.0f;
}
}

View File

@@ -1,97 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The Universal Windows Platform (UWP) platform settings asset archetype. Allows to edit asset via editor.
/// </summary>
public class UWPPlatformSettings : SettingsBase
{
/// <summary>
/// The preferred launch windowing mode.
/// </summary>
public enum WindowMode
{
/// <summary>
/// The full screen mode
/// </summary>
FullScreen = 0,
/// <summary>
/// The view size.
/// </summary>
ViewSize = 1,
}
/// <summary>
/// The display orientation modes. Can be combined as flags.
/// </summary>
[Flags]
public enum DisplayOrientations
{
/// <summary>
/// The none.
/// </summary>
None = 0,
/// <summary>
/// The landscape.
/// </summary>
Landscape = 1,
/// <summary>
/// The landscape flipped.
/// </summary>
LandscapeFlipped = 2,
/// <summary>
/// The portrait.
/// </summary>
Portrait = 4,
/// <summary>
/// The portrait flipped.
/// </summary>
PortraitFlipped = 8,
}
/// <summary>
/// The preferred launch windowing mode. Always fullscreen on Xbox.
/// </summary>
[DefaultValue(WindowMode.FullScreen)]
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The preferred launch windowing mode. Always fullscreen on Xbox.")]
public WindowMode PreferredLaunchWindowingMode = WindowMode.FullScreen;
/// <summary>
/// The display orientation modes. Can be combined as flags.
/// </summary>
[DefaultValue(DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped)]
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The display orientation modes. Can be combined as flags.")]
public DisplayOrientations AutoRotationPreferences = DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped;
/// <summary>
/// The location of the package certificate (relative to the project).
/// </summary>
[DefaultValue("")]
[EditorOrder(1010), EditorDisplay("Other"), Tooltip("The location of the package certificate (relative to the project).")]
public string CertificateLocation = string.Empty;
/// <summary>
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(true)]
[EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")]
public bool SupportDX11 = true;
/// <summary>
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(false)]
[EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")]
public bool SupportDX10 = false;
}
}

View File

@@ -1,90 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.ComponentModel;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
{
/// <summary>
/// The Windows platform settings asset archetype. Allows to edit asset via editor.
/// </summary>
public class WindowsPlatformSettings : SettingsBase
{
/// <summary>
/// The default game window mode.
/// </summary>
[DefaultValue(GameWindowMode.Windowed)]
[EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")]
public GameWindowMode WindowMode = GameWindowMode.Windowed;
/// <summary>
/// The default game window width (in pixels).
/// </summary>
[DefaultValue(1280)]
[EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")]
public int ScreenWidth = 1280;
/// <summary>
/// The default game window height (in pixels).
/// </summary>
[DefaultValue(720)]
[EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")]
public int ScreenHeight = 720;
/// <summary>
/// Enables resizing the game window by the user.
/// </summary>
[DefaultValue(false)]
[EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")]
public bool ResizableWindow = false;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
[DefaultValue(false)]
[EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")]
public bool RunInBackground = false;
/// <summary>
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
/// </summary>
[DefaultValue(false)]
[EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")]
public bool ForceSingleInstance = false;
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>
[DefaultValue(null)]
[EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")]
public Texture OverrideIcon;
/// <summary>
/// Enables support for DirectX 12. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(false)]
[EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 12"), Tooltip("Enables support for DirectX 12. Disabling it reduces compiled shaders count.")]
public bool SupportDX12 = false;
/// <summary>
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(true)]
[EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")]
public bool SupportDX11 = true;
/// <summary>
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(false)]
[EditorOrder(2020), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")]
public bool SupportDX10 = false;
/// <summary>
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
/// </summary>
[DefaultValue(false)]
[EditorOrder(2030), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")]
public bool SupportVulkan = false;
}
}

View File

@@ -174,6 +174,11 @@ struct FLAXENGINE_API CookingData
/// </summary>
BuildOptions Options;
/// <summary>
/// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).
/// </summary>
Array<String> CustomDefines;
/// <summary>
/// The original output path (actual OutputPath could be modified by the Platform Tools or a plugin for additional layout customizations or packaging). This path is preserved.
/// </summary>

View File

@@ -43,53 +43,11 @@
#endif
#if PLATFORM_TOOLS_XBOX_SCARLETT
#include "Platforms/XboxScarlett/Editor/PlatformTools/XboxScarlettPlatformTools.h"
#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettPlatformSettings.h"
#endif
#if PLATFORM_TOOLS_ANDROID
#include "Platform/Android/AndroidPlatformTools.h"
#include "Engine/Platform/Android/AndroidPlatformSettings.h"
#endif
void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data)
{
#define LOAD_SETTINGS(nodeName, settingsType) \
{ \
Guid id = JsonTools::GetGuid(data, nodeName); \
if (id.IsValid()) \
{ \
AssetReference<JsonAsset> subAsset = Content::LoadAsync<JsonAsset>(id); \
if (subAsset) \
{ \
if (!subAsset->WaitForLoaded()) \
{ \
settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \
settingsType::Instance()->Apply(); \
} \
} \
else \
{ LOG(Warning, "Cannot load " nodeName " settings"); } \
} \
}
#if PLATFORM_TOOLS_WINDOWS
LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings);
#endif
#if PLATFORM_TOOLS_UWP || PLATFORM_TOOLS_XBOX_ONE
LOAD_SETTINGS("UWPPlatform", UWPPlatformSettings);
#endif
#if PLATFORM_TOOLS_LINUX
LOAD_SETTINGS("LinuxPlatform", LinuxPlatformSettings);
#endif
#if PLATFORM_TOOLS_PS4
LOAD_SETTINGS("PS4Platform", PS4PlatformSettings);
#endif
#if PLATFORM_TOOLS_XBOX_SCARLETT
LOAD_SETTINGS("XboxScarlettPlatform", XboxScarlettPlatformSettings);
#endif
#if PLATFORM_TOOLS_ANDROID
LOAD_SETTINGS("AndroidPlatform", AndroidPlatformSettings);
#endif
}
namespace GameCookerImpl
{
MMethod* Internal_OnEvent = nullptr;
@@ -299,7 +257,7 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
return result;
}
void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options)
void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines)
{
if (IsRunning())
{
@@ -323,6 +281,7 @@ void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
data.Platform = platform;
data.Configuration = configuration;
data.Options = options;
data.CustomDefines = customDefines;
data.OutputPath = outputPath;
FileSystem::NormalizePath(data.OutputPath);
data.OutputPath = data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OutputPath);

View File

@@ -84,7 +84,8 @@ public:
/// <param name="configuration">The build configuration.</param>
/// <param name="outputPath">The output path (output directory).</param>
/// <param name="options">The build options.</param>
API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options);
/// <param name="customDefines">The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).</param>
API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines);
/// <summary>
/// Sends a cancel event to the game building service.

View File

@@ -12,6 +12,10 @@
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Core/Config/BuildSettings.h"
#include "Editor/Utilities/EditorUtilities.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform);
namespace
{
@@ -106,7 +110,8 @@ void AndroidPlatformTools::OnBuildStarted(CookingData& data)
bool AndroidPlatformTools::OnPostProcess(CookingData& data)
{
const auto platformSettings = AndroidPlatformSettings::Instance();
const auto gameSettings = GameSettings::Get();
const auto platformSettings = AndroidPlatformSettings::Get();
const auto platformDataPath = data.GetPlatformBinariesRoot();
const auto assetsPath = data.OutputPath;
const auto jniLibsPath = data.OriginalOutputPath / TEXT("app/jniLibs");
@@ -125,11 +130,11 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
// Setup package name (eg. com.company.project)
String packageName = platformSettings->PackageName;
{
String productName = GameSettings::ProductName;
String productName = gameSettings->ProductName;
productName.Replace(TEXT(" "), TEXT(""));
productName.Replace(TEXT("."), TEXT(""));
productName.Replace(TEXT("-"), TEXT(""));
String companyName = GameSettings::CompanyName;
String companyName = gameSettings->CompanyName;
companyName.Replace(TEXT(" "), TEXT(""));
companyName.Replace(TEXT("."), TEXT(""));
companyName.Replace(TEXT("-"), TEXT(""));
@@ -235,7 +240,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidPermissions}"), permissions);
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidAttributes}"), attributes);
const String stringsPath = data.OriginalOutputPath / TEXT("app/src/main/res/values/strings.xml");
EditorUtilities::ReplaceInFile(stringsPath, TEXT("${ProjectName}"), GameSettings::ProductName);
EditorUtilities::ReplaceInFile(stringsPath, TEXT("${ProjectName}"), gameSettings->ProductName);
// Deploy native binaries to the output location (per-ABI)
const String abiBinariesPath = jniLibsPath / abi;
@@ -256,7 +261,8 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
// TODO: expose event to inject custom gradle and manifest options or custom binaries into app
if (BuildSettings::Instance()->SkipPackaging)
const auto buildSettings = BuildSettings::Get();
if (buildSettings->SkipPackaging)
{
return false;
}
@@ -286,7 +292,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
#else
const Char* gradlew = TEXT("gradlew");
#endif
const bool distributionPackage = BuildSettings::Instance()->ForDistribution;
const bool distributionPackage = buildSettings->ForDistribution;
const String gradleCommand = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
const int32 result = Platform::RunProcess(gradleCommand, data.OriginalOutputPath, envVars, true);
if (result != 0)
@@ -297,7 +303,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
// Copy result package
const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk"));
const String outputApk = data.OriginalOutputPath / GameSettings::ProductName + TEXT(".apk");
const String outputApk = data.OriginalOutputPath / gameSettings->ProductName + TEXT(".apk");
if (FileSystem::CopyFile(outputApk, apk))
{
LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);

View File

@@ -9,6 +9,10 @@
#include "Editor/Utilities/EditorUtilities.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
IMPLEMENT_SETTINGS_GETTER(LinuxPlatformSettings, LinuxPlatform);
const Char* LinuxPlatformTools::GetDisplayName() const
{
@@ -32,7 +36,8 @@ ArchitectureType LinuxPlatformTools::GetArchitecture() const
bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
{
const auto platformSettings = LinuxPlatformSettings::Instance();
const auto gameSettings = GameSettings::Get();
const auto platformSettings = LinuxPlatformSettings::Get();
const auto outputPath = data.OutputPath;
// Copy binaries
@@ -60,7 +65,7 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
// Apply game executable file name
#if !BUILD_DEBUG
const String outputExePath = outputPath / TEXT("FlaxGame");
const String gameExePath = outputPath / GameSettings::ProductName;
const String gameExePath = outputPath / gameSettings->ProductName;
if (FileSystem::FileExists(outputExePath) && gameExePath.Compare(outputExePath, StringSearchCase::IgnoreCase) == 0)
{
if (FileSystem::MoveFile(gameExePath, outputExePath, true))

View File

@@ -11,6 +11,10 @@
#include "Engine/Serialization/FileWriteStream.h"
#include "Editor/Utilities/EditorUtilities.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
IMPLEMENT_SETTINGS_GETTER(UWPPlatformSettings, UWPPlatform);
bool UWPPlatformTools::UseAOT() const
{
@@ -37,7 +41,8 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
bool isXboxOne = data.Platform == BuildPlatform::XboxOne;
const auto platformDataPath = Globals::StartupFolder / TEXT("Source/Platforms");
const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries");
const auto platformSettings = UWPPlatformSettings::Instance();
const auto gameSettings = GameSettings::Get();
const auto platformSettings = UWPPlatformSettings::Get();
Array<byte> fileTemplate;
// Copy binaries
@@ -66,7 +71,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
}
const auto projectName = GameSettings::ProductName;
const auto projectName = gameSettings->ProductName;
auto defaultNamespace = projectName;
ScriptsBuilder::FilterNamespaceText(defaultNamespace);
const StringAnsi projectGuid = "{3A9A2246-71DD-4567-9ABF-3E040310E30E}";
@@ -102,7 +107,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
// Generate new temp cert if missing
if (!FileSystem::FileExists(dstCertificatePath))
{
if (EditorUtilities::GenerateCertificate(GameSettings::CompanyName, dstCertificatePath))
if (EditorUtilities::GenerateCertificate(gameSettings->CompanyName, dstCertificatePath))
{
LOG(Warning, "Failed to create certificate.");
}
@@ -159,8 +164,8 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
auto now = DateTime::Now();
file->WriteTextFormatted(
(char*)fileTemplate.Get()
, GameSettings::ProductName.ToStringAnsi()
, GameSettings::CompanyName.ToStringAnsi()
, gameSettings->ProductName.ToStringAnsi()
, gameSettings->CompanyName.ToStringAnsi()
, now.GetYear()
);
hasError = file->HasError();
@@ -382,7 +387,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
file->WriteTextFormatted(
(char*)fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Display Name
, GameSettings::CompanyName.ToStringAnsi() // {1} Company Name
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
, productId.ToStringAnsi() // {2} Product ID
, defaultNamespace.ToStringAnsi() // {3} Default Namespace
);

View File

@@ -8,6 +8,10 @@
#include "Engine/Core/Config/GameSettings.h"
#include "Editor/Utilities/EditorUtilities.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
IMPLEMENT_SETTINGS_GETTER(WindowsPlatformSettings, WindowsPlatform);
const Char* WindowsPlatformTools::GetDisplayName() const
{
@@ -31,7 +35,7 @@ ArchitectureType WindowsPlatformTools::GetArchitecture() const
bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
{
const auto platformSettings = WindowsPlatformSettings::Instance();
const auto platformSettings = WindowsPlatformSettings::Get();
const auto& outputPath = data.OutputPath;
// Apply executable icon

View File

@@ -217,6 +217,11 @@ bool CompileScriptsStep::Perform(CookingData& data)
args += TEXT(" -SkipTargets=FlaxGame");
}
#endif
for (auto& define : data.CustomDefines)
{
args += TEXT(" -D");
args += define;
}
if (ScriptsBuilder::RunBuildTool(args))
{
data.Error(TEXT("Failed to compile game scripts."));

View File

@@ -160,8 +160,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
// Invalidate shaders and assets with shaders if need to rebuild them
bool invalidateShaders = false;
const bool shadersNoOptimize = BuildSettings::Instance()->ShadersNoOptimize;
const bool shadersGenerateDebugData = BuildSettings::Instance()->ShadersGenerateDebugData;
const auto buildSettings = BuildSettings::Get();
const bool shadersNoOptimize = buildSettings->ShadersNoOptimize;
const bool shadersGenerateDebugData = buildSettings->ShadersGenerateDebugData;
if (shadersNoOptimize != Settings.Global.ShadersNoOptimize)
{
LOG(Info, "ShadersNoOptimize option has been modified.");
@@ -175,7 +176,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
#if PLATFORM_TOOLS_WINDOWS
if (data.Platform == BuildPlatform::Windows32 || data.Platform == BuildPlatform::Windows64)
{
const auto settings = WindowsPlatformSettings::Instance();
const auto settings = WindowsPlatformSettings::Get();
const bool modified =
Settings.Windows.SupportDX11 != settings->SupportDX11 ||
Settings.Windows.SupportDX10 != settings->SupportDX10 ||
@@ -190,7 +191,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
#if PLATFORM_TOOLS_UWP
if (data.Platform == BuildPlatform::UWPx86 || data.Platform == BuildPlatform::UWPx64)
{
const auto settings = UWPPlatformSettings::Instance();
const auto settings = UWPPlatformSettings::Get();
const bool modified =
Settings.UWP.SupportDX11 != settings->SupportDX11 ||
Settings.UWP.SupportDX10 != settings->SupportDX10;
@@ -204,7 +205,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
#if PLATFORM_TOOLS_LINUX
if (data.Platform == BuildPlatform::LinuxX64)
{
const auto settings = LinuxPlatformSettings::Instance();
const auto settings = LinuxPlatformSettings::Get();
const bool modified =
Settings.Linux.SupportVulkan != settings->SupportVulkan;
if (modified)
@@ -322,6 +323,8 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
auto sourceLength = sourceChunk->Size();
Encryption::DecryptBytes((byte*)source, sourceLength);
source[sourceLength - 1] = 0;
while (sourceLength > 2 && source[sourceLength - 1] == 0)
sourceLength--;
// Init shader cache output stream
// TODO: reuse MemoryWriteStream per cooking process to reduce dynamic memory allocations
@@ -369,7 +372,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
case BuildPlatform::Windows64:
{
const char* platformDefineName = "PLATFORM_WINDOWS";
const auto settings = WindowsPlatformSettings::Instance();
const auto settings = WindowsPlatformSettings::Get();
if (settings->SupportDX12)
{
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
@@ -393,7 +396,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
case BuildPlatform::UWPx64:
{
const char* platformDefineName = "PLATFORM_UWP";
const auto settings = UWPPlatformSettings::Instance();
const auto settings = UWPPlatformSettings::Get();
if (settings->SupportDX11)
{
COMPILE_PROFILE(DirectX_SM5, SHADER_FILE_CHUNK_INTERNAL_D3D_SM5_CACHE);
@@ -415,7 +418,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
case BuildPlatform::LinuxX64:
{
const char* platformDefineName = "PLATFORM_LINUX";
const auto settings = LinuxPlatformSettings::Instance();
const auto settings = LinuxPlatformSettings::Get();
if (settings->SupportVulkan)
{
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
@@ -881,7 +884,8 @@ bool CookAssetsStep::Perform(CookingData& data)
data.StepProgress(TEXT("Loading build cache"), 0);
// Prepare
const auto buildSettings = BuildSettings::Instance();
const auto gameSettings = GameSettings::Get();
const auto buildSettings = BuildSettings::Get();
const int32 contentKey = buildSettings->ContentKey == 0 ? rand() : buildSettings->ContentKey;
AssetsRegistry.Clear();
AssetPathsMapping.Clear();
@@ -892,22 +896,21 @@ bool CookAssetsStep::Perform(CookingData& data)
// Update build settings
{
const auto settings = WindowsPlatformSettings::Instance();
const auto settings = WindowsPlatformSettings::Get();
cache.Settings.Windows.SupportDX11 = settings->SupportDX11;
cache.Settings.Windows.SupportDX10 = settings->SupportDX10;
cache.Settings.Windows.SupportVulkan = settings->SupportVulkan;
}
{
const auto settings = UWPPlatformSettings::Instance();
const auto settings = UWPPlatformSettings::Get();
cache.Settings.UWP.SupportDX11 = settings->SupportDX11;
cache.Settings.UWP.SupportDX10 = settings->SupportDX10;
}
{
const auto settings = LinuxPlatformSettings::Instance();
const auto settings = LinuxPlatformSettings::Get();
cache.Settings.Linux.SupportVulkan = settings->SupportVulkan;
}
{
const auto buildSettings = BuildSettings::Instance();
cache.Settings.Global.ShadersNoOptimize = buildSettings->ShadersNoOptimize;
cache.Settings.Global.ShadersGenerateDebugData = buildSettings->ShadersGenerateDebugData;
}
@@ -1004,7 +1007,7 @@ bool CookAssetsStep::Perform(CookingData& data)
// Create build game header
{
GameHeaderFlags gameFlags = GameHeaderFlags::None;
if (!GameSettings::NoSplashScreen)
if (!gameSettings->NoSplashScreen)
gameFlags |= GameHeaderFlags::ShowSplashScreen;
// Open file
@@ -1022,17 +1025,17 @@ bool CookAssetsStep::Perform(CookingData& data)
Array<byte> bytes;
bytes.Resize(808 + sizeof(Guid));
Platform::MemoryClear(bytes.Get(), bytes.Count());
int32 length = sizeof(Char) * GameSettings::ProductName.Length();
Platform::MemoryCopy(bytes.Get() + 0, GameSettings::ProductName.Get(), length);
int32 length = sizeof(Char) * gameSettings->ProductName.Length();
Platform::MemoryCopy(bytes.Get() + 0, gameSettings->ProductName.Get(), length);
bytes[length] = 0;
bytes[length + 1] = 0;
length = sizeof(Char) * GameSettings::CompanyName.Length();
Platform::MemoryCopy(bytes.Get() + 400, GameSettings::CompanyName.Get(), length);
length = sizeof(Char) * gameSettings->CompanyName.Length();
Platform::MemoryCopy(bytes.Get() + 400, gameSettings->CompanyName.Get(), length);
bytes[length + 400] = 0;
bytes[length + 401] = 0;
*(int32*)(bytes.Get() + 800) = (int32)gameFlags;
*(int32*)(bytes.Get() + 804) = contentKey;
*(Guid*)(bytes.Get() + 808) = GameSettings::SplashScreen;
*(Guid*)(bytes.Get() + 808) = gameSettings->SplashScreen;
Encryption::EncryptBytes(bytes.Get(), bytes.Count());
stream->WriteArray(bytes);

View File

@@ -12,6 +12,7 @@ bool DeployDataStep::Perform(CookingData& data)
{
data.StepProgress(TEXT("Deploying engine data"), 0);
const String depsRoot = data.GetPlatformBinariesRoot();
const auto gameSettings = GameSettings::Get();
// Setup output folders and copy required data
const auto contentDir = data.OutputPath / TEXT("Content");
@@ -73,8 +74,9 @@ bool DeployDataStep::Perform(CookingData& data)
data.AddRootEngineAsset(TEXT("Shaders/SSR"));
data.AddRootEngineAsset(TEXT("Shaders/VolumetricFog"));
data.AddRootEngineAsset(TEXT("Engine/DefaultMaterial"));
data.AddRootEngineAsset(TEXT("Engine/DefaultDeformableMaterial"));
data.AddRootEngineAsset(TEXT("Engine/DefaultTerrainMaterial"));
if (!GameSettings::NoSplashScreen && !GameSettings::SplashScreen.IsValid())
if (!gameSettings->NoSplashScreen && !gameSettings->SplashScreen.IsValid())
data.AddRootEngineAsset(TEXT("Engine/Textures/Logo"));
data.AddRootEngineAsset(TEXT("Engine/Textures/NormalTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/BlackTexture"));
@@ -98,7 +100,7 @@ bool DeployDataStep::Perform(CookingData& data)
// Register game assets
data.StepProgress(TEXT("Deploying game data"), 50);
auto& buildSettings = *BuildSettings::Instance();
auto& buildSettings = *BuildSettings::Get();
for (auto& e : buildSettings.AdditionalAssets)
data.AddRootAsset(e.GetID());
Array<String> files;

View File

@@ -38,18 +38,23 @@ bool ValidateStep::Perform(CookingData& data)
#endif
// Load game settings (may be modified via editor)
GameSettings::Load();
if (GameSettings::Load())
{
data.Error(TEXT("Failed to load game settings."));
return true;
}
data.AddRootAsset(Globals::ProjectContentFolder / TEXT("GameSettings.json"));
// Validate game settings
auto gameSettings = GameSettings::Get();
{
if (GameSettings::ProductName.IsEmpty())
if (gameSettings->ProductName.IsEmpty())
{
data.Error(TEXT("Missing product name."));
return true;
}
if (GameSettings::CompanyName.IsEmpty())
if (gameSettings->CompanyName.IsEmpty())
{
data.Error(TEXT("Missing company name."));
return true;
@@ -58,7 +63,7 @@ bool ValidateStep::Perform(CookingData& data)
// TODO: validate version
AssetInfo info;
if (!Content::GetAssetInfo(GameSettings::FirstScene, info))
if (!Content::GetAssetInfo(gameSettings->FirstScene, info))
{
data.Error(TEXT("Missing first scene. Set it in the game settings."));
return true;

View File

@@ -624,7 +624,7 @@ namespace FlaxEditor.CustomEditors
return FindPrefabRoot(actor.Parent);
}
private ISceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId)
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId)
{
if (actor.PrefabObjectID == prefabObjectId)
return actor;
@@ -667,7 +667,7 @@ namespace FlaxEditor.CustomEditors
{
// Special case for object references
// If prefab object has reference to other object in prefab needs to revert to matching prefab instance object not the reference prefab object value
if (Values.ReferenceValue is ISceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink)
if (Values.ReferenceValue is SceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink)
{
if (Values.Count > 1)
{

View File

@@ -24,43 +24,6 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
private Guid _linkedPrefabId;
/// <inheritdoc />
protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
{
// Note: we cannot specify actor properties editor types directly because we want to keep editor classes in FlaxEditor assembly
int order = item.Order?.Order ?? int.MinValue;
switch (order)
{
// Override static flags editor
case -80:
item.CustomEditor = new CustomEditorAttribute(typeof(ActorStaticFlagsEditor));
break;
// Override layer editor
case -69:
item.CustomEditor = new CustomEditorAttribute(typeof(ActorLayerEditor));
break;
// Override tag editor
case -68:
item.CustomEditor = new CustomEditorAttribute(typeof(ActorTagEditor));
break;
// Override position/scale editor
case -30:
case -10:
item.CustomEditor = new CustomEditorAttribute(typeof(ActorTransformEditor.PositionScaleEditor));
break;
// Override orientation editor
case -20:
item.CustomEditor = new CustomEditorAttribute(typeof(ActorTransformEditor.OrientationEditor));
break;
}
base.SpawnProperty(itemLayout, itemValues, item);
}
/// <inheritdoc />
protected override List<ItemInfo> GetItemsForType(ScriptType type)
{
@@ -243,7 +206,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
node.Text = CustomEditorsUtil.GetPropertyNameUI(removed.PrefabObject.GetType().Name);
}
// Actor or Script
else if (editor.Values[0] is ISceneObject sceneObject)
else if (editor.Values[0] is SceneObject sceneObject)
{
node.TextColor = sceneObject.HasPrefabLink ? FlaxEngine.GUI.Style.Current.ProgressNormal : FlaxEngine.GUI.Style.Current.BackgroundSelected;
node.Text = CustomEditorsUtil.GetPropertyNameUI(sceneObject.GetType().Name);

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.Linq;
using FlaxEditor.Content.Settings;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Dedicated
{
/// <summary>
/// Custom editor for <see cref="NavAgentMask"/>.
/// </summary>
/// <seealso cref="ActorEditor" />
[CustomEditor(typeof(NavAgentMask)), DefaultEditor]
internal class NavAgentMaskEditor : CustomEditor
{
private CheckBox[] _checkBoxes;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
{
var settings = GameSettings.Load<NavigationSettings>();
if (settings.NavMeshes == null || settings.NavMeshes.Length == 0)
{
layout.Label("Missing navmesh settings");
return;
}
_checkBoxes = new CheckBox[settings.NavMeshes.Length];
for (int i = 0; i < settings.NavMeshes.Length; i++)
{
ref var navmesh = ref settings.NavMeshes[i];
var property = layout.AddPropertyItem(navmesh.Name, navmesh.Agent.ToString());
property.Labels.Last().TextColorHighlighted = navmesh.Color;
var checkbox = property.Checkbox().CheckBox;
UpdateCheckbox(checkbox, i);
checkbox.Tag = i;
checkbox.StateChanged += OnCheckboxStateChanged;
_checkBoxes[i] = checkbox;
}
}
/// <inheritdoc />
protected override void Deinitialize()
{
_checkBoxes = null;
base.Deinitialize();
}
/// <inheritdoc />
public override void Refresh()
{
if (_checkBoxes != null)
{
for (int i = 0; i < _checkBoxes.Length; i++)
{
UpdateCheckbox(_checkBoxes[i], i);
}
}
base.Refresh();
}
private void OnCheckboxStateChanged(CheckBox checkBox)
{
var i = (int)checkBox.Tag;
var value = (NavAgentMask)Values[0];
var mask = 1u << i;
value.Mask &= ~mask;
value.Mask |= checkBox.Checked ? mask : 0;
SetValue(value);
}
private void UpdateCheckbox(CheckBox checkbox, int i)
{
for (var j = 0; j < Values.Count; j++)
{
var value = (((NavAgentMask)Values[j]).Mask & (1 << i)) != 0;
if (j == 0)
{
checkbox.Checked = value;
}
else if (checkbox.State != CheckBoxState.Intermediate)
{
if (checkbox.Checked != value)
checkbox.State = CheckBoxState.Intermediate;
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEditor.Actions;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Dedicated
{
/// <summary>
/// Custom editor for <see cref="Spline"/>.
/// </summary>
/// <seealso cref="ActorEditor" />
[CustomEditor(typeof(Spline)), DefaultEditor]
public class SplineEditor : ActorEditor
{
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
{
base.Initialize(layout);
if (Values.HasDifferentTypes == false)
{
layout.Space(10);
var grid = layout.CustomContainer<UniformGridPanel>();
grid.CustomControl.SlotsHorizontally = 2;
grid.CustomControl.SlotsVertically = 1;
grid.Button("Set Linear Tangents").Button.Clicked += OnSetTangentsLinear;
grid.Button("Set Smooth Tangents").Button.Clicked += OnSetTangentsSmooth;
}
}
private void OnSetTangentsLinear()
{
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
for (int i = 0; i < Values.Count; i++)
{
if (Values[i] is Spline spline)
{
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
spline.SetTangentsLinear();
if (enableUndo)
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
SplineNode.OnSplineEdited(spline);
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
}
}
}
private void OnSetTangentsSmooth()
{
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
for (int i = 0; i < Values.Count; i++)
{
if (Values[i] is Spline spline)
{
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
spline.SetTangentsSmooth();
if (enableUndo)
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
SplineNode.OnSplineEdited(spline);
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
}
}
}
}
}

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections;
using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
@@ -48,6 +49,15 @@ namespace FlaxEditor.CustomEditors.Editors
Array.Copy(array, oldSize - 1, newValues, i, 1);
}
}
else if (newSize > 0)
{
// Initialize new entries with default values
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
for (int i = 0; i < newSize; i++)
{
newValues.SetValue(defaultValue, i);
}
}
SetValue(newValues);
}

View File

@@ -116,7 +116,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Try get CollectionAttribute for collection editor meta
var attributes = Values.GetAttributes();
Type overrideEditorType = null;
float spacing = 0.0f;
float spacing = 10.0f;
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
if (collection != null)
{

View File

@@ -517,23 +517,22 @@ namespace FlaxEditor.CustomEditors.Editors
if (item.Header != null)
itemLayout.Header(item.Header.Text);
// Peek values
ValueContainer itemValues;
try
{
itemValues = item.GetValues(Values);
// Peek values
ValueContainer itemValues = item.GetValues(Values);
// Spawn property editor
SpawnProperty(itemLayout, itemValues, item);
}
catch (Exception ex)
{
Editor.LogWarning("Failed to get object values for item " + item);
Editor.LogWarning("Failed to setup values and UI for item " + item);
Editor.LogWarning(ex.Message);
Editor.LogWarning(ex.StackTrace);
return;
}
// Spawn property editor
SpawnProperty(itemLayout, itemValues, item);
// Expand all parent groups if need to
if (item.ExpandGroups)
{

View File

@@ -58,7 +58,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
else if (newSize > 0)
{
// Fill new entries
// Fill new entries with default value
var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType);
for (int i = oldSize; i < newSize; i++)
{

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.CustomEditors.GUI;
using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI;
@@ -553,6 +554,8 @@ namespace FlaxEditor.CustomEditors
var group = Group(name, true);
group.Panel.Close(false);
group.Panel.TooltipText = tooltip;
group.Panel.Tag = editor;
group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
return group.Object(values, editor);
}
@@ -560,6 +563,23 @@ namespace FlaxEditor.CustomEditors
return property.Object(values, editor);
}
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Vector2 location)
{
var linkedEditor = (CustomEditor)groupPanel.Tag;
var menu = new ContextMenu();
var revertToPrefab = menu.AddButton("Revert to Prefab", linkedEditor.RevertToReferenceValue);
revertToPrefab.Enabled = linkedEditor.CanRevertReferenceValue;
var resetToDefault = menu.AddButton("Reset to default", linkedEditor.RevertToDefaultValue);
resetToDefault.Enabled = linkedEditor.CanRevertDefaultValue;
menu.AddSeparator();
menu.AddButton("Copy", linkedEditor.Copy);
var paste = menu.AddButton("Paste", linkedEditor.Paste);
paste.Enabled = linkedEditor.CanPaste;
menu.Show(groupPanel, location);
}
/// <summary>
/// Adds object property editor. Selects proper <see cref="CustomEditor"/> based on overrides.
/// </summary>

View File

@@ -167,14 +167,14 @@ namespace FlaxEditor.CustomEditors
{
if (_hasReferenceValue)
{
if (_referenceValue is ISceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink)
if (_referenceValue is SceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink)
{
for (int i = 0; i < Count; i++)
{
if (this[i] == referenceSceneObject)
continue;
if (this[i] == null || (this[i] is ISceneObject valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID))
if (this[i] == null || (this[i] is SceneObject valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID))
return true;
}
}

View File

@@ -26,7 +26,7 @@ public class Editor : EditorModule
// Platform Tools inside external platform implementation location
options.PrivateDefinitions.Add(macro);
options.SourcePaths.Add(externalPath);
options.SourceFiles.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", platform, "Engine", "Platform", platform + "PlatformSettings.cs"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", platform, "Engine", "Platform", platform + "PlatformSettings.cs"));
}
}
}

View File

@@ -242,8 +242,6 @@ namespace FlaxEditor
StateMachine = new EditorStateMachine(this);
Undo = new EditorUndo(this);
ScriptsBuilder.ScriptsReloadBegin += ScriptsBuilder_ScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd += ScriptsBuilder_ScriptsReloadEnd;
UIControl.FallbackParentGetDelegate += OnUIControlFallbackParentGet;
}
@@ -260,18 +258,6 @@ namespace FlaxEditor
return null;
}
private void ScriptsBuilder_ScriptsReloadBegin()
{
EnsureState<EditingSceneState>();
StateMachine.GoToState<ReloadingScriptsState>();
}
private void ScriptsBuilder_ScriptsReloadEnd()
{
EnsureState<ReloadingScriptsState>();
StateMachine.GoToState<EditingSceneState>();
}
internal void RegisterModule(EditorModule module)
{
Log("Register Editor module " + module);
@@ -497,9 +483,6 @@ namespace FlaxEditor
Surface.VisualScriptSurface.NodesCache.Clear();
Instance = null;
ScriptsBuilder.ScriptsReloadBegin -= ScriptsBuilder_ScriptsReloadBegin;
ScriptsBuilder.ScriptsReloadEnd -= ScriptsBuilder_ScriptsReloadEnd;
// Invoke new instance if need to open a project
if (!string.IsNullOrEmpty(_projectToOpen))
{

View File

@@ -16,6 +16,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// The base class for <see cref="CurveBase{T}"/> editors. Allows to use generic curve editor without type information at compile-time.
/// </summary>
[HideInEditor]
public abstract class CurveEditorBase : ContainerControl
{
/// <summary>

View File

@@ -11,6 +11,7 @@ namespace FlaxEditor.GUI.Tabs
/// Represents control which contains collection of <see cref="Tab"/>.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
public class Tabs : ContainerControl
{
/// <summary>

View File

@@ -14,6 +14,7 @@ namespace FlaxEditor.GUI
/// The generic keyframes animation editor control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
public class KeyframesEditor : ContainerControl
{
/// <summary>

View File

@@ -20,6 +20,7 @@ namespace FlaxEditor.GUI.Timeline
/// The timeline control that contains tracks section and headers. Can be used to create time-based media interface for camera tracks editing, audio mixing and events tracking.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
public class Timeline : ContainerControl
{
private static readonly KeyValuePair<float, string>[] FPSValues =

View File

@@ -71,7 +71,7 @@ namespace FlaxEditor.Gizmo
if (hit != null)
{
// For child actor nodes (mesh, link or sth) we need to select it's owning actor node first or any other child node (but not a child actor)
if (hit is ActorChildNode actorChildNode)
if (hit is ActorChildNode actorChildNode && !actorChildNode.CanBeSelectedDirectly)
{
var parentNode = actorChildNode.ParentNode;
bool canChildBeSelected = sceneEditing.Selection.Contains(parentNode);

View File

@@ -24,7 +24,7 @@
#include "Engine/ContentImporters/ImportAudio.h"
#include "Engine/ContentImporters/CreateCollisionData.h"
#include "Engine/ContentImporters/CreateJson.h"
#include "Engine/Core/Config/LayersTagsSettings.h"
#include "Engine/Level/Level.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Core/Cache.h"
#include "Engine/CSG/CSGBuilder.h"
@@ -302,22 +302,20 @@ namespace CustomEditorsUtilInternal
}
}
namespace LayersAndTagsSettingsInternal
namespace LayersAndTagsSettingsInternal1
{
MonoArray* GetCurrentTags()
{
auto settings = LayersAndTagsSettings::Instance();
return MUtils::ToArray(settings->Tags);
return MUtils::ToArray(Level::Tags);
}
MonoArray* GetCurrentLayers()
{
const auto settings = LayersAndTagsSettings::Instance();
return MUtils::ToArray(Span<String>(settings->Layers, Math::Max(1, settings->GetNonEmptyLayerNamesCount())));
return MUtils::ToArray(Span<String>(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount())));
}
}
namespace GameSettingsInternal
namespace GameSettingsInternal1
{
void Apply()
{
@@ -1054,9 +1052,9 @@ public:
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions", &GetTextureImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions", &GetModelImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions", &GetAudioImportOptions);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", &LayersAndTagsSettingsInternal::GetCurrentTags);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", &LayersAndTagsSettingsInternal::GetCurrentLayers);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.GameSettings::Apply", &GameSettingsInternal::Apply);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", &LayersAndTagsSettingsInternal1::GetCurrentTags);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", &LayersAndTagsSettingsInternal1::GetCurrentLayers);
ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.GameSettings::Apply", &GameSettingsInternal1::Apply);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CloseSplashScreen", &CloseSplashScreen);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateAsset", &CreateAsset);
ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateVisualScript", &CreateVisualScript);

View File

@@ -200,7 +200,8 @@ namespace FlaxEditor.Modules
/// </summary>
/// <param name="actor">The actor.</param>
/// <param name="parent">The parent actor. Set null as default.</param>
public void Spawn(Actor actor, Actor parent = null)
/// <param name="autoSelect">True if automatically select the spawned actor, otherwise false.</param>
public void Spawn(Actor actor, Actor parent = null, bool autoSelect = true)
{
bool isPlayMode = Editor.StateMachine.IsPlayMode;
@@ -225,7 +226,15 @@ namespace FlaxEditor.Modules
actorNode.PostSpawn();
// Create undo action
var action = new DeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
IUndoAction action = new DeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
if (autoSelect)
{
var before = Selection.ToArray();
Selection.Clear();
Selection.Add(actorNode);
OnSelectionChanged();
action = new MultiUndoAction(action, new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo));
}
Undo.AddAction(action);
// Mark scene as dirty
@@ -449,7 +458,15 @@ namespace FlaxEditor.Modules
var pasteAction = PasteActorsAction.Paste(data, pasteTargetActor?.ID ?? Guid.Empty);
if (pasteAction != null)
{
OnPasteAction(pasteAction);
pasteAction.Do(out _, out var nodeParents);
// Select spawned objects (parents only)
var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast<SceneGraphNode>().ToArray(), OnSelectionUndo);
selectAction.Do();
// Build single compound undo action that pastes the actors and selects the created objects (parents only)
Undo.AddAction(new MultiUndoAction(pasteAction, selectAction));
OnSelectionChanged();
}
}
@@ -468,12 +485,57 @@ namespace FlaxEditor.Modules
public void Duplicate()
{
// Peek things that can be copied (copy all actors)
var objects = Selection.Where(x => x.CanCopyPaste).ToList().BuildAllNodes().Where(x => x.CanCopyPaste && x is ActorNode).ToList();
if (objects.Count == 0)
var nodes = Selection.Where(x => x.CanDuplicate).ToList().BuildAllNodes();
if (nodes.Count == 0)
return;
var actors = new List<Actor>();
var newSelection = new List<SceneGraphNode>();
List<IUndoAction> customUndoActions = null;
foreach (var node in nodes)
{
if (node.CanDuplicate)
{
if (node is ActorNode actorNode)
{
actors.Add(actorNode.Actor);
}
else
{
var customDuplicatedObject = node.Duplicate(out var customUndoAction);
if (customDuplicatedObject != null)
newSelection.Add(customDuplicatedObject);
if (customUndoAction != null)
{
if (customUndoActions == null)
customUndoActions = new List<IUndoAction>();
customUndoActions.Add(customUndoAction);
}
}
}
}
if (actors.Count == 0)
{
// Duplicate custom scene graph nodes only without actors
if (newSelection.Count != 0)
{
// Select spawned objects (parents only)
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
selectAction.Do();
// Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only)
var customUndoActionsCount = customUndoActions?.Count ?? 0;
var undoActions = new IUndoAction[1 + customUndoActionsCount];
for (int i = 0; i < customUndoActionsCount; i++)
undoActions[i] = customUndoActions[i];
undoActions[undoActions.Length - 1] = selectAction;
Undo.AddAction(new MultiUndoAction(undoActions));
OnSelectionChanged();
}
return;
}
// Serialize actors
var actors = objects.ConvertAll(x => ((ActorNode)x).Actor);
var data = Actor.ToBytes(actors.ToArray());
if (data == null)
{
@@ -485,22 +547,26 @@ namespace FlaxEditor.Modules
var pasteAction = PasteActorsAction.Duplicate(data, Guid.Empty);
if (pasteAction != null)
{
OnPasteAction(pasteAction);
pasteAction.Do(out _, out var nodeParents);
// Select spawned objects (parents only)
newSelection.AddRange(nodeParents);
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
selectAction.Do();
// Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only)
var customUndoActionsCount = customUndoActions?.Count ?? 0;
var undoActions = new IUndoAction[2 + customUndoActionsCount];
undoActions[0] = pasteAction;
for (int i = 0; i < customUndoActionsCount; i++)
undoActions[i + 1] = customUndoActions[i];
undoActions[undoActions.Length - 1] = selectAction;
Undo.AddAction(new MultiUndoAction(undoActions));
OnSelectionChanged();
}
}
private void OnPasteAction(PasteActorsAction pasteAction)
{
pasteAction.Do(out _, out var nodeParents);
// Select spawned objects
var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast<SceneGraphNode>().ToArray(), OnSelectionUndo);
selectAction.Do();
Undo.AddAction(new MultiUndoAction(pasteAction, selectAction));
OnSelectionChanged();
}
/// <summary>
/// Called when selection gets changed. Invokes the other events and updates editor. Call it when you manually modify selected objects collection.
/// </summary>

View File

@@ -507,7 +507,7 @@ namespace FlaxEditor.Modules
_toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)");
_toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)");
ToolStrip.AddSeparator();
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build32, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes (Ctrl+F10)");
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build32, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)");
ToolStrip.AddSeparator();
_toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play32, Editor.Simulation.RequestPlayOrStopPlay).LinkTooltip("Start/Stop game (F5)");
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause32, Editor.Simulation.RequestResumeOrPause).LinkTooltip("Pause/Resume game(F6)");

View File

@@ -32,6 +32,42 @@ namespace FlaxEditor.Options
LastOpened,
}
/// <summary>
/// The build actions.
/// </summary>
public enum BuildAction
{
/// <summary>
/// Builds Constructive Solid Geometry brushes into meshes.
/// </summary>
[Tooltip("Builds Constructive Solid Geometry brushes into meshes.")]
CSG,
/// <summary>
/// Builds Env Probes and Sky Lights to prerendered cube textures.
/// </summary>
[Tooltip("Builds Env Probes and Sky Lights to prerendered cube textures.")]
EnvProbes,
/// <summary>
/// Builds static lighting into lightmaps.
/// </summary>
[Tooltip("Builds static lighting into lightmaps.")]
StaticLighting,
/// <summary>
/// Builds navigation meshes.
/// </summary>
[Tooltip("Builds navigation meshes.")]
NavMesh,
/// <summary>
/// Compiles the scripts.
/// </summary>
[Tooltip("Compiles the scripts.")]
CompileScripts,
}
/// <summary>
/// Gets or sets the scene to load on editor startup.
/// </summary>
@@ -53,6 +89,19 @@ namespace FlaxEditor.Options
[EditorDisplay("General", "Editor FPS"), EditorOrder(110), Tooltip("Limit for the editor draw/update frames per second rate (FPS). Use higher values if you need more responsive interface or lower values to use less device power. Value 0 disables any limits.")]
public float EditorFPS { get; set; } = 60.0f;
/// <summary>
/// Gets or sets the sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).
/// </summary>
[EditorDisplay("General"), EditorOrder(200), Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")]
public BuildAction[] BuildActions { get; set; } =
{
BuildAction.CSG,
BuildAction.EnvProbes,
BuildAction.StaticLighting,
BuildAction.EnvProbes,
BuildAction.NavMesh,
};
/// <summary>
/// Gets or sets a value indicating whether perform automatic scripts reload on main window focus.
/// </summary>

View File

@@ -8,6 +8,7 @@ namespace FlaxEditor.Progress
/// <summary>
/// Base class for all editor handlers used to report actions progress to the user.
/// </summary>
[HideInEditor]
public abstract class ProgressHandler
{
/// <summary>

View File

@@ -19,6 +19,11 @@ namespace FlaxEditor.SceneGraph
/// </summary>
public readonly int Index;
/// <summary>
/// Gets a value indicating whether this node can be selected directly without selecting parent actor node first.
/// </summary>
public virtual bool CanBeSelectedDirectly => false;
/// <summary>
/// Initializes a new instance of the <see cref="ActorChildNode"/> class.
/// </summary>
@@ -58,6 +63,9 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc />
public override bool CanCopyPaste => false;
/// <inheritdoc />
public override bool CanDuplicate => false;
/// <inheritdoc />
public override bool CanDrag => false;
@@ -66,6 +74,18 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc />
public override object UndoRecordObject => ParentNode.UndoRecordObject;
/// <inheritdoc />
public override void Dispose()
{
// Unlink from the parent
if (parentNode is ActorNode parentActorNode && parentActorNode.ActorChildNodes != null)
{
parentActorNode.ActorChildNodes.Remove(this);
}
base.Dispose();
}
}
/// <summary>
@@ -77,20 +97,28 @@ namespace FlaxEditor.SceneGraph
public abstract class ActorChildNode<T> : ActorChildNode where T : ActorNode
{
/// <summary>
/// The actor.
/// The actor node.
/// </summary>
protected readonly T _actor;
protected T _node;
/// <summary>
/// Initializes a new instance of the <see cref="ActorChildNode{T}"/> class.
/// </summary>
/// <param name="actor">The parent actor.</param>
/// <param name="node">The parent actor node.</param>
/// <param name="id">The child id.</param>
/// <param name="index">The child index.</param>
protected ActorChildNode(T actor, Guid id, int index)
protected ActorChildNode(T node, Guid id, int index)
: base(id, index)
{
_actor = actor;
_node = node;
}
/// <inheritdoc />
public override void OnDispose()
{
_node = null;
base.OnDispose();
}
}
}

View File

@@ -41,7 +41,7 @@ namespace FlaxEditor.SceneGraph
/// <summary>
/// The actor child nodes used to represent special parts of the actor (meshes, links, surfaces).
/// </summary>
public readonly List<ActorChildNode> ActorChildNodes = new List<ActorChildNode>();
public List<ActorChildNode> ActorChildNodes;
/// <summary>
/// Initializes a new instance of the <see cref="ActorNode"/> class.
@@ -108,6 +108,8 @@ namespace FlaxEditor.SceneGraph
/// <returns>The node</returns>
public ActorChildNode AddChildNode(ActorChildNode node)
{
if (ActorChildNodes == null)
ActorChildNodes = new List<ActorChildNode>();
ActorChildNodes.Add(node);
node.ParentNode = this;
return node;
@@ -125,9 +127,12 @@ namespace FlaxEditor.SceneGraph
root.OnActorChildNodesDispose(this);
}
for (int i = 0; i < ActorChildNodes.Count; i++)
ActorChildNodes[i].Dispose();
ActorChildNodes.Clear();
if (ActorChildNodes != null)
{
for (int i = 0; i < ActorChildNodes.Count; i++)
ActorChildNodes[i].Dispose();
ActorChildNodes.Clear();
}
}
/// <summary>
@@ -177,6 +182,9 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc />
public override bool CanCopyPaste => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0;
/// <inheritdoc />
public override bool CanDuplicate => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0;
/// <inheritdoc />
public override bool IsActive => _actor.IsActive;
@@ -275,8 +283,12 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc />
public override void Dispose()
{
// Cleanup UI
_treeNode.Dispose();
if (ActorChildNodes != null)
{
ActorChildNodes.Clear();
ActorChildNodes = null;
}
base.Dispose();
}

View File

@@ -78,14 +78,14 @@ namespace FlaxEditor.SceneGraph.Actors
{
get
{
var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor;
var actor = (BoxVolume)_node.Actor;
var localOffset = _offset * actor.Size;
Transform localTrans = new Transform(localOffset);
return actor.Transform.LocalToWorld(localTrans);
}
set
{
var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor;
var actor = (BoxVolume)_node.Actor;
Transform localTrans = actor.Transform.WorldToLocal(value);
var prevLocalOffset = _offset * actor.Size;
var localOffset = Vector3.Abs(_offset) * 2.0f * localTrans.Translation;

View File

@@ -37,13 +37,13 @@ namespace FlaxEditor.SceneGraph.Actors
{
get
{
var actor = (NavLink)((NavLinkNode)ParentNode).Actor;
var actor = (NavLink)_node.Actor;
Transform localTrans = new Transform(_isStart ? actor.Start : actor.End);
return actor.Transform.LocalToWorld(localTrans);
}
set
{
var actor = (NavLink)((NavLinkNode)ParentNode).Actor;
var actor = (NavLink)_node.Actor;
Transform localTrans = actor.Transform.WorldToLocal(value);
if (_isStart)
actor.Start = localTrans.Translation;

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
{
/// <summary>
/// Actor node for <see cref="NavModifierVolume"/>.
/// </summary>
/// <seealso cref="BoxVolumeNode" />
[HideInEditor]
public sealed class NavModifierVolumeNode : BoxVolumeNode
{
/// <inheritdoc />
public NavModifierVolumeNode(Actor actor)
: base(actor)
{
}
}
}

View File

@@ -57,6 +57,9 @@ namespace FlaxEditor.SceneGraph.Actors
/// <inheritdoc />
public override bool CanCopyPaste => false;
/// <inheritdoc />
public override bool CanDuplicate => false;
/// <inheritdoc />
public override bool CanDelete => false;

View File

@@ -0,0 +1,372 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Modules;
using FlaxEngine;
using FlaxEngine.Json;
using Object = FlaxEngine.Object;
namespace FlaxEditor.SceneGraph.Actors
{
/// <summary>
/// Scene tree node for <see cref="Spline"/> actor type.
/// </summary>
[HideInEditor]
public sealed class SplineNode : ActorNode
{
private sealed class SplinePointNode : ActorChildNode<SplineNode>
{
public unsafe SplinePointNode(SplineNode node, Guid id, int index)
: base(node, id, index)
{
var g = (JsonSerializer.GuidInterop*)&id;
g->D++;
AddChild(new SplinePointTangentNode(node, id, index, true));
g->D++;
AddChild(new SplinePointTangentNode(node, id, index, false));
}
public override bool CanBeSelectedDirectly => true;
public override bool CanDuplicate => true;
public override bool CanDelete => true;
public override bool CanUseState => true;
public override Transform Transform
{
get
{
var actor = (Spline)_node.Actor;
return actor.GetSplineTransform(Index);
}
set
{
var actor = (Spline)_node.Actor;
actor.SetSplineTransform(Index, value);
OnSplineEdited(actor);
}
}
struct Data
{
public Guid Spline;
public int Index;
public BezierCurve<Transform>.Keyframe Keyframe;
}
public override StateData State
{
get
{
var actor = (Spline)_node.Actor;
return new StateData
{
TypeName = typeof(SplinePointNode).FullName,
CreateMethodName = nameof(Create),
State = JsonSerializer.Serialize(new Data
{
Spline = actor.ID,
Index = Index,
Keyframe = actor.GetSplineKeyframe(Index),
}),
};
}
set => throw new NotImplementedException();
}
public override void Delete()
{
var actor = (Spline)_node.Actor;
actor.RemoveSplinePoint(Index);
}
class DuplicateUndoAction : IUndoAction, ISceneEditAction
{
public Guid SplineId;
public int Index;
public float Time;
public Transform Value;
public string ActionString => "Duplicate spline point";
public void Dispose()
{
}
public void Do()
{
var spline = Object.Find<Spline>(ref SplineId);
spline.InsertSplineLocalPoint(Index, Time, Value);
}
public void Undo()
{
var spline = Object.Find<Spline>(ref SplineId);
spline.RemoveSplinePoint(Index);
}
public void MarkSceneEdited(SceneModule sceneModule)
{
var spline = Object.Find<Spline>(ref SplineId);
sceneModule.MarkSceneEdited(spline.Scene);
OnSplineEdited(spline);
}
}
public override SceneGraphNode Duplicate(out IUndoAction undoAction)
{
var actor = (Spline)_node.Actor;
int newIndex;
float newTime;
if (Index == actor.SplinePointsCount - 1)
{
// Append to the end
newIndex = actor.SplinePointsCount;
newTime = actor.GetSplineTime(newIndex - 1) + 1.0f;
}
else
{
// Insert between this and next point
newIndex = Index + 1;
newTime = (actor.GetSplineTime(Index) + actor.GetSplineTime(Index + 1)) * 0.5f;
}
var action = new DuplicateUndoAction
{
SplineId = actor.ID,
Index = newIndex,
Time = newTime,
Value = actor.GetSplineLocalTransform(Index),
};
actor.InsertSplineLocalPoint(newIndex, newTime, action.Value);
undoAction = action;
var splineNode = (SplineNode)SceneGraphFactory.FindNode(action.SplineId);
splineNode.OnUpdate();
OnSplineEdited(actor);
return splineNode.ActorChildNodes[newIndex];
}
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
{
var actor = (Spline)_node.Actor;
var pos = actor.GetSplinePoint(Index);
normal = -ray.Ray.Direction;
return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance);
}
public override void OnDebugDraw(ViewportDebugDrawData data)
{
var actor = (Spline)_node.Actor;
var pos = actor.GetSplinePoint(Index);
var tangentIn = actor.GetSplineTangent(Index, true).Translation;
var tangentOut = actor.GetSplineTangent(Index, false).Translation;
// Draw spline path
ParentNode.OnDebugDraw(data);
// Draw selected point highlight
DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false);
// Draw tangent points
if (tangentIn != pos)
{
DebugDraw.DrawLine(pos, tangentIn, Color.White.AlphaMultiplied(0.6f), 0, false);
DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.White, 0, false);
}
if (tangentIn != pos)
{
DebugDraw.DrawLine(pos, tangentOut, Color.White.AlphaMultiplied(0.6f), 0, false);
DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.White, 0, false);
}
}
public override void OnContextMenu(ContextMenu contextMenu)
{
ParentNode.OnContextMenu(contextMenu);
}
public static SceneGraphNode Create(StateData state)
{
var data = JsonSerializer.Deserialize<Data>(state.State);
var spline = Object.Find<Spline>(ref data.Spline);
spline.InsertSplineLocalPoint(data.Index, data.Keyframe.Time, data.Keyframe.Value, false);
spline.SetSplineKeyframe(data.Index, data.Keyframe);
var splineNode = (SplineNode)SceneGraphFactory.FindNode(data.Spline);
if (splineNode == null)
return null;
splineNode.OnUpdate();
OnSplineEdited(spline);
return splineNode.ActorChildNodes[data.Index];
}
}
private sealed class SplinePointTangentNode : ActorChildNode
{
private SplineNode _node;
private int _index;
private bool _isIn;
public SplinePointTangentNode(SplineNode node, Guid id, int index, bool isIn)
: base(id, isIn ? 0 : 1)
{
_node = node;
_index = index;
_isIn = isIn;
}
public override Transform Transform
{
get
{
var actor = (Spline)_node.Actor;
return actor.GetSplineTangent(_index, _isIn);
}
set
{
var actor = (Spline)_node.Actor;
actor.SetSplineTangent(_index, value, _isIn);
}
}
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
{
var actor = (Spline)_node.Actor;
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
normal = -ray.Ray.Direction;
return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance);
}
public override void OnDebugDraw(ViewportDebugDrawData data)
{
// Draw spline and spline point
ParentNode.OnDebugDraw(data);
// Draw selected tangent highlight
var actor = (Spline)_node.Actor;
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.YellowGreen, 0, false);
}
public override void OnContextMenu(ContextMenu contextMenu)
{
ParentNode.OnContextMenu(contextMenu);
}
public override void OnDispose()
{
_node = null;
base.OnDispose();
}
}
/// <inheritdoc />
public SplineNode(Actor actor)
: base(actor)
{
OnUpdate();
FlaxEngine.Scripting.Update += OnUpdate;
}
private unsafe void OnUpdate()
{
// Sync spline points with gizmo handles
var actor = (Spline)Actor;
var dstCount = actor.SplinePointsCount;
if (dstCount > 1 && actor.IsLoop)
dstCount--; // The last point is the same as the first one for loop mode
var srcCount = ActorChildNodes?.Count ?? 0;
if (dstCount != srcCount)
{
// Remove unused points
while (srcCount > dstCount)
ActorChildNodes[srcCount-- - 1].Dispose();
// Add new points
var id = ID;
var g = (JsonSerializer.GuidInterop*)&id;
g->D += (uint)srcCount * 3;
while (srcCount < dstCount)
{
g->D += 3;
AddChildNode(new SplinePointNode(this, id, srcCount++));
}
}
}
/// <inheritdoc />
public override void PostSpawn()
{
base.PostSpawn();
// Setup for an initial spline
var spline = (Spline)Actor;
spline.AddSplineLocalPoint(Vector3.Zero, false);
spline.AddSplineLocalPoint(new Vector3(0, 0, 100.0f));
}
/// <inheritdoc />
public override void OnContextMenu(ContextMenu contextMenu)
{
base.OnContextMenu(contextMenu);
contextMenu.AddButton("Add spline model", OnAddSplineModel);
contextMenu.AddButton("Add spline collider", OnAddSplineCollider);
contextMenu.AddButton("Add spline rope body", OnAddSplineRopeBody);
}
private void OnAddSplineModel()
{
var actor = new SplineModel
{
StaticFlags = Actor.StaticFlags,
Transform = Actor.Transform,
};
Editor.Instance.SceneEditing.Spawn(actor, Actor);
}
private void OnAddSplineCollider()
{
var actor = new SplineCollider
{
StaticFlags = Actor.StaticFlags,
Transform = Actor.Transform,
};
// TODO: auto pick the collision data if already using spline model
Editor.Instance.SceneEditing.Spawn(actor, Actor);
}
private void OnAddSplineRopeBody()
{
var actor = new SplineRopeBody
{
StaticFlags = StaticFlags.None,
Transform = Actor.Transform,
};
Editor.Instance.SceneEditing.Spawn(actor, Actor);
}
internal static void OnSplineEdited(Spline spline)
{
var collider = spline.GetChild<SplineCollider>();
if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode)
{
var options = Editor.Instance.Options.Options.General;
if (options.AutoRebuildNavMesh)
{
Navigation.BuildNavMesh(collider.Scene, collider.Box, options.AutoRebuildNavMeshTimeoutMs);
}
}
}
/// <inheritdoc />
public override void OnDispose()
{
FlaxEngine.Scripting.Update -= OnUpdate;
base.OnDispose();
}
}
}

View File

@@ -56,6 +56,9 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc />
public override bool CanCopyPaste => false;
/// <inheritdoc />
public override bool CanDuplicate => false;
/// <inheritdoc />
public override bool CanDelete => false;

View File

@@ -62,8 +62,14 @@ namespace FlaxEditor.SceneGraph
CustomNodesTypes.Add(typeof(NavMeshBoundsVolume), typeof(NavMeshBoundsVolumeNode));
CustomNodesTypes.Add(typeof(BoxVolume), typeof(BoxVolumeNode));
CustomNodesTypes.Add(typeof(NavLink), typeof(NavLinkNode));
CustomNodesTypes.Add(typeof(NavModifierVolume), typeof(NavModifierVolumeNode));
CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode));
CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode));
CustomNodesTypes.Add(typeof(Spline), typeof(SplineNode));
CustomNodesTypes.Add(typeof(SplineModel), typeof(ActorNode));
CustomNodesTypes.Add(typeof(SplineCollider), typeof(ColliderNode));
CustomNodesTypes.Add(typeof(SplineRopeBody), typeof(ActorNode));
CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode));
}
/// <summary>

View File

@@ -15,7 +15,7 @@ namespace FlaxEditor.SceneGraph
/// A <see cref="SceneModule"/> class is responsible for Scene Graph management.
/// </summary>
[HideInEditor]
public abstract class SceneGraphNode : ITransformable
public abstract class SceneGraphNode
{
/// <summary>
/// The parent node.
@@ -65,6 +65,11 @@ namespace FlaxEditor.SceneGraph
/// </summary>
public virtual bool CanCopyPaste => true;
/// <summary>
/// Gets a value indicating whether this instance can be duplicated by the user.
/// </summary>
public virtual bool CanDuplicate => true;
/// <summary>
/// Gets a value indicating whether this node can be deleted by the user.
/// </summary>
@@ -312,6 +317,53 @@ namespace FlaxEditor.SceneGraph
{
}
/// <summary>
/// Called when scene tree window wants to show the context menu. Allows to add custom options.
/// </summary>
public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu)
{
}
/// <summary>
/// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node).
/// </summary>
public struct StateData
{
/// <summary>
/// The name of the scene graph node type (full).
/// </summary>
public string TypeName;
/// <summary>
/// The name of the method (in <see cref="TypeName"/>) that takes this state as a parameter and returns the created scene graph node. Used by the undo actions to restore deleted objects.
/// </summary>
public string CreateMethodName;
/// <summary>
/// The custom state data (as string).
/// </summary>
public string State;
/// <summary>
/// The custom state data (as raw bytes).
/// </summary>
public byte[] StateRaw;
}
/// <summary>
/// Gets a value indicating whether this node can use <see cref="State"/> property for editor undo operations.
/// </summary>
public virtual bool CanUseState => false;
/// <summary>
/// Gets or sets the node state.
/// </summary>
public virtual StateData State
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
/// <summary>
/// Deletes object represented by this node eg. actor.
/// </summary>
@@ -319,6 +371,17 @@ namespace FlaxEditor.SceneGraph
{
}
/// <summary>
/// Duplicates this object. Valid only if <see cref="CanDuplicate"/> returns true.
/// </summary>
/// <param name="undoAction">The undo action that duplicated the object (already performed), null if skip it.</param>
/// <returns>The duplicated object node.</returns>
public virtual SceneGraphNode Duplicate(out IUndoAction undoAction)
{
undoAction = null;
return null;
}
/// <summary>
/// Releases the node and the child tree. Disposed all GUI parts and used resources.
/// </summary>

View File

@@ -53,6 +53,8 @@ namespace FlaxEditor.Scripting
return fieldInfo.IsPublic;
if (_managed is PropertyInfo propertyInfo)
return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsPublic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsPublic);
if (_managed is EventInfo eventInfo)
return eventInfo.GetAddMethod().IsPublic;
if (_custom != null)
return _custom.IsPublic;
return false;
@@ -72,6 +74,8 @@ namespace FlaxEditor.Scripting
return fieldInfo.IsStatic;
if (_managed is PropertyInfo propertyInfo)
return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsStatic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsStatic);
if (_managed is EventInfo eventInfo)
return eventInfo.GetAddMethod().IsStatic;
if (_custom != null)
return _custom.IsStatic;
return false;
@@ -138,6 +142,11 @@ namespace FlaxEditor.Scripting
/// </summary>
public bool IsMethod => _managed is MethodInfo || (_custom?.IsMethod ?? false);
/// <summary>
/// Gets a value indicating whether this member is an event.
/// </summary>
public bool IsEvent => _managed is EventInfo || (_custom?.IsEvent ?? false);
/// <summary>
/// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field).
/// </summary>
@@ -173,7 +182,7 @@ namespace FlaxEditor.Scripting
}
/// <summary>
/// Gets the method parameters count (valid for methods only).
/// Gets the method parameters count (valid for methods and events only).
/// </summary>
public int ParametersCount
{
@@ -181,6 +190,8 @@ namespace FlaxEditor.Scripting
{
if (_managed is MethodInfo methodInfo)
return methodInfo.GetParameters().Length;
if (_managed is EventInfo eventInfo)
return eventInfo.EventHandlerType.GetMethod("Invoke").GetParameters().Length;
if (_custom != null)
return _custom.ParametersCount;
return 0;
@@ -383,7 +394,7 @@ namespace FlaxEditor.Scripting
}
/// <summary>
/// Gets the method parameters metadata.
/// Gets the method parameters metadata (or event delegate signature parameters).
/// </summary>
public Parameter[] GetParameters()
{
@@ -416,6 +427,11 @@ namespace FlaxEditor.Scripting
}
return result;
}
if (_managed is EventInfo eventInfo)
{
var invokeMethod = eventInfo.EventHandlerType.GetMethod("Invoke");
return new ScriptMemberInfo(invokeMethod).GetParameters();
}
return _custom.GetParameters();
}
@@ -634,6 +650,11 @@ namespace FlaxEditor.Scripting
/// </summary>
public static readonly ScriptType Null;
/// <summary>
/// A <see cref="ScriptType" /> that is System.Void.
/// </summary>
public static readonly ScriptType Void = new ScriptType(typeof(void));
/// <summary>
/// Gets the type of the script as <see cref="System.Type"/>.
/// </summary>
@@ -1463,6 +1484,11 @@ namespace FlaxEditor.Scripting
/// </summary>
bool IsMethod { get; }
/// <summary>
/// Gets a value indicating whether this member is an event.
/// </summary>
bool IsEvent { get; }
/// <summary>
/// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field).
/// </summary>
@@ -1504,7 +1530,7 @@ namespace FlaxEditor.Scripting
object[] GetAttributes(bool inherit);
/// <summary>
/// Gets the method parameters metadata.
/// Gets the method parameters metadata (or event delegate signature parameters).
/// </summary>
ScriptMemberInfo.Parameter[] GetParameters();

View File

@@ -59,11 +59,19 @@ namespace FlaxEditor.Scripting
if (type.Type == typeof(MaterialSceneTextures))
return MaterialSceneTextures.BaseColor;
if (type.IsValueType)
return type.CreateInstance();
{
var value = type.CreateInstance();
Utilities.Utils.InitDefaultValues(value);
return value;
}
if (new ScriptType(typeof(object)).IsAssignableFrom(type))
return null;
if (type.CanCreateInstance)
return type.CreateInstance();
{
var value = type.CreateInstance();
Utilities.Utils.InitDefaultValues(value);
return value;
}
throw new NotSupportedException("Cannot create default value for type " + type);
}

View File

@@ -1,6 +1,9 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Options;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
using FlaxEngine.Utilities;
@@ -16,6 +19,9 @@ namespace FlaxEditor.States
{
private sealed class SubStateMachine : StateMachine
{
public int ActionIndex = -1;
public readonly List<GeneralOptions.BuildAction> Actions = new List<GeneralOptions.BuildAction>();
protected override void SwitchState(State nextState)
{
if (CurrentState != null && nextState != null)
@@ -27,10 +33,40 @@ namespace FlaxEditor.States
private abstract class SubState : State
{
public virtual bool DirtyScenes => true;
public virtual bool CanReloadScripts => false;
public virtual void Before()
{
}
public virtual void Update()
{
}
public virtual void Done()
{
var stateMachine = (SubStateMachine)StateMachine;
stateMachine.ActionIndex++;
if (stateMachine.ActionIndex < stateMachine.Actions.Count)
{
var action = stateMachine.Actions[stateMachine.ActionIndex];
var state = stateMachine.States.FirstOrDefault(x => x is ActionState a && a.Action == action);
if (state != null)
{
StateMachine.GoToState(state);
}
else
{
Editor.LogError($"Missing or invalid build scene action {action}.");
}
return;
}
StateMachine.GoToState<EndState>();
}
public virtual void Cancel()
{
StateMachine.GoToState<EndState>();
@@ -45,18 +81,31 @@ namespace FlaxEditor.States
{
public override void OnEnter()
{
var editor = Editor.Instance;
foreach (var scene in Level.Scenes)
var stateMachine = (SubStateMachine)StateMachine;
var scenesDirty = false;
foreach (var state in stateMachine.States)
{
scene.ClearLightmaps();
editor.Scene.MarkSceneEdited(scene);
((SubState)state).Before();
scenesDirty |= ((SubState)state).DirtyScenes;
}
StateMachine.GoToState<CSGState>();
if (scenesDirty)
{
foreach (var scene in Level.Scenes)
Editor.Instance.Scene.MarkSceneEdited(scene);
}
Done();
}
}
private sealed class CSGState : SubState
private abstract class ActionState : SubState
{
public abstract GeneralOptions.BuildAction Action { get; }
}
private sealed class CSGState : ActionState
{
public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.CSG;
public override void OnEnter()
{
foreach (var scene in Level.Scenes)
@@ -68,13 +117,14 @@ namespace FlaxEditor.States
public override void Update()
{
if (!Editor.Internal_GetIsCSGActive())
StateMachine.GoToState<EnvProbesNoGIState>();
Done();
}
}
private class EnvProbesNoGIState : SubState
private class EnvProbesState : ActionState
{
public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.EnvProbes;
public override void OnEnter()
{
Editor.Instance.Scene.ExecuteOnGraph(node =>
@@ -94,12 +144,20 @@ namespace FlaxEditor.States
public override void Update()
{
if (!Editor.Instance.ProgressReporting.BakeEnvProbes.IsActive)
StateMachine.GoToState<StaticLightingState>();
Done();
}
}
private sealed class StaticLightingState : SubState
private sealed class StaticLightingState : ActionState
{
public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.StaticLighting;
public override void Before()
{
foreach (var scene in Level.Scenes)
scene.ClearLightmaps();
}
public override void OnEnter()
{
Editor.LightmapsBakeEnd += OnLightmapsBakeEnd;
@@ -110,7 +168,6 @@ namespace FlaxEditor.States
OnLightmapsBakeEnd(false);
}
/// <inheritdoc />
public override void Cancel()
{
Editor.Internal_BakeLightmaps(true);
@@ -125,21 +182,14 @@ namespace FlaxEditor.States
private void OnLightmapsBakeEnd(bool failed)
{
StateMachine.GoToState<EnvProbesWithGIState>();
Done();
}
}
private sealed class EnvProbesWithGIState : EnvProbesNoGIState
private sealed class NavMeshState : ActionState
{
public override void Update()
{
if (!Editor.Instance.ProgressReporting.BakeEnvProbes.IsActive)
StateMachine.GoToState<NavMeshState>();
}
}
public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.NavMesh;
private sealed class NavMeshState : SubState
{
public override void OnEnter()
{
foreach (var scene in Level.Scenes)
@@ -151,7 +201,58 @@ namespace FlaxEditor.States
public override void Update()
{
if (!Navigation.IsBuildingNavMesh)
StateMachine.GoToState<EndState>();
Done();
}
}
private sealed class CompileScriptsState : ActionState
{
private bool _compiled, _reloaded;
public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.CompileScripts;
public override bool DirtyScenes => false;
public override bool CanReloadScripts => true;
public override void OnEnter()
{
_compiled = _reloaded = false;
ScriptsBuilder.Compile();
ScriptsBuilder.CompilationSuccess += OnCompilationSuccess;
ScriptsBuilder.CompilationFailed += OnCompilationFailed;
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
}
public override void OnExit(State nextState)
{
ScriptsBuilder.CompilationSuccess -= OnCompilationSuccess;
ScriptsBuilder.CompilationFailed -= OnCompilationFailed;
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
base.OnExit(nextState);
}
private void OnCompilationSuccess()
{
_compiled = true;
}
private void OnCompilationFailed()
{
Cancel();
}
private void OnScriptsReloadEnd()
{
_reloaded = true;
}
public override void Update()
{
if (_compiled && _reloaded)
Done();
}
}
@@ -173,10 +274,10 @@ namespace FlaxEditor.States
_stateMachine.AddState(new BeginState());
_stateMachine.AddState(new SetupState());
_stateMachine.AddState(new CSGState());
_stateMachine.AddState(new EnvProbesNoGIState());
_stateMachine.AddState(new EnvProbesState());
_stateMachine.AddState(new StaticLightingState());
_stateMachine.AddState(new EnvProbesWithGIState());
_stateMachine.AddState(new NavMeshState());
_stateMachine.AddState(new CompileScriptsState());
_stateMachine.AddState(new EndState());
_stateMachine.GoToState<BeginState>();
}
@@ -192,6 +293,9 @@ namespace FlaxEditor.States
/// <inheritdoc />
public override bool CanEditContent => false;
/// <inheritdoc />
public override bool CanReloadScripts => ((SubState)_stateMachine.CurrentState).CanReloadScripts;
/// <inheritdoc />
public override bool IsPerformanceHeavy => true;
@@ -215,6 +319,11 @@ namespace FlaxEditor.States
{
Editor.Log("Starting scenes build...");
_startTime = DateTime.Now;
_stateMachine.ActionIndex = -1;
_stateMachine.Actions.Clear();
var actions = (GeneralOptions.BuildAction[])Editor.Options.Options.General.BuildActions?.Clone();
if (actions != null)
_stateMachine.Actions.AddRange(actions);
_stateMachine.GoToState<SetupState>();
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEngine.Utilities;
namespace FlaxEditor.States
{
@@ -39,5 +40,26 @@ namespace FlaxEditor.States
{
UpdateFPS();
}
/// <inheritdoc />
public override void OnEnter()
{
base.OnEnter();
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
/// <inheritdoc />
public override void OnExit(State nextState)
{
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
base.OnExit(nextState);
}
private void OnScriptsReloadBegin()
{
StateMachine.GoToState<ReloadingScriptsState>();
}
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEngine.Utilities;
namespace FlaxEditor.States
{
@@ -18,5 +19,26 @@ namespace FlaxEditor.States
: base(editor)
{
}
/// <inheritdoc />
public override void OnEnter()
{
base.OnEnter();
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
}
/// <inheritdoc />
public override void OnExit(State nextState)
{
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
base.OnExit(nextState);
}
private void OnScriptsReloadEnd()
{
StateMachine.GoToState<EditingSceneState>();
}
}
}

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