Merge branch 'master' into Improve-HighlightedPopUpColor

This commit is contained in:
Phantom
2026-03-12 12:26:26 +01:00
15 changed files with 243 additions and 66 deletions

View File

@@ -36,6 +36,30 @@
#include "CreateAnimation.h"
#include "CreateBehaviorTree.h"
#include "CreateJson.h"
#include "Engine/Content/Assets/Model.h"
namespace
{
bool IsAssetTypeNameTextureFile(const String& typeName)
{
return typeName == Texture::TypeName || typeName == SpriteAtlas::TypeName;
}
bool IsAssetTypeNameModelFile(const String& typeName)
{
return typeName == Model::TypeName || typeName == SkinnedModel::TypeName || typeName == Animation::TypeName;
}
bool IsAssetTypeNameMatch(const String& a, const String& b)
{
// Special case when reimporting model/texture but different type
if (IsAssetTypeNameTextureFile(a) && IsAssetTypeNameTextureFile(b))
return true;
if (IsAssetTypeNameModelFile(a) && IsAssetTypeNameModelFile(b))
return true;
return a == b;
}
}
// Tags used to detect asset creation mode
const String AssetsImportingManager::CreateTextureTag(TEXT("Texture"));
@@ -84,8 +108,6 @@ CreateAssetContext::CreateAssetContext(const StringView& inputPath, const String
CustomArg = arg;
Data.Header.ID = id;
SkipMetadata = false;
// TODO: we should use ASNI only chars path (Assimp can use only that kind)
OutputPath = Content::CreateTemporaryAssetPath();
}
@@ -122,6 +144,24 @@ CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
Data.Metadata.Copy((const byte*)buffer.GetString(), (uint32)buffer.GetSize());
}
// Check if target asset already exists but has different type
AssetInfo targetAssetInfo;
if (Content::GetAssetInfo(TargetAssetPath, targetAssetInfo) && !IsAssetTypeNameMatch(targetAssetInfo.TypeName, Data.Header.TypeName))
{
// Change path
int32 index = 0;
String newTargetAssetPath;
do
{
newTargetAssetPath = StringUtils::GetDirectoryName(TargetAssetPath);
newTargetAssetPath /= StringUtils::GetFileNameWithoutExtension(TargetAssetPath) + String::Format(TEXT(" ({})."), index++) + FileSystem::GetExtension(TargetAssetPath);
} while (index < 100 && FileSystem::FileExists(newTargetAssetPath));
TargetAssetPath = newTargetAssetPath;
// Change id
Data.Header.ID = Guid::New();
}
// Save file
result = FlaxStorage::Create(OutputPath, Data) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
if (result == CreateAssetResult::Ok)

View File

@@ -22,7 +22,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Utilities/Encryption.h"
#define FOLIAGE_GET_DRAW_MODES(renderContext, type) (type.DrawModes & renderContext.View.Pass & renderContext.View.GetShadowsDrawPassMask(type.ShadowsMode))
#define FOLIAGE_GET_DRAW_MODES(renderContext, type) (type._drawModes & renderContext.View.Pass & renderContext.View.GetShadowsDrawPassMask(type.ShadowsMode))
#define FOLIAGE_CAN_DRAW(renderContext, type) (type.IsReady() && FOLIAGE_GET_DRAW_MODES(renderContext, type) != DrawPass::None && type.Model->CanBeRendered())
Foliage::Foliage(const SpawnParams& params)
@@ -360,7 +360,7 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::D
draw.DrawState = &instance.DrawState;
draw.Bounds = sphere;
draw.PerInstanceRandom = instance.Random;
draw.DrawModes = type.DrawModes;
draw.DrawModes = type._drawModes;
draw.SetStencilValue(_layer);
type.Model->Draw(context.RenderContext, draw);
@@ -597,14 +597,22 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me
void Foliage::InitType(const RenderView& view, FoliageType& type)
{
const DrawPass drawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
const DrawPass drawModes = type._drawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
type._canDraw = type.IsReady() && drawModes != DrawPass::None && type.Model && type.Model->CanBeRendered();
bool drawModesDirty = false;
for (int32 j = 0; j < type.Entries.Count(); j++)
{
auto& e = type.Entries[j];
e.ReceiveDecals = type.ReceiveDecals != 0;
e.ShadowsMode = type.ShadowsMode;
if (type._drawModesDirty)
{
type._drawModesDirty = 0;
drawModesDirty = true;
}
}
if (drawModesDirty)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::DrawModes);
}
int32 Foliage::GetInstancesCount() const
@@ -1250,7 +1258,7 @@ void Foliage::Draw(RenderContext& renderContext)
draw.Deformation = nullptr;
draw.Bounds = instance.Bounds;
draw.PerInstanceRandom = instance.Random;
draw.DrawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
draw.DrawModes = type._drawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
draw.SetStencilValue(_layer);
type.Model->Draw(renderContext, draw);
return;

