chore: checkpoint current workspace changes

This commit is contained in:
2026-04-11 22:14:02 +08:00
parent 3e55f8c204
commit 8848cfd958
227 changed files with 34027 additions and 6711 deletions

View File

@@ -12,6 +12,12 @@
namespace XCEngine {
namespace Resources {
enum class ArtifactStorageKind : Core::uint8 {
Unknown = 0,
LegacyDirectory = 1,
SingleFileContainer = 2
};
class Mesh;
class Material;
@@ -49,8 +55,10 @@ public:
Containers::String importerName;
Core::uint32 importerVersion = 0;
ResourceType resourceType = ResourceType::Unknown;
ArtifactStorageKind storageKind = ArtifactStorageKind::Unknown;
Containers::String artifactDirectory;
Containers::String mainArtifactPath;
Containers::String mainEntryName;
Containers::String sourceHash;
Containers::String metaHash;
Core::uint64 sourceFileSize = 0;
@@ -68,7 +76,10 @@ public:
AssetGUID assetGuid;
ResourceType resourceType = ResourceType::Unknown;
Containers::String artifactMainPath;
Containers::String artifactMainEntryPath;
Containers::String artifactDirectory;
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
Containers::String mainEntryName;
LocalID mainLocalID = kMainAssetLocalID;
};
@@ -100,7 +111,7 @@ public:
const Containers::String& GetLastErrorMessage() const { return m_lastErrorMessage; }
private:
static constexpr Core::uint32 kCurrentImporterVersion = 7;
static constexpr Core::uint32 kBaseImporterVersion = 7;
void EnsureProjectLayout();
void LoadSourceAssetDB();
@@ -126,6 +137,7 @@ private:
static Containers::String NormalizePathString(const Containers::String& path);
static Containers::String MakeKey(const Containers::String& path);
static Containers::String GetImporterNameForPath(const Containers::String& relativePath, bool isFolder);
static Core::uint32 GetCurrentImporterVersion(const Containers::String& importerName);
static ResourceType GetPrimaryResourceTypeForImporter(const Containers::String& importerName);
bool ShouldReimport(const SourceAssetRecord& sourceRecord,
@@ -154,6 +166,8 @@ private:
const SourceAssetRecord& sourceRecord,
const std::vector<ArtifactDependencyRecord>& dependencies = {}) const;
Containers::String BuildArtifactDirectory(const Containers::String& artifactKey) const;
Containers::String BuildArtifactFilePath(const Containers::String& artifactKey,
const char* extension) const;
static Containers::String ReadWholeFileText(const std::filesystem::path& path);
static Containers::String ComputeFileHash(const std::filesystem::path& path);
static Core::uint64 GetFileSizeValue(const std::filesystem::path& path);

View File

@@ -47,7 +47,11 @@ public:
AssetGUID assetGuid;
ResourceType resourceType = ResourceType::Unknown;
Containers::String runtimeLoadPath;
Containers::String artifactMainPath;
Containers::String artifactMainEntryPath;
Containers::String artifactDirectory;
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
Containers::String mainEntryName;
LocalID mainLocalID = kMainAssetLocalID;
};

View File

@@ -20,6 +20,7 @@ public:
virtual void SetTopology(uint32_t topologyType) = 0; // PrimitiveTopologyType
virtual void SetRenderTargetFormats(uint32_t count, const uint32_t* formats, uint32_t depthFormat) = 0;
virtual void SetSampleCount(uint32_t count) = 0;
virtual void SetSampleQuality(uint32_t quality) = 0;
virtual void SetComputeShader(RHIShader* shader) = 0;
// State query

View File

