diff --git a/CMakeLists.txt b/CMakeLists.txt index ee8e0e2..8960577 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.7...3.23) if(${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) endif() -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) # Handle superbuild first option (USE_SUPERBUILD "Whether or not a superbuild should be invoked" ON) @@ -46,6 +46,7 @@ find_package(assimp CONFIG REQUIRED) find_package(Stb REQUIRED) find_package(EnTT CONFIG REQUIRED) find_package(FreeType CONFIG REQUIRED) +find_package(harfbuzz CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED) find_package(glm CONFIG REQUIRED) find_package(Ktx CONFIG REQUIRED) @@ -66,11 +67,14 @@ add_library(Engine SHARED "") target_compile_definitions(Engine PRIVATE GLFW_WINDOWS) target_include_directories(Engine PRIVATE src/Engine) +target_include_directories(Engine PRIVATE ${VCPKG_INSTALLED_DIR}/x64-windows/include/) target_link_libraries(Engine PUBLIC Vulkan::Vulkan) target_link_libraries(Engine PUBLIC EnTT::EnTT) target_link_libraries(Engine PUBLIC glfw) target_link_libraries(Engine PUBLIC glm::glm) target_link_libraries(Engine PUBLIC freetype) +target_link_libraries(Engine PUBLIC harfbuzz::harfbuzz) +target_link_libraries(Engine PUBLIC harfbuzz::harfbuzz-subset) target_link_libraries(Engine PUBLIC assimp::assimp) target_link_libraries(Engine PUBLIC KTX::ktx) target_link_libraries(Engine PUBLIC nlohmann_json::nlohmann_json) @@ -78,8 +82,8 @@ target_link_libraries(Engine PUBLIC crcpp) target_link_libraries(Engine PUBLIC fmt::fmt) target_link_libraries(Engine PUBLIC unofficial::lunasvg::lunasvg) target_link_libraries(Engine PUBLIC GPUOpen::VulkanMemoryAllocator) +target_link_libraries(Engine PUBLIC ${VCPKG_INSTALLED_DIR}/x64-windows/lib/slang.lib) -target_link_libraries(Engine PUBLIC slang) if(APPLE) target_link_libraries(Engine PUBLIC metal) SET(CMAKE_OSX_DEPLOYMENT_TARGET 14.3) @@ -105,8 +109,8 @@ target_include_directories(Benchmark PRIVATE src/Benchmark) if(MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) - target_compile_options(Engine PUBLIC /std:c++20 /Zi /MP14 /W4 /wd4505) - target_compile_options(Editor PUBLIC /std:c++20 /Zi /MP14 /W4) + target_compile_options(Engine PUBLIC /Zi /MP14 /W4 /wd4505) + target_compile_options(Editor PUBLIC /Zi /MP14 /W4) target_sources(Engine INTERFACE $ $ @@ -115,8 +119,8 @@ if(MSVC) Seele.natvis DESTINATION Seele/) else() - target_compile_options(Engine PUBLIC -g -Wall -Wextra -Wno-error -pedantic -std=c++20 -Wno-missing-field-initializers -Wno-unused-function) - target_compile_options(Editor PUBLIC -g -Wall -Wextra -Wno-error -pedantic -std=c++20) + target_compile_options(Engine PUBLIC -g -Wall -Wextra -Wno-error -pedantic -Wno-missing-field-initializers -Wno-unused-function) + target_compile_options(Editor PUBLIC -g -Wall -Wextra -Wno-error -pedantic) endif() if(APPLE) #Metal lib throws that internally for some reason @@ -132,8 +136,8 @@ add_subdirectory(src/) if(WIN32) add_custom_target(dll_copy ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SLANG_ROOT}bin/slang.dll $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SLANG_ROOT}bin/slang-glslang.dll $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VCPKG_INSTALLED_DIR}/x64-windows/bin/slang.dll $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VCPKG_INSTALLED_DIR}/x64-windows/bin/slang-glslang.dll $ COMMAND_EXPAND_LISTS DEPENDS Editor) elseif(APPLE) diff --git a/cmake/SuperBuild.cmake b/cmake/SuperBuild.cmake index aa9c9fc..42d34fa 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -1,36 +1,5 @@ include (ExternalProject) - -#--------------SLang------------------------------ -add_library(slang SHARED IMPORTED) -if(WIN32) - add_library(slang-glslang SHARED IMPORTED) - set(SLANG_ROOT ${PROJECT_SOURCE_DIR}/../slang/build/Release/) - set_target_properties(slang-glslang PROPERTIES IMPORTED_LOCATION ${SLANG_ROOT}bin/slang-glslang.dll) - set_target_properties(slang-glslang PROPERTIES IMPORTED_IMPLIB ${SLANG_ROOT}lib/slang.lib) - set_target_properties(slang PROPERTIES IMPORTED_LOCATION ${SLANG_ROOT}bin/slang.dll) - set_target_properties(slang PROPERTIES IMPORTED_IMPLIB ${SLANG_ROOT}lib/slang.lib) - target_link_libraries(slang INTERFACE slang-glslang) - install(FILES - ${SLANG_ROOT}bin/slang-glslang.dll - ${SLANG_ROOT}bin/slang.dll - DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) - install(FILES - ${SLANG_ROOT}lib/slang.lib - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) -elseif(APPLE) - set(BINARY_ROOT ${PROJECT_SOURCE_DIR}/../slang/build/Debug) - set_target_properties(slang PROPERTIES IMPORTED_LOCATION ${BINARY_ROOT}/lib/libslang.dylib) - install(FILES - ${BINARY_ROOT}/lib/libslang.dylib - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) -endif() -target_include_directories(slang INTERFACE - $ - $ -) - - #--------------CRC++------------------------------ add_library(crcpp INTERFACE) diff --git a/external/vcpkg b/external/vcpkg index 509f71e..ab42fb3 160000 --- a/external/vcpkg +++ b/external/vcpkg @@ -1 +1 @@ -Subproject commit 509f71e53f45e46c13fa7935d2f6a45803580c07 +Subproject commit ab42fb3032acd29dd12cea7897f85025526a60e6 diff --git a/res/fonts/arial.ttf b/res/fonts/arial.ttf new file mode 100644 index 0000000..8682d94 Binary files /dev/null and b/res/fonts/arial.ttf differ diff --git a/res/fonts/arialbd.ttf b/res/fonts/arialbd.ttf new file mode 100644 index 0000000..a6037e6 Binary files /dev/null and b/res/fonts/arialbd.ttf differ diff --git a/res/fonts/arialbi.ttf b/res/fonts/arialbi.ttf new file mode 100644 index 0000000..6a1fa0f Binary files /dev/null and b/res/fonts/arialbi.ttf differ diff --git a/res/fonts/ariali.ttf b/res/fonts/ariali.ttf new file mode 100644 index 0000000..3801997 Binary files /dev/null and b/res/fonts/ariali.ttf differ diff --git a/res/fonts/ariblk.ttf b/res/fonts/ariblk.ttf new file mode 100644 index 0000000..e7ae345 Binary files /dev/null and b/res/fonts/ariblk.ttf differ diff --git a/res/shaders/UIPass.slang b/res/shaders/UIPass.slang index 15ab839..7b4de78 100644 --- a/res/shaders/UIPass.slang +++ b/res/shaders/UIPass.slang @@ -59,5 +59,5 @@ float4 fragmentMain( if(style.textureIndex == -1) { return float4(style.color, style.opacity); } - return float4(pParams.textures[style.textureIndex].Sample(pParams.sampler, texCoords.xy).xyz, style.opacity); + return pParams.textures[style.textureIndex].Sample(pParams.sampler, texCoords.xy); } \ No newline at end of file diff --git a/res/shaders/lib/BRDF.slang b/res/shaders/lib/BRDF.slang index 0da07b3..ad7c5a9 100644 --- a/res/shaders/lib/BRDF.slang +++ b/res/shaders/lib/BRDF.slang @@ -16,6 +16,7 @@ struct Phong : IBRDF float3 normal; float3 ambient; float shininess; + float3 emissive; __init() { @@ -25,6 +26,7 @@ struct Phong : IBRDF normal = float3(0, 0, 1); ambient = float3(0, 0, 0); shininess = 0; + emissive = float3(0, 0, 0); } float3 evaluate(float3x3 tangentToWorld, float3 viewDir_WS, float3 lightDir_WS, float3 lightColor) @@ -59,6 +61,7 @@ struct BlinnPhong : IBRDF float3 normal; float shininess; float3 ambient; + float3 emissive; __init() { @@ -66,8 +69,9 @@ struct BlinnPhong : IBRDF alpha = 1; specularColor = float3(0, 0, 0); normal = float3(0, 0, 1); - shininess = 0; + shininess = 4; ambient = float3(0, 0, 0); + emissive = float3(0, 0, 0); } float3 evaluate(float3x3 tangentToWorld, float3 viewDir_WS, float3 lightDir_WS, float3 lightColor) @@ -77,7 +81,7 @@ struct BlinnPhong : IBRDF float3 h = normalize(lightDir_WS + viewDir_WS); float specular = pow(saturate(dot(normal_WS, h)), shininess); - return (baseColor * diffuse * lightColor);// + (specularColor * specular); + return (baseColor * diffuse * lightColor) + (specularColor * specular); } float3 evaluateAmbient() @@ -99,12 +103,14 @@ struct CelShading : IBRDF float3 baseColor; float alpha; float3 normal; + float3 emissive; __init() { baseColor = float3(0, 0, 0); alpha = 1; normal = float3(0, 0, 1); + emissive = float3(0, 0, 0); } float3 evaluate(float3x3 tangentToWorld, float3 viewDir_WS, float3 lightDir_WS, float3 lightColor) @@ -146,6 +152,7 @@ struct CookTorrance : IBRDF float roughness; float metallic; float ambientOcclusion; + float3 emissive; __init() { @@ -155,6 +162,7 @@ struct CookTorrance : IBRDF roughness = 0; metallic = 0; ambientOcclusion = 1; + emissive = float3(0, 0, 0); } float TrowbridgeReitzGGX(float3 normal, float3 halfway) diff --git a/res/shaders/lib/MaterialParameter.slang b/res/shaders/lib/MaterialParameter.slang index 52d25af..c38cd3d 100644 --- a/res/shaders/lib/MaterialParameter.slang +++ b/res/shaders/lib/MaterialParameter.slang @@ -106,15 +106,15 @@ struct FragmentParameter FragmentParameter result; result.position_CS = f0.position_CS * barycentricCoords.x + f1.position_CS * barycentricCoords.y + f2.position_CS * barycentricCoords.z; #ifndef POS_ONLY - result.normal_WS = f0.normal_WS * barycentricCoords.x + f1.normal_WS * barycentricCoords.y + f2.normal_WS * barycentricCoords.z; - result.tangent_WS = f0.tangent_WS * barycentricCoords.x + f1.tangent_WS * barycentricCoords.y + f2.tangent_WS * barycentricCoords.z; + result.normal_WS = f0.normal_WS * barycentricCoords.x + f1.normal_WS * barycentricCoords.y + f2.normal_WS * barycentricCoords.z; + result.tangent_WS = f0.tangent_WS * barycentricCoords.x + f1.tangent_WS * barycentricCoords.y + f2.tangent_WS * barycentricCoords.z; result.biTangent_WS = f0.biTangent_WS * barycentricCoords.x + f1.biTangent_WS * barycentricCoords.y + f2.biTangent_WS * barycentricCoords.z; - result.position_WS = f0.position_WS * barycentricCoords.x + f1.position_WS * barycentricCoords.y + f2.position_WS * barycentricCoords.z; - result.vertexColor = f0.vertexColor * barycentricCoords.x + f1.vertexColor * barycentricCoords.y + f2.vertexColor * barycentricCoords.z; - result.texCoords0 = f0.texCoords0 * barycentricCoords.x + f1.texCoords0 * barycentricCoords.y + f2.texCoords0 * barycentricCoords.z; - result.texCoords1 = f0.texCoords1 * barycentricCoords.x + f1.texCoords1 * barycentricCoords.y + f2.texCoords1 * barycentricCoords.z; - result.texCoords2 = f0.texCoords2 * barycentricCoords.x + f1.texCoords2 * barycentricCoords.y + f2.texCoords2 * barycentricCoords.z; - result.texCoords3 = f0.texCoords3 * barycentricCoords.x + f1.texCoords3 * barycentricCoords.y + f2.texCoords3 * barycentricCoords.z; + result.position_WS = f0.position_WS * barycentricCoords.x + f1.position_WS * barycentricCoords.y + f2.position_WS * barycentricCoords.z; + result.vertexColor = f0.vertexColor * barycentricCoords.x + f1.vertexColor * barycentricCoords.y + f2.vertexColor * barycentricCoords.z; + result.texCoords0 = f0.texCoords0 * barycentricCoords.x + f1.texCoords0 * barycentricCoords.y + f2.texCoords0 * barycentricCoords.z; + result.texCoords1 = f0.texCoords1 * barycentricCoords.x + f1.texCoords1 * barycentricCoords.y + f2.texCoords1 * barycentricCoords.z; + result.texCoords2 = f0.texCoords2 * barycentricCoords.x + f1.texCoords2 * barycentricCoords.y + f2.texCoords2 * barycentricCoords.z; + result.texCoords3 = f0.texCoords3 * barycentricCoords.x + f1.texCoords3 * barycentricCoords.y + f2.texCoords3 * barycentricCoords.z; #endif return result; } diff --git a/res/shaders/raytracing/ClosestHit.slang b/res/shaders/raytracing/ClosestHit.slang index 2898c26..0d4a3d9 100644 --- a/res/shaders/raytracing/ClosestHit.slang +++ b/res/shaders/raytracing/ClosestHit.slang @@ -9,6 +9,69 @@ import MATERIAL_FILE_NAME; // simplification: all BLAS only have 1 geometry +/*vec3 nextEventEstimation(vec3 accmat, vec3 w, vec3 x, vec3 nl, float kt, bool useAtt, vec3 rnd) { + uint triId = 0; + uint sphereId = 0; + ShadingParams paramsls; + Material matls; + vec3 result = vec3(0); + // Direct Illumination: Next Event Estimation over any present lights + for (int i = spheres.length(); i-->0;) { + Sphere ls = spheres[i]; + if (all(equal(ls.e, vec3(0)))) continue; // skip non-emissive spheres + vec3 xc = ls.geo.xyz - x; + vec3 sw = normalize(xc), su = normalize(cross((abs(sw.x)>.1 ? vec3(0,1,0) : vec3(1,0,0)), sw)), sv = cross(sw,su); + float cos_a_max = sqrt(float(1 - ls.geo.w*ls.geo.w / dot(xc,xc))); + float cos_a = 1 - rnd.x + rnd.x*cos_a_max, sin_a = sqrt(1 - cos_a*cos_a); + float phi = 2 * pi * rnd.y; + vec3 l = normalize(su*cos(phi)*sin_a + sv*sin(phi)*sin_a + sw*cos_a); // sampled direction towards light + if (intersect(Ray(x,l), matls, paramsls, sphereId, triId) && sphereId == i) { // test if shadow ray hits this light source + float omega = 2 * pi * (1-cos_a_max); + if(useAtt) { + float tau = exp(-kt * length(x - paramsls.x)); + result += max(dot(l,nl),0) * ls.e * omega * tau; + } else { + result += accmat / pi * max(dot(l,nl),0) * ls.e * omega; // brdf term obj.c.xyz already in accmat, 1/pi for brdf + } + } + } + for(int i = meshLights.length(); i-->0;) { + MeshDescriptor mesh = meshes[meshLights[i]]; + for(int j = 0; j < mesh.numIndices; j+=3) { + vec3 A = vec3(vertices[mesh.vertexOffset + indices[mesh.indexOffset + j + 0]]); + vec3 B = vec3(vertices[mesh.vertexOffset + indices[mesh.indexOffset + j + 1]]); + vec3 C = vec3(vertices[mesh.vertexOffset + indices[mesh.indexOffset + j + 2]]); + float b1 = 1 - sqrt(rnd.x); + float b2 = (1 - rnd.y) * sqrt(rnd.x); + float b3 = rnd.y * sqrt(rnd.x); + vec3 P = A * b1 + B * b2 + C * b3; + vec3 omega = P - x; + vec3 l = normalize(omega); + float v = 0.f; + if(intersect(Ray(x,l), matls, paramsls, sphereId, triId) && triId == j) { + v = 1.0f; + } + vec3 e1 = C - A; + vec3 e2 = B - A; + float area = length(cross(e1, e2)) / 2; + float rayLen = length(omega); + float cosTheta = dot(nl, l); + float cosThetaDash = dot(paramsls.n, -l); + float factor = area * (cosThetaDash / (rayLen * rayLen)); + if(useAtt) { + float tau = phase(w, l) * exp(-kt * length(x - paramsls.x)); + result += tau * matls.e * max(cosTheta, 0) * factor; + } else { + result += accmat * (matls.e * max(cosTheta, 0) * factor) / pi; + } + } + } + return result; + } + +*/ + +const float eps = 1e-5; [shader("closesthit")] void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttributes attr) { @@ -46,12 +109,11 @@ void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttribu let brdf = Material.prepare(materialParams); float3 normal_WS = normalize(mul(params.getTangentToWorld(), brdf.normal)); - float3 normalLight_WS = dot(normal_WS,WorldRayOrigin())<0 ? normal_WS : -normal_WS; - float3 intersection_WS = params.position_WS + normal_WS * 0.001f; + float3 normalLight_WS = dot(normal_WS,WorldRayDirection())<0 ? normal_WS : -normal_WS; + float3 intersection_WS = params.position_WS; hitValue.depth++; - float3 localAccRad = float3(0); - float3 rnd = rand01(uint3(vertexIndex0, vertexIndex1, vertexIndex2)); + float3 rnd = rand01(hitValue.rndSeed); //float kt = ka + ks; //float s = -log(rnd.z) / kt; //float3 xs = r.o + s * r.d; @@ -73,13 +135,13 @@ void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttribu // continue; //} - //float p = max(max(brdf.baseColor.x, brdf.baseColor.color.y), brdf.baseColor.color.z); + //float p = max(max(brdf.baseColor.x, brdf.baseColor.y), brdf.baseColor.z); //if(hitValue.depth > 5) { // if (rnd.z >= p) return; // else hitValue.accmat /= p; //} - + hitValue.light += brdf.emissive * hitValue.emissive + brdf.evaluateAmbient(); //-- Ideal DIFFUSE reflection //if(bool(useNEE)) { // accrad += nextEventEstimation(accmat, r.d, params.x, params.nl, kt, false, rnd); @@ -89,22 +151,25 @@ void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttribu float3 l = -pLightEnv.directionalLights[i].direction.xyz; RayDesc rayDesc; rayDesc.TMax = 10000.0f; - rayDesc.TMin = 0.001f; + rayDesc.TMin = eps; rayDesc.Origin = x; rayDesc.Direction = l; RayPayload payload; payload.depth = hitValue.depth; payload.emissive = 1; payload.anyHit = true; + payload.hit = false; + payload.rndSeed = hitValue.rndSeed; TraceRay(pRayTracingParams.scene, 0, 0xff, 0, 0, 0, rayDesc, payload); // we have missed all geometry, so directional light is affecting us - if(!payload.hit) { - localAccRad += pLightEnv.directionalLights[i].illuminate(lightingParams, brdf); - } + //if(!payload.hit) { + //hitValue.light += pLightEnv.directionalLights[i].illuminate(lightingParams, brdf); + //} } for(uint i = 0; i < pLightEnv.numPointLights; ++i) { RayPayload payload; + payload.rndSeed = hitValue.rndSeed; float3 x = intersection_WS; float3 l = pLightEnv.pointLights[i].position_WS.xyz - intersection_WS; if(length(l) > pLightEnv.pointLights[i].colorRange.w) { @@ -112,40 +177,37 @@ void closestHit(inout RayPayload hitValue, in BuiltInTriangleIntersectionAttribu } RayDesc rayDesc; rayDesc.TMax = 1.0f; - rayDesc.TMin = 0.001f; + rayDesc.TMin = eps; rayDesc.Origin = x; rayDesc.Direction = l; TraceRay(pRayTracingParams.scene, 0, 0xff, 0, 0, 0, rayDesc, payload); // hitting only after the light if(!payload.hit) { - localAccRad += pLightEnv.pointLights[i].illuminate(lightingParams, brdf); + hitValue.light += pLightEnv.pointLights[i].illuminate(lightingParams, brdf); } } // Indirect Illumination: cosine-weighted importance sampling if(hitValue.depth < 12) { float r1 = 2 * PI * rnd.x, r2 = rnd.y, r2s = sqrt(r2); float3 w = normalLight_WS; - float3 u = normalize((cross(abs(w.x)>0.1 ? float3(0,1,0) : float3(1,0,0), w))); + float3 u = normalize(cross(abs(w.x)>0.1 ? float3(0,1,0) : float3(1,0,0), w)); float3 v = cross(w,u); RayDesc rayDesc; rayDesc.TMax = 10000.0f; - rayDesc.TMin = 0.001f; + rayDesc.TMin = eps; rayDesc.Origin = intersection_WS; rayDesc.Direction = normalize(u*cos(r1)*r2s + v * sin(r1)*r2s + w * sqrt(1 - r2)); RayPayload payload; payload.light = float3(0); - payload.emissive = 0; // in the next bounce, consider reflective part only! - payload.depth = hitValue.depth+1; + payload.emissive = 1; + payload.hit = false; + //payload.emissive = 0; // in the next bounce, consider reflective part only! + payload.depth = hitValue.depth; payload.anyHit = false; + payload.rndSeed = hitValue.rndSeed + 1; TraceRay(pRayTracingParams.scene, 0, 0xff, 0, 0, 0, rayDesc, payload); - if(payload.hit) { - DirectionalLight dir; - dir.color = float4(payload.light, 0); - dir.direction = float4(-rayDesc.Direction, 0); - localAccRad += dir.illuminate(lightingParams, brdf); - } + float bias = dot(normalLight_WS, rayDesc.Direction); + hitValue.light += brdf.evaluate(params.getTangentToWorld(), -WorldRayDirection(), rayDesc.Direction, payload.light / bias); } - - hitValue.light += localAccRad; } \ No newline at end of file diff --git a/res/shaders/raytracing/RayGen.slang b/res/shaders/raytracing/RayGen.slang index 40301af..0ab95ef 100644 --- a/res/shaders/raytracing/RayGen.slang +++ b/res/shaders/raytracing/RayGen.slang @@ -108,10 +108,13 @@ void raygen() payload.light=float3(0); payload.emissive = 1; payload.depth = 1; + payload.rndSeed = rndSeed + 1; payload.anyHit = false; TraceRay(pRayTracingParams.scene, 0, 0xff, 0, 0, 0, rayDesc, payload); if(pSamps.pass == 0) pRayTracingParams.radianceAccumulator[pix] = float4(0); - pRayTracingParams.radianceAccumulator[pix] += float4(payload.light / pSamps.samplesPerPixel, 0); - pRayTracingParams.image[pix] = float4(clamp(pRayTracingParams.radianceAccumulator[pix].xyz, 0, 1), 1); + float3 accumulatedRadiance = payload.light / pSamps.samplesPerPixel; + pRayTracingParams.radianceAccumulator[pix] += float4(accumulatedRadiance, 0); + float3 compensatedRadiance = pRayTracingParams.radianceAccumulator[pix].xyz * pSamps.samplesPerPixel / (pSamps.pass + 1); + pRayTracingParams.image[pix] = float4(clamp(compensatedRadiance, 0, 1), 1); } diff --git a/res/shaders/raytracing/RayTracingData.slang b/res/shaders/raytracing/RayTracingData.slang index ea7ba9a..bd81488 100644 --- a/res/shaders/raytracing/RayTracingData.slang +++ b/res/shaders/raytracing/RayTracingData.slang @@ -25,6 +25,7 @@ struct RayPayload uint depth; bool hit; bool anyHit; + uint3 rndSeed; }; float3 rand01(uint3 x){ // pseudo-random number generator diff --git a/src/Editor/Asset/FontLoader.cpp b/src/Editor/Asset/FontLoader.cpp index 7291c82..1887a90 100644 --- a/src/Editor/Asset/FontLoader.cpp +++ b/src/Editor/Asset/FontLoader.cpp @@ -32,9 +32,9 @@ void FontLoader::importAsset(FontImportArgs args) { // so we create a single pixel empty texture void FontLoader::import(FontImportArgs args, PFontAsset asset) { std::ifstream stream(args.filePath.c_str(), std::ios::binary | std::ios::ate); - Array ttfFile(stream.tellg()); + Array ttfFile(stream.tellg()); stream.seekg(0); - stream.read((char*)ttfFile.data(), ttfFile.size()); + stream.read(ttfFile.data(), ttfFile.size()); asset->ttfFile = std::move(ttfFile); asset->graphics = graphics; asset->loadFace(); diff --git a/src/Editor/Asset/MeshLoader.cpp b/src/Editor/Asset/MeshLoader.cpp index 5bf822b..8136c0f 100644 --- a/src/Editor/Asset/MeshLoader.cpp +++ b/src/Editor/Asset/MeshLoader.cpp @@ -92,6 +92,8 @@ constexpr const char* KEY_AMBIENT_COLOR = "k_a"; constexpr const char* KEY_SHININESS = "k_shiny"; constexpr const char* KEY_ROUGHNESS = "k_r"; constexpr const char* KEY_METALLIC = "k_m"; +constexpr const char* KEY_EMISSIVE_COLOR = "k_e"; +constexpr const char* KEY_EMISSIVE_INTENSITY = "k_ei"; constexpr const char* KEY_DIFFUSE_TEXTURE = "tex_d"; constexpr const char* KEY_SPECULAR_TEXTURE = "tex_s"; @@ -101,6 +103,7 @@ constexpr const char* KEY_SHININESS_TEXTURE = "tex_shiny"; constexpr const char* KEY_ROUGHNESS_TEXTURE = "tex_r"; constexpr const char* KEY_METALLIC_TEXTURE = "tex_m"; constexpr const char* KEY_AMBIENT_OCCLUSION_TEXTURE = "tex_ao"; +constexpr const char* KEY_EMISSIVE_TEXTURE = "tex_emissive"; void MeshLoader::loadMaterials(const aiScene* scene, const Array& textures, const std::string& baseName, const std::filesystem::path& meshDirectory, const std::string& importPath, @@ -353,9 +356,19 @@ void MeshLoader::loadMaterials(const aiScene* scene, const Array& addTextureParameter(KEY_AMBIENT_OCCLUSION_TEXTURE, aiTextureType_AMBIENT_OCCLUSION, i, outputAO, {0, -1, -1, -1}); } + // Emissive + //addScalarParameter(KEY_EMISSIVE_INTENSITY, AI_MATKEY_EMISSIVE_INTENSITY); + addVectorParameter(KEY_EMISSIVE_COLOR, AI_MATKEY_COLOR_EMISSIVE); + std::string outputEmissive = KEY_EMISSIVE_COLOR; + uint32 numEmissive = material->GetTextureCount(aiTextureType_EMISSION_COLOR); + for (uint32 i = 0; i < numEmissive; ++i) { + addTextureParameter(KEY_EMISSIVE_TEXTURE, aiTextureType_EMISSION_COLOR, i, outputEmissive); + } + MaterialNode brdf; brdf.variables["baseColor"] = outputDiffuse; brdf.variables["alpha"] = outputAlpha; + brdf.variables["emissive"] = outputEmissive; if (!outputNormal.empty()) { expressions.add(new MulExpression()); expressions.back()->key = "NormalMul"; @@ -381,6 +394,13 @@ void MeshLoader::loadMaterials(const aiScene* scene, const Array& case aiShadingMode_Toon: brdf.profile = "CelShading"; break; + case aiShadingMode_Blinn: + brdf.profile = "BlinnPhong"; + brdf.variables["specularColor"] = outputSpecular; + brdf.variables["ambient"] = outputAmbient; + brdf.variables["shininess"] = outputShininess; + break; + default: case aiShadingMode_CookTorrance: brdf.profile = "CookTorrance"; brdf.variables["roughness"] = outputRoughness; @@ -389,13 +409,6 @@ void MeshLoader::loadMaterials(const aiScene* scene, const Array& brdf.variables["ambientOcclusion"] = outputAmbient; } break; - default: - case aiShadingMode_Blinn: - brdf.profile = "BlinnPhong"; - brdf.variables["specularColor"] = outputSpecular; - brdf.variables["ambient"] = outputAmbient; - brdf.variables["shininess"] = outputShininess; - break; }; uint32 twoSided = false; float opacity = 1.0f; diff --git a/src/Editor/Asset/TextureLoader.cpp b/src/Editor/Asset/TextureLoader.cpp index c92cc28..8330c32 100644 --- a/src/Editor/Asset/TextureLoader.cpp +++ b/src/Editor/Asset/TextureLoader.cpp @@ -111,12 +111,12 @@ void TextureLoader::import(TextureImportArgs args, PTextureAsset textureAsset) { ktxBasisParams basisParams = { .structSize = sizeof(ktxBasisParams), .uastc = true, - .threadCount = 14, + .threadCount = 1, .uastcFlags = KTX_PACK_UASTC_LEVEL_FASTEST, .uastcRDO = true, }; - //KTX_ASSERT(ktxTexture2_CompressBasisEx(kTexture, &basisParams)); - //KTX_ASSERT(ktxTexture2_DeflateZstd(kTexture, 20)); + // KTX_ASSERT(ktxTexture2_CompressBasisEx(kTexture, &basisParams)); + // KTX_ASSERT(ktxTexture2_DeflateZstd(kTexture, 20)); char writer[100]; snprintf(writer, sizeof(writer), "%s version %s", "SeeleEngine", "0.0.1"); diff --git a/src/Editor/Window/InspectorView.cpp b/src/Editor/Window/InspectorView.cpp index 06b22a9..37dcb25 100644 --- a/src/Editor/Window/InspectorView.cpp +++ b/src/Editor/Window/InspectorView.cpp @@ -15,9 +15,9 @@ InspectorView::InspectorView(Gfx::PGraphics graphics, PWindow window, const View new UI::Div(UI::Attributes{}, { new UI::Div({}, { - new UI::Text("OtherTestT"), + new UI::Text("Other Test T"), }), - new UI::Text("Test"), + new UI::Text("Test fasdf agll;sdfgkj ahsf"), }))) { renderGraph.addPass(new UIPass(graphics, uiSystem)); renderGraph.setViewport(viewport); diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 69e18b8..7a09734 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -54,6 +54,8 @@ Array generateEdges() { return edges; } +Array generateArray() { return Array(); } + int main() { std::string gameName = "MeshShadingDemo"; #ifdef WIN32 @@ -113,26 +115,22 @@ int main() { // AssetImporter::importTexture(TextureImportArgs{ // .filePath = sourcePath / "import/textures/wgen.png", //}); - // AssetImporter::importMesh(MeshImportArgs{ + //AssetImporter::importMesh(MeshImportArgs{ // .filePath = sourcePath / "import/models/after-the-rain-vr-sound/source/Whitechapel.glb", // .importPath = "Whitechapel", //}); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/plane.obj", - // .importPath = "", - // }); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/city-suburbs/source/city-suburbs.obj", - // .importPath = "suburbs", - // }); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/minecraft-medieval-city.fbx", - // .importPath = "minecraft", - // }); - // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/Volvo/Volvo.fbx", - // .importPath = "Volvo", - // }); + AssetImporter::importMesh(MeshImportArgs{ + .filePath = sourcePath / "import/models/box.glb", + .importPath = "", + }); + AssetImporter::importMesh(MeshImportArgs{ + .filePath = sourcePath / "import/models/rttest.glb", + .importPath = "", + }); + AssetImporter::importMesh(MeshImportArgs{ + .filePath = sourcePath / "import/models/town_hall.glb", + .importPath = "", + }); getThreadPool().waitIdle(); vd->commitMeshes(); WindowCreateInfo mainWindowInfo = { @@ -142,26 +140,26 @@ int main() { .preferredFormat = Gfx::SE_FORMAT_B8G8R8A8_SRGB, }; auto window = windowManager->addWindow(graphics, mainWindowInfo); - // ViewportCreateInfo sceneViewInfo = { - // .dimensions = - // { - // .size = {1920, 1080}, - // .offset = {0, 0}, - // }, - // .numSamples = Gfx::SE_SAMPLE_COUNT_4_BIT, - // }; - // OGameView sceneView = new Editor::PlayView(graphics, window, sceneViewInfo, binaryPath.generic_string()); - // sceneView->setFocused(); - OInspectorView inspectorView = new Editor::InspectorView(graphics, window, - ViewportCreateInfo{ - .dimensions = - { - .size = {1920, 1080}, - .offset = {0, 0}, - }, - .fieldOfView = 0, - .numSamples = Gfx::SE_SAMPLE_COUNT_1_BIT, - }); + ViewportCreateInfo sceneViewInfo = { + .dimensions = + { + .size = {1920, 1080}, + .offset = {0, 0}, + }, + .numSamples = Gfx::SE_SAMPLE_COUNT_4_BIT, + }; + OGameView sceneView = new Editor::PlayView(graphics, window, sceneViewInfo, binaryPath.generic_string()); + sceneView->setFocused(); + //OInspectorView inspectorView = new Editor::InspectorView(graphics, window, + // ViewportCreateInfo{ + // .dimensions = + // { + // .size = {1920, 1080}, + // .offset = {0, 0}, + // }, + // .fieldOfView = 0, + // .numSamples = Gfx::SE_SAMPLE_COUNT_1_BIT, + // }); while (windowManager->isActive() && getGlobals().running) { windowManager->render(); diff --git a/src/Engine/Asset/FontAsset.cpp b/src/Engine/Asset/FontAsset.cpp index 0acb33d..d0676c9 100644 --- a/src/Engine/Asset/FontAsset.cpp +++ b/src/Engine/Asset/FontAsset.cpp @@ -12,8 +12,11 @@ FontAsset::FontAsset(std::string_view folderPath, std::string_view name) : Asset } FontAsset::~FontAsset() { - FT_Done_Face(face); + FT_Done_Face(ft_face); FT_Done_FreeType(ft); + hb_font_destroy(hb_font); + hb_face_destroy(hb_face); + hb_blob_destroy(blob); } void FontAsset::save(ArchiveBuffer& buffer) const { Serialization::save(buffer, ttfFile); } @@ -25,23 +28,57 @@ void FontAsset::load(ArchiveBuffer& buffer) { } void FontAsset::loadFace() { - FT_Error error = FT_New_Memory_Face(ft, ttfFile.data(), ttfFile.size(), 0, &face); + FT_Error error = FT_New_Memory_Face(ft, (unsigned char*)ttfFile.data(), ttfFile.size(), 0, &ft_face); assert(!error); + blob = hb_blob_create(ttfFile.data(), ttfFile.size(), HB_MEMORY_MODE_DUPLICATE, nullptr, nullptr); + hb_face = hb_face_create(blob, 0); + hb_font = hb_font_create(hb_face); +} +UVector2 FontAsset::shapeText(std::string_view view, uint32 fontSize, Array& render) { + loadGlyphs(fontSize); + hb_buffer_t* buf = hb_buffer_create(); + hb_buffer_add_utf8(buf, view.data(), view.size(), 0, -1); + hb_buffer_set_language(buf, hb_language_from_string("en", -1)); + hb_buffer_set_script(buf, HB_SCRIPT_LATIN); + hb_buffer_set_direction(buf, HB_DIRECTION_LTR); + hb_shape(hb_font, buf, nullptr, 0); + uint32 num_glyphs; + hb_glyph_info_t* glyphs = hb_buffer_get_glyph_infos(buf, &num_glyphs); + hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buf, &num_glyphs); + Vector2 min = Vector2(std::numeric_limits::max()); + Vector2 max = Vector2(std::numeric_limits::lowest()); + int32 xppem, yppem; + hb_font_get_scale(hb_font, &xppem, &yppem); + Vector2 cursor = Vector2(0); + for (uint32 i = 0; i < num_glyphs; ++i) { + hb_codepoint_t glyphid = glyphs[i].codepoint; + float xOffset = positions[i].x_offset * fontSize / xppem; + float yOffset = positions[i].y_offset * fontSize / yppem; + float xAdvance = positions[i].x_advance * fontSize / xppem; + float yAdvance = positions[i].y_advance * fontSize / yppem; + Vector2 position = cursor + Vector2(xOffset, yOffset); + Vector2 dimensions = fontSizes[fontSize].glyphs[glyphid].size; + render.add(RenderGlyph{ + .texture = fontSizes[fontSize].glyphs[glyphid].texture, + .position = position, + .dimensions = dimensions, + }); + cursor += Vector2(xAdvance, yAdvance); + min = Vector2(std::min(min.x, position.x), std::min(min.y, position.y)); + max = Vector2(std::max(max.x, position.x + dimensions.x), std::max(min.y, position.y + dimensions.y)); + } + hb_buffer_destroy(buf); + return UVector2(max - min); } -void FontAsset::loadFontSize(uint32 fontSize) { - FT_Set_Pixel_Sizes(face, 0, fontSize); - fontSizes[fontSize].ascent = face->ascender; - fontSizes[fontSize].descent = face->descender; - fontSizes[fontSize].linegap = 0; - for (uint32 c = 0; c < 256; ++c) { - if (FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_COLOR)) { - continue; - } - FontAsset::Glyph& glyph = fontSizes[fontSize].glyphs[c]; - glyph.size = IVector2(face->glyph->bitmap.width, face->glyph->bitmap.rows); - glyph.bearing = IVector2(face->glyph->bitmap_left, face->glyph->bitmap_top); - glyph.advance = face->glyph->advance.x; +void FontAsset::loadGlyphs(uint32 fontSize) { + FT_Set_Pixel_Sizes(ft_face, 0, fontSize); + for (uint32 i = 0; i < ft_face->num_glyphs; ++i) { + assert(FT_Load_Glyph(ft_face, i, FT_LOAD_RENDER | FT_LOAD_COLOR) == 0); + FontAsset::Glyph& glyph = fontSizes[fontSize].glyphs[i]; + glyph.size = IVector2(ft_face->glyph->bitmap.width, ft_face->glyph->bitmap.rows); + glyph.bearing = IVector2(ft_face->glyph->bitmap_left, ft_face->glyph->bitmap_top); + glyph.advance = ft_face->glyph->advance.x; glyph.textureData.resize(glyph.size.x * glyph.size.y); if (glyph.textureData.size() == 0) { glyph.size.x = 1; @@ -49,7 +86,7 @@ void FontAsset::loadFontSize(uint32 fontSize) { glyph.textureData.add(0); // load a single transparent pixel, so that we dont have to handle empty textures later } else { - std::memcpy(glyph.textureData.data(), face->glyph->bitmap.buffer, glyph.textureData.size()); + std::memcpy(glyph.textureData.data(), ft_face->glyph->bitmap.buffer, glyph.textureData.size()); } glyph.texture = graphics->createTexture2D(TextureCreateInfo{ .sourceData = diff --git a/src/Engine/Asset/FontAsset.h b/src/Engine/Asset/FontAsset.h index 13ac6aa..d91055c 100644 --- a/src/Engine/Asset/FontAsset.h +++ b/src/Engine/Asset/FontAsset.h @@ -4,6 +4,7 @@ #include "Graphics/Texture.h" #include "Math/Math.h" #include +#include namespace Seele { class FontAsset : public Asset { @@ -25,25 +26,26 @@ class FontAsset : public Asset { void load(ArchiveBuffer& buffer); }; struct FontData { - int32 ascent; - int32 descent; - int32 linegap; Map glyphs; }; - const Glyph& getGlyphData(char c, uint32 fontSize) { - if (!fontSizes.contains(fontSize)) - loadFontSize(fontSize); - return fontSizes[fontSize].glyphs[c]; - } - const FontData& getFontData(uint32 fontSize) { return fontSizes[fontSize]; } void loadFace(); + struct RenderGlyph { + Gfx::PTexture2D texture; + UVector2 position; + UVector2 dimensions; + }; + UVector2 shapeText(std::string_view view, uint32 fontSize, Array& render); + private: - void loadFontSize(uint32 fontSize); + void loadGlyphs(uint32 fontSize); Gfx::PGraphics graphics; + hb_blob_t* blob; + hb_face_t* hb_face; + hb_font_t* hb_font; FT_Library ft; - FT_Face face; - Array ttfFile; + FT_Face ft_face; + Array ttfFile; Map fontSizes; friend class FontLoader; }; diff --git a/src/Engine/Component/Camera.cpp b/src/Engine/Component/Camera.cpp index 6c1940b..f0a78d3 100644 --- a/src/Engine/Component/Camera.cpp +++ b/src/Engine/Component/Camera.cpp @@ -44,7 +44,7 @@ void Camera::moveY(float amount) { } void Camera::buildViewMatrix() { - Vector eyePos = getTransform().getPosition() + Vector(0, 0, -5); + Vector eyePos = getTransform().getPosition(); Vector lookAt = eyePos + getTransform().getForward(); viewMatrix = glm::lookAt(eyePos, lookAt, Vector(0, 1, 0)); cameraPos = eyePos; diff --git a/src/Engine/Concepts.h b/src/Engine/Concepts.h index 8dc870a..81b20d6 100644 --- a/src/Engine/Concepts.h +++ b/src/Engine/Concepts.h @@ -1,7 +1,12 @@ #pragma once #include +#include namespace Seele { + +template +concept container_compatible_range = (std::ranges::input_range) && std::convertible_to, T>; + template concept invocable = std::invocable; diff --git a/src/Engine/Containers/Array.h b/src/Engine/Containers/Array.h index 5ac7983..0e166e8 100644 --- a/src/Engine/Containers/Array.h +++ b/src/Engine/Containers/Array.h @@ -1,5 +1,6 @@ #pragma once #include "EngineTypes.h" +#include "Concepts.h" #include #include #include @@ -117,7 +118,20 @@ template > s assert(_data != nullptr); std::uninitialized_copy(init.begin(), init.end(), begin()); } - + template Range> + constexpr Array(std::from_range_t, Range&& rg, const allocator_type& alloc = allocator_type()) + : arraySize(0), allocated(0), allocator(alloc) { + if constexpr (std::ranges::sized_range || std::ranges::forward_range) { + arraySize = std::ranges::size(rg); + allocated = arraySize; + _data = allocateArray(allocated); + std::ranges::uninitialized_copy(rg.begin(), rg.end(), begin(), end()); + } else { + for (auto it = std::ranges::begin(rg); it != std::ranges::end(rg); it++) { + add(*it); + } + } + } constexpr Array(const Array& other) : arraySize(other.arraySize), allocated(other.allocated), allocator(std::allocator_traits::select_on_container_copy_construction(other.allocator)) { diff --git a/src/Engine/Graphics/RenderPass/UIPass.cpp b/src/Engine/Graphics/RenderPass/UIPass.cpp index b97d99c..cb22350 100644 --- a/src/Engine/Graphics/RenderPass/UIPass.cpp +++ b/src/Engine/Graphics/RenderPass/UIPass.cpp @@ -58,40 +58,21 @@ void UIPass::beginFrame(const Component::Camera& cam) { for (auto& render : renderElements) { float x = render.position.x; float y = render.position.y; - if (render.text.empty()) { - //todo: convert using actual tree depth - elements.add(RenderElementStyle{ - .x = x, - .y = y, - .w = render.dimensions.x, - .h = render.dimensions.y, - .color = render.backgroundColor, - .z = render.level / 100.f, - }); - } else { - y = y + render.baseline; - for (uint32 c : render.text) { - const FontAsset::Glyph& glyph = render.font->getGlyphData(c, render.fontSize); - Vector2 bearing = Vector2(glyph.bearing); - Vector2 size = Vector2(glyph.size); - float xpos = x + glyph.bearing.x; - float ypos = y - bearing.y; - - float w = size.x; - float h = size.y; - - glyphs.add(GlyphInstanceData{ - .x = xpos, - .y = ypos, - .z = render.level / 100.0f, - .width = w, - .height = h, - .glyphIndex = (uint32)usedTextures.size(), - }); - usedTextures.add(glyph.texture); - x += glyph.advance / 64.0f; - } + //todo: convert using actual tree depth + uint32 textureIndex = -1ul; + if (render.backgroundTexture != nullptr) { + textureIndex = usedTextures.size(); + usedTextures.add(render.backgroundTexture); } + elements.add(RenderElementStyle{ + .x = x, + .y = y, + .w = render.dimensions.x, + .h = render.dimensions.y, + .color = render.backgroundColor, + .z = render.level / 100.f, + .textureIndex = textureIndex, + }); } glyphInstanceBuffer->rotateBuffer(sizeof(GlyphInstanceData) * glyphs.size()); glyphInstanceBuffer->updateContents(0, sizeof(GlyphInstanceData) * glyphs.size(), glyphs.data()); @@ -115,6 +96,9 @@ void UIPass::beginFrame(const Component::Camera& cam) { uiDescriptorSet = uiDescriptorLayout->allocateDescriptorSet(); uiDescriptorSet->updateBuffer(0, 0, elementBuffer); uiDescriptorSet->updateSampler(1, 0, glyphSampler); + for (uint32 i = 0; i < usedTextures.size(); ++i) { + uiDescriptorSet->updateTexture(2, i, usedTextures[i]); + } uiDescriptorSet->writeChanges(); } diff --git a/src/Engine/Graphics/Vulkan/RayTracing.cpp b/src/Engine/Graphics/Vulkan/RayTracing.cpp index 4a2c9ee..e9370e7 100644 --- a/src/Engine/Graphics/Vulkan/RayTracing.cpp +++ b/src/Engine/Graphics/Vulkan/RayTracing.cpp @@ -122,7 +122,6 @@ TopLevelAS::TopLevelAS(PGraphics graphics, const Gfx::TopLevelASCreateInfo& crea .flags = 0, .size = buildSizesInfo.accelerationStructureSize, .usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - }, VmaAllocationCreateInfo{ .usage = VMA_MEMORY_USAGE_AUTO, @@ -156,7 +155,7 @@ TopLevelAS::TopLevelAS(PGraphics graphics, const Gfx::TopLevelASCreateInfo& crea VkAccelerationStructureBuildGeometryInfoKHR buildGeometry = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR, .pNext = nullptr, - .flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR, + .flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR, .mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, .dstAccelerationStructure = handle, .geometryCount = 1, diff --git a/src/Engine/UI/Element.h b/src/Engine/UI/Element.h index ef12cad..bd3922d 100644 --- a/src/Engine/UI/Element.h +++ b/src/Engine/UI/Element.h @@ -7,14 +7,10 @@ namespace Seele { namespace UI { struct UIRender { - std::string text; - PFontAsset font; - uint32 fontSize; - Vector textColor; + Gfx::PTexture2D backgroundTexture; Vector backgroundColor; Vector2 position; Vector2 dimensions; - uint32 baseline; uint32 z; uint32 level; }; @@ -45,15 +41,15 @@ class Element { } else if (style.widthType == DimensionType::Percent) { dimensions.x = parentSize.x * style.width / 100.0f; } - - for (auto& child : children) { - child->layout(maxDimensions - Vector2(style.marginLeft + style.marginRight, style.marginTop + style.marginBottom)); - } if (style.heightType == DimensionType::Pixel) { dimensions.y = style.height; } else if (style.heightType == DimensionType::Percent) { dimensions.y = parentSize.y * style.height / 100.0f; } + + for (auto& child : children) { + child->layout(maxDimensions - Vector2(style.marginLeft + style.marginRight, style.marginTop + style.marginBottom)); + } switch (style.innerDisplay) { case InnerDisplayType::Flow: flowLayout(); @@ -73,7 +69,7 @@ class Element { // create a new line in case the element doesnt fit on the current one anymore, // but keep in current line in case we are still at the start, as a new line wouldnt help here if (cursor.x + child->dimensions.x > maxDimensions.x - child->style.left && cursor.x != 0) { - cursor.x = 0; + cursor.x = style.paddingLeft; cursor.y += lineHeight; lineHeight = 0; } @@ -94,7 +90,7 @@ class Element { if (child->style.position == PositionType::Static || child->style.position == PositionType::Relative) { // create a new line in case we are not already at one if (cursor.x != 0) { - cursor.x = 0; + cursor.x = style.paddingLeft; cursor.y += lineHeight; } child->position.x = cursor.x + child->style.marginLeft; diff --git a/src/Engine/UI/Element/Text.h b/src/Engine/UI/Element/Text.h index b53cb47..f08fb96 100644 --- a/src/Engine/UI/Element/Text.h +++ b/src/Engine/UI/Element/Text.h @@ -1,6 +1,7 @@ #pragma once #include "UI/Element.h" #include "UI/Style/Class.h" +#include namespace Seele { namespace UI { @@ -13,31 +14,24 @@ template class Text : public Element { (classes::apply(style), ...); } virtual void layout(UVector2 parentSize) override { - size_t cursor = 0; - for (uint32 i = 0; i < text.size() - 1; ++i) { - dimensions.x += style.fontFamily->getGlyphData(text[i], style.fontSize).advance / 64.0f; - } - dimensions.x += style.fontFamily->getGlyphData(text[text.size() - 1], style.fontSize).size.x; - dimensions.y = style.fontSize; + UVector2 cursor = UVector2(0); + dimensions = style.fontFamily->shapeText(text, style.fontSize, glyphRenders); } virtual Array render(Vector2 anchor, uint32 level) override { - return { - UIRender{ - .text = text, - .font = style.fontFamily, - .fontSize = style.fontSize, - .position = position + anchor, - .dimensions = dimensions, - .baseline = style.fontSize * 3 / 4, // TODO: improve - .z = style.z, - .level = level, - }, - }; + return Array(std::from_range, glyphRenders | std::views::transform([=](const FontAsset::RenderGlyph& glyph) { + return UIRender{ + .backgroundTexture = glyph.texture, + .position = Vector2(glyph.position) + anchor, + .dimensions = Vector2(glyph.dimensions), + .level = level, + }; + })); } // height = fontsize * 1.12 private: std::string text; + Array glyphRenders; }; } // namespace UI } // namespace Seele \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 9fc0b26..e5182c0 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,6 +12,8 @@ "fmt", "gtest", "vulkan-memory-allocator", - "lunasvg" + "lunasvg", + "harfbuzz", + "shader-slang" ] } \ No newline at end of file