View File

@@ -13,6 +13,7 @@ FoliageType::FoliageType()
, Index(-1)
{
_isReady = 0;
_drawModesDirty = 0;
ReceiveDecals = true;
UseDensityScaling = false;
@@ -32,7 +33,7 @@ FoliageType& FoliageType::operator=(const FoliageType& other)
CullDistance = other.CullDistance;
CullDistanceRandomRange = other.CullDistanceRandomRange;
ScaleInLightmap = other.ScaleInLightmap;
DrawModes = other.DrawModes;
SetDrawModes(other._drawModes);
ShadowsMode = other.ShadowsMode;
PaintDensity = other.PaintDensity;
PaintRadius = other.PaintRadius;
@@ -69,6 +70,19 @@ void FoliageType::SetMaterials(const Array<MaterialBase*>& value)
Entries[i].Material = value[i];
}
DrawPass FoliageType::GetDrawModes() const
{
return _drawModes;
}
void FoliageType::SetDrawModes(DrawPass value)
{
if (_drawModes == value)
return;
_drawModes = value;
_drawModesDirty = 1;
}
Float3 FoliageType::GetRandomScale() const
{
Float3 result;
@@ -150,7 +164,7 @@ void FoliageType::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(CullDistance);
SERIALIZE(CullDistanceRandomRange);
SERIALIZE(ScaleInLightmap);
SERIALIZE(DrawModes);
SERIALIZE_MEMBER(DrawModes, _drawModes);
SERIALIZE(ShadowsMode);
SERIALIZE_BIT(ReceiveDecals);
SERIALIZE_BIT(UseDensityScaling);
@@ -191,7 +205,7 @@ void FoliageType::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
DESERIALIZE(CullDistance);
DESERIALIZE(CullDistanceRandomRange);
DESERIALIZE(ScaleInLightmap);
DESERIALIZE(DrawModes);
DESERIALIZE_MEMBER(DrawModes, _drawModes);
DESERIALIZE(ShadowsMode);
DESERIALIZE_BIT(ReceiveDecals);
DESERIALIZE_BIT(UseDensityScaling);

View File

@@ -48,6 +48,8 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public ScriptingOb
private:
uint8 _isReady : 1;
uint8 _canDraw : 1;
uint8 _drawModesDirty : 1;
DrawPass _drawModes = DrawPass::Depth | DrawPass::GBuffer | DrawPass::Forward;
public:
/// <summary>
@@ -124,9 +126,15 @@ public:
API_FIELD() float ScaleInLightmap = 1.0f;
/// <summary>
/// The draw passes to use for rendering this foliage type.
/// Gets the draw passes to use for rendering this foliage type.
/// </summary>
API_FIELD() DrawPass DrawModes = DrawPass::Depth | DrawPass::GBuffer | DrawPass::Forward;
API_PROPERTY(Attributes="DefaultValue(DrawPass.Depth | DrawPass.GBuffer | DrawPass.Forward)")
DrawPass GetDrawModes() const;
/// <summary>
/// Sets the draw passes to use for rendering this foliage type.
/// </summary>
API_PROPERTY() void SetDrawModes(DrawPass value);
/// <summary>
/// The shadows casting mode.
@@ -184,7 +192,7 @@ public:
API_FIELD() float PlacementRandomRollAngle = 0.0f;
/// <summary>
/// The density scaling scale applied to the global scale for the foliage instances of this type. Can be used to boost or reduce density scaling effect on this foliage type. Default is 1.
/// The density scale factor applied to the global scale for the foliage instances of this type. Can be used to boost or reduce density scaling effect on this foliage type. Default is 1. Lower to reduce density scaling effect when downscaling foliage via global quality/scalability.
/// </summary>
API_FIELD() float DensityScalingScale = 1.0f;

View File

@@ -158,6 +158,14 @@ void MeshAccessor::Stream::CopyTo(Span<Float3> dst) const
{
Platform::MemoryCopy(dst.Get(), _data.Get(), _data.Length());
}
else if (IsLinear(PixelFormat::R16G16B16A16_Float))
{
for (int32 i = 0; i < count; i++)
{
auto v = *(Half4*)(_data.Get() + i * _stride);
dst.Get()[i] = Float3(Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y), Float16Compressor::Decompress(v.Z));
}
}
else
{
for (int32 i = 0; i < count; i++)

View File

@@ -66,6 +66,20 @@ void StaticModel::SetBoundsScale(float value)
UpdateBounds();
}
DrawPass StaticModel::GetDrawModes() const
{
return _drawModes;
}
void StaticModel::SetDrawModes(DrawPass value)
{
if (_drawModes == value)
return;
_drawModes = value;
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::DrawModes);
}
int32 StaticModel::GetLODBias() const
{
return _lodBias;
@@ -330,13 +344,13 @@ void StaticModel::Draw(RenderContext& renderContext)
return;
if (renderContext.View.Pass == DrawPass::GlobalSDF)
{
if (EnumHasAnyFlags(DrawModes, DrawPass::GlobalSDF) && Model->SDF.Texture)
if (EnumHasAnyFlags(_drawModes, DrawPass::GlobalSDF) && Model->SDF.Texture)
GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, _transform, _box);
return;
}
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
{
if (EnumHasAnyFlags(DrawModes, DrawPass::GlobalSurfaceAtlas) && Model->SDF.Texture)
if (EnumHasAnyFlags(_drawModes, DrawPass::GlobalSurfaceAtlas) && Model->SDF.Texture)
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, _transform, Model->LODs.Last().GetBox());
return;
}
@@ -353,7 +367,7 @@ void StaticModel::Draw(RenderContext& renderContext)
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = DrawModes;
draw.DrawModes = _drawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -390,7 +404,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = DrawModes;
draw.DrawModes = _drawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -435,7 +449,7 @@ void StaticModel::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(LODBias, _lodBias);
SERIALIZE_MEMBER(ForcedLOD, _forcedLod);
SERIALIZE_MEMBER(SortOrder, _sortOrder);
SERIALIZE(DrawModes);
SERIALIZE_MEMBER(DrawModes, _drawModes);
if (HasLightmap()
#if USE_EDITOR
@@ -487,7 +501,7 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
DESERIALIZE_MEMBER(LODBias, _lodBias);
DESERIALIZE_MEMBER(ForcedLOD, _forcedLod);
DESERIALIZE_MEMBER(SortOrder, _sortOrder);
DESERIALIZE(DrawModes);
DESERIALIZE_MEMBER(DrawModes, _drawModes);
DESERIALIZE_MEMBER(LightmapIndex, Lightmap.TextureIndex);
DESERIALIZE_MEMBER(LightmapArea, Lightmap.UVsArea);
@@ -537,27 +551,27 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
if (member != stream.MemberEnd() && member->value.IsBool() && member->value.GetBool())
{
MARK_CONTENT_DEPRECATED();
DrawModes = DrawPass::Depth;
_drawModes = DrawPass::Depth;
}
}
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
_drawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
_drawModes |= DrawPass::GlobalSurfaceAtlas;
}
{
const auto member = stream.FindMember("RenderPasses");
if (member != stream.MemberEnd() && member->value.IsInt())
{
DrawModes = (DrawPass)member->value.GetInt();
_drawModes = (DrawPass)member->value.GetInt();
}
}
}