@@ -54,6 +54,7 @@ struct RenderDirectionalShadowData {
bool enabled = false;
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
Math::Vector4 shadowParams = Math::Vector4::Zero();
Math::Vector4 shadowOptions = Math::Vector4::Zero();
RHI::RHIResourceView* shadowMap = nullptr;
bool IsValid() const {

View File

@@ -29,6 +29,11 @@ struct BuiltinSkyboxMaterialData {
BuiltinSkyboxTextureMode textureMode = BuiltinSkyboxTextureMode::None;
};
struct BuiltinDepthStyleMaterialConstants {
Math::Vector4 baseColorFactor = Math::Vector4::One();
Math::Vector4 alphaCutoffParams = Math::Vector4(0.5f, 0.0f, 0.0f, 0.0f);
};
struct MaterialConstantLayoutView {
const Resources::MaterialConstantFieldDesc* fields = nullptr;
size_t count = 0;
@@ -277,6 +282,40 @@ inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Re
return { constantBufferData.Data(), constantBufferData.Size(), layoutView };
}
inline BuiltinDepthStyleMaterialConstants BuildBuiltinDepthStyleMaterialConstants(
const Resources::Material* material) {
BuiltinDepthStyleMaterialConstants constants = {};
constants.baseColorFactor = ResolveBuiltinBaseColorFactor(material);
constants.alphaCutoffParams = Math::Vector4(ResolveBuiltinAlphaCutoff(material), 0.0f, 0.0f, 0.0f);
return constants;
}
inline MaterialConstantPayloadView ResolveBuiltinDepthStyleMaterialConstantPayload(
const Resources::Material* material,
BuiltinDepthStyleMaterialConstants& outConstants,
Resources::MaterialConstantFieldDesc (&outLayout)[2]) {
outConstants = BuildBuiltinDepthStyleMaterialConstants(material);
outLayout[0].name = "gBaseColorFactor";
outLayout[0].type = Resources::MaterialPropertyType::Float4;
outLayout[0].offset = 0u;
outLayout[0].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[0].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].name = "gAlphaCutoffParams";
outLayout[1].type = Resources::MaterialPropertyType::Float4;
outLayout[1].offset = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
MaterialConstantLayoutView layoutView = {};
layoutView.fields = outLayout;
layoutView.count = 2u;
layoutView.size = sizeof(BuiltinDepthStyleMaterialConstants);
return { &outConstants, sizeof(BuiltinDepthStyleMaterialConstants), layoutView };
}
inline bool TryResolveMaterialBufferResourceView(
const Resources::Material* material,
const BuiltinPassResourceBindingDesc& binding,

View File

@@ -43,8 +43,8 @@ public:
const Containers::String& GetShaderPath() const;
private:
bool EnsureInitialized(const RenderContext& renderContext, RHI::Format renderTargetFormat);
bool CreateResources(const RenderContext& renderContext, RHI::Format renderTargetFormat);
bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface);
bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface);
void DestroyResources();
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
@@ -53,6 +53,8 @@ private:
RHI::RHIDevice* m_device = nullptr;
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
RHI::Format m_renderTargetFormat = RHI::Format::Unknown;
uint32_t m_renderTargetSampleCount = 1u;
uint32_t m_renderTargetSampleQuality = 0u;
Resources::ResourceHandle<Resources::Shader> m_shader;
RHI::RHISampler* m_sampler = nullptr;
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;

View File

@@ -133,6 +133,8 @@ private:
uint32_t renderTargetCount = 0;
uint32_t renderTargetFormat = 0;
uint32_t depthStencilFormat = 0;
uint32_t sampleCount = 1;
uint32_t sampleQuality = 0;
bool operator==(const PipelineStateKey& other) const {
return renderState == other.renderState &&
@@ -141,7 +143,9 @@ private:
keywordSignature == other.keywordSignature &&
renderTargetCount == other.renderTargetCount &&
renderTargetFormat == other.renderTargetFormat &&
depthStencilFormat == other.depthStencilFormat;
depthStencilFormat == other.depthStencilFormat &&
sampleCount == other.sampleCount &&
sampleQuality == other.sampleQuality;
}
};
@@ -154,6 +158,8 @@ private:
hash ^= std::hash<uint32_t>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.sampleCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.sampleQuality) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};

View File

@@ -43,8 +43,8 @@ public:
const Containers::String& GetShaderPath() const;
private:
bool EnsureInitialized(const RenderContext& renderContext, RHI::Format renderTargetFormat);
bool CreateResources(const RenderContext& renderContext, RHI::Format renderTargetFormat);
bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface);
bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface);
void DestroyResources();
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
@@ -53,6 +53,8 @@ private:
RHI::RHIDevice* m_device = nullptr;
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
RHI::Format m_renderTargetFormat = RHI::Format::Unknown;
uint32_t m_renderTargetSampleCount = 1u;
uint32_t m_renderTargetSampleQuality = 0u;
Resources::ResourceHandle<Resources::Shader> m_shader;
RHI::RHISampler* m_sampler = nullptr;
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;

