#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine { namespace Resources { namespace { constexpr const char* kBuiltinPrefix = "builtin://"; constexpr const char* kBuiltinMeshPrefix = "builtin://meshes/"; constexpr const char* kBuiltinMaterialPrefix = "builtin://materials/"; constexpr const char* kBuiltinShaderPrefix = "builtin://shaders/"; constexpr const char* kBuiltinTexturePrefix = "builtin://textures/"; constexpr const char* kBuiltinDefaultPrimitiveMaterialPath = "builtin://materials/default-primitive"; constexpr const char* kBuiltinForwardLitShaderPath = "builtin://shaders/forward-lit"; constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id"; constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/object-id-outline"; constexpr const char* kBuiltinInfiniteGridShaderPath = "builtin://shaders/infinite-grid"; constexpr const char* kBuiltinDefaultPrimitiveTexturePath = "builtin://textures/default-primitive-albedo"; constexpr float kPi = 3.14159265358979323846f; struct MeshBuffers { std::vector vertices; std::vector indices; }; size_t CalculateBuiltinShaderMemorySize(const Shader& shader); constexpr const char* kBuiltinForwardLitShaderManifestRelativePath = "engine/assets/builtin/shaders/forward-lit/forward-lit.shader"; constexpr const char* kBuiltinObjectIdShaderManifestRelativePath = "engine/assets/builtin/shaders/object-id/object-id.shader"; constexpr const char* kBuiltinObjectIdOutlineShaderManifestRelativePath = "engine/assets/builtin/shaders/object-id-outline/object-id-outline.shader"; constexpr const char* kBuiltinInfiniteGridShaderManifestRelativePath = "engine/assets/builtin/shaders/infinite-grid/infinite-grid.shader"; Containers::String NormalizeBuiltinAssetPath(const std::filesystem::path& path) { return Containers::String(path.lexically_normal().generic_string().c_str()); } bool TryResolveBuiltinAssetPathFromAnchor( const std::filesystem::path& anchor, const std::filesystem::path& relativePath, std::filesystem::path& outPath) { if (anchor.empty()) { return false; } std::error_code ec; std::filesystem::path current = anchor.lexically_normal(); if (std::filesystem::is_regular_file(current, ec)) { current = current.parent_path(); } while (!current.empty()) { const std::filesystem::path candidate = (current / relativePath).lexically_normal(); if (std::filesystem::exists(candidate, ec)) { outPath = candidate; return true; } const std::filesystem::path parent = current.parent_path(); if (parent == current) { break; } current = parent; } return false; } bool TryResolveBuiltinShaderManifestPath( const std::filesystem::path& relativePath, Containers::String& outPath) { std::filesystem::path resolvedPath; std::error_code ec; if (TryResolveBuiltinAssetPathFromAnchor(std::filesystem::current_path(ec), relativePath, resolvedPath)) { outPath = NormalizeBuiltinAssetPath(resolvedPath); return true; } const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot(); if (!resourceRoot.Empty() && TryResolveBuiltinAssetPathFromAnchor(std::filesystem::path(resourceRoot.CStr()), relativePath, resolvedPath)) { outPath = NormalizeBuiltinAssetPath(resolvedPath); return true; } if (TryResolveBuiltinAssetPathFromAnchor(std::filesystem::path(__FILE__), relativePath, resolvedPath)) { outPath = NormalizeBuiltinAssetPath(resolvedPath); return true; } return false; } const char* GetBuiltinShaderManifestRelativePath(const Containers::String& builtinShaderPath) { if (builtinShaderPath == Containers::String(kBuiltinForwardLitShaderPath)) { return kBuiltinForwardLitShaderManifestRelativePath; } if (builtinShaderPath == Containers::String(kBuiltinObjectIdShaderPath)) { return kBuiltinObjectIdShaderManifestRelativePath; } if (builtinShaderPath == Containers::String(kBuiltinObjectIdOutlineShaderPath)) { return kBuiltinObjectIdOutlineShaderManifestRelativePath; } if (builtinShaderPath == Containers::String(kBuiltinInfiniteGridShaderPath)) { return kBuiltinInfiniteGridShaderManifestRelativePath; } return nullptr; } bool TryResolveBuiltinShaderManifestPath( const Containers::String& builtinShaderPath, Containers::String& outPath) { const char* relativePath = GetBuiltinShaderManifestRelativePath(builtinShaderPath); if (relativePath == nullptr) { return false; } return TryResolveBuiltinShaderManifestPath(std::filesystem::path(relativePath), outPath); } Shader* LoadBuiltinShaderFromManifest( const Containers::String& builtinPath, const Containers::String& manifestPath) { ShaderLoader shaderLoader; LoadResult result = shaderLoader.Load(manifestPath); if (!result || result.resource == nullptr) { return nullptr; } auto* shader = static_cast(result.resource); shader->m_path = builtinPath; shader->m_guid = ResourceGUID::Generate(builtinPath); shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); return shader; } Shader* TryLoadBuiltinShaderFromManifest(const Containers::String& builtinPath) { Containers::String manifestPath; if (!TryResolveBuiltinShaderManifestPath(builtinPath, manifestPath)) { return nullptr; } return LoadBuiltinShaderFromManifest(builtinPath, manifestPath); } const char kBuiltinForwardHlsl[] = R"( Texture2D gBaseColorTexture : register(t1); SamplerState gLinearSampler : register(s1); cbuffer PerObjectConstants : register(b1) { float4x4 gProjectionMatrix; float4x4 gViewMatrix; float4x4 gModelMatrix; float4x4 gNormalMatrix; float4 gMainLightDirectionAndIntensity; float4 gMainLightColorAndFlags; }; cbuffer MaterialConstants : register(b2) { float4 gBaseColorFactor; }; struct VSInput { float3 position : POSITION; float3 normal : NORMAL; float2 texcoord : TEXCOORD0; }; struct PSInput { float4 position : SV_POSITION; float3 normalWS : TEXCOORD0; float2 texcoord : TEXCOORD1; }; PSInput MainVS(VSInput input) { PSInput output; float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f)); float4 positionVS = mul(gViewMatrix, positionWS); output.position = mul(gProjectionMatrix, positionVS); output.normalWS = mul((float3x3)gNormalMatrix, input.normal); output.texcoord = input.texcoord; return output; } float4 MainPS(PSInput input) : SV_TARGET { float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor; if (gMainLightColorAndFlags.a < 0.5f) { return baseColor; } float3 normalWS = normalize(input.normalWS); float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = saturate(dot(normalWS, directionToLightWS)); float3 lighting = float3(0.28f, 0.28f, 0.28f) + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); return float4(baseColor.rgb * lighting, baseColor.a); } )"; const char kBuiltinForwardVertexShader[] = R"(#version 430 layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; layout(std140, binding = 1) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; mat4 gNormalMatrix; vec4 gMainLightDirectionAndIntensity; vec4 gMainLightColorAndFlags; }; out vec3 vNormalWS; out vec2 vTexCoord; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); vec4 positionVS = gViewMatrix * positionWS; gl_Position = gProjectionMatrix * positionVS; vNormalWS = mat3(gNormalMatrix) * aNormal; vTexCoord = aTexCoord; } )"; const char kBuiltinForwardFragmentShader[] = R"(#version 430 layout(binding = 1) uniform sampler2D uBaseColorTexture; layout(std140, binding = 1) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; mat4 gNormalMatrix; vec4 gMainLightDirectionAndIntensity; vec4 gMainLightColorAndFlags; }; layout(std140, binding = 2) uniform MaterialConstants { vec4 gBaseColorFactor; }; in vec3 vNormalWS; in vec2 vTexCoord; layout(location = 0) out vec4 fragColor; void main() { vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor; if (gMainLightColorAndFlags.w < 0.5) { fragColor = baseColor; return; } vec3 normalWS = normalize(vNormalWS); vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = max(dot(normalWS, directionToLightWS), 0.0); vec3 lighting = vec3(0.28) + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); fragColor = vec4(baseColor.rgb * lighting, baseColor.a); } )"; const char kBuiltinForwardVulkanVertexShader[] = R"(#version 450 layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; layout(set = 1, binding = 0, std140) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; mat4 gNormalMatrix; vec4 gMainLightDirectionAndIntensity; vec4 gMainLightColorAndFlags; }; layout(location = 0) out vec3 vNormalWS; layout(location = 1) out vec2 vTexCoord; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); vec4 positionVS = gViewMatrix * positionWS; gl_Position = gProjectionMatrix * positionVS; vNormalWS = mat3(gNormalMatrix) * aNormal; vTexCoord = aTexCoord; } )"; const char kBuiltinForwardVulkanFragmentShader[] = R"(#version 450 layout(set = 3, binding = 0) uniform texture2D uBaseColorTexture; layout(set = 4, binding = 0) uniform sampler uLinearSampler; layout(set = 1, binding = 0, std140) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; mat4 gNormalMatrix; vec4 gMainLightDirectionAndIntensity; vec4 gMainLightColorAndFlags; }; layout(set = 2, binding = 0, std140) uniform MaterialConstants { vec4 gBaseColorFactor; }; layout(location = 0) in vec3 vNormalWS; layout(location = 1) in vec2 vTexCoord; layout(location = 0) out vec4 fragColor; void main() { vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor; if (gMainLightColorAndFlags.w < 0.5) { fragColor = baseColor; return; } vec3 normalWS = normalize(vNormalWS); vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = max(dot(normalWS, directionToLightWS), 0.0); vec3 lighting = vec3(0.28) + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); fragColor = vec4(baseColor.rgb * lighting, baseColor.a); } )"; const char kBuiltinObjectIdHlsl[] = R"( cbuffer PerObjectConstants : register(b0) { float4x4 gProjectionMatrix; float4x4 gViewMatrix; float4x4 gModelMatrix; float4 gObjectIdColor; }; struct VSInput { float3 position : POSITION; }; struct PSInput { float4 position : SV_POSITION; }; PSInput MainVS(VSInput input) { PSInput output; float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0)); float4 positionVS = mul(gViewMatrix, positionWS); output.position = mul(gProjectionMatrix, positionVS); return output; } float4 MainPS(PSInput input) : SV_TARGET { return gObjectIdColor; } )"; const char kBuiltinObjectIdVertexShader[] = R"(#version 430 layout(location = 0) in vec3 aPosition; layout(std140, binding = 0) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; vec4 gObjectIdColor; }; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); vec4 positionVS = gViewMatrix * positionWS; gl_Position = gProjectionMatrix * positionVS; } )"; const char kBuiltinObjectIdFragmentShader[] = R"(#version 430 layout(std140, binding = 0) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; vec4 gObjectIdColor; }; layout(location = 0) out vec4 fragColor; void main() { fragColor = gObjectIdColor; } )"; const char kBuiltinObjectIdVulkanVertexShader[] = R"(#version 450 layout(location = 0) in vec3 aPosition; layout(set = 0, binding = 0, std140) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; vec4 gObjectIdColor; }; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); vec4 positionVS = gViewMatrix * positionWS; gl_Position = gProjectionMatrix * positionVS; } )"; const char kBuiltinObjectIdVulkanFragmentShader[] = R"(#version 450 layout(set = 0, binding = 0, std140) uniform PerObjectConstants { mat4 gProjectionMatrix; mat4 gViewMatrix; mat4 gModelMatrix; vec4 gObjectIdColor; }; layout(location = 0) out vec4 fragColor; void main() { fragColor = gObjectIdColor; } )"; const char kBuiltinObjectIdOutlineHlsl[] = R"( cbuffer OutlineConstants : register(b0) { float4 gViewportSizeAndTexelSize; float4 gOutlineColor; float4 gSelectedInfo; float4 gSelectedObjectColors[256]; }; Texture2D gObjectIdTexture : register(t0); struct VSOutput { float4 position : SV_POSITION; }; VSOutput MainVS(uint vertexId : SV_VertexID) { static const float2 positions[3] = { float2(-1.0, -1.0), float2(-1.0, 3.0), float2( 3.0, -1.0) }; VSOutput output; output.position = float4(positions[vertexId], 0.0, 1.0); return output; } int2 ClampPixelCoord(int2 pixelCoord) { const int2 maxCoord = int2( max((int)gViewportSizeAndTexelSize.x - 1, 0), max((int)gViewportSizeAndTexelSize.y - 1, 0)); return clamp(pixelCoord, int2(0, 0), maxCoord); } float4 LoadObjectId(int2 pixelCoord) { return gObjectIdTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)); } bool IsSelectedObject(float4 objectIdColor) { if (objectIdColor.a <= 0.0) { return false; } const int selectedCount = min((int)gSelectedInfo.x, 256); [loop] for (int i = 0; i < selectedCount; ++i) { const float4 selectedColor = gSelectedObjectColors[i]; if (all(abs(objectIdColor - selectedColor) <= float4( 0.0025, 0.0025, 0.0025, 0.0025))) { return true; } } return false; } float4 MainPS(VSOutput input) : SV_TARGET { const int2 pixelCoord = int2(input.position.xy); const bool debugSelectionMask = gSelectedInfo.y > 0.5; const bool centerSelected = IsSelectedObject(LoadObjectId(pixelCoord)); if (debugSelectionMask) { return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); } if (centerSelected) { discard; } const int outlineWidth = max((int)gSelectedInfo.z, 1); float outline = 0.0; [loop] for (int y = -2; y <= 2; ++y) { [loop] for (int x = -2; x <= 2; ++x) { if (x == 0 && y == 0) { continue; } const float distancePixels = length(float2((float)x, (float)y)); if (distancePixels > outlineWidth) { continue; } if (!IsSelectedObject(LoadObjectId(pixelCoord + int2(x, y)))) { continue; } const float weight = saturate(1.0 - ((distancePixels - 1.0) / max((float)outlineWidth, 1.0))); outline = max(outline, weight); } } if (outline <= 0.001) { discard; } return float4(gOutlineColor.rgb, gOutlineColor.a * outline); } )"; const char kBuiltinInfiniteGridHlsl[] = R"( cbuffer GridConstants : register(b0) { float4x4 gViewProjectionMatrix; float4 gCameraPositionAndScale; float4 gCameraRightAndFade; float4 gCameraUpAndTanHalfFov; float4 gCameraForwardAndAspect; float4 gViewportNearFar; float4 gGridTransition; }; struct VSOutput { float4 position : SV_POSITION; }; VSOutput MainVS(uint vertexId : SV_VertexID) { static const float2 positions[3] = { float2(-1.0, -1.0), float2(-1.0, 3.0), float2( 3.0, -1.0) }; VSOutput output; output.position = float4(positions[vertexId], 0.0, 1.0); return output; } float PristineGridLine(float2 uv) { float2 deriv = max(fwidth(uv), float2(1e-6, 1e-6)); float2 uvMod = frac(uv); float2 uvDist = min(uvMod, 1.0 - uvMod); float2 distInPixels = uvDist / deriv; float2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels); float density = max(deriv.x, deriv.y); float densityFade = 1.0 - smoothstep(0.5, 1.0, density); return max(lineAlpha.x, lineAlpha.y) * densityFade; } float AxisLineAA(float coord, float deriv) { float distInPixels = abs(coord) / max(deriv, 1e-6); return 1.0 - smoothstep(0.0, 1.5, distInPixels); } struct GridLayer { float minor; float major; }; GridLayer SampleGridLayer(float2 worldPos2D, float baseScale) { GridLayer layer; const float2 gridCoord1 = worldPos2D / baseScale; const float2 gridCoord10 = worldPos2D / (baseScale * 10.0); const float grid1 = PristineGridLine(gridCoord1); const float grid10 = PristineGridLine(gridCoord10); const float2 deriv1 = fwidth(gridCoord1); const float lodFactor = smoothstep(0.3, 0.6, max(deriv1.x, deriv1.y)); layer.major = max(grid10, grid1 * 0.35); layer.minor = grid1 * (1.0 - lodFactor); return layer; } struct PSOutput { float4 color : SV_TARGET0; float depth : SV_Depth; }; PSOutput MainPS(VSOutput input) { const float2 viewportSize = max(gViewportNearFar.xy, float2(1.0, 1.0)); const float scale = max(gCameraPositionAndScale.w, 1e-4); const float fadeDistance = max(gCameraRightAndFade.w, scale * 10.0); const float tanHalfFov = max(gCameraUpAndTanHalfFov.w, 1e-4); const float aspect = max(gCameraForwardAndAspect.w, 1e-4); const float transitionBlend = saturate(gGridTransition.x); const float nearClip = gViewportNearFar.z; const float sceneFarClip = gViewportNearFar.w; const float2 ndc = float2( (input.position.x / viewportSize.x) * 2.0 - 1.0, 1.0 - (input.position.y / viewportSize.y) * 2.0); const float3 cameraPosition = gCameraPositionAndScale.xyz; const float3 rayDirection = normalize( gCameraForwardAndAspect.xyz + ndc.x * aspect * tanHalfFov * gCameraRightAndFade.xyz + ndc.y * tanHalfFov * gCameraUpAndTanHalfFov.xyz); if (abs(rayDirection.y) < 1e-5) { discard; } const float t = -cameraPosition.y / rayDirection.y; if (t <= nearClip) { discard; } const float3 worldPosition = cameraPosition + rayDirection * t; float depth = 0.999999; if (t < sceneFarClip) { const float4 clipPosition = mul(gViewProjectionMatrix, float4(worldPosition, 1.0)); if (clipPosition.w <= 1e-6) { discard; } depth = clipPosition.z / clipPosition.w; if (depth <= 0.0 || depth >= 1.0) { discard; } } const float radialFade = 1.0 - smoothstep(fadeDistance * 0.3, fadeDistance, length(worldPosition - cameraPosition)); const float normalFade = smoothstep(0.0, 0.15, abs(rayDirection.y)); const float fadeFactor = radialFade * normalFade; if (fadeFactor < 1e-3) { discard; } const float2 worldPos2D = worldPosition.xz; const GridLayer baseLayer = SampleGridLayer(worldPos2D, scale); const GridLayer nextLayer = SampleGridLayer(worldPos2D, scale * 10.0); const float minorGridIntensity = lerp(baseLayer.minor, nextLayer.minor, transitionBlend); const float majorGridIntensity = lerp(baseLayer.major, nextLayer.major, transitionBlend); float3 finalColor = float3(0.56, 0.56, 0.56); float finalAlpha = max( 0.13 * minorGridIntensity * fadeFactor, 0.28 * majorGridIntensity * fadeFactor); const float2 worldDeriv = max(fwidth(worldPos2D), float2(1e-6, 1e-6)); const float xAxisAlpha = AxisLineAA(worldPos2D.y, worldDeriv.y) * fadeFactor; const float zAxisAlpha = AxisLineAA(worldPos2D.x, worldDeriv.x) * fadeFactor; const float axisAlpha = max(xAxisAlpha, zAxisAlpha); finalAlpha = max(finalAlpha, 0.34 * saturate(axisAlpha)); if (finalAlpha < 1e-3) { discard; } PSOutput output; output.color = float4(finalColor, finalAlpha); output.depth = depth; return output; } )"; Math::Bounds ComputeBounds(const std::vector& vertices) { if (vertices.empty()) { return Math::Bounds(); } Math::Vector3 min = vertices.front().position; Math::Vector3 max = vertices.front().position; for (const StaticMeshVertex& vertex : vertices) { min.x = std::min(min.x, vertex.position.x); min.y = std::min(min.y, vertex.position.y); min.z = std::min(min.z, vertex.position.z); max.x = std::max(max.x, vertex.position.x); max.y = std::max(max.y, vertex.position.y); max.z = std::max(max.z, vertex.position.z); } Math::Bounds bounds; bounds.SetMinMax(min, max); return bounds; } StaticMeshVertex MakeVertex( const Math::Vector3& position, const Math::Vector3& normal, const Math::Vector3& tangent, const Math::Vector2& uv) { StaticMeshVertex vertex; vertex.position = position; vertex.normal = normal.Normalized(); vertex.tangent = tangent.Normalized(); vertex.bitangent = Math::Vector3::Cross(vertex.normal, vertex.tangent).Normalized(); vertex.uv0 = uv; return vertex; } void AppendQuad( MeshBuffers& buffers, const Math::Vector3& bottomLeft, const Math::Vector3& bottomRight, const Math::Vector3& topRight, const Math::Vector3& topLeft, const Math::Vector3& normal, const Math::Vector3& tangent) { const Core::uint32 baseIndex = static_cast(buffers.vertices.size()); buffers.vertices.push_back(MakeVertex(bottomLeft, normal, tangent, Math::Vector2(0.0f, 0.0f))); buffers.vertices.push_back(MakeVertex(bottomRight, normal, tangent, Math::Vector2(1.0f, 0.0f))); buffers.vertices.push_back(MakeVertex(topRight, normal, tangent, Math::Vector2(1.0f, 1.0f))); buffers.vertices.push_back(MakeVertex(topLeft, normal, tangent, Math::Vector2(0.0f, 1.0f))); buffers.indices.push_back(baseIndex + 0); buffers.indices.push_back(baseIndex + 1); buffers.indices.push_back(baseIndex + 2); buffers.indices.push_back(baseIndex + 0); buffers.indices.push_back(baseIndex + 2); buffers.indices.push_back(baseIndex + 3); } void FlipTriangleWinding(MeshBuffers& buffers) { for (size_t index = 0; index + 2 < buffers.indices.size(); index += 3) { std::swap(buffers.indices[index + 1], buffers.indices[index + 2]); } } MeshBuffers CreateCubeMeshBuffers() { MeshBuffers buffers; const float half = 0.5f; AppendQuad( buffers, Math::Vector3(-half, -half, half), Math::Vector3(half, -half, half), Math::Vector3(half, half, half), Math::Vector3(-half, half, half), Math::Vector3::Forward(), Math::Vector3::Right()); AppendQuad( buffers, Math::Vector3(half, -half, -half), Math::Vector3(-half, -half, -half), Math::Vector3(-half, half, -half), Math::Vector3(half, half, -half), Math::Vector3::Back(), Math::Vector3::Left()); AppendQuad( buffers, Math::Vector3(-half, -half, -half), Math::Vector3(-half, -half, half), Math::Vector3(-half, half, half), Math::Vector3(-half, half, -half), Math::Vector3::Left(), Math::Vector3::Forward()); AppendQuad( buffers, Math::Vector3(half, -half, half), Math::Vector3(half, -half, -half), Math::Vector3(half, half, -half), Math::Vector3(half, half, half), Math::Vector3::Right(), Math::Vector3::Back()); AppendQuad( buffers, Math::Vector3(-half, half, half), Math::Vector3(half, half, half), Math::Vector3(half, half, -half), Math::Vector3(-half, half, -half), Math::Vector3::Up(), Math::Vector3::Right()); AppendQuad( buffers, Math::Vector3(-half, -half, -half), Math::Vector3(half, -half, -half), Math::Vector3(half, -half, half), Math::Vector3(-half, -half, half), Math::Vector3::Down(), Math::Vector3::Right()); return buffers; } MeshBuffers CreateQuadMeshBuffers() { MeshBuffers buffers; AppendQuad( buffers, Math::Vector3(-0.5f, -0.5f, 0.0f), Math::Vector3(0.5f, -0.5f, 0.0f), Math::Vector3(0.5f, 0.5f, 0.0f), Math::Vector3(-0.5f, 0.5f, 0.0f), Math::Vector3::Forward(), Math::Vector3::Right()); return buffers; } MeshBuffers CreatePlaneMeshBuffers() { MeshBuffers buffers; constexpr int kSegments = 10; constexpr float kSize = 10.0f; const float halfSize = kSize * 0.5f; const float step = kSize / static_cast(kSegments); for (int z = 0; z <= kSegments; ++z) { for (int x = 0; x <= kSegments; ++x) { const float px = -halfSize + static_cast(x) * step; const float pz = -halfSize + static_cast(z) * step; const float u = static_cast(x) / static_cast(kSegments); const float v = static_cast(z) / static_cast(kSegments); buffers.vertices.push_back(MakeVertex( Math::Vector3(px, 0.0f, pz), Math::Vector3::Up(), Math::Vector3::Right(), Math::Vector2(u, v))); } } const int rowStride = kSegments + 1; for (int z = 0; z < kSegments; ++z) { for (int x = 0; x < kSegments; ++x) { const Core::uint32 i0 = static_cast(z * rowStride + x); const Core::uint32 i1 = i0 + 1; const Core::uint32 i2 = i0 + static_cast(rowStride); const Core::uint32 i3 = i2 + 1; buffers.indices.push_back(i0); buffers.indices.push_back(i3); buffers.indices.push_back(i1); buffers.indices.push_back(i0); buffers.indices.push_back(i2); buffers.indices.push_back(i3); } } return buffers; } MeshBuffers CreateUvSphereMeshBuffers() { MeshBuffers buffers; constexpr int kLongitudeSegments = 24; constexpr int kLatitudeSegments = 16; constexpr float kRadius = 0.5f; for (int latitude = 0; latitude <= kLatitudeSegments; ++latitude) { const float v = static_cast(latitude) / static_cast(kLatitudeSegments); const float theta = v * kPi; const float sinTheta = std::sin(theta); const float cosTheta = std::cos(theta); for (int longitude = 0; longitude <= kLongitudeSegments; ++longitude) { const float u = static_cast(longitude) / static_cast(kLongitudeSegments); const float phi = u * (2.0f * kPi); const float sinPhi = std::sin(phi); const float cosPhi = std::cos(phi); const Math::Vector3 normal(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta); Math::Vector3 tangent(-sinPhi, 0.0f, cosPhi); if (tangent.SqrMagnitude() <= 0.000001f) { tangent = Math::Vector3::Right(); } buffers.vertices.push_back(MakeVertex( normal * kRadius, normal, tangent, Math::Vector2(u, 1.0f - v))); } } const int stride = kLongitudeSegments + 1; for (int latitude = 0; latitude < kLatitudeSegments; ++latitude) { for (int longitude = 0; longitude < kLongitudeSegments; ++longitude) { const Core::uint32 i0 = static_cast(latitude * stride + longitude); const Core::uint32 i1 = i0 + 1; const Core::uint32 i2 = i0 + static_cast(stride); const Core::uint32 i3 = i2 + 1; buffers.indices.push_back(i0); buffers.indices.push_back(i2); buffers.indices.push_back(i1); buffers.indices.push_back(i1); buffers.indices.push_back(i2); buffers.indices.push_back(i3); } } return buffers; } MeshBuffers CreateCylinderMeshBuffers() { MeshBuffers buffers; constexpr int kRadialSegments = 24; constexpr float kRadius = 0.5f; constexpr float kHalfHeight = 1.0f; for (int ring = 0; ring <= 1; ++ring) { const float y = ring == 0 ? -kHalfHeight : kHalfHeight; const float v = static_cast(ring); for (int segment = 0; segment <= kRadialSegments; ++segment) { const float u = static_cast(segment) / static_cast(kRadialSegments); const float angle = u * (2.0f * kPi); const float cosAngle = std::cos(angle); const float sinAngle = std::sin(angle); const Math::Vector3 normal(cosAngle, 0.0f, sinAngle); const Math::Vector3 tangent(-sinAngle, 0.0f, cosAngle); buffers.vertices.push_back(MakeVertex( Math::Vector3(cosAngle * kRadius, y, sinAngle * kRadius), normal, tangent, Math::Vector2(u, v))); } } const int sideStride = kRadialSegments + 1; for (int segment = 0; segment < kRadialSegments; ++segment) { const Core::uint32 i0 = static_cast(segment); const Core::uint32 i1 = i0 + 1; const Core::uint32 i2 = i0 + static_cast(sideStride); const Core::uint32 i3 = i2 + 1; buffers.indices.push_back(i0); buffers.indices.push_back(i2); buffers.indices.push_back(i1); buffers.indices.push_back(i1); buffers.indices.push_back(i2); buffers.indices.push_back(i3); } const auto appendCap = [&](bool topCap) { const float y = topCap ? kHalfHeight : -kHalfHeight; const Math::Vector3 normal = topCap ? Math::Vector3::Up() : Math::Vector3::Down(); const Core::uint32 centerIndex = static_cast(buffers.vertices.size()); buffers.vertices.push_back(MakeVertex( Math::Vector3(0.0f, y, 0.0f), normal, Math::Vector3::Right(), Math::Vector2(0.5f, 0.5f))); for (int segment = 0; segment <= kRadialSegments; ++segment) { const float u = static_cast(segment) / static_cast(kRadialSegments); const float angle = u * (2.0f * kPi); const float cosAngle = std::cos(angle); const float sinAngle = std::sin(angle); buffers.vertices.push_back(MakeVertex( Math::Vector3(cosAngle * kRadius, y, sinAngle * kRadius), normal, Math::Vector3::Right(), Math::Vector2(cosAngle * 0.5f + 0.5f, sinAngle * 0.5f + 0.5f))); } for (int segment = 0; segment < kRadialSegments; ++segment) { const Core::uint32 rim0 = centerIndex + 1 + static_cast(segment); const Core::uint32 rim1 = rim0 + 1; if (topCap) { buffers.indices.push_back(centerIndex); buffers.indices.push_back(rim0); buffers.indices.push_back(rim1); } else { buffers.indices.push_back(centerIndex); buffers.indices.push_back(rim1); buffers.indices.push_back(rim0); } } }; appendCap(true); appendCap(false); return buffers; } MeshBuffers CreateCapsuleMeshBuffers() { MeshBuffers buffers; constexpr int kRadialSegments = 24; constexpr int kHemisphereSegments = 8; constexpr float kRadius = 0.5f; constexpr float kHalfCylinderHeight = 0.5f; struct RingDefinition { float y = 0.0f; float radius = 0.0f; Math::Vector3 normalBase = Math::Vector3::Zero(); }; std::vector rings; rings.reserve(static_cast(kHemisphereSegments * 2 + 2)); for (int step = 0; step <= kHemisphereSegments; ++step) { const float t = static_cast(step) / static_cast(kHemisphereSegments); const float angle = -0.5f * kPi + t * (0.5f * kPi); const float ringRadius = std::cos(angle) * kRadius; const float y = std::sin(angle) * kRadius - kHalfCylinderHeight; const Math::Vector3 normalBase(0.0f, std::sin(angle), 0.0f); rings.push_back({ y, ringRadius, normalBase }); } for (int step = 0; step <= kHemisphereSegments; ++step) { const float t = static_cast(step) / static_cast(kHemisphereSegments); const float angle = t * (0.5f * kPi); const float ringRadius = std::cos(angle) * kRadius; const float y = std::sin(angle) * kRadius + kHalfCylinderHeight; const Math::Vector3 normalBase(0.0f, std::sin(angle), 0.0f); rings.push_back({ y, ringRadius, normalBase }); } for (size_t ringIndex = 0; ringIndex < rings.size(); ++ringIndex) { const float v = rings.size() > 1 ? static_cast(ringIndex) / static_cast(rings.size() - 1) : 0.0f; for (int segment = 0; segment <= kRadialSegments; ++segment) { const float u = static_cast(segment) / static_cast(kRadialSegments); const float angle = u * (2.0f * kPi); const float cosAngle = std::cos(angle); const float sinAngle = std::sin(angle); const Math::Vector3 radial(cosAngle, 0.0f, sinAngle); Math::Vector3 normal( radial.x * rings[ringIndex].radius, rings[ringIndex].normalBase.y * kRadius, radial.z * rings[ringIndex].radius); normal = normal.Normalized(); if (normal.SqrMagnitude() <= 0.000001f) { normal = rings[ringIndex].y >= 0.0f ? Math::Vector3::Up() : Math::Vector3::Down(); } const Math::Vector3 tangent(-sinAngle, 0.0f, cosAngle); buffers.vertices.push_back(MakeVertex( Math::Vector3(radial.x * rings[ringIndex].radius, rings[ringIndex].y, radial.z * rings[ringIndex].radius), normal, tangent, Math::Vector2(u, 1.0f - v))); } } const int stride = kRadialSegments + 1; for (size_t ringIndex = 0; ringIndex + 1 < rings.size(); ++ringIndex) { for (int segment = 0; segment < kRadialSegments; ++segment) { const Core::uint32 i0 = static_cast(ringIndex * stride + static_cast(segment)); const Core::uint32 i1 = i0 + 1; const Core::uint32 i2 = i0 + static_cast(stride); const Core::uint32 i3 = i2 + 1; buffers.indices.push_back(i0); buffers.indices.push_back(i2); buffers.indices.push_back(i1); buffers.indices.push_back(i1); buffers.indices.push_back(i2); buffers.indices.push_back(i3); } } return buffers; } Mesh* BuildMeshResource( const Containers::String& path, const char* displayName, MeshBuffers&& buffers) { if (buffers.vertices.empty() || buffers.indices.empty()) { return nullptr; } auto* mesh = new Mesh(); IResource::ConstructParams params; params.name = Containers::String(displayName); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; mesh->Initialize(params); mesh->SetVertexData( buffers.vertices.data(), buffers.vertices.size() * sizeof(StaticMeshVertex), static_cast(buffers.vertices.size()), sizeof(StaticMeshVertex), VertexAttribute::Position | VertexAttribute::Normal | VertexAttribute::Tangent | VertexAttribute::Bitangent | VertexAttribute::UV0); if (buffers.vertices.size() > 65535u) { mesh->SetIndexData( buffers.indices.data(), buffers.indices.size() * sizeof(Core::uint32), static_cast(buffers.indices.size()), true); } else { std::vector compactIndices; compactIndices.reserve(buffers.indices.size()); for (Core::uint32 index : buffers.indices) { compactIndices.push_back(static_cast(index)); } mesh->SetIndexData( compactIndices.data(), compactIndices.size() * sizeof(Core::uint16), static_cast(compactIndices.size()), false); } const Math::Bounds bounds = ComputeBounds(buffers.vertices); mesh->SetBounds(bounds); MeshSection section = {}; section.baseVertex = 0; section.vertexCount = static_cast(buffers.vertices.size()); section.startIndex = 0; section.indexCount = static_cast(buffers.indices.size()); section.materialID = 0; section.bounds = bounds; mesh->AddSection(section); return mesh; } void AddBuiltinShaderStageVariant( Shader& shader, const Containers::String& passName, ShaderType stage, ShaderLanguage language, ShaderBackend backend, const char* sourceCode, const char* entryPoint, const char* profile) { ShaderStageVariant variant = {}; variant.stage = stage; variant.language = language; variant.backend = backend; variant.sourceCode = Containers::String(sourceCode); variant.entryPoint = Containers::String(entryPoint); variant.profile = Containers::String(profile); shader.AddPassVariant(passName, variant); } size_t CalculateBuiltinShaderMemorySize(const Shader& shader) { size_t memorySize = sizeof(Shader) + shader.GetName().Length() + shader.GetPath().Length(); for (const ShaderPass& pass : shader.GetPasses()) { memorySize += pass.name.Length(); for (const ShaderPassTagEntry& tag : pass.tags) { memorySize += tag.name.Length(); memorySize += tag.value.Length(); } for (const ShaderStageVariant& variant : pass.variants) { memorySize += variant.entryPoint.Length(); memorySize += variant.profile.Length(); memorySize += variant.sourceCode.Length(); memorySize += variant.compiledBinary.Size(); } } return memorySize; } Shader* BuildBuiltinForwardLitShader(const Containers::String& path) { if (Shader* shader = TryLoadBuiltinShaderFromManifest(path)) { return shader; } auto* shader = new Shader(); IResource::ConstructParams params; params.name = Containers::String("Builtin Forward Lit"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; shader->Initialize(params); const Containers::String passName("ForwardLit"); shader->SetPassTag(passName, "LightMode", "ForwardBase"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinForwardHlsl, "MainVS", "vs_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinForwardHlsl, "MainPS", "ps_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::GLSL, ShaderBackend::OpenGL, kBuiltinForwardVertexShader, "main", "vs_4_30"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::GLSL, ShaderBackend::OpenGL, kBuiltinForwardFragmentShader, "main", "fs_4_30"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::GLSL, ShaderBackend::Vulkan, kBuiltinForwardVulkanVertexShader, "main", "vs_4_50"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::GLSL, ShaderBackend::Vulkan, kBuiltinForwardVulkanFragmentShader, "main", "fs_4_50"); shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); return shader; } Shader* BuildBuiltinObjectIdShader(const Containers::String& path) { if (Shader* shader = TryLoadBuiltinShaderFromManifest(path)) { return shader; } auto* shader = new Shader(); IResource::ConstructParams params; params.name = Containers::String("Builtin Object Id"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; shader->Initialize(params); const Containers::String passName("ObjectId"); shader->SetPassTag(passName, "LightMode", "ObjectId"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinObjectIdHlsl, "MainVS", "vs_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinObjectIdHlsl, "MainPS", "ps_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::GLSL, ShaderBackend::OpenGL, kBuiltinObjectIdVertexShader, "main", "vs_4_30"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::GLSL, ShaderBackend::OpenGL, kBuiltinObjectIdFragmentShader, "main", "fs_4_30"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::GLSL, ShaderBackend::Vulkan, kBuiltinObjectIdVulkanVertexShader, "main", "vs_4_50"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::GLSL, ShaderBackend::Vulkan, kBuiltinObjectIdVulkanFragmentShader, "main", "fs_4_50"); shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); return shader; } Shader* BuildBuiltinInfiniteGridShader(const Containers::String& path) { if (Shader* shader = TryLoadBuiltinShaderFromManifest(path)) { return shader; } auto* shader = new Shader(); IResource::ConstructParams params; params.name = Containers::String("Builtin Infinite Grid"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; shader->Initialize(params); const Containers::String passName("InfiniteGrid"); shader->SetPassTag(passName, "LightMode", "InfiniteGrid"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinInfiniteGridHlsl, "MainVS", "vs_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinInfiniteGridHlsl, "MainPS", "ps_5_0"); shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); return shader; } Shader* BuildBuiltinObjectIdOutlineShader(const Containers::String& path) { if (Shader* shader = TryLoadBuiltinShaderFromManifest(path)) { return shader; } auto* shader = new Shader(); IResource::ConstructParams params; params.name = Containers::String("Builtin Object Id Outline"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; shader->Initialize(params); const Containers::String passName("ObjectIdOutline"); shader->SetPassTag(passName, "LightMode", "ObjectIdOutline"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Vertex, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinObjectIdOutlineHlsl, "MainVS", "vs_5_0"); AddBuiltinShaderStageVariant( *shader, passName, ShaderType::Fragment, ShaderLanguage::HLSL, ShaderBackend::D3D12, kBuiltinObjectIdOutlineHlsl, "MainPS", "ps_5_0"); shader->m_memorySize = CalculateBuiltinShaderMemorySize(*shader); return shader; } Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) { auto* material = new Material(); IResource::ConstructParams params; params.name = Containers::String("Default Primitive Material"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; material->Initialize(params); MaterialRenderState renderState = {}; renderState.cullMode = MaterialCullMode::Back; material->SetRenderState(renderState); material->SetRenderQueue(MaterialRenderQueue::Geometry); material->SetShader(ResourceManager::Get().Load(GetBuiltinForwardLitShaderPath())); material->SetTexture( Containers::String("baseColorTexture"), ResourceManager::Get().Load(GetBuiltinDefaultPrimitiveTexturePath())); material->RecalculateMemorySize(); return material; } Texture* BuildDefaultPrimitiveTexture(const Containers::String& path) { static const unsigned char kTexturePixels[4] = { 214, 214, 214, 255 }; auto* texture = new Texture(); IResource::ConstructParams params; params.name = Containers::String("Default Primitive Albedo"); params.path = path; params.guid = ResourceGUID::Generate(path); params.memorySize = 0; texture->Initialize(params); if (!texture->Create( 1, 1, 1, 1, TextureType::Texture2D, TextureFormat::RGBA8_UNORM, kTexturePixels, sizeof(kTexturePixels))) { delete texture; return nullptr; } return texture; } } // namespace bool IsBuiltinResourcePath(const Containers::String& path) { return path.StartsWith(kBuiltinPrefix); } bool IsBuiltinMeshPath(const Containers::String& path) { return path.StartsWith(kBuiltinMeshPrefix); } bool IsBuiltinMaterialPath(const Containers::String& path) { return path.StartsWith(kBuiltinMaterialPrefix); } bool IsBuiltinShaderPath(const Containers::String& path) { return path.StartsWith(kBuiltinShaderPrefix); } bool IsBuiltinTexturePath(const Containers::String& path) { return path.StartsWith(kBuiltinTexturePrefix); } const char* GetBuiltinPrimitiveDisplayName(BuiltinPrimitiveType primitiveType) { switch (primitiveType) { case BuiltinPrimitiveType::Cube: return "Cube"; case BuiltinPrimitiveType::Sphere: return "Sphere"; case BuiltinPrimitiveType::Capsule: return "Capsule"; case BuiltinPrimitiveType::Cylinder: return "Cylinder"; case BuiltinPrimitiveType::Plane: return "Plane"; case BuiltinPrimitiveType::Quad: return "Quad"; default: return "Primitive"; } } Containers::String GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType primitiveType) { switch (primitiveType) { case BuiltinPrimitiveType::Cube: return Containers::String("builtin://meshes/cube"); case BuiltinPrimitiveType::Sphere: return Containers::String("builtin://meshes/sphere"); case BuiltinPrimitiveType::Capsule: return Containers::String("builtin://meshes/capsule"); case BuiltinPrimitiveType::Cylinder: return Containers::String("builtin://meshes/cylinder"); case BuiltinPrimitiveType::Plane: return Containers::String("builtin://meshes/plane"); case BuiltinPrimitiveType::Quad: return Containers::String("builtin://meshes/quad"); default: return Containers::String(); } } Containers::String GetBuiltinDefaultPrimitiveMaterialPath() { return Containers::String(kBuiltinDefaultPrimitiveMaterialPath); } Containers::String GetBuiltinForwardLitShaderPath() { return Containers::String(kBuiltinForwardLitShaderPath); } Containers::String GetBuiltinObjectIdShaderPath() { return Containers::String(kBuiltinObjectIdShaderPath); } Containers::String GetBuiltinObjectIdOutlineShaderPath() { return Containers::String(kBuiltinObjectIdOutlineShaderPath); } Containers::String GetBuiltinInfiniteGridShaderPath() { return Containers::String(kBuiltinInfiniteGridShaderPath); } Containers::String GetBuiltinDefaultPrimitiveTexturePath() { return Containers::String(kBuiltinDefaultPrimitiveTexturePath); } bool TryParseBuiltinPrimitiveType(const Containers::String& path, BuiltinPrimitiveType& outPrimitiveType) { if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cube)) { outPrimitiveType = BuiltinPrimitiveType::Cube; return true; } if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Sphere)) { outPrimitiveType = BuiltinPrimitiveType::Sphere; return true; } if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Capsule)) { outPrimitiveType = BuiltinPrimitiveType::Capsule; return true; } if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cylinder)) { outPrimitiveType = BuiltinPrimitiveType::Cylinder; return true; } if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Plane)) { outPrimitiveType = BuiltinPrimitiveType::Plane; return true; } if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Quad)) { outPrimitiveType = BuiltinPrimitiveType::Quad; return true; } return false; } LoadResult CreateBuiltinMeshResource(const Containers::String& path) { BuiltinPrimitiveType primitiveType = BuiltinPrimitiveType::Cube; if (!TryParseBuiltinPrimitiveType(path, primitiveType)) { return LoadResult(Containers::String("Unknown builtin mesh: ") + path); } MeshBuffers buffers; switch (primitiveType) { case BuiltinPrimitiveType::Cube: buffers = CreateCubeMeshBuffers(); break; case BuiltinPrimitiveType::Sphere: buffers = CreateUvSphereMeshBuffers(); break; case BuiltinPrimitiveType::Capsule: buffers = CreateCapsuleMeshBuffers(); break; case BuiltinPrimitiveType::Cylinder: buffers = CreateCylinderMeshBuffers(); break; case BuiltinPrimitiveType::Plane: buffers = CreatePlaneMeshBuffers(); break; case BuiltinPrimitiveType::Quad: buffers = CreateQuadMeshBuffers(); break; default: return LoadResult(Containers::String("Unsupported builtin mesh: ") + path); } FlipTriangleWinding(buffers); Mesh* mesh = BuildMeshResource(path, GetBuiltinPrimitiveDisplayName(primitiveType), std::move(buffers)); if (mesh == nullptr) { return LoadResult(Containers::String("Failed to create builtin mesh: ") + path); } return LoadResult(mesh); } LoadResult CreateBuiltinMaterialResource(const Containers::String& path) { if (path != GetBuiltinDefaultPrimitiveMaterialPath()) { return LoadResult(Containers::String("Unknown builtin material: ") + path); } Material* material = BuildDefaultPrimitiveMaterial(path); if (material == nullptr) { return LoadResult(Containers::String("Failed to create builtin material: ") + path); } return LoadResult(material); } LoadResult CreateBuiltinShaderResource(const Containers::String& path) { Shader* shader = nullptr; if (path == GetBuiltinForwardLitShaderPath()) { shader = BuildBuiltinForwardLitShader(path); } else if (path == GetBuiltinObjectIdShaderPath()) { shader = BuildBuiltinObjectIdShader(path); } else if (path == GetBuiltinObjectIdOutlineShaderPath()) { shader = BuildBuiltinObjectIdOutlineShader(path); } else if (path == GetBuiltinInfiniteGridShaderPath()) { shader = BuildBuiltinInfiniteGridShader(path); } else { return LoadResult(Containers::String("Unknown builtin shader: ") + path); } if (shader == nullptr) { return LoadResult(Containers::String("Failed to create builtin shader: ") + path); } return LoadResult(shader); } LoadResult CreateBuiltinTextureResource(const Containers::String& path) { if (path != GetBuiltinDefaultPrimitiveTexturePath()) { return LoadResult(Containers::String("Unknown builtin texture: ") + path); } Texture* texture = BuildDefaultPrimitiveTexture(path); if (texture == nullptr) { return LoadResult(Containers::String("Failed to create builtin texture: ") + path); } return LoadResult(texture); } } // namespace Resources } // namespace XCEngine