View File

@@ -23,6 +23,7 @@ private:
bool _vertexColorsDirty;
byte _vertexColorsCount;
int8 _sortOrder;
DrawPass _drawModes = DrawPass::Default;
Array<Color32> _vertexColorsData[MODEL_MAX_LODS];
GPUBuffer* _vertexColorsBuffer[MODEL_MAX_LODS];
Model* _residencyChangedModel = nullptr;
@@ -40,12 +41,6 @@ public:
API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Model\")")
AssetReference<Model> Model;
/// <summary>
/// The draw passes to use for rendering this object.
/// </summary>
API_FIELD(Attributes="EditorOrder(15), DefaultValue(DrawPass.Default), EditorDisplay(\"Model\")")
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The baked lightmap entry.
/// </summary>
@@ -74,6 +69,17 @@ public:
/// </summary>
API_PROPERTY() void SetBoundsScale(float value);
/// <summary>
/// Gets the draw passes to use for rendering this object.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(DrawPass.Default), EditorDisplay(\"Model\")")
DrawPass GetDrawModes() const;
/// <summary>
/// Sets the draw passes to use for rendering this object.
/// </summary>
API_PROPERTY() void SetDrawModes(DrawPass value);
/// <summary>
/// Gets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
/// </summary>

View File

@@ -56,6 +56,7 @@ public:
Layer = 4,
StaticFlags = 8,
AutoDelayDuringRendering = 16, // Conditionally allow updating data during rendering when writes are locked
DrawModes = 32,
Auto = Visual | Bounds | Layer,
};