View File

@@ -55,8 +55,8 @@ public:
const InfiniteGridPassData& data);
private:
bool EnsureInitialized(const RenderContext& renderContext);
bool CreateResources(const RenderContext& renderContext);
bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface);
bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface);
void DestroyResources();
RHI::RHIDevice* m_device = nullptr;
@@ -67,6 +67,9 @@ private:
RHI::RHIDescriptorSet* m_constantSet = nullptr;
Containers::String m_shaderPath;
Resources::ResourceHandle<Resources::Shader> m_builtinInfiniteGridShader;
RHI::Format m_renderTargetFormat = RHI::Format::Unknown;
RHI::Format m_depthStencilFormat = RHI::Format::Unknown;
uint32_t m_renderTargetSampleCount = 1u;
};
} // namespace Passes

View File

@@ -31,6 +31,11 @@ struct ObjectIdOutlineStyle {
bool debugSelectionMask = false;
};
struct ObjectIdOutlinePassInputs {
RHI::RHIResourceView* objectIdTextureView = nullptr;
RHI::ResourceStates objectIdTextureState = RHI::ResourceStates::PixelShaderResource;
};
class BuiltinObjectIdOutlinePass {
public:
explicit BuiltinObjectIdOutlinePass(Containers::String shaderPath = Containers::String());
@@ -50,7 +55,7 @@ public:
bool Render(
const RenderContext& renderContext,
const RenderSurface& surface,
RHI::RHIResourceView* objectIdTextureView,
const ObjectIdOutlinePassInputs& inputs,
const std::vector<uint64_t>& selectedObjectIds,
const ObjectIdOutlineStyle& style = {});
@@ -62,8 +67,8 @@ private:
std::array<Math::Vector4, kMaxSelectedObjectCount> selectedObjectColors = {};
};
bool EnsureInitialized(const RenderContext& renderContext);
bool CreateResources(const RenderContext& renderContext);
bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface);
bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface);
void DestroyResources();
bool HasCreatedResources() const;
void ResetState();
@@ -78,6 +83,8 @@ private:
RHI::RHIDescriptorSet* m_textureSet = nullptr;
Containers::String m_shaderPath;
std::optional<Resources::ResourceHandle<Resources::Shader>> m_builtinObjectIdOutlineShader;
RHI::Format m_renderTargetFormat = RHI::Format::Unknown;
uint32_t m_renderTargetSampleCount = 1u;
};
} // namespace Passes

View File

@@ -27,6 +27,13 @@ struct SelectionOutlineStyle {
bool debugSelectionMask = false;
};
struct SelectionOutlinePassInputs {
RHI::RHIResourceView* selectionMaskTextureView = nullptr;
RHI::ResourceStates selectionMaskState = RHI::ResourceStates::PixelShaderResource;
RHI::RHIResourceView* depthTextureView = nullptr;
RHI::ResourceStates depthTextureState = RHI::ResourceStates::DepthWrite;
};
class BuiltinSelectionOutlinePass {
public:
explicit BuiltinSelectionOutlinePass(Containers::String shaderPath = Containers::String());
@@ -44,8 +51,7 @@ public:
bool Render(
const RenderContext& renderContext,
const RenderSurface& surface,
RHI::RHIResourceView* selectionMaskTextureView,
RHI::RHIResourceView* depthTextureView,
const SelectionOutlinePassInputs& inputs,
const SelectionOutlineStyle& style = {});
private:
@@ -56,8 +62,8 @@ private:
Math::Vector4 depthParams = Math::Vector4::Zero();
};
bool EnsureInitialized(const RenderContext& renderContext);
bool CreateResources(const RenderContext& renderContext);
bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface);
bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface);
void DestroyResources();
bool HasCreatedResources() const;
void ResetState();
@@ -72,6 +78,8 @@ private:
RHI::RHIDescriptorSet* m_textureSet = nullptr;
Containers::String m_shaderPath;
std::optional<Resources::ResourceHandle<Resources::Shader>> m_builtinSelectionOutlineShader;
RHI::Format m_renderTargetFormat = RHI::Format::Unknown;
uint32_t m_renderTargetSampleCount = 1u;
};
} // namespace Passes