View File

@@ -408,7 +408,8 @@ public:
if (GLOBAL_SDF_ACTOR_IS_STATIC(a) && ObjectTypes.Contains(a->GetTypeHandle()))
{
ScopeWriteLock lock(Locker);
OnSceneRenderingDirty(BoundingBox::FromSphere(prevBounds));
if (flags != DrawModes && flags != Layer && flags != StaticFlags)
OnSceneRenderingDirty(BoundingBox::FromSphere(prevBounds));
OnSceneRenderingDirty(a->GetBox());
}
}

View File

@@ -549,19 +549,22 @@ bool Terrain::DrawSetup(RenderContext& renderContext)
const DrawPass drawModes = DrawModes & renderContext.View.Pass;
if (drawModes == DrawPass::GlobalSDF)
{
const float chunkSize = TERRAIN_UNITS_PER_VERTEX * (float)_chunkSize;
const float posToUV = 0.25f / chunkSize;
Float4 localToUV(posToUV, posToUV, 0.0f, 0.0f);
const float chunkScale = 0.25f / (TERRAIN_UNITS_PER_VERTEX * (float)_chunkSize); // Patch heightfield is divided into 4x4 chunks
for (const TerrainPatch* patch : _patches)
{
if (!patch->Heightmap)
continue;
GPUTexture* heightfield = patch->Heightmap->GetTexture();
float size = (float)heightfield->Width();
Float4 localToUV;
localToUV.X = localToUV.Y = chunkScale * (size - 1) / size; // Skip the last edge texel
localToUV.Z = localToUV.W = 0.5f / size; // Include half-texel offset
Transform patchTransform;
patchTransform.Translation = patch->_offset + Vector3(0, patch->_yOffset, 0);
patchTransform.Orientation = Quaternion::Identity;
patchTransform.Scale = Float3(1.0f, patch->_yHeight, 1.0f);
patchTransform = _transform.LocalToWorld(patchTransform);
GlobalSignDistanceFieldPass::Instance()->RasterizeHeightfield(this, patch->Heightmap->GetTexture(), patchTransform, patch->_bounds, localToUV);
GlobalSignDistanceFieldPass::Instance()->RasterizeHeightfield(this, heightfield, patchTransform, patch->_bounds, localToUV);
}
return true;
}

View File

@@ -9,6 +9,7 @@
#include "Engine/Content/Assets/Model.h"
#include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Graphics/Models/MeshAccessor.h"
#include "Engine/Profiler/ProfilerCPU.h"
PACK_STRUCT(struct GPUBVH {
@@ -321,7 +322,6 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex)
lodIndex = Math::Clamp(lodIndex, model->HighestResidentLODIndex(), model->LODs.Count() - 1);
ModelLOD& lod = model->LODs[lodIndex];
_meshes.EnsureCapacity(_meshes.Count() + lod.Meshes.Count());
bool failed = false;
for (int32 i = 0; i < lod.Meshes.Count(); i++)
{
auto& mesh = lod.Meshes[i];
@@ -336,25 +336,19 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex)
auto& meshData = _meshes.AddOne();
meshData.Asset = model;
model->AddReference();
if (model->IsVirtual())
{
meshData.Indices = mesh.GetTriangleCount() * 3;
meshData.Vertices = mesh.GetVertexCount();
failed |= mesh.DownloadDataGPU(MeshBufferType::Index, meshData.IndexBuffer);
failed |= mesh.DownloadDataGPU(MeshBufferType::Vertex0, meshData.VertexBuffer);
}
else
{
failed |= mesh.DownloadDataCPU(MeshBufferType::Index, meshData.IndexBuffer, meshData.Indices);
failed |= mesh.DownloadDataCPU(MeshBufferType::Vertex0, meshData.VertexBuffer, meshData.Vertices);
}
if (failed)
MeshAccessor accessor;
MeshBufferType bufferTypes[2] = { MeshBufferType::Index, MeshBufferType::Vertex0 };
if (accessor.LoadMesh(&mesh, false, ToSpan(bufferTypes, 2)))
return;
if (!meshData.IndexBuffer.IsAllocated() && meshData.IndexBuffer.Length() != 0)
{
// BVH nodes modifies index buffer (sorts data in-place) so clone it
meshData.IndexBuffer.Copy(meshData.IndexBuffer.Get(), meshData.IndexBuffer.Length());
}
auto indexStream = accessor.Index();
auto positionStream = accessor.Position();
if (!indexStream.IsValid() || !positionStream.IsValid())
return;
meshData.Indices = indexStream.GetCount();
meshData.Vertices = positionStream.GetCount();
meshData.IndexBuffer.Copy(indexStream.GetData());
meshData.VertexBuffer.Allocate(meshData.Vertices * sizeof(Float3));
positionStream.CopyTo(ToSpan(meshData.VertexBuffer.Get<Float3>(), meshData.Vertices));
meshData.Use16BitIndexBuffer = mesh.Use16BitIndexBuffer();
meshData.Bounds = mesh.GetBox();
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
#if FLAX_EDITOR
using FlaxEditor.Options;
#endif
@@ -42,6 +44,38 @@ namespace FlaxEngine.GUI
'<',
};
/// <summary>
/// The allowable characters to use for the text.
/// </summary>
[Flags]
public enum AllowableCharacters
{
/// <summary>
/// Wether to not allow any character in the text.
/// </summary>
None = 0,
/// <summary>
/// Whether to use letters in the text.
/// </summary>
Letters = 1 << 0,
/// <summary>
/// Whether to use numbers in the text.
/// </summary>
Numbers = 1 << 1,
/// <summary>
/// Whether to use symbols in the text.
/// </summary>
Symbols = 1 << 2,
/// <summary>
/// Whether to use all characters in the text.
/// </summary>
All = Letters | Numbers | Symbols,
}
/// <summary>
/// Default height of the text box
/// </summary>
@@ -86,6 +120,11 @@ namespace FlaxEngine.GUI
/// Flag used to indicate whenever text can contain multiple lines.
/// </summary>
protected bool _isMultiline;
/// <summary>
/// The characters to allow in the text.
/// </summary>
protected AllowableCharacters _charactersToAllow = AllowableCharacters.All;
/// <summary>
/// Flag used to indicate whenever text is read-only and cannot be modified by the user.
@@ -188,6 +227,16 @@ namespace FlaxEngine.GUI
}
}
/// <summary>
/// The character to allow in the text.
/// </summary>
[EditorOrder(41), Tooltip("The character to allow in the text.")]
public AllowableCharacters CharactersToAllow
{
get => _charactersToAllow;
set => _charactersToAllow = value;
}
/// <summary>
/// Gets or sets the maximum number of characters the user can type into the text box control.
/// </summary>
@@ -395,15 +444,42 @@ namespace FlaxEngine.GUI
value = value.GetLines()[0];
}
if (_text != value)
if (_text.Equals(value, StringComparison.Ordinal))
return;
if (CharactersToAllow != AllowableCharacters.All)
{
Deselect();
ResetViewOffset();
_text = value;
OnTextChanged();
if (CharactersToAllow == AllowableCharacters.None)
{
value = string.Empty;
}
else
{
if (!CharactersToAllow.HasFlag(AllowableCharacters.Letters))
{
if (value != null)
value = new string(value.Where(c => !char.IsLetter(c)).ToArray());
}
if (!CharactersToAllow.HasFlag(AllowableCharacters.Numbers))
{
if (value != null)
value = new string(value.Where(c => !char.IsNumber(c)).ToArray());
}
if (!CharactersToAllow.HasFlag(AllowableCharacters.Symbols))
{
if (value != null)
value = new string(value.Where(c => !char.IsSymbol(c)).ToArray());
}
value ??= string.Empty;
}
}
Deselect();
ResetViewOffset();
_text = value;
OnTextChanged();
}
/// <summary>