View File

@@ -41,6 +41,9 @@ public:
const char* GetName() const override;
bool Initialize(const RenderContext& context) override;
bool PrepareVolumeResources(
const RenderContext& context,
const RenderSceneData& sceneData);
bool Execute(const RenderPassContext& context) override;
void Shutdown() override;
@@ -140,6 +143,8 @@ private:
uint32_t renderTargetCount = 0;
uint32_t renderTargetFormat = 0;
uint32_t depthStencilFormat = 0;
uint32_t sampleCount = 1;
uint32_t sampleQuality = 0;
bool operator==(const PipelineStateKey& other) const {
return renderState == other.renderState &&
@@ -148,7 +153,9 @@ private:
keywordSignature == other.keywordSignature &&
renderTargetCount == other.renderTargetCount &&
renderTargetFormat == other.renderTargetFormat &&
depthStencilFormat == other.depthStencilFormat;
depthStencilFormat == other.depthStencilFormat &&
sampleCount == other.sampleCount &&
sampleQuality == other.sampleQuality;
}
};
@@ -161,6 +168,8 @@ private:
hash ^= std::hash<uint32_t>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.sampleCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.sampleQuality) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};

View File

@@ -128,6 +128,7 @@ struct DirectionalShadowRenderPlan {
Math::Vector3 lightDirection = Math::Vector3::Back();
Math::Vector3 focusPoint = Math::Vector3::Zero();
float orthographicHalfExtent = 0.0f;
float texelWorldSize = 0.0f;
float nearClipPlane = 0.1f;
float farClipPlane = 0.0f;
uint32_t mapWidth = 0;

View File

@@ -22,6 +22,7 @@ struct RenderPassContext {
const RenderSceneData& sceneData;
const RenderSurface* sourceSurface = nullptr;
RHI::RHIResourceView* sourceColorView = nullptr;
RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common;
};
class RenderPass {

View File

@@ -51,6 +51,16 @@ public:
void SetColorStateAfter(RHI::ResourceStates state) { m_colorStateAfter = state; }
RHI::ResourceStates GetColorStateAfter() const { return m_colorStateAfter; }
void SetDepthStateBefore(RHI::ResourceStates state) { m_depthStateBefore = state; }
RHI::ResourceStates GetDepthStateBefore() const { return m_depthStateBefore; }
void SetDepthStateAfter(RHI::ResourceStates state) { m_depthStateAfter = state; }
RHI::ResourceStates GetDepthStateAfter() const { return m_depthStateAfter; }
void SetSampleDesc(uint32_t sampleCount, uint32_t sampleQuality = 0);
uint32_t GetSampleCount() const { return m_sampleCount; }
uint32_t GetSampleQuality() const { return m_sampleQuality; }
private:
uint32_t m_width = 0;
uint32_t m_height = 0;
@@ -63,6 +73,10 @@ private:
bool m_autoTransition = true;
RHI::ResourceStates m_colorStateBefore = RHI::ResourceStates::Present;
RHI::ResourceStates m_colorStateAfter = RHI::ResourceStates::Present;
RHI::ResourceStates m_depthStateBefore = RHI::ResourceStates::DepthWrite;
RHI::ResourceStates m_depthStateAfter = RHI::ResourceStates::DepthWrite;
uint32_t m_sampleCount = 1;
uint32_t m_sampleQuality = 0;
};
} // namespace Rendering

View File