View File

@@ -170,15 +170,14 @@ void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID)
// Convert voxel world-space position into heightfield local-space position and get heightfield UV
float4x4 worldToLocal = ToMatrix4x4(objectData.WorldToVolume);
float3 volumePos = mul(float4(voxelWorldPos, 1), worldToLocal).xyz;
float3 volumeUV = volumePos * objectData.VolumeToUVWMul + objectData.VolumeToUVWAdd;
float2 heightfieldUV = float2(volumeUV.x, volumeUV.z);
// Sample heightfield around the voxel location (heightmap uses point sampler)
Texture2D<float4> heightmap = ObjectsTextures[i];
float4 localToUV = float4(objectData.VolumeToUVWMul.xz, objectData.VolumeToUVWAdd.xz);
#if 1
float3 n00, n10, n01, n11;
bool h00, h10, h01, h11;
float offset = CascadeVoxelSize * 2;
float offset = CascadeVoxelSize;
float3 p00 = SampleHeightmap(heightmap, volumePos + float3(-offset, 0, 0), localToUV, n00, h00, objectData.MipOffset);
float3 p10 = SampleHeightmap(heightmap, volumePos + float3(+offset, 0, 0), localToUV, n10, h10, objectData.MipOffset);
float3 p01 = SampleHeightmap(heightmap, volumePos + float3(0, 0, -offset), localToUV, n01, h01, objectData.MipOffset);
@@ -189,6 +188,11 @@ void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID)
float3 heightfieldNormal = (n00 + n10 + n01 + n11) * 0.25f;
heightfieldNormal = normalize(heightfieldNormal);
bool isHole = h00 || h10 || h01 || h11;
#else
float3 heightfieldNormal;
bool isHole;
float3 heightfieldPosition = SampleHeightmap(heightmap, volumePos, localToUV, heightfieldNormal, isHole, objectData.MipOffset);
#endif
// Skip holes and pixels outside the heightfield
if (isHole)

View File

@@ -35,11 +35,11 @@ float3 SampleHeightmap(Texture2D<float4> heightmap, float3 localPosition, float4
{
// Sample heightmap
float2 uv = localPosition.xz * localToUV.xy + localToUV.zw;
float4 value = heightmap.SampleLevel(SamplerPointClamp, uv, mipOffset);
float4 value = heightmap.SampleLevel(SamplerLinearClamp, uv, mipOffset);
// Decode heightmap
normal = DecodeHeightmapNormal(value, isHole);
float height = DecodeHeightmapHeight(value);;
float height = DecodeHeightmapHeight(value);
float3 position = float3(localPosition.x, height, localPosition.z);
// UVs outside the heightmap are empty