@@ -1,13 +1,19 @@
#pragma once
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Resources {
class GaussianSplat;
bool SerializeGaussianSplatArtifactPayload(const GaussianSplat& gaussianSplat,
Containers::Array<Core::uint8>& outPayload,
Containers::String* outErrorMessage = nullptr);
bool WriteGaussianSplatArtifactFile(const Containers::String& artifactPath,
const GaussianSplat& gaussianSplat,
Containers::String* outErrorMessage = nullptr);

View File

@@ -1,13 +1,19 @@
#pragma once
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Core/Types.h>
namespace XCEngine {
namespace Resources {
class Model;
bool SerializeModelArtifactPayload(const Model& model,
Containers::Array<Core::uint8>& outPayload,
Containers::String* outErrorMessage = nullptr);
bool WriteModelArtifactFile(const Containers::String& artifactPath,
const Model& model,
Containers::String* outErrorMessage = nullptr);

View File

@@ -0,0 +1,91 @@
#pragma once
#include <XCEngine/Core/Asset/AssetGUID.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <unordered_map>
namespace XCEngine {
namespace Resources {
constexpr Core::uint32 kShaderCompilationCacheSchemaVersion = 1;
enum class ShaderBytecodeFormat : Core::uint32 {
Unknown = 0,
DXIL,
DXBC,
SPIRV,
GLSLSource,
OpenGLProgramBinary
};
struct ShaderCompileKey {
Containers::String shaderPath;
Containers::String sourceHash;
Containers::String dependencyHash;
Containers::String passName;
Containers::String entryPoint;
Containers::String profile;
Containers::String compilerName;
Containers::String compilerVersion;
Containers::String optionsSignature;
ShaderType stage = ShaderType::Fragment;
ShaderLanguage sourceLanguage = ShaderLanguage::HLSL;
ShaderBackend backend = ShaderBackend::Generic;
Containers::Array<Containers::String> keywords;
void Normalize();
Containers::String BuildSignature() const;
Containers::String BuildCacheKey() const;
};
struct ShaderCacheEntry {
ShaderCompileKey key;
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
Containers::Array<Core::uint8> payload;
};
class ShaderCompilationCache {
public:
void Initialize(const Containers::String& libraryRoot);
void Shutdown();
bool IsInitialized() const { return !m_libraryRoot.Empty(); }
const Containers::String& GetLibraryRoot() const { return m_libraryRoot; }
const Containers::String& GetDatabasePath() const { return m_databasePath; }
Core::uint32 GetRecordCount() const { return static_cast<Core::uint32>(m_records.size()); }
Containers::String BuildCacheKey(const ShaderCompileKey& key) const;
Containers::String BuildCacheRelativePath(const ShaderCompileKey& key) const;
Containers::String BuildCacheAbsolutePath(const ShaderCompileKey& key) const;
bool Store(const ShaderCacheEntry& entry,
Containers::String* outErrorMessage = nullptr);
bool TryLoad(const ShaderCompileKey& key,
ShaderCacheEntry& outEntry,
Containers::String* outErrorMessage = nullptr) const;
private:
struct ShaderCacheRecord {
ShaderBackend backend = ShaderBackend::Generic;
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
Containers::String relativePath;
Core::uint64 payloadSize = 0;
};
void LoadDatabase();
void SaveDatabase() const;
static Containers::String BuildBackendDirectoryName(ShaderBackend backend);
static AssetGUID ComputeKeyGuid(const Containers::String& cacheKey);
Containers::String m_libraryRoot;
Containers::String m_databasePath;
std::unordered_map<std::string, ShaderCacheRecord> m_records;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,144 @@
#pragma once
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
#include <algorithm>
#include <cctype>
#include <initializer_list>
#include <string>
namespace XCEngine {
namespace Resources {
namespace Detail {
enum class TextureSemanticUsage {
Unknown,
Color,
Data
};
inline std::string ToLowerCopy(const Containers::String& value) {
std::string lower = value.CStr() != nullptr ? std::string(value.CStr()) : std::string();
std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char character) {
return static_cast<char>(std::tolower(character));
});
return lower;
}
inline bool ContainsAnyToken(const std::string& value, std::initializer_list<const char*> tokens) {
for (const char* token : tokens) {
if (token != nullptr && value.find(token) != std::string::npos) {
return true;
}
}
return false;
}
inline TextureSemanticUsage ClassifyTextureSemanticUsage(const std::string& identifierLower) {
if (identifierLower.empty()) {
return TextureSemanticUsage::Unknown;
}
// Color ramps are authored visually even when they drive toon bands.
if (ContainsAnyToken(identifierLower, { "shadow_ramp", "specular_ramp", "toon_ramp", "ramp" })) {
return TextureSemanticUsage::Color;
}
if (ContainsAnyToken(
identifierLower,
{ "normal",
"nrm",
"lightmap",
"light_map",
"metal",
"metallic",
"rough",
"roughness",
"smoothness",
"gloss",
"mask",
"occlusion",
"ao",
"orm",
"height",
"displace",
"face_shadow",
"faceshadow",
"lookup" })) {
return TextureSemanticUsage::Data;
}
if (ContainsAnyToken(
identifierLower,
{ "diffuse",
"albedo",
"basecolor",
"base_color",
"basemap",
"base_map",
"maintex",
"main_tex",
"colormap",
"color_map",
"emissive",
"emission" })) {
return TextureSemanticUsage::Color;
}
return TextureSemanticUsage::Unknown;
}
inline bool ResolveTextureSRGBUsage(const Containers::String& identifier, const char* propertyName) {
const TextureSemanticUsage identifierUsage = ClassifyTextureSemanticUsage(ToLowerCopy(identifier));
if (identifierUsage == TextureSemanticUsage::Color) {
return true;
}
if (identifierUsage == TextureSemanticUsage::Data) {
return false;
}
if (propertyName != nullptr && propertyName[0] != '\0') {
const Containers::String propertyIdentifier(propertyName);
const TextureSemanticUsage propertyUsage =
ClassifyTextureSemanticUsage(ToLowerCopy(propertyIdentifier));
if (propertyUsage == TextureSemanticUsage::Color) {
return true;
}
if (propertyUsage == TextureSemanticUsage::Data) {
return false;
}
}
// Match Unity-style defaults: unknown image assets are assumed to be color textures.
return true;
}
} // namespace Detail
inline TextureImportSettings BuildTextureImportSettingsFromIdentifier(
const Containers::String& identifier,
const char* propertyName = nullptr) {
TextureImportSettings settings;
const bool importAsSRGB = Detail::ResolveTextureSRGBUsage(identifier, propertyName);
settings.SetSRGB(importAsSRGB);
settings.SetTargetFormat(importAsSRGB ? TextureFormat::RGBA8_SRGB : TextureFormat::RGBA8_UNORM);
return settings;
}
inline TextureImportSettings BuildTextureImportSettingsForMaterialProperty(
const char* propertyName,
const Containers::String& texturePath = Containers::String()) {
if (!texturePath.Empty()) {
return BuildTextureImportSettingsFromIdentifier(texturePath, propertyName);
}
return BuildTextureImportSettingsFromIdentifier(
propertyName != nullptr ? Containers::String(propertyName) : Containers::String(),
propertyName);
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,341 @@
#pragma once
#include <XCEngine/UI/Core/UIInvalidation.h>
#include <XCEngine/UI/Types.h>
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
namespace XCEngine::UI::Widgets {
enum class UIDragDropOperation : std::uint8_t {
None = 0,
Copy = 1 << 0,
Move = 1 << 1,
Link = 1 << 2
};
inline constexpr UIDragDropOperation operator|(
UIDragDropOperation left,
UIDragDropOperation right) {
return static_cast<UIDragDropOperation>(
static_cast<std::uint8_t>(left) |
static_cast<std::uint8_t>(right));
}
inline constexpr UIDragDropOperation operator&(
UIDragDropOperation left,
UIDragDropOperation right) {
return static_cast<UIDragDropOperation>(
static_cast<std::uint8_t>(left) &
static_cast<std::uint8_t>(right));
}
inline constexpr UIDragDropOperation& operator|=(
UIDragDropOperation& left,
UIDragDropOperation right) {
left = left | right;
return left;
}
inline constexpr bool HasAnyUIDragDropOperation(
UIDragDropOperation value,
UIDragDropOperation flags) {
return (value & flags) != UIDragDropOperation::None;
}
struct UIDragDropPayload {
std::string typeId = {};
std::string itemId = {};
std::string label = {};
};
struct UIDragDropSourceDescriptor {
UIElementId ownerId = 0u;
std::string sourceId = {};
UIPoint pointerDownPosition = {};
UIDragDropPayload payload = {};
UIDragDropOperation allowedOperations = UIDragDropOperation::Move;
float activationDistance = 4.0f;
};
struct UIDragDropTargetDescriptor {
UIElementId ownerId = 0u;
std::string targetId = {};
std::span<const std::string_view> acceptedPayloadTypes = {};
UIDragDropOperation acceptedOperations =
UIDragDropOperation::Copy |
UIDragDropOperation::Move |
UIDragDropOperation::Link;
UIDragDropOperation preferredOperation = UIDragDropOperation::Move;
};
struct UIDragDropState {
bool armed = false;
bool active = false;
UIElementId sourceOwnerId = 0u;
std::string sourceId = {};
UIPoint pointerDownPosition = {};
UIPoint pointerPosition = {};
UIDragDropPayload payload = {};
UIDragDropOperation allowedOperations = UIDragDropOperation::None;
float activationDistance = 4.0f;
UIElementId targetOwnerId = 0u;
std::string targetId = {};
UIDragDropOperation previewOperation = UIDragDropOperation::None;
};
struct UIDragDropResult {
bool armed = false;
bool activated = false;
bool targetChanged = false;
bool completed = false;
bool cancelled = false;
UIElementId sourceOwnerId = 0u;
UIElementId targetOwnerId = 0u;
std::string sourceId = {};
std::string targetId = {};
std::string payloadTypeId = {};
std::string payloadItemId = {};
UIDragDropOperation operation = UIDragDropOperation::None;
};
inline constexpr bool IsUIDragDropInProgress(const UIDragDropState& state) {
return state.armed || state.active;
}
inline constexpr bool HasResolvedUIDragDropTarget(const UIDragDropState& state) {
return state.targetOwnerId != 0u &&
!state.targetId.empty() &&
state.previewOperation != UIDragDropOperation::None;
}
inline bool DoesUIDragDropPayloadTypeMatch(
std::string_view payloadType,
std::span<const std::string_view> acceptedPayloadTypes) {
if (acceptedPayloadTypes.empty()) {
return true;
}
return std::find(
acceptedPayloadTypes.begin(),
acceptedPayloadTypes.end(),
payloadType) != acceptedPayloadTypes.end();
}
inline constexpr UIDragDropOperation PickUIDragDropOperation(
UIDragDropOperation availableOperations,
UIDragDropOperation preferredOperations = UIDragDropOperation::None) {
const UIDragDropOperation preferredAvailable =
availableOperations & preferredOperations;
if (HasAnyUIDragDropOperation(preferredAvailable, UIDragDropOperation::Move)) {
return UIDragDropOperation::Move;
}
if (HasAnyUIDragDropOperation(preferredAvailable, UIDragDropOperation::Copy)) {
return UIDragDropOperation::Copy;
}
if (HasAnyUIDragDropOperation(preferredAvailable, UIDragDropOperation::Link)) {
return UIDragDropOperation::Link;
}
if (HasAnyUIDragDropOperation(availableOperations, UIDragDropOperation::Move)) {
return UIDragDropOperation::Move;
}
if (HasAnyUIDragDropOperation(availableOperations, UIDragDropOperation::Copy)) {
return UIDragDropOperation::Copy;
}
if (HasAnyUIDragDropOperation(availableOperations, UIDragDropOperation::Link)) {
return UIDragDropOperation::Link;
}
return UIDragDropOperation::None;
}
inline UIDragDropOperation ResolveUIDragDropOperation(
UIDragDropOperation allowedOperations,
const UIDragDropTargetDescriptor& target) {
const UIDragDropOperation supportedOperations =
allowedOperations & target.acceptedOperations;
if (supportedOperations == UIDragDropOperation::None) {
return UIDragDropOperation::None;
}
return PickUIDragDropOperation(
supportedOperations,
target.preferredOperation);
}
inline UIDragDropOperation ResolveUIDragDropOperation(
const UIDragDropState& state,
const UIDragDropTargetDescriptor& target) {
if (!state.active ||
!DoesUIDragDropPayloadTypeMatch(state.payload.typeId, target.acceptedPayloadTypes)) {
return UIDragDropOperation::None;
}
return ResolveUIDragDropOperation(state.allowedOperations, target);
}
inline void FillUIDragDropResult(
const UIDragDropState& state,
UIDragDropResult& result) {
result.sourceOwnerId = state.sourceOwnerId;
result.targetOwnerId = state.targetOwnerId;
result.sourceId = state.sourceId;
result.targetId = state.targetId;
result.payloadTypeId = state.payload.typeId;
result.payloadItemId = state.payload.itemId;
result.operation = state.previewOperation;
}
inline bool BeginUIDragDrop(
const UIDragDropSourceDescriptor& descriptor,
UIDragDropState& outState,
UIDragDropResult* outResult = nullptr) {
if (descriptor.ownerId == 0u ||
descriptor.allowedOperations == UIDragDropOperation::None) {
return false;
}
outState = {};
outState.armed = true;
outState.sourceOwnerId = descriptor.ownerId;
outState.sourceId = descriptor.sourceId;
outState.pointerDownPosition = descriptor.pointerDownPosition;
outState.pointerPosition = descriptor.pointerDownPosition;
outState.payload = descriptor.payload;
outState.allowedOperations = descriptor.allowedOperations;
outState.activationDistance = (std::max)(descriptor.activationDistance, 0.0f);
if (outResult != nullptr) {
*outResult = {};
outResult->armed = true;
FillUIDragDropResult(outState, *outResult);
}
return true;
}
inline bool UpdateUIDragDropPointer(
UIDragDropState& state,
const UIPoint& pointerPosition,
UIDragDropResult* outResult = nullptr) {
if (!IsUIDragDropInProgress(state)) {
return false;
}
state.pointerPosition = pointerPosition;
UIDragDropResult localResult = {};
FillUIDragDropResult(state, localResult);
if (!state.active) {
const float deltaX = pointerPosition.x - state.pointerDownPosition.x;
const float deltaY = pointerPosition.y - state.pointerDownPosition.y;
const float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance >= state.activationDistance) {
state.active = true;
localResult.activated = true;
}
}
if (outResult != nullptr) {
*outResult = std::move(localResult);
}
return true;
}
inline void ClearUIDragDropTarget(
UIDragDropState& state,
UIDragDropResult* outResult = nullptr) {
const bool changed =
state.targetOwnerId != 0u ||
!state.targetId.empty() ||
state.previewOperation != UIDragDropOperation::None;
state.targetOwnerId = 0u;
state.targetId.clear();
state.previewOperation = UIDragDropOperation::None;
if (outResult != nullptr) {
*outResult = {};
FillUIDragDropResult(state, *outResult);
outResult->targetChanged = changed;
}
}
inline bool UpdateUIDragDropTarget(
UIDragDropState& state,
const UIDragDropTargetDescriptor* target,
UIDragDropResult* outResult = nullptr) {
if (!state.active) {
ClearUIDragDropTarget(state, outResult);
return false;
}
if (target == nullptr) {
ClearUIDragDropTarget(state, outResult);
return false;
}
const UIDragDropOperation resolvedOperation =
ResolveUIDragDropOperation(state, *target);
if (resolvedOperation == UIDragDropOperation::None) {
ClearUIDragDropTarget(state, outResult);
return false;
}
const bool changed =
state.targetOwnerId != target->ownerId ||
state.targetId != target->targetId ||
state.previewOperation != resolvedOperation;
state.targetOwnerId = target->ownerId;
state.targetId = target->targetId;
state.previewOperation = resolvedOperation;
if (outResult != nullptr) {
*outResult = {};
FillUIDragDropResult(state, *outResult);
outResult->targetChanged = changed;
}
return true;
}
inline bool EndUIDragDrop(
UIDragDropState& state,
UIDragDropResult& outResult) {
if (!IsUIDragDropInProgress(state)) {
return false;
}
outResult = {};
FillUIDragDropResult(state, outResult);
if (state.active && HasResolvedUIDragDropTarget(state)) {
outResult.completed = true;
} else {
outResult.cancelled = true;
}
state = {};
return true;
}
inline bool CancelUIDragDrop(
UIDragDropState& state,
UIDragDropResult* outResult = nullptr) {
if (!IsUIDragDropInProgress(state)) {
return false;
}
if (outResult != nullptr) {
*outResult = {};
FillUIDragDropResult(state, *outResult);
outResult->cancelled = true;
}
state = {};
return true;
}
} // namespace XCEngine::UI::Widgets

View File

@@ -27,6 +27,7 @@ public:
bool RemoveSelection(std::string_view selectionId);
bool ClearSelection();
bool ToggleSelection(std::string selectionId);
bool ToggleSelectionMembership(std::string selectionId, bool makePrimary = true);
private:
static void NormalizeSelectionIds(std::vector<std::string>& selectionIds);