From 1393f1b77aeff97b0330dfd6c1bd613c74d2c4e4 Mon Sep 17 00:00:00 2001 From: mrq Date: Sun, 18 Oct 2020 00:00:00 -0500 Subject: [PATCH] Commit for 2020.10.18.7z --- .../shaders/display.rendertarget.frag.glsl | 6 +- .../shaders/display.rendertarget.vert.glsl | 4 +- bin/data/shaders/display.subpass.frag.glsl | 280 +--- bin/data/shaders/gui.frag.glsl | 3 +- bin/data/shaders/gui.gradient.frag.glsl | 38 - bin/data/shaders/gui.stereo.vert.glsl | 1 + bin/data/shaders/gui.text.frag.glsl | 1 + bin/data/shaders/gui.text.stereo.vert.glsl | 1 + bin/data/shaders/gui.text.vert.glsl | 1 + bin/data/shaders/gui.vert.glsl | 1 + bin/data/shaders/mcdonalds.frag.glsl | 39 + client/main.cpp | 16 +- engine/inc/uf/engine/object/object.h | 3 + engine/inc/uf/ext/ext.h | 2 + engine/inc/uf/ext/vulkan/buffer.h | 6 +- engine/inc/uf/ext/vulkan/device.h | 24 +- engine/inc/uf/ext/vulkan/graphic.inl | 2 +- engine/inc/uf/ext/vulkan/rendertarget.h | 1 + engine/inc/uf/ext/vulkan/texture.h | 50 +- engine/inc/uf/ext/vulkan/vulkan.h | 3 + engine/inc/uf/utils/graphic/mesh.h | 22 - engine/inc/uf/utils/image/atlas.h | 1 + engine/inc/uf/utils/string/ext.h | 1 + engine/src/engine/object/behaviors/gltf.cpp | 95 +- engine/src/engine/object/object.cpp | 55 +- engine/src/ext/gltf/gltf.cpp | 9 + engine/src/ext/openvr/openvr.cpp | 2 +- engine/src/ext/vulkan/device.cpp | 165 +- engine/src/ext/vulkan/graphic.cpp | 113 +- engine/src/ext/vulkan/rendermode.cpp | 15 +- engine/src/ext/vulkan/rendermodes/compute.cpp | 2 +- .../src/ext/vulkan/rendermodes/deferred.cpp | 44 +- .../ext/vulkan/rendermodes/rendertarget.cpp | 2 +- engine/src/ext/vulkan/rendertarget.cpp | 16 +- engine/src/ext/vulkan/texture.cpp | 337 ++-- engine/src/ext/vulkan/vulkan.cpp | 26 +- engine/src/utils/image/atlas.cpp | 11 +- engine/src/utils/string/ext.cpp | 73 + ext/behaviors/light/behavior.cpp | 1 + ext/behaviors/portal/behavior.cpp | 1 + ext/behaviors/scene/behavior.cpp | 18 +- ext/behaviors/staticemitter/behavior.cpp | 30 +- ext/gui/behavior.cpp | 1478 ++++++++--------- ext/gui/gui.h | 27 +- ext/gui/manager/behavior.cpp | 204 +++ ext/gui/manager/behavior.h | 17 + ext/main.cpp | 41 +- ext/scenes/raytrace/behavior.cpp | 2 +- ext/scenes/worldscape/behavior.cpp | 8 +- ext/scenes/worldscape/gui/battle.cpp | 46 +- ext/scenes/worldscape/gui/dialogue.cpp | 4 +- ext/scenes/worldscape/gui/pause/behavior.cpp | 39 +- ext/scenes/worldscape/housamo/battle.cpp | 28 +- ext/scenes/worldscape/housamo/dialogue.cpp | 10 +- ext/scenes/worldscape/terrain/behavior.cpp | 98 +- ext/scenes/worldscape/terrain/generator.cpp | 7 +- ext/scenes/worldscape/terrain/generator.h | 2 +- ext/scenes/worldscape/terrain/region.cpp | 27 +- program.sh | 4 +- 59 files changed, 2147 insertions(+), 1416 deletions(-) delete mode 100644 bin/data/shaders/gui.gradient.frag.glsl create mode 100644 bin/data/shaders/mcdonalds.frag.glsl create mode 100644 ext/gui/manager/behavior.cpp create mode 100644 ext/gui/manager/behavior.h diff --git a/bin/data/shaders/display.rendertarget.frag.glsl b/bin/data/shaders/display.rendertarget.frag.glsl index 569a23b4..f817f3d0 100644 --- a/bin/data/shaders/display.rendertarget.frag.glsl +++ b/bin/data/shaders/display.rendertarget.frag.glsl @@ -21,20 +21,20 @@ layout (location = 1) out vec4 outNormal; layout (location = 2) out vec4 outPosition; void main() { - outAlbedoSpecular = texture(sampler2D(albedoTexture, samp), inUv); if ( inCursor.radius.x <= 0 && inCursor.radius.y <= 0 ) { vec2 uv = gl_FragCoord.xy / textureSize(albedoTexture, 0); - // uv.x = 1-uv.x; outAlbedoSpecular = texture(sampler2D(albedoTexture, samp), uv); outNormal = texture(sampler2D(normalTexture, samp), uv); outPosition = texture(sampler2D(positionTexture, samp), uv); - if ( outAlbedoSpecular.a < 0.01f ) outAlbedoSpecular = vec4(0,0,0,1); + // if ( outAlbedoSpecular.a < 0.01f ) outAlbedoSpecular = vec4(0,0,0,1); return; } + outAlbedoSpecular = texture(sampler2D(albedoTexture, samp), inUv); float dist = pow(inUv.x - inCursor.position.x, 2) / pow(inCursor.radius.x, 2) + pow(inUv.y - inCursor.position.y, 2) / pow(inCursor.radius.y, 2); if ( dist <= 1 ) { float attenuation = dist; outAlbedoSpecular.rgb = mix( inCursor.color.rgb * inCursor.color.a, outAlbedoSpecular.rgb, attenuation ); } + if ( inAlpha < 1.0 ) outAlbedoSpecular.a = inAlpha; } \ No newline at end of file diff --git a/bin/data/shaders/display.rendertarget.vert.glsl b/bin/data/shaders/display.rendertarget.vert.glsl index 651c07f7..1141c455 100644 --- a/bin/data/shaders/display.rendertarget.vert.glsl +++ b/bin/data/shaders/display.rendertarget.vert.glsl @@ -27,12 +27,14 @@ struct Matrices { layout (binding = 0) uniform UBO { Matrices matrices; Cursor cursor; - vec2 alpha; +// float alpha; +// float padding; } ubo; void main() { outUv = inUv; outCursor = ubo.cursor; + outAlpha = 1; gl_Position = ubo.matrices.model[PushConstant.pass] * vec4(inPos.xy, 0.0, 1.0); } \ No newline at end of file diff --git a/bin/data/shaders/display.subpass.frag.glsl b/bin/data/shaders/display.subpass.frag.glsl index 1aa37978..ac88c716 100644 --- a/bin/data/shaders/display.subpass.frag.glsl +++ b/bin/data/shaders/display.subpass.frag.glsl @@ -7,7 +7,11 @@ layout (input_attachment_index = 0, binding = 2) uniform subpassInput samplerNor layout (input_attachment_index = 0, binding = 3) uniform subpassInput samplerPosition; // layout (input_attachment_index = 0, binding = 4) uniform subpassInput samplerDepth; layout (binding = 5) uniform sampler2D samplerShadows[LIGHTS]; - +/* +layout (std140, binding = 6) buffer Palette { + vec4 palette[]; +}; +*/ layout (location = 0) in vec2 inUv; layout (location = 1) in flat uint inPushConstantPass; @@ -47,6 +51,7 @@ struct Light { float power; int type; float depthBias; + vec2 padding; mat4 view; mat4 projection; }; @@ -62,13 +67,15 @@ struct Space { } position, normal, view; struct Fog { - vec2 range; vec4 color; + vec2 range; + vec2 padding; }; struct Mode { uint type; uint scalar; + vec2 padding; vec4 parameters; }; @@ -188,7 +195,6 @@ vec3 hslToRgb(vec3 HSL) { float C = (1 - abs(2 * HSL.z - 1)) * HSL.y; return (RGB - 0.5) * C + HSL.z; } - vec3 rgbToHsl(vec3 RGB) { float Epsilon = 1e-10; vec3 HCV; { @@ -209,7 +215,6 @@ float hueDistance(float h1, float h2) { } const float lightnessSteps = 4.0; float lightnessStep(float l) { - /* Quantize the lightness to one of `lightnessSteps` values */ return floor((0.5 + l * lightnessSteps)) / lightnessSteps; } const int indexMatrix16x16[256] = int[](0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255, @@ -228,54 +233,11 @@ const int indexMatrix16x16[256] = int[](0,192, 48,240, 12,204, 60,252, 3,195, 5 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117, 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213, 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85); -float indexValue16x16() { - int x = int(mod(gl_FragCoord.x, 16)); - int y = int(mod(gl_FragCoord.y, 16)); +float indexValue16x16( float scale ) { + int x = int(mod(gl_FragCoord.x * scale, 16)); + int y = int(mod(gl_FragCoord.y * scale, 16)); return indexMatrix16x16[(x + y * 16)] / 256.0; } -const int paletteSize = 40; -const vec3 palette16x16[paletteSize] = vec3[]( - vec3( 0, 0, 0), - vec3( 0, 0, 0.502), - vec3( 0, 0, 0.753), - vec3( 0, 0, 1), - vec3( 0, 1, 0.251), - vec3( 0, 1, 0.50), - vec3( 24.0/360.0, 1, 0.251), - vec3( 24.0/360.0, 1, 0.5), - vec3( 24.0/360.0, 1, 0.625), - vec3( 24.0/360.0, 1, 0.751), - vec3( 30.0/360.0, 1, 0.251), - vec3( 60.0/360.0, 0.333, 0.376), - vec3( 60.0/360.0, 1, 0.251), - vec3( 60.0/360.0, 1, 0.5), - vec3( 60.0/360.0, 1, 0.751), - vec3(120.0/360.0, 1, 0.251), - vec3(120.0/360.0, 1, 0.5), - vec3(120.0/360.0, 1, 0.751), - vec3(150.0/360.0, 1, 0.251), - vec3(150.0/360.0, 1, 0.5), - vec3(150.0/360.0, 1, 0.751), - vec3(180.0/360.0, 1, 0.125), - vec3(180.0/360.0, 1, 0.251), - vec3(180.0/360.0, 1, 0.5), - vec3(180.0/360.0, 1, 0.751), - vec3(210.0/360.0, 1, 0.251), - vec3(210.0/360.0, 1, 0.5), - vec3(210.0/360.0, 1, 0.751), - vec3(240.0/360.0, 1, 0.251), - vec3(240.0/360.0, 1, 0.5), - vec3(240.0/360.0, 1, 0.751), - vec3(270.0/360.0, 1, 0.251), - vec3(270.0/360.0, 1, 0.5), - vec3(270.0/360.0, 1, 0.751), - vec3(300.0/360.0, 1, 0.251), - vec3(300.0/360.0, 1, 0.5), - vec3(300.0/360.0, 1, 0.751), - vec3(330.0/360.0, 1, 0.251), - vec3(330.0/360.0, 1, 0.5), - vec3(330.0/360.0, 1, 0.751) -); const int indexMatrix8x8[64] = int[](0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46, 6, 38, @@ -284,90 +246,28 @@ const int indexMatrix8x8[64] = int[](0, 32, 8, 40, 2, 34, 10, 42, 51, 19, 59, 27, 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21); -float indexValue8x8() { - int x = int(mod(gl_FragCoord.x, 8)); - int y = int(mod(gl_FragCoord.y, 8)); +float indexValue8x8( float scale ) { + int x = int(mod(gl_FragCoord.x * scale, 8)); + int y = int(mod(gl_FragCoord.y * scale, 8)); return indexMatrix8x8[(x + y * 8)] / 64.0; } -const int palette8x8Size = 28; -const vec3 palette8x8[28] = vec3[]( - vec3(0, 0, 0), - vec3(0, 0, 1), - vec3(0, 0, 0.502), - vec3(0, 0, 0.753), - vec3(0, 1, 0.251), - vec3(0, 1, 0.50), - vec3(60.0/360.0, 1, 0.251), - vec3(60.0/360.0, 1, 0.5), - vec3(120.0/360.0, 1, 0.251), - vec3(120.0/360.0, 1, 0.5), - vec3(180.0/360.0, 1, 0.251), - vec3(180.0/360.0, 1, 0.5), - vec3(240.0/360.0, 1, 0.251), - vec3(240.0/360.0, 1, 0.5), - vec3(300.0/360.0, 1, 0.251), - vec3(300.0/360.0, 1, 0.5), - vec3(60.0/360.0, 0.333, 0.376), - vec3(60.0/360.0, 1, 0.751), - vec3(180.0/360.0, 1, 0.125), - vec3(150.0/360.0, 1, 0.5), - vec3(210.0/360.0, 1, 0.5), - vec3(180.0/360.0, 1, 0.751), - vec3(210.0/360.0, 1, 0.251), - vec3(240.0/360.0, 1, 0.751), - vec3(270.0/360.0, 1, 0.5), - vec3(330.0/360.0, 1, 0.5), - vec3(30.0/360.0, 1, 0.251), - vec3(24.0/360.0, 1, 0.625) -); const int indexMatrix4x4[16] = int[](0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5); - -float indexValue4x4() { - int x = int(mod(gl_FragCoord.x, 4)); - int y = int(mod(gl_FragCoord.y, 4)); +float indexValue4x4( float scale ) { + int x = int(mod(gl_FragCoord.x * scale, 4)); + int y = int(mod(gl_FragCoord.y * scale, 4)); return indexMatrix4x4[(x + y * 4)] / 16.0; } -const int palette4x4Size = 28; -const vec3 palette4x4[28] = vec3[]( - vec3(0, 0, 0), - vec3(0, 0, 1), - vec3(0, 0, 0.502), - vec3(0, 0, 0.753), - vec3(0, 1, 0.251), - vec3(0, 1, 0.50), - vec3(60.0/360.0, 1, 0.251), - vec3(60.0/360.0, 1, 0.5), - vec3(120.0/360.0, 1, 0.251), - vec3(120.0/360.0, 1, 0.5), - vec3(180.0/360.0, 1, 0.251), - vec3(180.0/360.0, 1, 0.5), - vec3(240.0/360.0, 1, 0.251), - vec3(240.0/360.0, 1, 0.5), - vec3(300.0/360.0, 1, 0.251), - vec3(300.0/360.0, 1, 0.5), - vec3(60.0/360.0, 0.333, 0.376), - vec3(60.0/360.0, 1, 0.751), - vec3(180.0/360.0, 1, 0.125), - vec3(150.0/360.0, 1, 0.5), - vec3(210.0/360.0, 1, 0.5), - vec3(180.0/360.0, 1, 0.751), - vec3(210.0/360.0, 1, 0.251), - vec3(240.0/360.0, 1, 0.751), - vec3(270.0/360.0, 1, 0.5), - vec3(330.0/360.0, 1, 0.5), - vec3(30.0/360.0, 1, 0.251), - vec3(24.0/360.0, 1, 0.625) -); -vec3[2] closestColors16x16(float hue) { +vec3[2] closestColors(float hue) { vec3 ret[2]; vec3 closest = vec3(-2, 0, 0); vec3 secondClosest = vec3(-2, 0, 0); vec3 temp; - for (int i = 0; i < paletteSize; ++i) { - temp = rgbToHsl(palette16x16[i]); +/* + for (int i = 0; i < palette.length(); ++i) { + temp = rgbToHsl(palette[i].rgb); float tempDistance = hueDistance(temp.x, hue); if (tempDistance < hueDistance(closest.x, hue)) { secondClosest = closest; @@ -378,123 +278,52 @@ vec3[2] closestColors16x16(float hue) { } } } +*/ ret[0] = closest; ret[1] = secondClosest; return ret; } -vec3 dither1_16x16(vec3 color) { - vec3 hsl = rgbToHsl(color); - - vec3 cs[2] = closestColors16x16(hsl.x); - vec3 c1 = cs[0]; - vec3 c2 = cs[1]; - float d = indexValue16x16(); - float hueDiff = hueDistance(hsl.x, c1.x) / hueDistance(c2.x, c1.x); - - float l1 = lightnessStep(max((hsl.z - 0.125), 0.0)); - float l2 = lightnessStep(min((hsl.z + 0.124), 1.0)); - float lightnessDiff = (hsl.z - l1) / (l2 - l1); - - vec3 resultColor = (hueDiff < d) ? c1 : c2; - resultColor.z = (lightnessDiff < d) ? l1 : l2; - return hslToRgb(resultColor); -} -vec3[2] closestColors8x8(float hue) { - vec3 ret[2]; - vec3 closest = vec3(-2, 0, 0); - vec3 secondClosest = vec3(-2, 0, 0); - vec3 temp; - for (int i = 0; i < palette8x8Size; ++i) { - temp = palette8x8[i]; - float tempDistance = hueDistance(temp.x, hue); - if (tempDistance < hueDistance(closest.x, hue)) { - secondClosest = closest; - closest = temp; - } else { - if (tempDistance < hueDistance(secondClosest.x, hue)) { - secondClosest = temp; - } - } - } - ret[0] = closest; - ret[1] = secondClosest; - return ret; -} -vec3 dither1_8x8(vec3 color) { - vec3 hsl = rgbToHsl(color); - - vec3 cs[2] = closestColors8x8(hsl.x); - vec3 c1 = cs[0]; - vec3 c2 = cs[1]; - float d = indexValue8x8(); - float hueDiff = hueDistance(hsl.x, c1.x) / hueDistance(c2.x, c1.x); - - float l1 = lightnessStep(max((hsl.z - 0.125), 0.0)); - float l2 = lightnessStep(min((hsl.z + 0.124), 1.0)); - float lightnessDiff = (hsl.z - l1) / (l2 - l1); - - vec3 resultColor = (hueDiff < d) ? c1 : c2; - //resultColor.z = (lightnessDiff < d) ? l1 : l2; - return hslToRgb(resultColor); -} -vec3[2] closestColors4x4(float hue) { - vec3 ret[2]; - vec3 closest = vec3(-2, 0, 0); - vec3 secondClosest = vec3(-2, 0, 0); - vec3 temp; - for (int i = 0; i < palette4x4Size; ++i) { - temp = palette4x4[i]; - float tempDistance = hueDistance(temp.x, hue); - if (tempDistance < hueDistance(closest.x, hue)) { - secondClosest = closest; - closest = temp; - } else { - if (tempDistance < hueDistance(secondClosest.x, hue)) { - secondClosest = temp; - } - } - } - ret[0] = closest; - ret[1] = secondClosest; - return ret; -} - -vec3 dither1_4x4(vec3 color) { - vec3 hsl = rgbToHsl(color); - - vec3 cs[2] = closestColors4x4(hsl.x); - vec3 c1 = cs[0]; - vec3 c2 = cs[1]; - float d = indexValue4x4(); - float hueDiff = hueDistance(hsl.x, c1.x) / hueDistance(c2.x, c1.x); - - float l1 = lightnessStep(max((hsl.z - 0.125), 0.0)); - float l2 = lightnessStep(min((hsl.z + 0.124), 1.0)); - float lightnessDiff = (hsl.z - l1) / (l2 - l1); - - vec3 resultColor = (hueDiff < d) ? c1 : c2; - //resultColor.z = (lightnessDiff < d) ? l1 : l2; - return hslToRgb(resultColor); -} float dither(float color) { float closestColor = (color < 0.5) ? 0 : 1; float secondClosestColor = 1 - closestColor; - float d; + float d = 1; // -0.5 - fract(ubo.mode.parameters.w / 8.0); if ( ubo.mode.scalar == 16 ) { - d = indexValue16x16(); + d = indexValue16x16(1); } else if ( ubo.mode.scalar == 8 ) { - d = indexValue8x8(); + d = indexValue8x8(1); } else if ( ubo.mode.scalar == 4 ) { - d = indexValue4x4(); + d = indexValue4x4(1); } float distance = abs(closestColor - color); return (distance < d) ? closestColor : secondClosestColor; } void dither(inout vec3 color) { vec3 hsl = rgbToHsl(color); - hsl.y = dither(hsl.y); + hsl.x = dither(hsl.x); color = hslToRgb(hsl); } +void dither1(inout vec3 color) { + vec3 hsl = rgbToHsl(color); + + float d = 0; + vec3 cs[2] = { hsl, hsl }; // closestColors(hsl.x); + float scale = 1; + if ( ubo.mode.scalar == 16 ) { + d = indexValue16x16(scale); + } else if ( ubo.mode.scalar == 8 ) { + d = indexValue8x8(scale); + } else if ( ubo.mode.scalar == 4 ) { + d = indexValue4x4(scale); + } + float hueDiff = hueDistance(hsl.x, cs[0].x) / hueDistance(cs[1].x, cs[0].x); + float l1 = lightnessStep(max((hsl.z - 0.125), 0.0)); + float l2 = lightnessStep(min((hsl.z + 0.124), 1.0)); + float lightnessDiff = (hsl.z - l1) / (l2 - l1); + + vec3 resultColor = (hueDiff < d) ? cs[0] : cs[1]; + resultColor.z = (lightnessDiff < d) ? l1 : l2; + color = hslToRgb(resultColor); +} float rand2(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453); @@ -541,15 +370,12 @@ void main() { fog(fragColor, litFactor); - if ( (ubo.mode.type & (0x1 >> 1)) == (0x1 >> 1) ) { - dither(fragColor); + if ( (ubo.mode.type & (0x1 << 0)) == (0x1 << 0) ) { + dither1(fragColor); } - if ( (ubo.mode.type & (0x1 >> 2)) == (0x1 >> 2) ) { + if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) { whitenoise(fragColor); } - if ( (ubo.mode.type & (0x1 >> 3)) == (0x1 >> 3) ) { - dither(fragColor); - } outFragColor = vec4(fragColor,1); } \ No newline at end of file diff --git a/bin/data/shaders/gui.frag.glsl b/bin/data/shaders/gui.frag.glsl index 3e683c5c..1206191c 100644 --- a/bin/data/shaders/gui.frag.glsl +++ b/bin/data/shaders/gui.frag.glsl @@ -7,6 +7,7 @@ struct Gui { vec4 color; int mode; float depth; + vec2 padding; }; layout (location = 0) in vec2 inUv; @@ -19,7 +20,7 @@ void main() { if ( inUv.x > inGui.offset.z ) discard; if ( inUv.y > inGui.offset.w ) discard; - outFragColor = texture(samplerColor, inUv);// vec4(inUv.s, inUv.t, 1.0, 1.0); + outFragColor = texture(samplerColor, inUv); if ( outFragColor.a < 0.001 ) discard; if ( inGui.mode == 1 ) { outFragColor = inGui.color; diff --git a/bin/data/shaders/gui.gradient.frag.glsl b/bin/data/shaders/gui.gradient.frag.glsl deleted file mode 100644 index 15683736..00000000 --- a/bin/data/shaders/gui.gradient.frag.glsl +++ /dev/null @@ -1,38 +0,0 @@ -#version 450 - -layout (binding = 1) uniform sampler2D samplerColor; - -struct Gui { - vec4 offset; - vec4 color; - int mode; - float depth; -}; - -layout (location = 0) in vec2 inUv; -layout (location = 1) in flat Gui inGui; -layout (location = 0) out vec4 outFragColor; - -void main() { - if ( inUv.x < inGui.offset.x ) discard; - if ( inUv.y < inGui.offset.y ) discard; - if ( inUv.x > inGui.offset.z ) discard; - if ( inUv.y > inGui.offset.w ) discard; - - outFragColor = texture(samplerColor, inUv);// vec4(inUv.s, inUv.t, 1.0, 1.0); - if ( outFragColor.a < 0.001 ) discard; - - if ( inGui.mode == 1 ) { - outFragColor = inGui.color; - } else { - outFragColor *= inGui.color; - } - - float end = 0.25; - if ( inUv.x < end ) { - outFragColor.a = inUv.x / end; - } else if ( inUv.x + end >= 1 ) { - float offset = (1 - inUv.x); - outFragColor.a = offset / end; - } -} \ No newline at end of file diff --git a/bin/data/shaders/gui.stereo.vert.glsl b/bin/data/shaders/gui.stereo.vert.glsl index 25e6dbdb..4ea62245 100644 --- a/bin/data/shaders/gui.stereo.vert.glsl +++ b/bin/data/shaders/gui.stereo.vert.glsl @@ -15,6 +15,7 @@ struct Gui { vec4 color; int mode; float depth; + vec2 padding; }; layout (binding = 0) uniform UBO { diff --git a/bin/data/shaders/gui.text.frag.glsl b/bin/data/shaders/gui.text.frag.glsl index 262e8c08..ba599489 100644 --- a/bin/data/shaders/gui.text.frag.glsl +++ b/bin/data/shaders/gui.text.frag.glsl @@ -13,6 +13,7 @@ struct Gui { float weight; int spread; float scale; + float padding; }; layout (location = 0) in vec2 inUv; diff --git a/bin/data/shaders/gui.text.stereo.vert.glsl b/bin/data/shaders/gui.text.stereo.vert.glsl index bbc1b558..96f05331 100644 --- a/bin/data/shaders/gui.text.stereo.vert.glsl +++ b/bin/data/shaders/gui.text.stereo.vert.glsl @@ -21,6 +21,7 @@ struct Gui { float weight; int spread; float scale; + float padding; }; layout (binding = 0) uniform UBO { diff --git a/bin/data/shaders/gui.text.vert.glsl b/bin/data/shaders/gui.text.vert.glsl index bbc1b558..96f05331 100644 --- a/bin/data/shaders/gui.text.vert.glsl +++ b/bin/data/shaders/gui.text.vert.glsl @@ -21,6 +21,7 @@ struct Gui { float weight; int spread; float scale; + float padding; }; layout (binding = 0) uniform UBO { diff --git a/bin/data/shaders/gui.vert.glsl b/bin/data/shaders/gui.vert.glsl index 25e6dbdb..4ea62245 100644 --- a/bin/data/shaders/gui.vert.glsl +++ b/bin/data/shaders/gui.vert.glsl @@ -15,6 +15,7 @@ struct Gui { vec4 color; int mode; float depth; + vec2 padding; }; layout (binding = 0) uniform UBO { diff --git a/bin/data/shaders/mcdonalds.frag.glsl b/bin/data/shaders/mcdonalds.frag.glsl new file mode 100644 index 00000000..825c2f04 --- /dev/null +++ b/bin/data/shaders/mcdonalds.frag.glsl @@ -0,0 +1,39 @@ +#version 450 + +layout (constant_id = 0) const uint TEXTURES = 87; +layout (binding = 1) uniform sampler2D samplerTextures[TEXTURES]; +struct Map { + uint target; + float blend; + ivec2 padding; +}; +layout (binding = 2) uniform UBO { + Map map[TEXTURES]; +} ubo; +layout (location = 0) in vec2 inUv; +layout (location = 1) in vec4 inColor; +layout (location = 2) in vec3 inNormal; +layout (location = 3) in vec3 inPosition; +layout (location = 4) flat in uint inId; + +layout (location = 0) out vec4 outAlbedoSpecular; +layout (location = 1) out vec4 outNormal; +layout (location = 2) out vec4 outPosition; + +void main() { + uint offset = inId; + outAlbedoSpecular = texture(samplerTextures[offset], inUv.xy); + Map mapped = ubo.map[offset]; + if ( offset != mapped.target ) { + outAlbedoSpecular = mix(texture(samplerTextures[offset], inUv.xy), texture(samplerTextures[mapped.target], inUv.xy), mapped.blend); + } else { + outAlbedoSpecular = texture(samplerTextures[offset], inUv.xy); + } + + if ( outAlbedoSpecular.a < 0.001 ) discard; + outAlbedoSpecular.rgb *= inColor.rgb; + outAlbedoSpecular.a = 1; + + outNormal = vec4(inNormal,1); + outPosition = vec4(inPosition,1); +} \ No newline at end of file diff --git a/client/main.cpp b/client/main.cpp index 793a9557..f2eecb75 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -6,6 +6,12 @@ #include int main(int argc, char** argv){ + for ( size_t i = 0; i < argc; ++i ) { + char* c_str = argv[i]; + std::string string(argv[i]); + ext::arguments.emplace_back(string); + } + std::atexit([]{ uf::iostream << "Termination via std::atexit()!" << "\n"; client::terminated = !(client::ready = ext::ready = false); @@ -42,13 +48,9 @@ int main(int argc, char** argv){ ext::render(); } catch ( std::runtime_error& e ) { uf::iostream << "RUNTIME ERROR: " << e.what() << "\n"; - std::abort(); - raise(SIGSEGV); - throw e; + break; } catch ( std::exception& e ) { uf::iostream << "EXCEPTION ERROR: " << e.what() << "\n"; - std::abort(); - raise(SIGSEGV); throw e; } catch ( bool handled ) { if (!handled) uf::iostream << "UNHANDLED ERROR: " << "???" << "\n"; @@ -59,8 +61,8 @@ int main(int argc, char** argv){ if ( !client::terminated ) { uf::iostream << "Natural termination!" << "\n"; - ext::terminate(); - client::terminate(); } + ext::terminate(); + client::terminate(); return 0; } \ No newline at end of file diff --git a/engine/inc/uf/engine/object/object.h b/engine/inc/uf/engine/object/object.h index 1aaafdaf..b225b0cf 100644 --- a/engine/inc/uf/engine/object/object.h +++ b/engine/inc/uf/engine/object/object.h @@ -31,9 +31,12 @@ namespace uf { template T loadChild( const std::string&, bool = true ); + std::string formatHookName( const std::string& name ); void queueHook( const std::string&, const std::string& = "", double = 0 ); std::vector callHook( const std::string&, const std::string& = "" ); std::size_t addHook( const std::string&, const uf::HookHandler::Readable::function_t& ); + + std::string grabURI( const std::string& filename, const std::string& root = "" ); }; } diff --git a/engine/inc/uf/ext/ext.h b/engine/inc/uf/ext/ext.h index a0e21cb8..067f4e45 100644 --- a/engine/inc/uf/ext/ext.h +++ b/engine/inc/uf/ext/ext.h @@ -1,6 +1,7 @@ #pragma once #include +#include #if !defined(EXT_STATIC) #if defined(UF_ENV_WINDOWS) @@ -38,6 +39,7 @@ #include namespace ext { extern bool EXT_API ready; + extern std::vector EXT_API arguments; extern void EXT_API initialize(); extern bool EXT_API running(); diff --git a/engine/inc/uf/ext/vulkan/buffer.h b/engine/inc/uf/ext/vulkan/buffer.h index 4b54ae8a..67892c78 100644 --- a/engine/inc/uf/ext/vulkan/buffer.h +++ b/engine/inc/uf/ext/vulkan/buffer.h @@ -11,7 +11,11 @@ namespace ext { VkDevice device; VkBuffer buffer = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; - VkDescriptorBufferInfo descriptor; + VkDescriptorBufferInfo descriptor = { + VK_NULL_HANDLE, + 0, + 0 + }; VkDeviceSize size = 0; VkDeviceSize alignment = 0; void* mapped = nullptr; diff --git a/engine/inc/uf/ext/vulkan/device.h b/engine/inc/uf/ext/vulkan/device.h index 5ec2f73b..510feaa1 100644 --- a/engine/inc/uf/ext/vulkan/device.h +++ b/engine/inc/uf/ext/vulkan/device.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace ext { namespace vulkan { @@ -13,9 +14,9 @@ namespace ext { VkPhysicalDevice physicalDevice; VkDevice logicalDevice; struct { - VkCommandPool graphics = VK_NULL_HANDLE; - VkCommandPool compute = VK_NULL_HANDLE; - VkCommandPool transfer = VK_NULL_HANDLE; + std::unordered_map graphics; + std::unordered_map compute; + std::unordered_map transfer; } commandPool; VkPhysicalDeviceProperties properties; @@ -42,10 +43,10 @@ namespace ext { std::vector queueFamilyProperties; struct { - VkQueue graphics; - VkQueue present; - VkQueue compute; - VkQueue transfer; + std::unordered_map graphics; + std::unordered_map present; + std::unordered_map compute; + std::unordered_map transfer; } queues; uf::Window* window; @@ -88,6 +89,15 @@ namespace ext { void *data = nullptr ); + enum QueueEnum { + GRAPHICS, + PRESENT, + COMPUTE, + TRANSFER, + }; + VkQueue& getQueue( QueueEnum ); + VkCommandPool& getCommandPool( QueueEnum ); + // RAII void initialize(); void destroy(); diff --git a/engine/inc/uf/ext/vulkan/graphic.inl b/engine/inc/uf/ext/vulkan/graphic.inl index 98007a88..356c47ea 100644 --- a/engine/inc/uf/ext/vulkan/graphic.inl +++ b/engine/inc/uf/ext/vulkan/graphic.inl @@ -18,7 +18,7 @@ void ext::vulkan::Graphic::initializeGeometry( uf::BaseMesh& mesh, bool st updateBuffer( (void*) mesh.indices.data(), mesh.indices.size() * mesh.sizes.indices, - 0, + 1, stage ); return; diff --git a/engine/inc/uf/ext/vulkan/rendertarget.h b/engine/inc/uf/ext/vulkan/rendertarget.h index 7f51e1a7..23b06102 100644 --- a/engine/inc/uf/ext/vulkan/rendertarget.h +++ b/engine/inc/uf/ext/vulkan/rendertarget.h @@ -9,6 +9,7 @@ namespace ext { VkFormat format; VkImageLayout layout; VkImageUsageFlags usage; + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; bool aliased = false; VkImage image; diff --git a/engine/inc/uf/ext/vulkan/texture.h b/engine/inc/uf/ext/vulkan/texture.h index 3105d83e..682442f4 100644 --- a/engine/inc/uf/ext/vulkan/texture.h +++ b/engine/inc/uf/ext/vulkan/texture.h @@ -15,21 +15,24 @@ namespace ext { struct { VkFilter min = VK_FILTER_LINEAR; VkFilter mag = VK_FILTER_LINEAR; + VkBorderColor borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; } filter; struct { VkSamplerAddressMode u = VK_SAMPLER_ADDRESS_MODE_REPEAT, v = VK_SAMPLER_ADDRESS_MODE_REPEAT, w = VK_SAMPLER_ADDRESS_MODE_REPEAT; + VkBool32 unnormalizedCoordinates = VK_FALSE; } addressMode; struct { + VkBool32 compareEnable = VK_FALSE; + VkCompareOp compareOp = VK_COMPARE_OP_ALWAYS; VkSamplerMipmapMode mode = VK_SAMPLER_MIPMAP_MODE_LINEAR; float lodBias = 0.0f; - } mip; - VkCompareOp compareOp = VK_COMPARE_OP_NEVER; - struct { float min = 0.0f; float max = 0.0f; - } lod; - float maxAnisotropy; - + } mip; + struct { + VkBool32 enabled = VK_TRUE; + float max = 16.0f; + } anisotropy; VkDescriptorImageInfo info; } descriptor; @@ -41,9 +44,10 @@ namespace ext { VkImage image; VkImageView view; - VkImageLayout imageLayout; + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkDeviceMemory deviceMemory; VkDescriptorImageInfo descriptor; + VkFormat format; Sampler sampler; @@ -63,9 +67,7 @@ namespace ext { VkImage image, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, - VkImageSubresourceRange subresourceRange, - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT + VkImageSubresourceRange subresourceRange ); static void setImageLayout( VkCommandBuffer cmdbuffer, @@ -73,8 +75,7 @@ namespace ext { VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT + uint32_t mipLevels ); }; struct UF_API Texture2D : public Texture { @@ -83,31 +84,27 @@ namespace ext { std::string filename, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); void loadFromFile( std::string filename, Device& device, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); void loadFromImage( uf::Image& image, Device& device, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); void loadFromImage( uf::Image& image, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - bool forceLinear = false + VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); void fromBuffers( void* buffer, @@ -119,13 +116,14 @@ namespace ext { VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); - void asRenderTarget( - Device& device, - uint32_t texWidth, - uint32_t texHeight, - VkFormat format = VK_FORMAT_R8G8B8A8_UNORM - ); + void asRenderTarget( Device& device, uint32_t texWidth, uint32_t texHeight, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM ); void aliasAttachment( const RenderTarget::Attachment& attachment, bool = true ); + void update( uf::Image& image, VkImageLayout ); + void update( void*, VkDeviceSize, VkImageLayout ); + inline void update( uf::Image& image ) { return this->update(image, this->imageLayout) ; } + inline void update( void* data, VkDeviceSize size ) { return this->update(data, size, this->imageLayout) ; } + + void generateMipmaps(VkCommandBuffer commandBuffer); }; } } \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/vulkan.h b/engine/inc/uf/ext/vulkan/vulkan.h index f0c0860c..71f50623 100644 --- a/engine/inc/uf/ext/vulkan/vulkan.h +++ b/engine/inc/uf/ext/vulkan/vulkan.h @@ -41,8 +41,11 @@ namespace ext { extern UF_API uint32_t width; extern UF_API uint32_t height; + extern UF_API uint8_t msaa; extern UF_API bool validation; + extern UF_API bool rebuildOnTickStart; + extern UF_API bool waitOnRenderEnd; extern UF_API std::vector validationFilters; extern UF_API std::vector requestedDeviceFeatures; extern UF_API std::vector requestedDeviceExtensions; diff --git a/engine/inc/uf/utils/graphic/mesh.h b/engine/inc/uf/utils/graphic/mesh.h index 80bb450a..643a94d5 100644 --- a/engine/inc/uf/utils/graphic/mesh.h +++ b/engine/inc/uf/utils/graphic/mesh.h @@ -248,26 +248,4 @@ namespace uf { } matrices; alignas(16) pod::Vector4f color = { 1, 1, 1, 0 }; }; - struct GuiMeshDescriptor { - struct { - alignas(16) pod::Matrix4f model; - } matrices; - struct { - alignas(16) pod::Vector4f offset; - alignas(16) pod::Vector4f color; - alignas(4) int32_t mode = 0; - alignas(4) float depth = 0.0f; - } gui; - }; - struct StereoGuiMeshDescriptor { - struct { - alignas(16) pod::Matrix4f model[2]; - } matrices; - struct { - alignas(16) pod::Vector4f offset; - alignas(16) pod::Vector4f color; - alignas(4) int32_t mode = 0; - alignas(4) float depth = 0.0f; - } gui; - }; } \ No newline at end of file diff --git a/engine/inc/uf/utils/image/atlas.h b/engine/inc/uf/utils/image/atlas.h index 60acbfc6..9a482e84 100644 --- a/engine/inc/uf/utils/image/atlas.h +++ b/engine/inc/uf/utils/image/atlas.h @@ -22,6 +22,7 @@ namespace uf { void addImage( const uint8_t*, const pod::Vector2ui&, std::size_t, std::size_t, bool = false, bool = false ); void generate(); + void clear(); pod::Vector2f mapUv( const pod::Vector2f&, size_t ); pod::Vector3f mapUv( const pod::Vector3f& ); diff --git a/engine/inc/uf/utils/string/ext.h b/engine/inc/uf/utils/string/ext.h index 72f4b8b2..23fd1bd9 100644 --- a/engine/inc/uf/utils/string/ext.h +++ b/engine/inc/uf/utils/string/ext.h @@ -12,5 +12,6 @@ namespace uf { std::string UF_API lowercase( const std::string& ); std::string UF_API uppercase( const std::string& ); std::vector UF_API split( const std::string&, const std::string& ); + std::string UF_API si( double value, const std::string& unit, size_t precision = 3 ); } } \ No newline at end of file diff --git a/engine/src/engine/object/behaviors/gltf.cpp b/engine/src/engine/object/behaviors/gltf.cpp index 6179199d..3a02db4e 100644 --- a/engine/src/engine/object/behaviors/gltf.cpp +++ b/engine/src/engine/object/behaviors/gltf.cpp @@ -11,12 +11,22 @@ #include #include #include +#include UF_BEHAVIOR_REGISTER_CPP(GltfBehavior) #define this (&self) void uf::GltfBehavior::initialize( uf::Object& self ) { uf::Serializer& metadata = this->getComponent(); + metadata["textures"]["additional"] = Json::Value(Json::arrayValue); // Default load: GLTF model + this->addHook( "asset:Load.%UID%", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + std::string filename = json["filename"].asString(); + if ( uf::io::extension(filename) != "png" ) return "false"; + auto& vector = metadata["textures"]["additional"]; + vector[vector.size()] = filename; + return "true"; + }); this->addHook( "asset:Load.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string filename = json["filename"].asString(); @@ -28,16 +38,39 @@ void uf::GltfBehavior::initialize( uf::Object& self ) { uf::Object* objectPointer = NULL; try { objectPointer = assetLoader.get(filename); } catch ( ... ) {} if ( !objectPointer ) return "false"; - - std::function filter = [&]( uf::Entity* entity ) { + objectPointer->process([&]( uf::Entity* entity ) { if ( !entity->hasComponent() || !entity->hasComponent() ) return; auto& graphic = entity->getComponent(); auto& mesh = entity->getComponent(); graphic.initialize(); graphic.initializeGeometry( mesh ); - graphic.material.attachShader("./data/shaders/gltf.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - graphic.material.attachShader("./data/shaders/gltf.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + { + std::string filename = "/gltf.stereo.vert.spv"; + if ( metadata["system"]["renderer"]["shaders"]["vertex"].isString() ) + filename = metadata["system"]["renderer"]["shaders"]["vertex"].asString(); + filename = this->grabURI( filename, metadata["system"]["root"].asString() ); + graphic.material.attachShader(filename, VK_SHADER_STAGE_VERTEX_BIT); + } + { + std::string filename = "/gltf.frag.spv"; + if ( metadata["system"]["renderer"]["shaders"]["fragment"].isString() ) + filename = metadata["system"]["renderer"]["shaders"]["fragment"].asString(); + filename = this->grabURI( filename, metadata["system"]["root"].asString() ); + graphic.material.attachShader(filename, VK_SHADER_STAGE_FRAGMENT_BIT); + } + + for ( int i = 0; i < metadata["textures"]["additional"].size(); ++i ) { + std::string filename = metadata["textures"]["additional"][i].asString(); + auto& scene = uf::scene::getCurrentScene(); + auto& assetLoader = scene.getComponent(); + const uf::Image* imagePointer = NULL; + try { imagePointer = &assetLoader.get(filename); } catch ( ... ) {} + if ( !imagePointer ) continue; + uf::Image image = *imagePointer; + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( image ); + } graphic.process = true; @@ -52,12 +85,15 @@ void uf::GltfBehavior::initialize( uf::Object& self ) { if ( binding.descriptorCount > 1 ) binding.descriptorCount = specializationConstants->textures; } - }; - objectPointer->process(filter); - + }); this->addChild(objectPointer->as()); objectPointer->initialize(); - + objectPointer->process([&]( uf::Entity* entity ) { + if ( !entity->hasComponent() || !entity->hasComponent() ) return; + auto& eMetadata = entity->getComponent(); + eMetadata["textures"]["map"] = metadata["textures"]["map"]; + uf::instantiator::bind( "GltfBehavior", *entity ); + }); return "true"; }); } @@ -65,7 +101,50 @@ void uf::GltfBehavior::destroy( uf::Object& self ) { } void uf::GltfBehavior::tick( uf::Object& self ) { + /* Update uniforms */ if ( this->hasComponent() ) { + auto& metadata = this->getComponent(); + auto& graphic = this->getComponent(); + if ( !graphic.initialized ) return; + auto& shader = graphic.material.shaders.back(); + if ( shader.uniforms.empty() ) return; + auto& userdata = shader.uniforms.front(); + struct UniformDescriptor { + // alignas(4) uint32_t mappings; + struct Mapping { + alignas(4) uint32_t target; + alignas(4) float blend; + uint32_t padding[2]; + } map; + }; + size_t uniforms_len = userdata.data().len; + uint8_t* uniforms_buffer = (uint8_t*) (void*) userdata; + UniformDescriptor* uniforms = (UniformDescriptor*) uniforms_buffer; + UniformDescriptor::Mapping* mappings = (UniformDescriptor::Mapping*) &uniforms_buffer[sizeof(UniformDescriptor) - sizeof(UniformDescriptor::Mapping)]; + size_t textures = graphic.material.textures.size(); + // uniforms->mappings = textures; + for ( size_t i = 0; i < textures; ++i ) { + mappings[i].target = i; + mappings[i].blend = 0.0f; + } + for ( auto it = metadata["textures"]["map"].begin(); it != metadata["textures"]["map"].end(); ++it ) { + std::string key = it.key().asString(); + uint32_t from = std::stoi(key); + uint32_t to = metadata["textures"]["map"][key][0].asUInt(); + float blend = 1.0f; + if ( metadata["textures"]["map"][key][1].asString() == "sin(time)" ) { + blend = sin(uf::physics::time::current)*0.5f+0.5f; + } else if ( metadata["textures"]["map"][key][1].asString() == "cos(time)" ) { + blend = cos(uf::physics::time::current)*0.5f+0.5f; + } else if ( metadata["textures"]["map"][key][1].isNumeric() ) { + blend = metadata["textures"]["map"][key][1].asFloat(); + } + if ( from >= textures || to >= textures ) continue; + mappings[from].target = to; + mappings[from].blend = blend; + } + shader.updateBuffer( (void*) uniforms_buffer, uniforms_len, 0, false ); + }; } void uf::GltfBehavior::render( uf::Object& self ) { diff --git a/engine/src/engine/object/object.cpp b/engine/src/engine/object/object.cpp index 93c99a74..131d9ca0 100644 --- a/engine/src/engine/object/object.cpp +++ b/engine/src/engine/object/object.cpp @@ -12,19 +12,6 @@ uf::Timer uf::Object::timer(false); namespace { uf::Object null; - std::string grabURI( std::string filename, std::string root = "" ) { - if ( filename.substr(0,8) == "https://" ) return filename; - std::string extension = uf::io::extension(filename); - if ( filename[0] == '/' || root == "" ) { - if ( filename.substr(0,9) == "/smtsamo/" ) root = "./data/"; - else if ( extension == "json" ) root = "./data/entities/"; - else if ( extension == "png" ) root = "./data/textures/"; - else if ( extension == "glb" ) root = "./data/models/"; - else if ( extension == "gltf" ) root = "./data/models/"; - else if ( extension == "ogg" ) root = "./data/audio/"; - } - return uf::io::sanitize(filename, root); - } } UF_OBJECT_REGISTER_BEGIN(Object) @@ -41,12 +28,22 @@ void uf::Object::queueHook( const std::string& name, const std::string& payload, uf::Serializer& metadata = this->getComponent(); metadata["system"]["hooks"]["queue"].append(queue); } -std::vector uf::Object::callHook( const std::string& n, const std::string& payload ) { - std::string name = uf::string::replace( n, "%UID%", std::to_string(this->getUid()) ); - return uf::hooks.call( name, payload ); +std::string uf::Object::formatHookName( const std::string& n ) { + std::unordered_map formats = { + {"%UID%", std::to_string(this->getUid())}, + {"%P-UID%", std::to_string(this->getParent().getUid())}, + }; + std::string name = n; + for ( auto& pair : formats ) { + name = uf::string::replace( name, pair.first, pair.second ); + } + return name; +} +std::vector uf::Object::callHook( const std::string& name, const std::string& payload ) { + return uf::hooks.call( this->formatHookName( name ), payload ); } std::size_t uf::Object::addHook( const std::string& name, const uf::HookHandler::Readable::function_t& callback ) { - std::string parsed = uf::string::replace( name, "%UID%", std::to_string(this->getUid()) ); + std::string parsed = this->formatHookName( name ); std::size_t id = uf::hooks.addHook( parsed, callback ); uf::Serializer& metadata = this->getComponent(); metadata["system"]["hooks"]["alloc"][parsed].append(id); @@ -76,6 +73,7 @@ bool uf::Object::reload( bool hard ) { uf::Serializer& metadata = this->getComponent(); if ( !metadata["system"]["source"].isString() ) return false; uf::Serializer json; + uf::Serializer payload; std::string filename = metadata["system"]["source"].asString(); //grabURI( metadata["system"]["source"].asString(), metadata["system"]["root"].asString() ); // uf::io::sanitize(metadata["system"]["source"].asString(), metadata["system"]["root"].asString()); if ( !json.readFromFile( filename ) ) { uf::iostream << "Error @ " << __FILE__ << ":" << __LINE__ << ": failed to open `" + filename + "`" << "\n"; @@ -83,11 +81,15 @@ bool uf::Object::reload( bool hard ) { return false; } if ( hard ) return this->load(filename); + + payload["old"] = metadata; for ( auto it = json["metadata"].begin(); it != json["metadata"].end(); ++it ) { metadata[it.key().asString()] = json["metadata"][it.key().asString()]; } + payload["new"] = metadata; std::cout << "Updated metadata for " << this->getName() << ": " << this->getUid() << std::endl; - this->queueHook("object:Reload.%UID%"); + + this->queueHook("object:Reload.%UID%", payload); return true; } @@ -306,6 +308,7 @@ bool uf::Object::load( const uf::Serializer& _json ) { json["root"] = uf::io::directory(filename); json["source"] = filename; // uf::io::filename(filename) json["hot reload"]["mtime"] = uf::io::mtime( filename ) + 10; + if ( this->loadChildUid(json) == -1 ) continue; } } @@ -411,4 +414,20 @@ std::size_t uf::Object::loadChildUid( const std::string& f, bool initialize ) { std::size_t uf::Object::loadChildUid( const uf::Serializer& json, bool initialize ) { uf::Object* pointer = this->loadChildPointer(json, initialize); return pointer ? pointer->getUid() : -1; +} + +std::string uf::Object::grabURI( const std::string& filename, const std::string& _root ) { + std::string root = _root; + if ( filename.substr(0,8) == "https://" ) return filename; + std::string extension = uf::io::extension(filename); + if ( filename[0] == '/' || root == "" ) { + if ( filename.substr(0,9) == "/smtsamo/" ) root = "./data/"; + else if ( extension == "json" ) root = "./data/entities/"; + else if ( extension == "png" ) root = "./data/textures/"; + else if ( extension == "glb" ) root = "./data/models/"; + else if ( extension == "gltf" ) root = "./data/models/"; + else if ( extension == "ogg" ) root = "./data/audio/"; + else if ( extension == "spv" ) root = "./data/shaders/"; + } + return uf::io::sanitize(filename, root); } \ No newline at end of file diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index fddbc0bf..e0b6350d 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace { @@ -224,6 +225,14 @@ void loadNode( uf::Object& entity, const tinygltf::Model& model, const tinygltf: auto& im = model.images[t.source]; uf::Image& image = images.emplace_back(); image.loadFromBuffer( &im.image[0], {im.width, im.height}, 8, im.component, true ); + /* + { + static int i = 0; + auto& pixels = image.getPixels(); + std::string hash = uf::string::sha256( pixels ); + image.save("./textures/" + std::to_string(i++) + "."+hash+".png"); + } + */ } if ( mode & ext::gltf::LoadMode::SEPARATE_MESHES ) { auto sampler = samplers.begin(); diff --git a/engine/src/ext/openvr/openvr.cpp b/engine/src/ext/openvr/openvr.cpp index 501e7296..06b42381 100644 --- a/engine/src/ext/openvr/openvr.cpp +++ b/engine/src/ext/openvr/openvr.cpp @@ -468,7 +468,7 @@ void ext::openvr::submit() { bool invert = swapEyes; vulkanData.m_pDevice = ( VkDevice_T * ) ext::vulkan::device; vulkanData.m_pPhysicalDevice = ( VkPhysicalDevice_T * ) ext::vulkan::device.physicalDevice; vulkanData.m_pInstance = ( VkInstance_T *) ext::vulkan::device.instance; - vulkanData.m_pQueue = ( VkQueue_T * ) ext::vulkan::device.queues.present; + vulkanData.m_pQueue = ( VkQueue_T * ) ext::vulkan::device.getQueue( ext::vulkan::Device::QueueEnum::PRESENT ); vulkanData.m_nQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present; vulkanData.m_nWidth = width; diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index 7ab50184..f82720fb 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -127,6 +127,15 @@ namespace { CHECK_FEATURE(inheritedQueries); } #undef CHECK_FEATURE + + #define CHECK_FEATURE2( NAME )\ + if ( feature == #NAME ) {\ + if ( device.features2.NAME == VK_TRUE ) {\ + device.enabledFeatures2.NAME = true;\ + if ( ext::vulkan::validation ) std::cout << "Enabled feature: " << feature << std::endl;\ + } else if ( ext::vulkan::validation ) std::cout << "Failed to enable feature: " << feature << std::endl;\ + } + #undef CHECK_FEATURE2 } uf::Serializer retrieveDeviceFeatures( ext::vulkan::Device& device ) { uf::Serializer json; @@ -192,6 +201,11 @@ namespace { CHECK_FEATURE(inheritedQueries); #undef CHECK_FEATURE + #define CHECK_FEATURE2( NAME )\ + json[#NAME]["supported"] = device.features2.NAME;\ + json[#NAME]["enabled"] = device.enabledFeatures2.NAME; + #undef CHECK_FEATURE2 + return json; } } @@ -295,7 +309,7 @@ int ext::vulkan::Device::rate( VkPhysicalDevice device ) { } VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel level, bool begin ){ - VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( commandPool.transfer, level, 1 ); + VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( getCommandPool(QueueEnum::TRANSFER), level, 1 ); VkCommandBuffer commandBuffer; VK_CHECK_RESULT( vkAllocateCommandBuffers( logicalDevice, &cmdBufAllocateInfo, &commandBuffer ) ); @@ -322,14 +336,14 @@ void ext::vulkan::Device::flushCommandBuffer( VkCommandBuffer commandBuffer, boo VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); // Submit to the queue - VK_CHECK_RESULT(vkQueueSubmit(device.queues.transfer, 1, &submitInfo, fence)); + VK_CHECK_RESULT(vkQueueSubmit( getQueue( QueueEnum::TRANSFER ), 1, &submitInfo, fence)); // vkQueueSubmit(device.queues.transfer, 1, &submitInfo, fence); // Wait for the fence to signal that command buffer has finished executing VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT)); vkDestroyFence(logicalDevice, fence, nullptr); - if ( free ) vkFreeCommandBuffers(logicalDevice, commandPool.transfer, 1, &commandBuffer); + if ( free ) vkFreeCommandBuffers(logicalDevice, getCommandPool( QueueEnum::TRANSFER ), 1, &commandBuffer); } VkResult ext::vulkan::Device::createBuffer( VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void *data ) { @@ -421,6 +435,71 @@ VkResult ext::vulkan::Device::createBuffer( // Attach the memory to the buffer object return buffer.bind(); } +VkCommandPool& ext::vulkan::Device::getCommandPool( ext::vulkan::Device::QueueEnum queueEnum ) { + uint32_t index = 0; + VkCommandPool* pool = NULL; + bool exists = false; + auto id = std::this_thread::get_id(); + switch ( queueEnum ) { + case QueueEnum::GRAPHICS: + index = device.queueFamilyIndices.graphics; + if ( commandPool.graphics.count(id) > 0 ) exists = true; + pool = &commandPool.graphics[id]; + break; + case QueueEnum::COMPUTE: + index = device.queueFamilyIndices.compute; + if ( commandPool.compute.count(id) > 0 ) exists = true; + pool = &commandPool.compute[id]; + break; + case QueueEnum::TRANSFER: + index = device.queueFamilyIndices.transfer; + if ( commandPool.transfer.count(id) > 0 ) exists = true; + pool = &commandPool.transfer[id]; + break; + } + if ( !exists ) { + VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = index; + cmdPoolInfo.flags = createFlags; + if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, pool ) != VK_SUCCESS ) + throw std::runtime_error("failed to create command pool for graphics!"); + } + return *pool; +} +VkQueue& ext::vulkan::Device::getQueue( ext::vulkan::Device::QueueEnum queueEnum ) { + uint32_t index = 0; + VkQueue* queue = NULL; + bool exists = false; + auto id = std::this_thread::get_id(); + switch ( queueEnum ) { + case QueueEnum::GRAPHICS: + index = device.queueFamilyIndices.graphics; + if ( queues.graphics.count(id) > 0 ) exists = true; + queue = &queues.graphics[id]; + break; + case QueueEnum::PRESENT: + index = device.queueFamilyIndices.present; + if ( queues.present.count(id) > 0 ) exists = true; + queue = &queues.present[id]; + break; + case QueueEnum::COMPUTE: + index = device.queueFamilyIndices.compute; + if ( queues.compute.count(id) > 0 ) exists = true; + queue = &queues.compute[id]; + break; + case QueueEnum::TRANSFER: + index = device.queueFamilyIndices.transfer; + if ( queues.transfer.count(id) > 0 ) exists = true; + queue = &queues.transfer[id]; + break; + } + if ( !exists ) { + vkGetDeviceQueue( device, index, 0, queue ); + } + return *queue; +} void ext::vulkan::Device::initialize() { const std::vector validationLayers = { @@ -540,7 +619,11 @@ void ext::vulkan::Device::initialize() { // Memory properties are used regularly for creating all kinds of buffers vkGetPhysicalDeviceMemoryProperties( this->physicalDevice, &memoryProperties ); } - if ( false ) { + { + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + memoryProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + vkGetPhysicalDeviceProperties2( this->physicalDevice, &properties2 ); // Features should be checked by the examples before using them vkGetPhysicalDeviceFeatures2( this->physicalDevice, &features2 ); @@ -553,6 +636,17 @@ void ext::vulkan::Device::initialize() { // assert(queueFamilyCount > 0); queueFamilyProperties.resize(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties( this->physicalDevice, &queueFamilyCount, queueFamilyProperties.data() ); + { + VkSampleCountFlags counts = properties.limits.framebufferColorSampleCounts & properties.limits.framebufferDepthSampleCounts; + uint8_t maxSamples = 1; + if (counts & VK_SAMPLE_COUNT_64_BIT) maxSamples = 64; + else if (counts & VK_SAMPLE_COUNT_32_BIT) maxSamples = 32; + else if (counts & VK_SAMPLE_COUNT_16_BIT) maxSamples = 16; + else if (counts & VK_SAMPLE_COUNT_8_BIT) maxSamples = 8; + else if (counts & VK_SAMPLE_COUNT_4_BIT) maxSamples = 4; + else if (counts & VK_SAMPLE_COUNT_2_BIT) maxSamples = 2; + ext::vulkan::msaa = std::min( maxSamples, ext::vulkan::msaa ); + } } // Create logical device { @@ -664,33 +758,9 @@ void ext::vulkan::Device::initialize() { std::cout << retrieveDeviceFeatures( *this ) << std::endl; } // Create command pool - { - VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.graphics; - cmdPoolInfo.flags = createFlags; - if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.graphics ) != VK_SUCCESS ) - throw std::runtime_error("failed to create command pool for graphics!"); - } - { - VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.compute; - cmdPoolInfo.flags = createFlags; - if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.compute ) != VK_SUCCESS ) - throw std::runtime_error("failed to create command pool for compute!"); - } - { - VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.transfer; - cmdPoolInfo.flags = createFlags; - if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.transfer ) != VK_SUCCESS ) - throw std::runtime_error("failed to create command pool for transfer!"); - } + getCommandPool( QueueEnum::GRAPHICS ); + getCommandPool( QueueEnum::COMPUTE ); + getCommandPool( QueueEnum::TRANSFER ); // Set queue { uint32_t graphicsQueueNodeIndex = UINT32_MAX; @@ -720,11 +790,16 @@ void ext::vulkan::Device::initialize() { } device.queueFamilyIndices.present = presentQueueNodeIndex; - - vkGetDeviceQueue( device, device.queueFamilyIndices.graphics, 0, &queues.graphics ); - vkGetDeviceQueue( device, device.queueFamilyIndices.present, 0, &queues.present ); - vkGetDeviceQueue( device, device.queueFamilyIndices.compute, 0, &queues.compute ); - vkGetDeviceQueue( device, device.queueFamilyIndices.transfer, 0, &queues.transfer ); + getQueue( QueueEnum::GRAPHICS ); + getQueue( QueueEnum::PRESENT ); + getQueue( QueueEnum::COMPUTE ); + getQueue( QueueEnum::TRANSFER ); + /* + vkGetDeviceQueue( device, device.queueFamilyIndices.graphics, 0, &queues.graphics[std::this_thread::get_id()] ); + vkGetDeviceQueue( device, device.queueFamilyIndices.present, 0, &queues.present[std::this_thread::get_id()] ); + vkGetDeviceQueue( device, device.queueFamilyIndices.compute, 0, &queues.compute[std::this_thread::get_id()] ); + vkGetDeviceQueue( device, device.queueFamilyIndices.transfer, 0, &queues.transfer[std::this_thread::get_id()] ); + */ } // Set formats { @@ -791,17 +866,17 @@ void ext::vulkan::Device::destroy() { vkDestroyPipelineCache( this->logicalDevice, this->pipelineCache, nullptr ); this->pipelineCache = nullptr; } - if ( this->commandPool.graphics ) { - vkDestroyCommandPool( this->logicalDevice, this->commandPool.graphics, nullptr ); - this->commandPool.graphics = nullptr; + for ( auto& pair : this->commandPool.graphics ) { + vkDestroyCommandPool( this->logicalDevice, pair.second, nullptr ); + pair.second = VK_NULL_HANDLE; } - if ( this->commandPool.compute ) { - vkDestroyCommandPool( this->logicalDevice, this->commandPool.compute, nullptr ); - this->commandPool.compute = nullptr; + for ( auto& pair : this->commandPool.compute ) { + vkDestroyCommandPool( this->logicalDevice, pair.second, nullptr ); + pair.second = VK_NULL_HANDLE; } - if ( this->commandPool.transfer ) { - vkDestroyCommandPool( this->logicalDevice, this->commandPool.transfer, nullptr ); - this->commandPool.transfer = nullptr; + for ( auto& pair : this->commandPool.transfer ) { + vkDestroyCommandPool( this->logicalDevice, pair.second, nullptr ); + pair.second = VK_NULL_HANDLE; } if ( this->logicalDevice ) { vkDestroyDevice( this->logicalDevice, nullptr ); diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index ff86d80e..ce8890c3 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -322,7 +322,7 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic ) { blendAttachmentStates.push_back(renderTarget.attachments[color.attachment].blendState); } // require blending if independentBlend is not an enabled feature - if ( !device.enabledFeatures.independentBlend ) { + if ( device.enabledFeatures.independentBlend == VK_FALSE ) { for ( size_t i = 1; i < blendAttachmentStates.size(); ++i ) { blendAttachmentStates[i] = blendAttachmentStates[0]; } @@ -365,8 +365,18 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic ) { VkPipelineViewportStateCreateInfo viewportState = ext::vulkan::initializers::pipelineViewportStateCreateInfo( 1, 1, 0 ); + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + switch ( ext::vulkan::msaa ) { + case 64: samples = VK_SAMPLE_COUNT_64_BIT; break; + case 32: samples = VK_SAMPLE_COUNT_32_BIT; break; + case 16: samples = VK_SAMPLE_COUNT_16_BIT; break; + case 8: samples = VK_SAMPLE_COUNT_8_BIT; break; + case 4: samples = VK_SAMPLE_COUNT_4_BIT; break; + case 2: samples = VK_SAMPLE_COUNT_2_BIT; break; + default: samples = VK_SAMPLE_COUNT_1_BIT; break; + } VkPipelineMultisampleStateCreateInfo multisampleState = ext::vulkan::initializers::pipelineMultisampleStateCreateInfo( - VK_SAMPLE_COUNT_1_BIT, + samples, 0 ); std::vector dynamicStateEnables = { @@ -460,7 +470,7 @@ void ext::vulkan::Pipeline::record( Graphic& graphic, VkCommandBuffer commandBuf } } // Bind descriptor sets describing shader binding points - vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); // Bind the rendering pipeline // The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time vkCmdBindPipeline(commandBuffer, bindPoint, pipeline); @@ -493,11 +503,46 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { } { for ( auto& shader : graphic.material.shaders ) { + std::vector buffersStorageVector; + std::vector buffersUniformsVector; + for ( auto& buffer : shader.buffers ) { + if ( buffer.usageFlags & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT ) { + auto& descriptor = buffersStorageVector.emplace_back(buffer.descriptor); + if ( descriptor.offset % device->properties.limits.minStorageBufferOffsetAlignment != 0 ) { + // std::cout << "Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor.pBufferInfo->offset << std::endl; + descriptor.offset = 0; + } + } + if ( buffer.usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ) { + auto& descriptor = buffersUniformsVector.emplace_back(buffer.descriptor); + if ( descriptor.offset % device->properties.limits.minUniformBufferOffsetAlignment != 0 ) { + // std::cout << "Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor.pBufferInfo->offset << std::endl; + descriptor.offset = 0; + } + } + } + for ( auto& buffer : graphic.buffers ) { + if ( buffer.usageFlags & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT ) { + auto& descriptor = buffersStorageVector.emplace_back(buffer.descriptor); + if ( descriptor.offset % device->properties.limits.minStorageBufferOffsetAlignment != 0 ) { + std::cout << __LINE__ << ": Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor.offset << std::endl; + descriptor.offset = 0; + } + } + if ( buffer.usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ) { + auto& descriptor = buffersUniformsVector.emplace_back(buffer.descriptor); + if ( descriptor.offset % device->properties.limits.minUniformBufferOffsetAlignment != 0 ) { + std::cout << __LINE__ << ": Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor.offset << std::endl; + descriptor.offset = 0; + } + } + } + auto textures = graphic.material.textures.begin(); auto samplers = graphic.material.samplers.begin(); - auto buffersUniforms = shader.buffers.begin(); - auto buffersStorage = graphic.buffers.begin(); auto attachments = inputDescriptors.begin(); + auto buffersStorage = buffersStorageVector.begin(); + auto buffersUniforms = buffersUniformsVector.begin(); for ( auto& layout : shader.descriptorSetLayoutBindings ) { @@ -509,9 +554,6 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { size_t imageInfosStart = imageInfos.size(); // assume we have a texture, and fill it in the slots as defaults size_t target = layout.descriptorCount; - if ( target == 132 ) { - target = graphic.material.textures.size(); - } for ( size_t i = 0; i < target; ++i ) { VkDescriptorImageInfo d = emptyTexture.descriptor; if ( textures != graphic.material.textures.end() ) { @@ -558,27 +600,38 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { imageInfo = &((samplers++)->descriptor.info); } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - if ( buffersUniforms == shader.buffers.end() ) { - std::cout << "buffersUniforms == shader.buffers.end()" << std::endl; + if ( buffersUniforms == buffersUniformsVector.end() ) { + std::cout << "buffersUniforms == buffersUniformsVector.end()" << std::endl; break; } + auto* descriptor = &(*(buffersUniforms++)); + if ( descriptor->offset % device->properties.limits.minUniformBufferOffsetAlignment != 0 ) { + std::cout << __LINE__ << ": Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor->offset << std::endl; + descriptor->offset = 0; + } writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, layout.descriptorType, layout.binding, - &((buffersUniforms++)->descriptor) + descriptor )); } break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - if ( buffersStorage == graphic.buffers.end() ) { - std::cout << "buffersStorage == graphic.buffers.end()" << std::endl; + if ( buffersStorage == buffersStorageVector.end() ) { + std::cout << "buffersStorage == buffersStorageVector.end()" << std::endl; break; } + auto* descriptor = &(*(buffersStorage++)); + if ( descriptor->offset % device->properties.limits.minStorageBufferOffsetAlignment != 0 ) { + std::cout << __LINE__ << ": Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor->offset << std::endl; + descriptor->offset = 0; + } + writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, layout.descriptorType, layout.binding, - &((buffersStorage++)->descriptor) + descriptor )); } break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { @@ -591,6 +644,16 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { } if ( imageInfo ) { + if ( layout.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ) { + if ( imageInfo->imageView == VK_NULL_HANDLE ) { + imageInfo = &emptyTexture.descriptor; + } + } + if ( layout.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ) { + if ( imageInfo->sampler == VK_NULL_HANDLE ) { + imageInfo->sampler = emptyTexture.sampler.sampler; + } + } writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, layout.descriptorType, @@ -602,6 +665,22 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { } } + for ( auto& descriptor : writeDescriptorSets ) { + if ( descriptor.pBufferInfo ) { + // std::cout << descriptor.pBufferInfo->offset << std::endl; + if ( descriptor.pBufferInfo->offset % device->properties.limits.minUniformBufferOffsetAlignment != 0 ) { + // std::cout << "Unaligned offset! Expecting " << device->properties.limits.minUniformBufferOffsetAlignment << ", got " << descriptor.pBufferInfo->offset << std::endl; + std::cout << "Invalid descriptor for buffer: " << descriptor.pBufferInfo->buffer << " " << descriptor.pBufferInfo->offset << " " << descriptor.pBufferInfo->range << ", invalidating..." << std::endl; + auto pointer = const_cast(descriptor.pBufferInfo); + pointer->offset = 0; + pointer->range = 0; + pointer->buffer = VK_NULL_HANDLE; + + // const_cast(descriptor.pBufferInfo)->offset = 0; + } + } + } + vkUpdateDescriptorSets( *device, writeDescriptorSets.size(), @@ -667,10 +746,12 @@ void ext::vulkan::Graphic::initialize( const std::string& renderModeName ) { RenderMode& renderMode = ext::vulkan::getRenderMode(renderModeName, true); this->descriptor.renderMode = renderModeName; + auto* device = renderMode.device; + if ( !device ) device = &ext::vulkan::device; - material.initialize( *renderMode.device ); + material.initialize( *device ); - ext::vulkan::Buffers::initialize( *renderMode.device ); + ext::vulkan::Buffers::initialize( *device ); } void ext::vulkan::Graphic::initializePipeline() { initializePipeline( this->descriptor, false ); diff --git a/engine/src/ext/vulkan/rendermode.cpp b/engine/src/ext/vulkan/rendermode.cpp index 6cce17b1..633e917c 100644 --- a/engine/src/ext/vulkan/rendermode.cpp +++ b/engine/src/ext/vulkan/rendermode.cpp @@ -80,14 +80,14 @@ void ext::vulkan::RenderMode::render() { submitInfo.commandBufferCount = 1; // Submit to the graphics queue passing a wait fence - VK_CHECK_RESULT(vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer])); + VK_CHECK_RESULT(vkQueueSubmit( device->getQueue( Device::QueueEnum::GRAPHICS ), 1, &submitInfo, fences[currentBuffer])); //vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer]); // Present the current buffer to the swap chain // Pass the semaphore signaled by the command buffer submission from the submit info as the wait semaphore for swap chain presentation // This ensures that the image is not presented to the windowing system until all commands have been submitted - VK_CHECK_RESULT(swapchain.queuePresent(device->queues.present, currentBuffer, renderCompleteSemaphore)); - VK_CHECK_RESULT(vkQueueWaitIdle(device->queues.present)); + VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( Device::QueueEnum::PRESENT ), currentBuffer, renderCompleteSemaphore)); + VK_CHECK_RESULT(vkQueueWaitIdle(device->getQueue( Device::QueueEnum::PRESENT ))); } void ext::vulkan::RenderMode::initialize( Device& device ) { @@ -105,7 +105,7 @@ void ext::vulkan::RenderMode::initialize( Device& device ) { commands.resize( swapchain.buffers ); VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( - this->getType() == "Compute" ? device.commandPool.compute : device.commandPool.graphics, + this->getType() == "Compute" ? device.getCommandPool(Device::QueueEnum::COMPUTE) : device.getCommandPool(Device::QueueEnum::GRAPHICS), VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast(commands.size()) ); @@ -145,20 +145,23 @@ void ext::vulkan::RenderMode::destroy() { renderTarget.destroy(); if ( commands.size() > 0 ) { - vkFreeCommandBuffers( *device, this->getType() == "Compute" ? device->commandPool.compute : device->commandPool.graphics, static_cast(commands.size()), commands.data()); + vkFreeCommandBuffers( *device, this->getType() == "Compute" ? device->getCommandPool(Device::QueueEnum::COMPUTE) : device->getCommandPool(Device::QueueEnum::GRAPHICS), static_cast(commands.size()), commands.data()); } + commands.clear(); if ( renderCompleteSemaphore != VK_NULL_HANDLE ) { vkDestroySemaphore( *device, renderCompleteSemaphore, nullptr); + renderCompleteSemaphore = VK_NULL_HANDLE; } for ( auto& fence : fences ) { vkDestroyFence( *device, fence, nullptr); - fence = VK_NULL_HANDLE; } + fences.clear(); } void ext::vulkan::RenderMode::synchronize( uint64_t timeout ) { if ( !device ) return; + if ( fences.empty() ) return; VK_CHECK_RESULT(vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, timeout )); } void ext::vulkan::RenderMode::pipelineBarrier( VkCommandBuffer command, uint8_t stage ) { diff --git a/engine/src/ext/vulkan/rendermodes/compute.cpp b/engine/src/ext/vulkan/rendermodes/compute.cpp index 8f8aea25..27863c27 100644 --- a/engine/src/ext/vulkan/rendermodes/compute.cpp +++ b/engine/src/ext/vulkan/rendermodes/compute.cpp @@ -115,7 +115,7 @@ void ext::vulkan::ComputeRenderMode::render() { submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commands[currentBuffer]; - VK_CHECK_RESULT(vkQueueSubmit(device->queues.compute, 1, &submitInfo, fences[currentBuffer])); + VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( Device::QueueEnum::COMPUTE ), 1, &submitInfo, fences[currentBuffer])); //vkQueueSubmit(device->queues.compute, 1, &submitInfo, fences[currentBuffer]); } void ext::vulkan::ComputeRenderMode::tick() { diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index ad58dc5b..dc369b0e 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -103,13 +103,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { { auto& scene = uf::scene::getCurrentScene(); auto& metadata = scene.getComponent(); - /* - struct SpecializationConstant { - int32_t maxLights = 16; - }; - auto& specializationConstants = shader.specializationConstants.get(); - specializationConstants.maxLights = metadata["system"]["config"]["engine"]["scenes"]["max lights"].asUInt64(); - */ + auto& shader = blitter.material.shaders.back(); struct SpecializationConstant { uint32_t maxLights = 16; @@ -120,6 +114,41 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { if ( binding.descriptorCount > 1 ) binding.descriptorCount = specializationConstants->maxLights; } + /* + std::vector palette; + // size of palette + if ( metadata["system"]["config"]["engine"]["scenes"]["palette"].isNumeric() ) { + size_t size = metadata["system"]["config"]["engine"]["scenes"]["palette"].asUInt64(); + for ( size_t x = 0; x < size; ++x ) { + palette.push_back(pod::Vector4f{ + (128.0f + 128.0f * sin(3.1415f * x / 16.0f)) / 256.0f, + (128.0f + 128.0f * sin(3.1415f * x / 32.0f)) / 256.0f, + (128.0f + 128.0f * sin(3.1415f * x / 64.0f)) / 256.0f, + 1.0f, + }); + } + // palette array + } else if ( metadata["system"]["config"]["engine"]["scenes"]["palette"].isArray() ) { + for ( int i = 0; i < metadata["system"]["config"]["engine"]["scenes"]["palette"].size(); ++i ) { + auto& color = palette.emplace_back(); + palette.push_back(pod::Vector4f{ + metadata["system"]["config"]["engine"]["scenes"]["palette"][i][0].asFloat(), + metadata["system"]["config"]["engine"]["scenes"]["palette"][i][1].asFloat(), + metadata["system"]["config"]["engine"]["scenes"]["palette"][i][2].asFloat(), + metadata["system"]["config"]["engine"]["scenes"]["palette"][i][3].asFloat(), + }); + } + } + if ( !palette.empty() ) { + shader.initializeBuffer( + (void*) palette.data(), + palette.size() * sizeof(pod::Vector4f), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + true + ); + } + */ } blitter.initializePipeline(); } @@ -133,6 +162,7 @@ void ext::vulkan::DeferredRenderMode::tick() { if ( blitter.initialized ) { auto& pipeline = blitter.getPipeline(); pipeline.update( blitter ); + ext::vulkan::rebuild = true; } } } diff --git a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp index 6c4b67bc..4eea32d8 100644 --- a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp +++ b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp @@ -145,7 +145,7 @@ void ext::vulkan::RenderTargetRenderMode::render() { submitInfo.pCommandBuffers = &commands[currentBuffer]; // Command buffers(s) to execute in this batch (submission) submitInfo.commandBufferCount = 1; - VK_CHECK_RESULT(vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer])); + VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( Device::QueueEnum::GRAPHICS ), 1, &submitInfo, fences[currentBuffer])); //vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer]); /* VkSemaphoreWaitInfo waitInfo = {}; diff --git a/engine/src/ext/vulkan/rendertarget.cpp b/engine/src/ext/vulkan/rendertarget.cpp index cf78e57d..3d9a6495 100644 --- a/engine/src/ext/vulkan/rendertarget.cpp +++ b/engine/src/ext/vulkan/rendertarget.cpp @@ -48,9 +48,21 @@ size_t ext::vulkan::RenderTarget::attach( VkFormat format, VkImageUsageFlags usa aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + switch ( ext::vulkan::msaa ) { + case 64: samples = VK_SAMPLE_COUNT_64_BIT; break; + case 32: samples = VK_SAMPLE_COUNT_32_BIT; break; + case 16: samples = VK_SAMPLE_COUNT_16_BIT; break; + case 8: samples = VK_SAMPLE_COUNT_8_BIT; break; + case 4: samples = VK_SAMPLE_COUNT_4_BIT; break; + case 2: samples = VK_SAMPLE_COUNT_2_BIT; break; + default: samples = VK_SAMPLE_COUNT_1_BIT; break; + } + attachment->layout = layout; attachment->format = format; attachment->usage = usage; + attachment->samples = samples; VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -59,7 +71,7 @@ size_t ext::vulkan::RenderTarget::attach( VkFormat format, VkImageUsageFlags usa imageCreateInfo.extent = { width, height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = USEVR ? 2 : 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.samples = samples; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = usage; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -151,7 +163,7 @@ void ext::vulkan::RenderTarget::initialize( Device& device ) { for ( auto& attachment : this->attachments ) { VkAttachmentDescription description; description.format = attachment.format; - description.samples = VK_SAMPLE_COUNT_1_BIT; + description.samples = attachment.samples; description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index ce7f99fb..180ca1eb 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -7,22 +7,29 @@ ext::vulkan::Texture2D ext::vulkan::Texture2D::empty; void ext::vulkan::Sampler::initialize( Device& device ) { this->device = &device; - + if ( device.enabledFeatures.samplerAnisotropy == VK_FALSE ) { + descriptor.anisotropy.enabled = VK_FALSE; + } { VkSamplerCreateInfo samplerCreateInfo = {}; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.minFilter = descriptor.filter.min; samplerCreateInfo.magFilter = descriptor.filter.mag; + samplerCreateInfo.borderColor = descriptor.filter.borderColor; samplerCreateInfo.addressModeU = descriptor.addressMode.u; samplerCreateInfo.addressModeV = descriptor.addressMode.v; samplerCreateInfo.addressModeW = descriptor.addressMode.w; + samplerCreateInfo.unnormalizedCoordinates = descriptor.addressMode.unnormalizedCoordinates; samplerCreateInfo.mipmapMode = descriptor.mip.mode; samplerCreateInfo.mipLodBias = descriptor.mip.lodBias; - samplerCreateInfo.compareOp = descriptor.compareOp; - samplerCreateInfo.minLod = descriptor.lod.min; - samplerCreateInfo.maxLod = descriptor.lod.max; - samplerCreateInfo.maxAnisotropy = descriptor.maxAnisotropy; + samplerCreateInfo.compareEnable = descriptor.mip.compareEnable; + samplerCreateInfo.compareOp = descriptor.mip.compareOp; + samplerCreateInfo.minLod = descriptor.mip.min; + samplerCreateInfo.maxLod = descriptor.mip.max; + + samplerCreateInfo.anisotropyEnable = descriptor.anisotropy.enabled; + samplerCreateInfo.maxAnisotropy = descriptor.anisotropy.max; VK_CHECK_RESULT(vkCreateSampler(device.logicalDevice, &samplerCreateInfo, nullptr, &sampler)); } { @@ -72,9 +79,7 @@ void ext::vulkan::Texture::setImageLayout( VkImage image, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, - VkImageSubresourceRange subresourceRange, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask + VkImageSubresourceRange subresourceRange ) { // Create an image barrier object VkImageMemoryBarrier imageMemoryBarrier = ext::vulkan::initializers::imageMemoryBarrier(); @@ -83,6 +88,9 @@ void ext::vulkan::Texture::setImageLayout( imageMemoryBarrier.image = image; imageMemoryBarrier.subresourceRange = subresourceRange; + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + // Source layouts (old) // Source access mask controls actions that have to be finished on the old layout // before it will be transitioned to the new layout @@ -165,6 +173,20 @@ void ext::vulkan::Texture::setImageLayout( break; } + if ( oldImageLayout == VK_IMAGE_LAYOUT_UNDEFINED && newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) { + imageMemoryBarrier.srcAccessMask = 0; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if ( oldImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + // Put barrier inside setup command buffer vkCmdPipelineBarrier( cmdbuffer, @@ -181,32 +203,29 @@ void ext::vulkan::Texture::setImageLayout( VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask + uint32_t mipLevels ) { VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = aspectMask; subresourceRange.baseMipLevel = 0; - subresourceRange.levelCount = 1; + subresourceRange.levelCount = mipLevels; subresourceRange.layerCount = 1; - setImageLayout(cmdbuffer, image, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask); + setImageLayout(cmdbuffer, image, oldImageLayout, newImageLayout, subresourceRange); } void ext::vulkan::Texture2D::loadFromFile( std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags, - VkImageLayout imageLayout, - bool forceLinear + VkImageLayout imageLayout ) { - return loadFromFile( filename, ext::vulkan::device, format, imageUsageFlags, imageLayout, forceLinear ); + return loadFromFile( filename, ext::vulkan::device, format, imageUsageFlags, imageLayout ); } void ext::vulkan::Texture2D::loadFromFile( std::string filename, Device& device, VkFormat format, VkImageUsageFlags imageUsageFlags, - VkImageLayout imageLayout, - bool forceLinear + VkImageLayout imageLayout ) { uf::Image image; image.open( filename ); @@ -281,18 +300,16 @@ void ext::vulkan::Texture2D::loadFromImage( uf::Image& image, VkFormat format, VkImageUsageFlags imageUsageFlags, - VkImageLayout imageLayout, - bool forceLinear + VkImageLayout imageLayout ) { - return loadFromImage( image, ext::vulkan::device, format, imageUsageFlags, imageLayout, forceLinear ); + return loadFromImage( image, ext::vulkan::device, format, imageUsageFlags, imageLayout ); } void ext::vulkan::Texture2D::loadFromImage( uf::Image& image, Device& device, VkFormat format, VkImageUsageFlags imageUsageFlags, - VkImageLayout imageLayout, - bool forceLinear + VkImageLayout imageLayout ) { switch ( image.getChannels() ) { // R @@ -358,37 +375,12 @@ void ext::vulkan::Texture2D::fromBuffers( VkImageLayout imageLayout ) { this->initialize(device, texWidth, texHeight); - this->mips = 1; - - // Use a separate command buffer for texture loading - VkCommandBuffer copyCmd = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // - Buffer staging; - - VK_CHECK_RESULT(device.createBuffer( - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - staging, - bufferSize, - data - )); - - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = 0; - bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = width; - bufferCopyRegion.imageExtent.height = height; - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = 0; // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = ext::vulkan::initializers::imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = this->mips; + imageCreateInfo.format = this->format = format; + imageCreateInfo.mipLevels = this->mips = static_cast(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; @@ -396,6 +388,10 @@ void ext::vulkan::Texture2D::fromBuffers( imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { width, height, 1 }; imageCreateInfo.usage = imageUsageFlags; + // Ensure that the TRANSFER_SRC bit is set for mip creation + if ( this->mips > 1 && !(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) { + imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } // Ensure that the TRANSFER_DST bit is set for staging if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; @@ -405,62 +401,19 @@ void ext::vulkan::Texture2D::fromBuffers( allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; VK_CHECK_RESULT(vmaCreateImage(allocator, &imageCreateInfo, &allocInfo, &image, &allocation, &allocationInfo)); deviceMemory = allocationInfo.deviceMemory; -/* - VK_CHECK_RESULT(vkCreateImage(device.logicalDevice, &imageCreateInfo, nullptr, &image)); - VkMemoryAllocateInfo memAlloc = staging.memAlloc; - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device.logicalDevice, image, &memReqs); - memAlloc.allocationSize = staging.memReqs.size; - memAlloc.memoryTypeIndex = device.getMemoryType(staging.memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device.logicalDevice, &memAlloc, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device.logicalDevice, image, deviceMemory, 0)); -*/ + // Create sampler + sampler.descriptor.mip.min = 0; + sampler.descriptor.mip.max = static_cast(this->mips); + sampler.initialize( device ); + + // Create image view VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = this->mips; subresourceRange.layerCount = 1; - - // Image barrier for optimal image (target) - // Optimal image will be used as destination for the copy - setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange - ); - // Copy mip levels from staging buffer - vkCmdCopyBufferToImage( - copyCmd, - staging.buffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &bufferCopyRegion - ); - // Change texture image layout to shader read after all mip levels have been copied - this->imageLayout = imageLayout; - setImageLayout( - copyCmd, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - imageLayout, - subresourceRange - ); - device.flushCommandBuffer(copyCmd); - // Clean up staging resources -/* - vkFreeMemory(device.logicalDevice, staging.memory, nullptr); - vkDestroyBuffer(device.logicalDevice, staging.buffer, nullptr); -*/ - staging.destroy(); - // Create sampler - sampler.initialize( device ); - - // Create image view VkImageViewCreateInfo viewCreateInfo = {}; viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewCreateInfo.pNext = NULL; @@ -471,7 +424,8 @@ void ext::vulkan::Texture2D::fromBuffers( viewCreateInfo.subresourceRange.levelCount = 1; viewCreateInfo.image = image; VK_CHECK_RESULT(vkCreateImageView(device.logicalDevice, &viewCreateInfo, nullptr, &view)); - // Update descriptor image info member that can be used for setting up descriptor sets + + this->update( data, bufferSize, imageLayout ); this->updateDescriptors(); } @@ -505,14 +459,7 @@ void ext::vulkan::Texture2D::asRenderTarget( Device& device, uint32_t width, uin allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; VK_CHECK_RESULT(vmaCreateImage(allocator, &imageCreateInfo, &allocInfo, &image, &allocation, &allocationInfo)); deviceMemory = allocationInfo.deviceMemory; -/* - VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &image)); - vkGetImageMemoryRequirements(device, image, &memReqs); - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &deviceMemory)); - VK_CHECK_RESULT(vkBindImageMemory(device, image, deviceMemory, 0)); -*/ + VkCommandBuffer layoutCmd = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -521,7 +468,8 @@ void ext::vulkan::Texture2D::asRenderTarget( Device& device, uint32_t width, uin image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - imageLayout + imageLayout, + this->mips ); device.flushCommandBuffer(layoutCmd, true); @@ -551,4 +499,177 @@ void ext::vulkan::Texture2D::aliasAttachment( const RenderTarget::Attachment& at if ( createSampler ) sampler.initialize( ext::vulkan::device ); this->updateDescriptors(); +} + +void ext::vulkan::Texture2D::update( uf::Image& image, VkImageLayout targetImageLayout ) { + if ( width != image.getDimensions()[0] || height != image.getDimensions()[1] ) + return; + return this->update( (void*) image.getPixelsPtr(), image.getPixels().size() ); +} +void ext::vulkan::Texture2D::update( void* data, VkDeviceSize bufferSize, VkImageLayout targetImageLayout ) { + // Update descriptor image info member that can be used for setting up descriptor sets + + auto& device = *this->device; + + if ( targetImageLayout == VK_IMAGE_LAYOUT_UNDEFINED ) { + targetImageLayout = this->imageLayout; + } + + // Create image view + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = this->mips; + subresourceRange.layerCount = 1; + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = width; + bufferCopyRegion.imageExtent.height = height; + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = 0; + // + Buffer staging; + VK_CHECK_RESULT(device.createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + staging, + bufferSize, + data + )); + + // Use a separate command buffer for texture loading + VkCommandBuffer commandBuffer = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + // Image barrier for optimal image (target) + // Optimal image will be used as destination for the copy + setImageLayout( + commandBuffer, + image, + imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange + ); + + // Copy mip levels from staging buffer + vkCmdCopyBufferToImage( + commandBuffer, + staging.buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &bufferCopyRegion + ); + + this->generateMipmaps(commandBuffer); + + setImageLayout( + commandBuffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + targetImageLayout, + subresourceRange + ); + device.flushCommandBuffer(commandBuffer); + // Clean up staging resources + staging.destroy(); + + this->imageLayout = targetImageLayout; + + this->updateDescriptors(); +} + +void ext::vulkan::Texture2D::generateMipmaps( VkCommandBuffer commandBuffer ) { + auto& device = *this->device; + + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties); + if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { + throw std::runtime_error("texture image format does not support linear blitting!"); + } + + + // base layer barrier + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + + int32_t mipWidth = width; + int32_t mipHeight = height; + for ( size_t i = 1; i < this->mips; ++i ) { + + // transition previous layer to read from it + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + // blit to current mip layer + VkImageBlit blit{}; + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, 1 }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage( + commandBuffer, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, + VK_FILTER_LINEAR + ); + + // transition previous layer back + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + + if (mipWidth > 1) mipWidth /= 2; + if (mipHeight > 1) mipHeight /= 2; + } +/* + barrier.subresourceRange.baseMipLevel = this->mips - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); +*/ } \ No newline at end of file diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index 6cf25580..535d3de6 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -10,8 +10,11 @@ uint32_t ext::vulkan::width = 1280; uint32_t ext::vulkan::height = 720; +uint8_t ext::vulkan::msaa = 1; bool ext::vulkan::validation = true; +bool ext::vulkan::rebuildOnTickStart = false; +bool ext::vulkan::waitOnRenderEnd = false; std::vector ext::vulkan::validationFilters; std::vector ext::vulkan::requestedDeviceFeatures; std::vector ext::vulkan::requestedDeviceExtensions; @@ -49,7 +52,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL ext::vulkan::debugCallback( const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData ) { - if ( messageSeverity <= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT ) return VK_FALSE; +// if ( messageSeverity <= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT ) return VK_FALSE; std::string message = pCallbackData->pMessage; for ( auto& filter : ext::vulkan::validationFilters ) { if ( message.find(filter) != std::string::npos ) return VK_FALSE; @@ -276,15 +279,14 @@ void ext::vulkan::initialize( uint8_t stage ) { } void ext::vulkan::tick() { ext::vulkan::mutex.lock(); - if ( ext::vulkan::resized ) ext::vulkan::rebuild = true; + if ( ext::vulkan::resized || ext::vulkan::rebuildOnTickStart ) { + ext::vulkan::rebuild = true; + } std::function filter = [&]( uf::Entity* entity ) { if ( !entity->hasComponent() ) return; ext::vulkan::Graphic& graphic = entity->getComponent(); - if ( graphic.initialized ) return; - - if ( !graphic.process ) return; - if ( graphic.initialized ) return; + if ( graphic.initialized || !graphic.process || graphic.initialized ) return; graphic.initializePipeline(); ext::vulkan::rebuild = true; }; @@ -310,7 +312,7 @@ void ext::vulkan::tick() { ext::vulkan::mutex.unlock(); } void ext::vulkan::render() { -// ext::vulkan::mutex.lock(); + ext::vulkan::mutex.lock(); if ( hasRenderMode("Gui", true) ) { RenderMode& primary = getRenderMode("Gui", true); auto it = std::find( renderModes.begin(), renderModes.end(), &primary ); @@ -331,7 +333,15 @@ void ext::vulkan::render() { } ext::vulkan::currentRenderMode = NULL; -// ext::vulkan::mutex.unlock(); + if ( ext::vulkan::waitOnRenderEnd ) { + for ( auto& renderMode : renderModes ) { + if ( !renderMode ) continue; + if ( !renderMode->execute ) continue; + renderMode->synchronize(); + } + vkDeviceWaitIdle( device ); + } + ext::vulkan::mutex.unlock(); } void ext::vulkan::destroy() { ext::vulkan::mutex.lock(); diff --git a/engine/src/utils/image/atlas.cpp b/engine/src/utils/image/atlas.cpp index 1046ca66..93a9e7b6 100644 --- a/engine/src/utils/image/atlas.cpp +++ b/engine/src/utils/image/atlas.cpp @@ -60,10 +60,10 @@ void uf::Atlas::generate() { auto& dstBuffer = this->m_image.getPixels(); for ( size_t i = 0; i < size.x * size.y * 4; i+=4 ) { - dstBuffer[i+0] = 255; + dstBuffer[i+0] = 0; dstBuffer[i+1] = 0; - dstBuffer[i+2] = 255; - dstBuffer[i+3] = 255; + dstBuffer[i+2] = 0; + dstBuffer[i+3] = 0; } for ( auto& it : stored.Get() ) { @@ -84,6 +84,11 @@ void uf::Atlas::generate() { } } } +void uf::Atlas::clear() { + this->m_images.clear(); + this->m_image.clear(); + this->m_atlas = uf::Atlas::atlas_t{}; +} pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) { BinPack2D::ContentAccumulator stored; this->m_atlas.CollectContent( stored ); diff --git a/engine/src/utils/string/ext.cpp b/engine/src/utils/string/ext.cpp index 86e8eaf2..e9640f0e 100644 --- a/engine/src/utils/string/ext.cpp +++ b/engine/src/utils/string/ext.cpp @@ -1,5 +1,9 @@ #include #include +#include +#include +#include +#include std::string UF_API uf::string::lowercase( const std::string& str ) { std::string lower = str; @@ -27,4 +31,73 @@ std::string UF_API uf::string::replace( const std::string& string, const std::st if( start_pos == std::string::npos ) return result; result.replace(start_pos, search.length(), replace); return result; +} +std::string UF_API uf::string::si( double value, const std::string& unit, size_t precision ) { + int power = floor(std::log10( value )); + double base = value / std::pow( 10, power ); + +// std::cout << base << " x 10^" << power << " -> "; + + size_t pValue = -1; + #define REDUCE(VAL)\ + while ( pValue > power && power > VAL ) {\ + --power;\ + base *= 10;\ + }\ + pValue = VAL; + #define INCREASE(VAL)\ + if ( pValue < power && power < VAL ) std::cout << " (increasing (" << pValue << ", " << VAL << ")) ";\ + while ( pValue < power && power < VAL ) {\ + ++power;\ + base /= 10;\ + }\ + pValue = VAL; + + REDUCE(24) + REDUCE(21) + REDUCE(18) + REDUCE(15) + REDUCE(12) + REDUCE( 9) + REDUCE( 6) + REDUCE( 3) +// REDUCE( 2) +// REDUCE( 1) + REDUCE( 0) +/* +// INCREASE(-1) + INCREASE(-2) + INCREASE(-3) + INCREASE(-6) + INCREASE(-9) + INCREASE(-12) +*/ + + // std::cout << base << " x 10^" << power << std::endl; + + std::stringstream ss; + ss << std::fixed << std::setprecision(precision) << base; + std::string string = ss.str(); + + switch ( power ) { + case 24: string += "Y"; break; + case 21: string += "Z"; break; + case 18: string += "E"; break; + case 15: string += "P"; break; + case 12: string += "T"; break; + case 9: string += "G"; break; + case 6: string += "M"; break; + case 3: string += "k"; break; + // case 2: string += "h"; break; + // case 1: string += "da"; break; + case 0: string += ""; break; + // case -1: string += "d"; break; + case -2: string += "c"; break; + case -3: string += "m"; break; + case -6: string += "μ"; break; + case -9: string += "n"; break; + case-12: string += "p"; break; + } + string += unit; + return string; } \ No newline at end of file diff --git a/ext/behaviors/light/behavior.cpp b/ext/behaviors/light/behavior.cpp index aac3a579..4d5667fd 100644 --- a/ext/behaviors/light/behavior.cpp +++ b/ext/behaviors/light/behavior.cpp @@ -172,6 +172,7 @@ void ext::LightBehavior::destroy( uf::Object& self ){ if ( this->hasComponent() ) { auto& renderMode = this->getComponent(); uf::renderer::removeRenderMode( &renderMode, false ); + this->deleteComponent(); } } #undef this \ No newline at end of file diff --git a/ext/behaviors/portal/behavior.cpp b/ext/behaviors/portal/behavior.cpp index 26d6fae5..6ec46f07 100644 --- a/ext/behaviors/portal/behavior.cpp +++ b/ext/behaviors/portal/behavior.cpp @@ -158,5 +158,6 @@ void ext::PortalBehavior::render( uf::Object& self ){ void ext::PortalBehavior::destroy( uf::Object& self ){ auto& renderMode = this->getComponent(); uf::renderer::removeRenderMode( &renderMode, false ); + this->deleteComponent(); } #undef this \ No newline at end of file diff --git a/ext/behaviors/scene/behavior.cpp b/ext/behaviors/scene/behavior.cpp index 7d545e03..52e090d7 100644 --- a/ext/behaviors/scene/behavior.cpp +++ b/ext/behaviors/scene/behavior.cpp @@ -85,7 +85,7 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { timer.reset(); uf::Serializer json = event; - uf::Object* manager = (uf::Object*) this->findByName("Gui Manager"); + uf::Object* manager = (uf::Object*) this->globalFindByName("Gui Manager"); if ( !manager ) return "false"; uf::Serializer payload; // uf::Object* gui = (uf::Object*) manager->findByUid( (payload["uid"] = manager->loadChildUid("/scenes/worldscape/gui/pause/menu.json", false)).asUInt64() ); @@ -109,8 +109,10 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { return "true"; }); /* store viewport size */ { - metadata["window"]["size"]["x"] = uf::renderer::width; - metadata["window"]["size"]["y"] = uf::renderer::height; + metadata["system"]["window"]["size"]["x"] = uf::renderer::width; + metadata["system"]["window"]["size"]["y"] = uf::renderer::height; + ext::gui::size.current.x = uf::renderer::width; + ext::gui::size.current.y = uf::renderer::height; this->addHook( "window:Resized", [&](const std::string& event)->std::string{ uf::Serializer json = event; @@ -120,7 +122,8 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { size.y = json["window"]["size"]["y"].asUInt64(); } - metadata["window"] = json["window"]; + metadata["system"]["window"] = json["system"]["window"]; + ext::gui::size.current = size; return "true"; }); @@ -160,7 +163,7 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { } /* Regain control if nothing requests it */ { - uf::Object* menu = (uf::Object*) this->findByName("Gui: Menu"); + uf::Object* menu = (uf::Object*) this->globalFindByName("Gui: Menu"); if ( !menu ) { uf::Serializer payload; payload["state"] = false; @@ -245,17 +248,20 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { alignas(16) pod::Vector4f ambient; struct Mode { alignas(8) pod::Vector2ui type; + alignas(8) pod::Vector2ui padding; alignas(16) pod::Vector4f parameters; } mode; struct { - alignas(8) pod::Vector2f range; alignas(16) pod::Vector4f color; + alignas(8) pod::Vector2f range; + alignas(8) pod::Vector2f padding; } fog; struct Light { alignas(16) pod::Vector4f position; alignas(16) pod::Vector4f color; alignas(4) uint32_t type; alignas(4) float depthBias; + alignas(8) pod::Vector2f padding; alignas(16) pod::Matrix4f view; alignas(16) pod::Matrix4f projection; } lights; diff --git a/ext/behaviors/staticemitter/behavior.cpp b/ext/behaviors/staticemitter/behavior.cpp index a90e6489..6b19b69a 100644 --- a/ext/behaviors/staticemitter/behavior.cpp +++ b/ext/behaviors/staticemitter/behavior.cpp @@ -4,15 +4,15 @@ #include #include +#include #include -#include - EXT_BEHAVIOR_REGISTER_CPP(StaticEmitterBehavior) -#define this ((uf::Scene*) &self) - +#define this ((uf::Object*) &self) void ext::StaticEmitterBehavior::initialize( uf::Object& self ) { - + auto& metadata = this->getComponent(); + auto* hud = this->loadChildPointer("/hud.json", true); + metadata["hud"]["uid"] = hud->getUid(); } void ext::StaticEmitterBehavior::tick( uf::Object& self ) { auto& scene = uf::scene::getCurrentScene(); @@ -37,9 +37,7 @@ void ext::StaticEmitterBehavior::tick( uf::Object& self ) { float pieces = metadata["static"]["pieces"].isNumeric() ? metadata["static"]["pieces"].asFloat() : 1000; uint32_t mode = sMetadata["system"]["renderer"]["shader"]["mode"].asUInt(); - if ( !(mode & 0x2) ) { - mode = mode | 0x2; - } + mode |= (0x1 << 1); sMetadata["system"]["renderer"]["shader"]["mode"] = mode; sMetadata["system"]["renderer"]["shader"]["parameters"][0] = flicker; @@ -47,6 +45,22 @@ void ext::StaticEmitterBehavior::tick( uf::Object& self ) { sMetadata["system"]["renderer"]["shader"]["parameters"][2] = staticBlend; sMetadata["system"]["renderer"]["shader"]["parameters"][3] = "time"; } + + static uf::Timer timer(false); + if ( !timer.running() ) timer.start(); + if ( metadata["hud"]["uid"].isNumeric() && timer.elapsed().asDouble() > 0.125 ) { + timer.reset(); + auto* hud = this->findByUid( metadata["hud"]["uid"].asUInt64() ); + auto& hMetadata = hud->getComponent(); + double d = uf::vector::distance( cTransform.position, transform.position ); + std::string string = uf::string::si( d, "m" ); + hMetadata["text settings"]["alpha"] = 0.5f; + if ( hMetadata["text settings"]["string"].asString() != string ) { + uf::Serializer payload; + payload["string"] = string; + hud->as().callHook("gui:UpdateString.%UID%", payload); + } + } } void ext::StaticEmitterBehavior::render( uf::Object& self ){} diff --git a/ext/gui/behavior.cpp b/ext/gui/behavior.cpp index b8e42a76..3e3187ee 100644 --- a/ext/gui/behavior.cpp +++ b/ext/gui/behavior.cpp @@ -1,6 +1,8 @@ #include "behavior.h" +#include "gui.h" #include +#include #include #include #include @@ -35,238 +37,147 @@ namespace { std::unordered_map> cache; } glyphs; + uf::Serializer defaultSettings; +} + +ext::Gui::Gui(){ + this->addBehavior(); +} + +std::vector ext::Gui::generateGlyphs( const std::string& _string ) { + uf::Object& gui = *this; + std::string string = _string; + uf::Serializer& metadata = gui.getComponent(); + std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); + if ( ::glyphs.cache[font].empty() ) { + ext::freetype::initialize( ::glyphs.glyph, font ); + } + if ( string == "" ) { + string = metadata["text settings"]["string"].asString(); + } + pod::Transform<>& transform = gui.getComponent>(); + std::vector gs; struct { - pod::Vector2ui current = { - uf::renderer::width, - uf::renderer::height, - }; - pod::Vector2ui reference = { - 1920, - 1080, - }; - } size; -/* - std::mutex mutex; - uint64_t uid = 0; - struct Job { - uint64_t uid; - }; - std::queue jobs; -*/ - struct GlyphDescriptor { struct { - alignas(16) pod::Matrix4f model[2]; - } matrices; + float x = 0; + float y = 0; + } origin; struct { - alignas(16) pod::Vector4f offset; - alignas(16) pod::Vector4f color; - alignas(4) int32_t mode = 0; - alignas(4) float depth = 0.0f; - alignas(4) int32_t sdf = false; - alignas(4) int32_t shadowbox = false; - alignas(16) pod::Vector4f stroke; - alignas(4) float weight; - alignas(4) int32_t spread; - alignas(4) float scale; - } gui; - }; - - struct GlyphBox { + float x = 0; + float y = 0; + } cursor; struct { - float x, y, w, h; - } box; - uint64_t code; - pod::Vector3f color; - }; - - pod::Matrix4 matrix; - - std::vector<::GlyphBox> generateGlyphs( uf::Object& gui, std::string string = "" ) { - uf::Serializer& metadata = gui.getComponent(); - std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); - if ( ::glyphs.cache[font].empty() ) { - ext::freetype::initialize( ::glyphs.glyph, font ); - } - if ( string == "" ) { - string = metadata["text settings"]["string"].asString(); - } - pod::Transform<>& transform = gui.getComponent>(); - std::vector<::GlyphBox> gs; - struct { - struct { - float x = 0; - float y = 0; - } origin; - struct { - float x = 0; - float y = 0; - } cursor; - struct { - float sum = 0; - float len = 0; - float proc = 0; - float tab = 4; - } average; - - struct { - float x = 0; - float y = 0; - } biggest; - - struct { - float w = 0; - float h = 0; - } box; - - struct { - std::vector container; - std::size_t index = 0; - } colors; - } stat; - - float scale = metadata["text settings"]["scale"].asFloat(); - - { - pod::Vector3f color = { - metadata["text settings"]["color"][0].asFloat(), - metadata["text settings"]["color"][1].asFloat(), - metadata["text settings"]["color"][2].asFloat(), - }; - stat.colors.container.push_back(color); - } - - unsigned long COLORCONTROL = 0x7F; - - { - std::string text = string; - std::regex regex("\\%\\#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})\\%"); - std::unordered_map colors; - std::smatch match; - bool matched = false; - int maxTries = 128; - while ( matched = std::regex_search( text, match, regex ) && --maxTries > 0 ) { - struct { - std::string str; - int dec; - } r, g, b; - r.str = match[1].str(); - g.str = match[2].str(); - b.str = match[3].str(); - - { std::stringstream stream; stream << r.str; stream >> std::hex >> r.dec; } - { std::stringstream stream; stream << g.str; stream >> std::hex >> g.dec; } - { std::stringstream stream; stream << b.str; stream >> std::hex >> b.dec; } - - pod::Vector3f color = { r.dec / 255.0f, g.dec / 255.0f, b.dec / 255.0f }; - - stat.colors.container.push_back(color); - text = uf::string::replace( text, "%#" + r.str + g.str + b.str + "%", "\x7F" ); - } - if ( maxTries == 0 ) { - text += "\n(error formatting)"; - } - string = text; - } - - std::wstring_convert, wchar_t> convert; - std::wstring str = convert.from_bytes(string); - if ( str.size() == 0 ) return gs; - - // Calculate statistics - { - // Find tallest glyph for new line - for ( auto it = str.begin(); it != str.end(); ++it ) { - unsigned long c = *it; if ( c == '\n' ) continue; if ( c == '\t' ) continue; if ( c == 0x01 ) continue; - std::string key = ""; { - key += std::to_string(c) + ";"; - key += metadata["text settings"]["padding"][0].asString() + ","; - key += metadata["text settings"]["padding"][1].asString() + ";"; - key += metadata["text settings"]["spread"].asString() + ";"; - key += metadata["text settings"]["size"].asString() + ";"; - key += metadata["text settings"]["font"].asString() + ";"; - key += metadata["text settings"]["sdf"].asString(); - } - uf::Glyph& glyph = ::glyphs.cache[font][key]; - - if ( !glyph.generated() ) { - glyph.setPadding( { metadata["text settings"]["padding"][0].asUInt(), metadata["text settings"]["padding"][1].asUInt() } ); - glyph.setSpread( metadata["text settings"]["spread"].asUInt() ); - if ( metadata["text settings"]["sdf"].asBool() ) glyph.useSdf(true); - glyph.generate( ::glyphs.glyph, c, metadata["text settings"]["size"].asInt() ); - } - - stat.biggest.x = std::max( (float) stat.biggest.x, (float) glyph.getSize().x); - stat.biggest.y = std::max( (float) stat.biggest.y, (float) glyph.getSize().y); - - stat.average.sum += glyph.getSize().x; - ++stat.average.len; - } - stat.average.proc = stat.average.sum / stat.average.len; - stat.average.tab *= stat.average.proc; - - // Calculate box: Second pass required because of tab - stat.cursor.x = 0; - stat.cursor.y = 0; - stat.origin.x = 0; - stat.origin.y = 0; - - for ( auto it = str.begin(); it != str.end(); ++it ) { - unsigned long c = *it; if ( c == '\n' ) { - stat.cursor.y += stat.biggest.y; - stat.cursor.x = 0; - continue; - } else if ( c == '\t' ) { - // Fixed movement vs Real Tabbing - if ( false ) { - stat.cursor.x += stat.average.tab; - } else { - stat.cursor.x = ((stat.cursor.x / stat.average.tab) + 1) * stat.average.tab; - } - continue; - } else if ( c == COLORCONTROL ) { - continue; - } - std::string key = ""; { - key += std::to_string(c) + ";"; - key += metadata["text settings"]["padding"][0].asString() + ","; - key += metadata["text settings"]["padding"][1].asString() + ";"; - key += metadata["text settings"]["spread"].asString() + ";"; - key += metadata["text settings"]["size"].asString() + ";"; - key += metadata["text settings"]["font"].asString() + ";"; - key += metadata["text settings"]["sdf"].asString(); - } - uf::Glyph& glyph = ::glyphs.cache[font][key]; - ::GlyphBox g; - - g.box.w = glyph.getSize().x; - g.box.h = glyph.getSize().y; - - g.box.x = stat.cursor.x + glyph.getBearing().x; - g.box.y = stat.cursor.y - glyph.getBearing().y; // - (glyph.getSize().y - glyph.getBearing().y); - - stat.cursor.x += (glyph.getAdvance().x); - } - - stat.origin.x = ( !metadata["text settings"]["world"].asBool() && transform.position.x != (int) transform.position.x ) ? transform.position.x * ::size.current.x : transform.position.x; - stat.origin.y = ( !metadata["text settings"]["world"].asBool() && transform.position.y != (int) transform.position.y ) ? transform.position.y * ::size.current.y : transform.position.y; - + float sum = 0; + float len = 0; + float proc = 0; + float tab = 4; + } average; - if ( metadata["text settings"]["origin"].isArray() ) { - stat.origin.x = metadata["text settings"]["origin"][0].asInt(); - stat.origin.y = metadata["text settings"]["origin"][1].asInt(); - } - else if ( metadata["text settings"]["origin"] == "top" ) stat.origin.y = ::size.current.y - stat.origin.y - stat.biggest.y;// else stat.origin.y = stat.origin.y; - if ( metadata["text settings"]["align"] == "right" ) stat.origin.x = ::size.current.x - stat.origin.x - stat.box.w;// else stat.origin.x = stat.origin.x; - else if ( metadata["text settings"]["align"] == "center" ) - stat.origin.x -= stat.box.w * 0.5f; - } + struct { + float x = 0; + float y = 0; + } biggest; - // Render Glyphs + struct { + float w = 0; + float h = 0; + } box; + + struct { + std::vector container; + std::size_t index = 0; + } colors; + } stat; + + float scale = metadata["text settings"]["scale"].asFloat(); + + { + pod::Vector3f color = { + metadata["text settings"]["color"][0].asFloat(), + metadata["text settings"]["color"][1].asFloat(), + metadata["text settings"]["color"][2].asFloat(), + }; + stat.colors.container.push_back(color); + } + + unsigned long COLORCONTROL = 0x7F; + + { + std::string text = string; + std::regex regex("\\%\\#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})\\%"); + std::unordered_map colors; + std::smatch match; + bool matched = false; + int maxTries = 128; + while ( matched = std::regex_search( text, match, regex ) && --maxTries > 0 ) { + struct { + std::string str; + int dec; + } r, g, b; + r.str = match[1].str(); + g.str = match[2].str(); + b.str = match[3].str(); + + { std::stringstream stream; stream << r.str; stream >> std::hex >> r.dec; } + { std::stringstream stream; stream << g.str; stream >> std::hex >> g.dec; } + { std::stringstream stream; stream << b.str; stream >> std::hex >> b.dec; } + + pod::Vector3f color = { r.dec / 255.0f, g.dec / 255.0f, b.dec / 255.0f }; + + stat.colors.container.push_back(color); + text = uf::string::replace( text, "%#" + r.str + g.str + b.str + "%", "\x7F" ); + } + if ( maxTries == 0 ) { + text += "\n(error formatting)"; + } + string = text; + } + + std::wstring_convert, wchar_t> convert; + std::wstring str = convert.from_bytes(string); + if ( str.size() == 0 ) return gs; + + // Calculate statistics + { + // Find tallest glyph for new line + for ( auto it = str.begin(); it != str.end(); ++it ) { + unsigned long c = *it; if ( c == '\n' ) continue; if ( c == '\t' ) continue; if ( c == 0x01 ) continue; + std::string key = ""; { + key += std::to_string(c) + ";"; + key += metadata["text settings"]["padding"][0].asString() + ","; + key += metadata["text settings"]["padding"][1].asString() + ";"; + key += metadata["text settings"]["spread"].asString() + ";"; + key += metadata["text settings"]["size"].asString() + ";"; + key += metadata["text settings"]["font"].asString() + ";"; + key += metadata["text settings"]["sdf"].asString(); + } + uf::Glyph& glyph = ::glyphs.cache[font][key]; + + if ( !glyph.generated() ) { + glyph.setPadding( { metadata["text settings"]["padding"][0].asUInt(), metadata["text settings"]["padding"][1].asUInt() } ); + glyph.setSpread( metadata["text settings"]["spread"].asUInt() ); + if ( metadata["text settings"]["sdf"].asBool() ) glyph.useSdf(true); + glyph.generate( ::glyphs.glyph, c, metadata["text settings"]["size"].asInt() ); + } + + stat.biggest.x = std::max( (float) stat.biggest.x, (float) glyph.getSize().x); + stat.biggest.y = std::max( (float) stat.biggest.y, (float) glyph.getSize().y); + + stat.average.sum += glyph.getSize().x; + ++stat.average.len; + } + stat.average.proc = stat.average.sum / stat.average.len; + stat.average.tab *= stat.average.proc; + + // Calculate box: Second pass required because of tab stat.cursor.x = 0; - stat.cursor.y = stat.biggest.y; + stat.cursor.y = 0; + stat.origin.x = 0; + stat.origin.y = 0; + for ( auto it = str.begin(); it != str.end(); ++it ) { unsigned long c = *it; if ( c == '\n' ) { - if ( metadata["text settings"]["direction"] == "down" ) stat.cursor.y -= stat.biggest.y; else stat.cursor.y += stat.biggest.y; + stat.cursor.y += stat.biggest.y; stat.cursor.x = 0; continue; } else if ( c == '\t' ) { @@ -277,11 +188,7 @@ namespace { stat.cursor.x = ((stat.cursor.x / stat.average.tab) + 1) * stat.average.tab; } continue; - } else if ( c == ' ' ) { - stat.cursor.x += stat.average.tab / 4.0f; - continue; } else if ( c == COLORCONTROL ) { - ++stat.colors.index; continue; } std::string key = ""; { @@ -294,268 +201,207 @@ namespace { key += metadata["text settings"]["sdf"].asString(); } uf::Glyph& glyph = ::glyphs.cache[font][key]; - - ::GlyphBox g; - g.code = c; + pod::GlyphBox g; g.box.w = glyph.getSize().x; g.box.h = glyph.getSize().y; - g.box.x = stat.cursor.x + (glyph.getBearing().x); + g.box.x = stat.cursor.x + glyph.getBearing().x; g.box.y = stat.cursor.y - glyph.getBearing().y; // - (glyph.getSize().y - glyph.getBearing().y); stat.cursor.x += (glyph.getAdvance().x); - - try { - g.color = stat.colors.container.at(stat.colors.index); - } catch ( ... ) { - std::cout << "Invalid color index `" << stat.colors.index << "` for string: " << string << ": (" << stat.colors.container.size() << ")" << std::endl; - g.color = { - metadata["text settings"]["color"][0].asFloat(), - metadata["text settings"]["color"][1].asFloat(), - metadata["text settings"]["color"][2].asFloat(), - }; - } - - gs.push_back(g); } - return gs; + + stat.origin.x = ( !metadata["text settings"]["world"].asBool() && transform.position.x != (int) transform.position.x ) ? transform.position.x * ext::gui::size.current.x : transform.position.x; + stat.origin.y = ( !metadata["text settings"]["world"].asBool() && transform.position.y != (int) transform.position.y ) ? transform.position.y * ext::gui::size.current.y : transform.position.y; + + + if ( metadata["text settings"]["origin"].isArray() ) { + stat.origin.x = metadata["text settings"]["origin"][0].asInt(); + stat.origin.y = metadata["text settings"]["origin"][1].asInt(); + } + else if ( metadata["text settings"]["origin"] == "top" ) stat.origin.y = ext::gui::size.current.y - stat.origin.y - stat.biggest.y;// else stat.origin.y = stat.origin.y; + if ( metadata["text settings"]["align"] == "right" ) stat.origin.x = ext::gui::size.current.x - stat.origin.x - stat.box.w;// else stat.origin.x = stat.origin.x; + else if ( metadata["text settings"]["align"] == "center" ) + stat.origin.x -= stat.box.w * 0.5f; } - void loadGui( uf::Object& gui, uf::Image& image ) { - uf::Serializer& metadata = gui.getComponent(); - metadata["render"] = true; - { - // this->addAlias(); - gui.addAlias(); + // Render Glyphs + stat.cursor.x = 0; + stat.cursor.y = stat.biggest.y; + for ( auto it = str.begin(); it != str.end(); ++it ) { + unsigned long c = *it; if ( c == '\n' ) { + if ( metadata["text settings"]["direction"] == "down" ) stat.cursor.y -= stat.biggest.y; else stat.cursor.y += stat.biggest.y; + stat.cursor.x = 0; + continue; + } else if ( c == '\t' ) { + // Fixed movement vs Real Tabbing + if ( false ) { + stat.cursor.x += stat.average.tab; + } else { + stat.cursor.x = ((stat.cursor.x / stat.average.tab) + 1) * stat.average.tab; + } + continue; + } else if ( c == ' ' ) { + stat.cursor.x += stat.average.tab / 4.0f; + continue; + } else if ( c == COLORCONTROL ) { + ++stat.colors.index; + continue; } - uf::GuiMesh& mesh = gui.getComponent(); - uf::Graphic& graphic = gui.getComponent(); - /* get original image size (before padding) */ { - metadata["original size"]["x"] = image.getDimensions().x; - metadata["original size"]["y"] = image.getDimensions().y; - - // image.padToPowerOfTwo(); - - metadata["current size"]["x"] = image.getDimensions().x; - metadata["current size"]["y"] = image.getDimensions().y; + std::string key = ""; { + key += std::to_string(c) + ";"; + key += metadata["text settings"]["padding"][0].asString() + ","; + key += metadata["text settings"]["padding"][1].asString() + ";"; + key += metadata["text settings"]["spread"].asString() + ";"; + key += metadata["text settings"]["size"].asString() + ";"; + key += metadata["text settings"]["font"].asString() + ";"; + key += metadata["text settings"]["sdf"].asString(); } - /* - pod::Vector2f correction = { - (metadata["current size"]["x"].asInt() - metadata["original size"]["x"].asInt()) / (metadata["current size"]["x"].asFloat()), - (metadata["current size"]["y"].asInt() - metadata["original size"]["y"].asInt()) / (metadata["current size"]["y"].asFloat()) + uf::Glyph& glyph = ::glyphs.cache[font][key]; + + pod::GlyphBox g; + g.code = c; + + g.box.w = glyph.getSize().x; + g.box.h = glyph.getSize().y; + + g.box.x = stat.cursor.x + (glyph.getBearing().x); + g.box.y = stat.cursor.y - glyph.getBearing().y; // - (glyph.getSize().y - glyph.getBearing().y); + + stat.cursor.x += (glyph.getAdvance().x); + + try { + g.color = stat.colors.container.at(stat.colors.index); + } catch ( ... ) { + std::cout << "Invalid color index `" << stat.colors.index << "` for string: " << string << ": (" << stat.colors.container.size() << ")" << std::endl; + g.color = { + metadata["text settings"]["color"][0].asFloat(), + metadata["text settings"]["color"][1].asFloat(), + metadata["text settings"]["color"][2].asFloat(), + }; + } + + gs.push_back(g); + } + return gs; +} + +void ext::Gui::load( uf::Image& image ) { + uf::Object& gui = *this; + uf::Serializer& metadata = gui.getComponent(); + { + // this->addAlias(); + // gui.addAlias(); + } + uf::GuiMesh& mesh = gui.getComponent(); + uf::Graphic& graphic = gui.getComponent(); + /* get original image size (before padding) */ { + metadata["original size"]["x"] = image.getDimensions().x; + metadata["original size"]["y"] = image.getDimensions().y; + + // image.padToPowerOfTwo(); + + metadata["current size"]["x"] = image.getDimensions().x; + metadata["current size"]["y"] = image.getDimensions().y; + } + + std::string suffix = ""; { + std::string _ = gui.getRootParent().getComponent()["shaders"]["gui"]["suffix"].asString(); + if ( _ != "" ) suffix = _ + "."; + } + if ( gui.getName() == "Gui: Text" ) { + pod::GlyphBox g; + g.box.x = metadata["text settings"]["box"][0].asFloat(); + g.box.y = metadata["text settings"]["box"][1].asFloat(); + g.box.w = metadata["text settings"]["box"][2].asFloat(); + g.box.h = metadata["text settings"]["box"][3].asFloat(); + + mesh.vertices = { + {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, + {{ g.box.x, g.box.y }, { 0.0f, 1.0f }}, + {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, + + {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, + {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, + {{ g.box.x + g.box.w, g.box.y + g.box.h }, { 1.0f, 0.0f }}, }; + for ( auto& vertex : mesh.vertices ) { + vertex.position.x /= ext::gui::size.reference.x; + vertex.position.y /= ext::gui::size.reference.y; + } + graphic.initialize( "Gui" ); + graphic.initializeGeometry( mesh ); + + struct { + std::string vertex = "./data/shaders/gui.text.vert.spv"; + std::string fragment = "./data/shaders/gui.text.frag.spv"; + } filenames; + if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); + if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); + else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui.text."+suffix+"frag.spv"; + + graphic.material.initializeShaders({ + {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, + {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, + }); + } else { mesh.vertices = { { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {-1.0f, -1.0f}, {0.0f, 1.0f-correction.y}, }, - { {1.0f, -1.0f}, {1.0f-correction.x, 1.0f-correction.y}, }, + { {-1.0f, -1.0f}, {0.0f, 1.0f}, }, + { {1.0f, -1.0f}, {1.0f, 1.0f}, }, { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {1.0f, -1.0f}, {1.0f-correction.x, 1.0f-correction.y}, }, - { {1.0f, 1.0f}, {1.0f-correction.x, 0.0f}, } + { {1.0f, -1.0f}, {1.0f, 1.0f}, }, + { {1.0f, 1.0f}, {1.0f, 0.0f}, } }; - */ - std::string suffix = ""; { - std::string _ = gui.getRootParent().getComponent()["shaders"]["gui"]["suffix"].asString(); - if ( _ != "" ) suffix = _ + "."; - } - if ( gui.getName() == "Gui: Text" ) { - ::GlyphBox g; - g.box.x = metadata["text settings"]["box"][0].asFloat(); - g.box.y = metadata["text settings"]["box"][1].asFloat(); - g.box.w = metadata["text settings"]["box"][2].asFloat(); - g.box.h = metadata["text settings"]["box"][3].asFloat(); - /* - if ( ext::openvr::enabled ) { - mesh.vertices = { - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, - {{ g.box.x, g.box.y }, { 0.0f, 1.0f }}, + graphic.initialize( "Gui" ); + graphic.initializeGeometry( mesh ); + struct { + std::string vertex = "./data/shaders/gui.vert.spv"; + std::string fragment = "./data/shaders/gui.frag.spv"; + } filenames; + if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); + if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); + else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui."+suffix+"frag.spv"; + + graphic.material.initializeShaders({ + {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, + {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, + }); + } - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x + g.box.w, g.box.y + g.box.h }, { 1.0f, 0.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, - }; - } else { - mesh.vertices = { - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x, g.box.y }, { 0.0f, 1.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, - {{ g.box.x + g.box.w, g.box.y + g.box.h }, { 1.0f, 0.0f }}, - }; - } - */ - mesh.vertices = { - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x, g.box.y }, { 0.0f, 1.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( image ); - {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, - {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, - {{ g.box.x + g.box.w, g.box.y + g.box.h }, { 1.0f, 0.0f }}, - }; - for ( auto& vertex : mesh.vertices ) { - vertex.position.x /= ::size.reference.x; - vertex.position.y /= ::size.reference.y; - } - graphic.initialize( "Gui" ); - graphic.initializeGeometry( mesh ); + { + pod::Transform<>& transform = gui.getComponent>(); + uf::GuiMesh& mesh = gui.getComponent(); + auto& texture = graphic.material.textures.front(); - struct { - std::string vertex = "./data/shaders/gui.text.vert.spv"; - std::string fragment = "./data/shaders/gui.text.frag.spv"; - } filenames; - if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); - if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); - else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui.text."+suffix+"frag.spv"; + pod::Vector2f textureSize = { + metadata["original size"]["x"].asFloat(), + metadata["original size"]["y"].asFloat() + }; - graphic.material.initializeShaders({ - {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, - {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, - }); - } else { - /* - if ( ext::openvr::enabled ) { - mesh.vertices = { - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - { {-1.0f, -1.0f}, {0.0f, 1.0f}, }, - - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {1.0f, 1.0f}, {1.0f, 0.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - }; - } else { - mesh.vertices = { - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {-1.0f, -1.0f}, {0.0f, 1.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - { {1.0f, 1.0f}, {1.0f, 0.0f}, } - }; - } - */ - mesh.vertices = { - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {-1.0f, -1.0f}, {0.0f, 1.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - - { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, - { {1.0f, -1.0f}, {1.0f, 1.0f}, }, - { {1.0f, 1.0f}, {1.0f, 0.0f}, } - }; - graphic.initialize( "Gui" ); - graphic.initializeGeometry( mesh ); - struct { - std::string vertex = "./data/shaders/gui.vert.spv"; - std::string fragment = "./data/shaders/gui.frag.spv"; - } filenames; - if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); - if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); - else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui."+suffix+"frag.spv"; - - graphic.material.initializeShaders({ - {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, - {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, - }); - } - - auto& texture = graphic.material.textures.emplace_back(); - texture.loadFromImage( image ); - - { - pod::Transform<>& transform = gui.getComponent>(); - uf::GuiMesh& mesh = gui.getComponent(); - auto& texture = graphic.material.textures.front(); - - pod::Vector2f textureSize = { - metadata["original size"]["x"].asFloat(), - metadata["original size"]["y"].asFloat() - }; - - if ( metadata["scaling"].asString() == "fixed" ) { - transform.scale = pod::Vector3{ (float) textureSize.x / ::size.current.x, (float) textureSize.y / ::size.current.y, 1 }; - } else if ( metadata["scaling"].asString() == "fixed-1080p" ) { - transform.scale = pod::Vector3{ (float) textureSize.x / 1920, (float) textureSize.y / 1080, 1 }; - } + if ( metadata["scaling"].asString() == "fixed" ) { + transform.scale = pod::Vector3{ (float) textureSize.x / ext::gui::size.current.x, (float) textureSize.y / ext::gui::size.current.y, 1 }; + } else if ( metadata["scaling"].asString() == "fixed-1080p" ) { + transform.scale = pod::Vector3{ (float) textureSize.x / 1920, (float) textureSize.y / 1080, 1 }; } } } -EXT_BEHAVIOR_REGISTER_CPP(GuiBehavior) -EXT_BEHAVIOR_REGISTER_AS_OBJECT(GuiBehavior, Gui) +EXT_OBJECT_REGISTER_BEGIN(Gui) + UF_OBJECT_REGISTER_BEHAVIOR(EntityBehavior) + UF_OBJECT_REGISTER_BEHAVIOR(ObjectBehavior) + EXT_OBJECT_REGISTER_BEHAVIOR(GuiBehavior) +EXT_OBJECT_REGISTER_END() #define this (&self) -void ext::GuiBehavior::initialize( uf::Object& self ) { - { - const uf::Scene& world = uf::scene::getCurrentScene(); - const uf::Serializer& _metadata = world.getComponent(); - ::size.current = { - _metadata["window"]["size"]["x"].asFloat(), - _metadata["window"]["size"]["y"].asFloat(), - }; - } +void ext::GuiBehavior::initialize( uf::Object& self ) { + auto& metadata = this->getComponent(); - uf::Serializer& metadata = this->getComponent(); - uf::Asset& assetLoader = this->getComponent(); - - - // master gui manager - this->addHook( "window:Resized", [&](const std::string& event)->std::string{ - uf::Serializer json = event; - if ( !this->hasComponent() ) return "false"; - - pod::Vector2ui size; { - size.x = json["window"]["size"]["x"].asUInt64(); - size.y = json["window"]["size"]["y"].asUInt64(); - } - - // ::matrix = uf::matrix::ortho( 0.0f, (float) size.x, 0.0f, (float) size.y ); - // ::matrix = uf::matrix::ortho( size.x * -0.5f, size.x * 0.5f, size.y * -0.5f, size.y * 0.5f ); - // ::matrix = uf::matrix::ortho( size.x * -1.0f, size.x * 1.0f, size.y * -1.0f, size.y * 1.0f ); - ::size.current = size; - // ::size.reference = size; - - return "true"; - } ); - if ( this->getName() == "Gui Manager" ) { - this->addHook( "window:Mouse.Moved", [&](const std::string& event)->std::string{ - uf::Serializer json = event; - - bool down = json["mouse"]["state"].asString() == "Down"; - bool clicked = false; - pod::Vector2ui position; { - position.x = json["mouse"]["position"]["x"].asInt() > 0 ? json["mouse"]["position"]["x"].asUInt() : 0; - position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; - } - pod::Vector2f click; { - click.x = (float) position.x / (float) ::size.current.x; - click.y = (float) position.y / (float) ::size.current.y; - - float x = (click.x = (click.x * 2.0f) - 1.0f); - float y = (click.y = (click.y * 2.0f) - 1.0f); - } - - { - auto& scene = uf::scene::getCurrentScene(); - auto& controller = scene.getController(); - auto& metadata = controller.getComponent(); - - if ( metadata["overlay"]["cursor"]["type"].asString() == "mouse" ) { - metadata["overlay"]["cursor"]["position"][0] = click.x; - metadata["overlay"]["cursor"]["position"][1] = click.y; - } - } - - return "true"; - }); - return; - } - this->addHook( "glyph:Load.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; unsigned long c = json["glyph"].asUInt64(); @@ -578,7 +424,7 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { pixels.insert( pixels.end(), buffer, buffer + len ); image.loadFromBuffer( pixels, glyph.getSize(), 8, 1, true ); } - loadGui( *this, image ); + this->as().load( image ); return "true"; }); this->addHook( "asset:Load.%UID%", [&](const std::string& event)->std::string{ @@ -595,10 +441,9 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { uf::Image image = *imagePointer; - loadGui( *this, image ); + this->as().load( image ); return "true"; }); - this->addHook( "window:Resized", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !this->hasComponent() ) return "false"; @@ -608,15 +453,13 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { size.y = json["window"]["size"]["y"].asUInt64(); } pod::Transform<>& transform = this->getComponent>(); - uf::GuiMesh& mesh = this->getComponent(); - uf::Graphic& graphic = this->getComponent(); - auto& texture = graphic.material.textures.front(); + // uf::Graphic& graphic = this->getComponent(); + // auto& texture = graphic.material.textures.front(); pod::Vector2f textureSize = { metadata["original size"]["x"].asFloat(), metadata["original size"]["y"].asFloat() }; - if ( metadata["text settings"].isObject() ) { } else if ( metadata["scaling"].asString() == "fixed" ) { transform.scale = pod::Vector3{ (float) textureSize.x / size.x, (float) textureSize.y / size.y, 1 }; @@ -626,24 +469,40 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { return "true"; } ); - - + if ( metadata["system"]["clickable"].asBool() ) { uf::Timer clickTimer(false); - clickTimer.start(); + // clickTimer.start( uf::Time<>(-1000000) ); + if ( !clickTimer.running() ) clickTimer.start(); this->addHook( "gui:Clicked.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; - if ( !metadata["events"]["click"].isObject() ) return "true"; - uf::Serializer payload = metadata["events"]["click"]["payload"]; - this->callHook(metadata["events"]["click"]["name"].asString(), payload); + + if ( metadata["events"]["click"].isObject() ) { + uf::Serializer event = metadata["events"]["click"]; + metadata["events"]["click"] = Json::arrayValue; + metadata["events"]["click"][0] = event; + } else if ( !metadata["events"]["click"].isArray() ) { + this->getParent().as().callHook("gui:Clicked.%UID%", event); + return "false"; + } + for ( int i = 0; i < metadata["events"]["click"].size(); ++i ) { + uf::Serializer event = metadata["events"]["click"][i]; + uf::Serializer payload = event["payload"]; + float delay = event["delay"].asFloat(); + if ( event["delay"].isNumeric() ) { + this->queueHook(event["name"].asString(), payload, event["delay"].asFloat()); + } else { + this->callHook(event["name"].asString(), payload ); + } + } return "true"; }); this->addHook( "window:Mouse.Click", [&](const std::string& event)->std::string{ uf::Serializer json = event; - if ( !this->hasComponent() ) return "false"; +// if ( !this->hasComponent() ) return "false"; uf::Serializer& metadata = this->getComponent(); + if ( metadata["world"].asBool() ) return "true"; - if ( !metadata["render"].asBool() ) return "true"; if ( metadata["box"] == Json::nullValue ) return "true"; bool down = json["mouse"]["state"].asString() == "Down"; @@ -654,50 +513,66 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; } pod::Vector2f click; { - click.x = (float) position.x / (float) ::size.current.x; - click.y = (float) position.y / (float) ::size.current.y; + click.x = (float) position.x / (float) ext::gui::size.current.x; + click.y = (float) position.y / (float) ext::gui::size.current.y; + + click.x = (click.x * 2.0f) - 1.0f; + click.y = (click.y * 2.0f) - 1.0f; + + float x = click.x; + float y = click.y; - float x = (click.x = (click.x * 2.0f) - 1.0f); - float y = (click.y = (click.y * 2.0f) - 1.0f); if (json["invoker"] == "vr" ) { x = json["mouse"]["position"]["x"].asFloat(); y = json["mouse"]["position"]["y"].asFloat(); } - for (int i = 0, j = metadata["box"].size() - 1; i < metadata["box"].size(); j = i++) { - float xi = metadata["box"][i]["x"].asFloat(), yi = metadata["box"][i]["y"].asFloat(); - float xj = metadata["box"][j]["x"].asFloat(), yj = metadata["box"][j]["y"].asFloat(); - bool intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - if (intersect) { - clicked = !clicked; - } - } + pod::Vector2f min = { metadata["box"]["min"]["x"].asFloat(), metadata["box"]["min"]["y"].asFloat() }; + pod::Vector2f max = { metadata["box"]["max"]["x"].asFloat(), metadata["box"]["max"]["y"].asFloat() }; + clicked = ( min.x <= x && min.y <= y && max.x >= x && max.y >= y ); } - /* - if ( clicked ) std::cout << "Clicked on " << this->m_name << std::endl; - else { - std::cout << click.x << ", " << click.y << " " << this->m_name << metadata["box"] << std::endl; - } - */ + } metadata["clicked"] = clicked; - - if ( metadata["clicked"].asBool() && clickTimer.elapsed().asDouble() >= 1 ) { - clickTimer.reset(); + if ( clicked ) { this->callHook("gui:Clicked.%UID%"); } return "true"; } ); } if ( metadata["system"]["hoverable"].asBool() ) { + uf::Timer hoverTimer(false); + hoverTimer.start( uf::Time<>(-1000000) ); + this->addHook( "gui:Hovered.%UID%", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + if ( metadata["events"]["hover"].isObject() ) { + uf::Serializer event = metadata["events"]["hover"]; + metadata["events"]["hover"] = Json::arrayValue; + metadata["events"]["hover"][0] = event; + } else if ( !metadata["events"]["hover"].isArray() ) { + this->getParent().as().callHook("gui:Clicked.%UID%", event); + return "false"; + } + for ( int i = 0; i < metadata["events"]["hover"].size(); ++i ) { + uf::Serializer event = metadata["events"]["hover"][i]; + uf::Serializer payload = event["payload"]; + float delay = event["delay"].asFloat(); + if ( event["delay"].isNumeric() ) { + this->queueHook(event["name"].asString(), payload, event["delay"].asFloat()); + } else { + this->callHook(event["name"].asString(), payload ); + } + } + return "true"; + }); this->addHook( "window:Mouse.Moved", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( this->getUid() == 0 ) return "false"; if ( !this->hasComponent() ) return "false"; uf::Serializer& metadata = this->getComponent(); if ( metadata["world"].asBool() ) return "true"; - if ( !metadata["render"].asBool() ) return "true"; if ( metadata["box"] == Json::nullValue ) return "true"; bool down = json["mouse"]["state"].asString() == "Down"; @@ -707,85 +582,280 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; } pod::Vector2f click; { - click.x = (float) position.x / (float) ::size.current.x; - click.y = (float) position.y / (float) ::size.current.y; + click.x = (float) position.x / (float) ext::gui::size.current.x; + click.y = (float) position.y / (float) ext::gui::size.current.y; - float x = (click.x = (click.x * 2.0f) - 1.0f); - float y = (click.y = (click.y * 2.0f) - 1.0f); + click.x = (click.x * 2.0f) - 1.0f; + click.y = (click.y * 2.0f) - 1.0f; + float x = click.x; + float y = click.y; - for (int i = 0, j = metadata["box"].size() - 1; i < metadata["box"].size(); j = i++) { - float xi = metadata["box"][i]["x"].asFloat(), yi = metadata["box"][i]["y"].asFloat(); - float xj = metadata["box"][j]["x"].asFloat(), yj = metadata["box"][j]["y"].asFloat(); - bool intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - if (intersect) { - clicked = !clicked; - } - } + pod::Vector2f min = { metadata["box"]["min"]["x"].asFloat(), metadata["box"]["min"]["y"].asFloat() }; + pod::Vector2f max = { metadata["box"]["max"]["x"].asFloat(), metadata["box"]["max"]["y"].asFloat() }; + clicked = ( min.x <= x && min.y <= y && max.x >= x && max.y >= y ); } metadata["hovered"] = clicked; + if ( clicked && hoverTimer.elapsed().asDouble() >= 1 ) { + hoverTimer.reset(); + this->callHook("gui:Hovered.%UID%"); + } + return "true"; } ); } if ( metadata["text settings"]["string"].isString() ) { - uf::Serializer defaultSettings; - defaultSettings.readFromFile("./data/entities/gui/text/string.json"); - - for ( auto it = defaultSettings["metadata"]["text settings"].begin(); it != defaultSettings["metadata"]["text settings"].end(); ++it ) { + if ( ::defaultSettings["metadata"].isNull() ) { + ::defaultSettings.readFromFile("./data/entities/gui/text/string.json"); + } + for ( auto it = ::defaultSettings["metadata"]["text settings"].begin(); it != ::defaultSettings["metadata"]["text settings"].end(); ++it ) { std::string key = it.key().asString(); if ( metadata["text settings"][key].isNull() ) { - metadata["text settings"][key] = defaultSettings["metadata"]["text settings"][key]; + metadata["text settings"][key] = ::defaultSettings["metadata"]["text settings"][key]; + } + } + if ( metadata["text settings"]["legacy"].asBool() ) { + float delay = 0.0f; + float scale = metadata["text settings"]["scale"].asFloat(); + std::vector glyphs = this->as().generateGlyphs(); + for ( auto& glyph : glyphs ) { + uf::Object& glyphElement = this->loadChild("/gui/text/letter.json", false); + + uf::Serializer& pMetadata = glyphElement.getComponent(); + + // pMetadata["events"] = metadata["events"]; + pMetadata["system"]["hot reload"] = metadata["system"]["hot reload"]; + pMetadata["text settings"] = metadata["text settings"]; + pMetadata["text settings"].removeMember("string"); + pMetadata["text settings"]["letter"] = (wchar_t) glyph.code; + + pMetadata["text settings"]["color"][0] = glyph.color[0]; + pMetadata["text settings"]["color"][1] = glyph.color[1]; + pMetadata["text settings"]["color"][2] = glyph.color[2]; + + pMetadata["text settings"]["box"][0] = glyph.box.x; + pMetadata["text settings"]["box"][1] = glyph.box.y; + pMetadata["text settings"]["box"][2] = glyph.box.w; + pMetadata["text settings"]["box"][3] = glyph.box.h; + + pMetadata["system"]["hoverable"] = metadata["system"]["hoverable"]; + pMetadata["system"]["clickable"] = metadata["system"]["clickable"]; + + glyphElement.initialize(); + + pod::Transform<>& pTransform = glyphElement.getComponent>(); + pTransform.scale.x = scale; + pTransform.scale.y = scale; + pTransform.reference = this->getComponentPointer>(); + + uf::Serializer payload; + payload["glyph"] = (uint64_t) glyph.code; + if ( metadata["text settings"]["scroll speed"].isNumeric() ) { + glyphElement.queueHook("glyph:Load.%UID%", payload, delay); + delay += metadata["text settings"]["scroll speed"].asFloat(); + } else { + glyphElement.callHook("glyph:Load.%UID%", payload); + } + } + return; + } + { + float delay = 0.0f; + float scale = metadata["text settings"]["scale"].asFloat(); + auto& transform = this->getComponent>(); + transform.scale.x = scale; + transform.scale.y = scale; + + std::vector glyphs = this->as().generateGlyphs(); + + std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); + std::string key = ""; { + key += metadata["text settings"]["padding"][0].asString() + ","; + key += metadata["text settings"]["padding"][1].asString() + ";"; + key += metadata["text settings"]["spread"].asString() + ";"; + key += metadata["text settings"]["size"].asString() + ";"; + key += metadata["text settings"]["font"].asString() + ";"; + key += metadata["text settings"]["sdf"].asString(); + } + auto& scene = uf::scene::getCurrentScene(); + auto& atlas = this->getComponent(); + auto& images = atlas.getImages(); + auto& mesh = this->getComponent(); + + auto& graphic = this->getComponent(); + mesh.vertices.reserve( glyphs.size() * 6 ); + for ( auto& g : glyphs ) { + auto& glyph = ::glyphs.cache[font][std::to_string((uint64_t) g.code) + ";"+key]; + // add bitmap to atlas + uf::Image& image = images.emplace_back(); + const uint8_t* buffer = glyph.getBuffer(); + uf::Image::container_t pixels; + std::size_t len = glyph.getSize().x * glyph.getSize().y; + pixels.insert( pixels.end(), buffer, buffer + len ); + image.loadFromBuffer( pixels, glyph.getSize(), 8, 1, true ); + // add vertices + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, pod::Vector2f{ 0.0f, 0.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y , 0 }, pod::Vector2f{ 0.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, pod::Vector2f{ 1.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, pod::Vector2f{ 0.0f, 0.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, pod::Vector2f{ 1.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, 0 }, pod::Vector2f{ 1.0f, 0.0f }, g.color}); } + atlas.generate(); + + for ( size_t i = 0, k = 0; i < mesh.vertices.size(); ++k, i += 6 ) { + for ( size_t j = 0; j < 6; ++j ) { + auto& vertex = mesh.vertices[i+j]; + vertex.position.x /= ext::gui::size.reference.x; + vertex.position.y /= ext::gui::size.reference.y; + + vertex.uv = atlas.mapUv( vertex.uv, k ); + vertex.uv.y = 1 - vertex.uv.y; + } + } + + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( atlas.getAtlas() ); + + graphic.initialize( "Gui" ); + graphic.initializeGeometry( mesh ); + + std::string suffix = ""; { + std::string _ = scene.getComponent()["shaders"]["gui"]["suffix"].asString(); + if ( _ != "" ) suffix = _ + "."; + } + struct { + std::string vertex = "./data/shaders/gui.text.vert.spv"; + std::string fragment = "./data/shaders/gui.text.frag.spv"; + } filenames; + if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); + if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); + else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui.text."+suffix+"frag.spv"; + + graphic.material.initializeShaders({ + {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, + {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, + }); } - float delay = 0.0f; - float scale = metadata["text settings"]["scale"].asFloat(); - std::vector<::GlyphBox> glyphs = generateGlyphs(*this); - // std::cout << "Loading string: " << metadata["text settings"]["string"] << std::endl; - for ( auto& glyph : glyphs ) { - // uf::Object* glyphElement = (uf::Object*) this->findByUid( this->loadChildUid("/gui/text/letter.json", false) ); - uf::Object& glyphElement = this->loadChild("/gui/text/letter.json", false); + + this->addHook( "object:Reload.%UID%", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + if ( json["old"]["text settings"]["string"] == json["new"]["text settings"]["string"] ) return "false"; + this->queueHook( "gui:UpdateString.%UID%"); + return "true"; + }); + this->addHook( "gui:UpdateString.%UID%", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + for ( auto it = ::defaultSettings["metadata"]["text settings"].begin(); it != ::defaultSettings["metadata"]["text settings"].end(); ++it ) { + std::string key = it.key().asString(); + if ( metadata["text settings"][key].isNull() ) { + metadata["text settings"][key] = ::defaultSettings["metadata"]["text settings"][key]; + } + } + if ( json["string"].isString() ) { + metadata["text settings"]["string"] = json["string"]; + } + std::string string = metadata["text settings"]["string"].asString(); + + float delay = 0.0f; + float scale = metadata["text settings"]["scale"].asFloat(); + std::vector glyphs = this->as().generateGlyphs( string ); + + std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); + std::string key = ""; { + key += metadata["text settings"]["padding"][0].asString() + ","; + key += metadata["text settings"]["padding"][1].asString() + ";"; + key += metadata["text settings"]["spread"].asString() + ";"; + key += metadata["text settings"]["size"].asString() + ";"; + key += metadata["text settings"]["font"].asString() + ";"; + key += metadata["text settings"]["sdf"].asString(); + } + auto& transform = this->getComponent>(); + auto& scene = uf::scene::getCurrentScene(); + auto& atlas = this->getComponent(); + auto& images = atlas.getImages(); + auto& mesh = this->getComponent(); + + auto& graphic = this->getComponent(); + + atlas.clear(); + mesh.vertices.clear(); + mesh.indices.clear(); + mesh.vertices.reserve( glyphs.size() * 6 ); + for ( auto& g : glyphs ) { + auto& glyph = ::glyphs.cache[font][std::to_string((uint64_t) g.code) + ";"+key]; + // add bitmap to atlas + uf::Image& image = images.emplace_back(); + const uint8_t* buffer = glyph.getBuffer(); + uf::Image::container_t pixels; + std::size_t len = glyph.getSize().x * glyph.getSize().y; + pixels.insert( pixels.end(), buffer, buffer + len ); + image.loadFromBuffer( pixels, glyph.getSize(), 8, 1, true ); + // add vertices + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, pod::Vector2f{ 0.0f, 0.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y , 0 }, pod::Vector2f{ 0.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, pod::Vector2f{ 1.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, pod::Vector2f{ 0.0f, 0.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, pod::Vector2f{ 1.0f, 1.0f }, g.color}); + mesh.vertices.push_back({pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, 0 }, pod::Vector2f{ 1.0f, 0.0f }, g.color}); + } + + atlas.generate(); + + transform.scale.x = scale; + transform.scale.y = scale; + for ( size_t i = 0, k = 0; i < mesh.vertices.size(); ++k, i += 6 ) { + for ( size_t j = 0; j < 6; ++j ) { + auto& vertex = mesh.vertices[i+j]; + vertex.position.x /= ext::gui::size.reference.x; + vertex.position.y /= ext::gui::size.reference.y; + + vertex.uv = atlas.mapUv( vertex.uv, k ); + vertex.uv.y = 1 - vertex.uv.y; + } + } + + for ( auto& texture : graphic.material.textures ) texture.destroy(); + graphic.material.textures.clear(); + + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( atlas.getAtlas() ); + // graphic.descriptor.indices = 0; + graphic.initializeGeometry( mesh ); + graphic.getPipeline().update( graphic ); + uf::renderer::rebuild = true; + /* - uf::Object* glyphElement = new uf::Object; - this->addChild(*glyphElement); - glyphElement->load("/gui/text/letter.json", true); + graphic.destroy(); + + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( atlas.getAtlas() ); + + graphic.initialize( "Gui" ); + graphic.initializeGeometry( mesh ); + + std::string suffix = ""; { + std::string _ = scene.getComponent()["shaders"]["gui"]["suffix"].asString(); + if ( _ != "" ) suffix = _ + "."; + } + struct { + std::string vertex = "./data/shaders/gui.text.vert.spv"; + std::string fragment = "./data/shaders/gui.text.frag.spv"; + } filenames; + if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); + if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); + else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui.text."+suffix+"frag.spv"; + + graphic.material.initializeShaders({ + {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, + {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT}, + }); */ - uf::Serializer& pMetadata = glyphElement.getComponent(); - pMetadata["events"] = metadata["events"]; - - pMetadata["text settings"] = metadata["text settings"]; - pMetadata["text settings"].removeMember("string"); - pMetadata["text settings"]["letter"] = (wchar_t) glyph.code; - - pMetadata["text settings"]["color"][0] = glyph.color[0]; - pMetadata["text settings"]["color"][1] = glyph.color[1]; - pMetadata["text settings"]["color"][2] = glyph.color[2]; - - pMetadata["text settings"]["box"][0] = glyph.box.x; - pMetadata["text settings"]["box"][1] = glyph.box.y; - pMetadata["text settings"]["box"][2] = glyph.box.w; - pMetadata["text settings"]["box"][3] = glyph.box.h; - - pMetadata["system"]["hoverable"] = metadata["system"]["hoverable"]; - pMetadata["system"]["clickable"] = metadata["system"]["clickable"]; - - glyphElement.initialize(); - - pod::Transform<>& pTransform = glyphElement.getComponent>(); - pTransform.scale.x = scale; - pTransform.scale.y = scale; - pTransform.reference = this->getComponentPointer>(); - - uf::Serializer payload; - payload["glyph"] = (uint64_t) glyph.code; - if ( metadata["text settings"]["scroll speed"].isNumeric() ) { - glyphElement.queueHook("glyph:Load.%UID%", payload, delay); - delay += metadata["text settings"]["scroll speed"].asFloat(); - } else { - glyphElement.callHook("glyph:Load.%UID%", payload); - } - } + return "true"; + }); } } void ext::GuiBehavior::tick( uf::Object& self ) { @@ -808,9 +878,8 @@ void ext::GuiBehavior::tick( uf::Object& self ) { void ext::GuiBehavior::render( uf::Object& self ){ uf::Serializer& metadata = this->getComponent(); - /* Update uniforms */ if ( this->hasComponent() ) { + /* Update uniforms */ if ( this->hasComponent() ) { auto& scene = uf::scene::getCurrentScene(); - auto& mesh = this->getComponent(); auto& graphic = this->getComponent(); auto& controller = scene.getController(); auto& camera = controller.getComponent(); @@ -826,10 +895,26 @@ void ext::GuiBehavior::render( uf::Object& self ){ int mode = 0; if ( !metadata["shader"].isNull() ) mode = metadata["shader"].asInt(); - if ( this->getName() == "Gui: Text" ) { - // ::GlyphDescriptor uniforms; - // auto& uniforms = graphic.uniforms<::GlyphDescriptor>(); - auto& uniforms = graphic.material.shaders.front().uniforms.front().get<::GlyphDescriptor>(); + if ( !metadata["text settings"]["legacy"].isNull() && ((metadata["text settings"]["legacy"].asBool() && this->getName() == "Gui: Text") || (!metadata["text settings"]["legacy"].asBool() && metadata["text settings"]["string"].isString())) ) { + struct GlyphDescriptor { + struct { + alignas(16) pod::Matrix4f model[2]; + } matrices; + struct { + alignas(16) pod::Vector4f offset; + alignas(16) pod::Vector4f color; + alignas(4) int32_t mode = 0; + alignas(4) float depth = 0.0f; + alignas(4) int32_t sdf = false; + alignas(4) int32_t shadowbox = false; + alignas(16) pod::Vector4f stroke; + alignas(4) float weight; + alignas(4) int32_t spread; + alignas(4) float scale; + alignas(4) float padding; + } gui; + }; + auto& uniforms = graphic.material.shaders.front().uniforms.front().get(); if ( !metadata["text settings"]["color"].isArray() ) { metadata["text settings"]["color"][0] = 1.0f; @@ -849,30 +934,16 @@ void ext::GuiBehavior::render( uf::Object& self ){ metadata["text settings"]["color"][2].asFloat(), metadata["text settings"]["color"][3].asFloat() }; + if ( metadata["alpha"].isNumeric() ) { + color[3] *= metadata["alpha"].asFloat(); + } pod::Vector4 stroke = { metadata["text settings"]["stroke"][0].asFloat(), metadata["text settings"]["stroke"][1].asFloat(), metadata["text settings"]["stroke"][2].asFloat(), metadata["text settings"]["stroke"][3].asFloat() }; - /* - if ( uf::Window::isKeyPressed("V") ) { - metadata["text settings"]["weight"] = metadata["text settings"]["weight"].asFloat() + uf::physics::time::delta; - std::cout << metadata["text settings"]["weight"].asFloat() << std::endl; - } - if ( uf::Window::isKeyPressed("C") ) { - metadata["text settings"]["weight"] = metadata["text settings"]["weight"].asFloat() - uf::physics::time::delta; - std::cout << metadata["text settings"]["weight"].asFloat() << std::endl; - } - if ( uf::Window::isKeyPressed("N") ) { - metadata["text settings"]["spread"] = metadata["text settings"]["spread"].asFloat() + uf::physics::time::delta; - std::cout << metadata["text settings"]["spread"].asFloat() << std::endl; - } - if ( uf::Window::isKeyPressed("B") ) { - metadata["text settings"]["spread"] = metadata["text settings"]["spread"].asFloat() - uf::physics::time::delta; - std::cout << metadata["text settings"]["spread"].asFloat() << std::endl; - } - */ + uniforms.gui.offset = offset; uniforms.gui.color = color; uniforms.gui.stroke = stroke; @@ -901,10 +972,11 @@ void ext::GuiBehavior::render( uf::Object& self ){ uf::Matrix4 translation, rotation, scale; pod::Transform<> flatten = uf::transform::flatten(transform, false); // make our own flattened position, for some reason this causes z to be 6.6E+28 - flatten.position = transform.position + transform.reference->position; + flatten.position = transform.position; + if ( transform.reference ) flatten.position += transform.reference->position; flatten.orientation.w *= -1; rotation = uf::quaternion::matrix(flatten.orientation); - // pod::Vector3f offsetSize = { ::size.current.x, ::size.current.y, 1 }; + // pod::Vector3f offsetSize = { ext::gui::size.current.x, ext::gui::size.current.y, 1 }; // translation = uf::matrix::translate( uf::matrix::identity(), flatten.position * offsetSize ); translation = uf::matrix::translate( uf::matrix::identity(), flatten.position ); scale = uf::matrix::scale( scale, transform.scale ); @@ -916,21 +988,50 @@ void ext::GuiBehavior::render( uf::Object& self ){ // graphic.updateBuffer( uniforms, 0, false ); graphic.material.shaders.front().updateBuffer( uniforms, 0, false ); // calculate click box - { - auto& model = uniforms.matrices.model[0]; + auto& model = uniforms.matrices.model[0]; + pod::Vector2f min = { 1, 1 }; + pod::Vector2f max = { -1, -1 }; + if ( this->hasComponent() ) { auto& mesh = this->getComponent(); - auto& vertices = mesh.vertices; - int i = 0; - for ( auto& vertex : vertices ) { + for ( auto& vertex : mesh.vertices ) { pod::Vector4f position = { vertex.position.x, vertex.position.y, 0, 1 }; // gcc10+ doesn't like implicit template arguments pod::Vector4f translated = uf::matrix::multiply( model, position ); - // points.push_back( translated ); - metadata["box"][i]["x"] = translated.x; - metadata["box"][i]["y"] = translated.y; - ++i; + min.x = std::min( min.x, translated.x ); + max.x = std::max( max.x, translated.x ); + + min.y = std::min( min.y, translated.y ); + max.y = std::max( max.y, translated.y ); + } + } else if ( this->hasComponent() ) { + auto& mesh = this->getComponent(); + for ( auto& vertex : mesh.vertices ) { + pod::Vector4f position = { vertex.position.x, vertex.position.y, 0, 1 }; + // gcc10+ doesn't like implicit template arguments + pod::Vector4f translated = uf::matrix::multiply( model, position ); + min.x = std::min( min.x, translated.x ); + max.x = std::max( max.x, translated.x ); + + min.y = std::min( min.y, translated.y ); + max.y = std::max( max.y, translated.y ); } } + metadata["box"]["min"]["x"] = min.x; + metadata["box"]["min"]["y"] = min.y; + metadata["box"]["max"]["x"] = max.x; + metadata["box"]["max"]["y"] = max.y; + /* + int i = 0; + for ( auto& vertex : vertices ) { + pod::Vector4f position = { vertex.position.x, vertex.position.y, 0, 1 }; + // gcc10+ doesn't like implicit template arguments + pod::Vector4f translated = uf::matrix::multiply( model, position ); + // points.push_back( translated ); + metadata["box"][i]["x"] = translated.x; + metadata["box"][i]["y"] = translated.y; + ++i; + } + */ } else { if ( !metadata["color"].isArray() ) { metadata["color"][0] = 1.0f; @@ -944,9 +1045,22 @@ void ext::GuiBehavior::render( uf::Object& self ){ metadata["color"][2].asFloat(), metadata["color"][3].asFloat() }; - // uf::StereoGuiMeshDescriptor uniforms; - // auto& uniforms = graphic.uniforms(); - auto& uniforms = graphic.material.shaders.front().uniforms.front().get(); + if ( metadata["alpha"].isNumeric() ) { + color[3] *= metadata["alpha"].asFloat(); + } + struct UniformDescriptor { + struct { + alignas(16) pod::Matrix4f model[2]; + } matrices; + struct { + alignas(16) pod::Vector4f offset; + alignas(16) pod::Vector4f color; + alignas(4) int32_t mode = 0; + alignas(4) float depth = 0.0f; + alignas(8) pod::Vector2f padding; + } gui; + }; + auto& uniforms = graphic.material.shaders.front().uniforms.front().get(); uniforms.gui.offset = offset; uniforms.gui.color = color; uniforms.gui.mode = mode; @@ -989,167 +1103,37 @@ void ext::GuiBehavior::render( uf::Object& self ){ auto& model = uniforms.matrices.model[0]; auto& mesh = this->getComponent(); auto& vertices = mesh.vertices; + pod::Vector2f min = { 1, 1 }; + pod::Vector2f max = { -1, -1 }; + for ( auto& vertex : vertices ) { + pod::Vector4f position = { vertex.position.x, vertex.position.y, 0, 1 }; + // gcc10+ doesn't like implicit template arguments + pod::Vector4f translated = uf::matrix::multiply( model, position ); + min.x = std::min( min.x, translated.x ); + max.x = std::max( max.x, translated.x ); + + min.y = std::min( min.y, translated.y ); + max.y = std::max( max.y, translated.y ); + } + metadata["box"]["min"]["x"] = min.x; + metadata["box"]["min"]["y"] = min.y; + metadata["box"]["max"]["x"] = max.x; + metadata["box"]["max"]["y"] = max.y; + /* int i = 0; for ( auto& vertex : vertices ) { pod::Vector4f position = { vertex.position.x, vertex.position.y, 0, 1 }; // gcc10+ doesn't like implicit template arguments pod::Vector4f translated = uf::matrix::multiply( model, position ); // points.push_back( translated ); - metadata["box"][i]["x"] = translated.x; - metadata["box"][i]["y"] = translated.y; + metadata["box"][0][i]["x"] = translated.x; + metadata["box"][0][i]["y"] = translated.y; ++i; } + */ } } - } else if ( this->getName() != "Gui Manager" ) { - bool hovered = false; - bool clicked = false; - for ( uf::Entity* entity : this->getChildren() ) { - if ( !entity ) continue; - if ( entity->getUid() == 0 ) continue; - uf::Serializer& pMetadata = entity->getComponent(); - if ( pMetadata["hovered"].asBool() ) hovered = true; - if ( pMetadata["clicked"].asBool() ) clicked = true; - } - metadata["clicked"] = clicked; - metadata["hovered"] = hovered; - } else { - { - /* Update GUI panel */ { - auto& scene = uf::scene::getCurrentScene(); - auto& controller = scene.getController(); - auto& camera = controller.getComponent(); - auto* renderMode = (uf::renderer::RenderTargetRenderMode*) &uf::renderer::getRenderMode("Gui"); - if ( renderMode->getName() == "Gui" ) { - auto& blitter = renderMode->blitter; - auto& metadata = controller.getComponent(); - - struct UniformDescriptor { - struct { - alignas(16) pod::Matrix4f models[2]; - } matrices; - struct { - alignas(8) pod::Vector2f position = { 0.5f, 0.5f }; - alignas(8) pod::Vector2f radius = { 0.1f, 0.1f }; - alignas(16) pod::Vector4f color = { 1, 1, 1, 1 }; - } cursor; - alignas(8) pod::Vector2f alpha; - }; - auto& shader = blitter.material.shaders.front(); - auto& uniforms = shader.uniforms.front().get(); - - for ( std::size_t i = 0; i < 2; ++i ) { - pod::Transform<> transform; - if ( metadata["overlay"]["position"].isArray() ) - transform.position = { - metadata["overlay"]["position"][0].asFloat(), - metadata["overlay"]["position"][1].asFloat(), - metadata["overlay"]["position"][2].asFloat(), - }; - if ( metadata["overlay"]["scale"].isArray() ) - transform.scale = { - metadata["overlay"]["scale"][0].asFloat(), - metadata["overlay"]["scale"][1].asFloat(), - metadata["overlay"]["scale"][2].asFloat(), - }; - if ( metadata["overlay"]["orientation"].isArray() ) - transform.orientation = { - metadata["overlay"]["orientation"][0].asFloat(), - metadata["overlay"]["orientation"][1].asFloat(), - metadata["overlay"]["orientation"][2].asFloat(), - metadata["overlay"]["orientation"][3].asFloat(), - }; - if ( ext::openvr::enabled && (metadata["overlay"]["enabled"].asBool() || uf::renderer::getRenderMode("Stereoscopic Deferred", true).getType() == "Stereoscopic Deferred" )) { - if ( metadata["overlay"]["floating"].asBool() ) { - pod::Matrix4f model = uf::transform::model( transform ); - uniforms.matrices.models[i] = camera.getProjection(i) * ext::openvr::hmdEyePositionMatrix( i == 0 ? vr::Eye_Left : vr::Eye_Right ) * model; - } else { - auto translation = uf::matrix::translate( uf::matrix::identity(), camera.getTransform().position + controller.getComponent>().position ); - auto rotation = uf::quaternion::matrix( controller.getComponent>().orientation * pod::Vector4f{1,1,1,-1} ); - - pod::Matrix4f model = translation * rotation * uf::transform::model( transform ); - uniforms.matrices.models[i] = camera.getProjection(i) * camera.getView(i) * model; - } - } else { - pod::Matrix4t<> model = uf::matrix::translate( uf::matrix::identity(), { 0, 0, 1 } ); - uniforms.matrices.models[i] = model; - } - uniforms.alpha.x = metadata["overlay"]["alpha"].asFloat(); - uniforms.alpha.y = 0; - - uniforms.cursor.position.x = (metadata["overlay"]["cursor"]["position"][0].asFloat() + 1.0f) * 0.5f; //(::mouse.position.x + 1.0f) * 0.5f; - uniforms.cursor.position.y = (metadata["overlay"]["cursor"]["position"][1].asFloat() + 1.0f) * 0.5f; //(::mouse.position.y + 1.0f) * 0.5f; - - pod::Vector3f cursorSize; - cursorSize.x = metadata["overlay"]["cursor"]["radius"].asFloat(); - cursorSize.y = metadata["overlay"]["cursor"]["radius"].asFloat(); - cursorSize = uf::matrix::multiply( uf::matrix::inverse( uf::matrix::scale( uf::matrix::identity() , transform.scale) ), cursorSize ); - - uniforms.cursor.radius.x = cursorSize.x; - uniforms.cursor.radius.y = cursorSize.y; - - uniforms.cursor.color.x = metadata["overlay"]["cursor"]["color"][0].asFloat(); - uniforms.cursor.color.y = metadata["overlay"]["cursor"]["color"][1].asFloat(); - uniforms.cursor.color.z = metadata["overlay"]["cursor"]["color"][2].asFloat(); - uniforms.cursor.color.w = metadata["overlay"]["cursor"]["color"][3].asFloat(); - } - shader.updateBuffer( (void*) &uniforms, sizeof(uniforms), 0 ); - } - } } - } - - if ( metadata["debug"]["moveable"].asBool() ) { - pod::Transform<>& transform = this->getComponent>(); - pod::Vector2f step = { - 4 / (::size.current.x > 0 ? ::size.current.x : ::size.reference.x), - 4 / (::size.current.y > 0 ? ::size.current.y : ::size.reference.y), - }; - if ( uf::Window::isKeyPressed("U") ) { - step.x = (step.y = uf::physics::time::delta * 0.5f); - } - - if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("J") ) { - transform.scale.x += step.x * -1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; - } - if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("L") ) { - transform.scale.x += step.x * 1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; - } - if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("I") ) { - transform.scale.y += step.y * -1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; - } - if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("K") ) { - transform.scale.y += step.y * 1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; - } - if ( uf::Window::isKeyPressed("J") ) { - transform.position.x += step.x * -1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; - } - if ( uf::Window::isKeyPressed("L") ) { - transform.position.x += step.x * 1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; - } - if ( uf::Window::isKeyPressed("I") ) { - transform.position.y += step.y * -1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; - } - if ( uf::Window::isKeyPressed("K") ) { - transform.position.y += step.y * 1; - pod::Transform<> flat = uf::transform::flatten( transform ); - std::cout << this->getName() << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; - } - } } void ext::GuiBehavior::destroy( uf::Object& self ){} #undef this \ No newline at end of file diff --git a/ext/gui/gui.h b/ext/gui/gui.h index 07b3de23..386cdc47 100644 --- a/ext/gui/gui.h +++ b/ext/gui/gui.h @@ -4,14 +4,35 @@ #include #include #include +#include +#include #include "behavior.h" +namespace pod { + struct GlyphBox { + struct { + float x, y, w, h; + } box; + uint64_t code; + pod::Vector3f color; + }; +} + namespace ext { class EXT_API Gui : public uf::Object { public: - Gui() { - this->addBehavior(); - } + typedef uf::BaseMesh glyph_mesh_t; + + Gui(); + std::vector generateGlyphs( const std::string& = "" ); + void load( uf::Image& ); }; + namespace gui { + struct Size { + pod::Vector2ui current = {}; + pod::Vector2ui reference = {}; + }; + extern Size size; + } } \ No newline at end of file diff --git a/ext/gui/manager/behavior.cpp b/ext/gui/manager/behavior.cpp new file mode 100644 index 00000000..b6619dc0 --- /dev/null +++ b/ext/gui/manager/behavior.cpp @@ -0,0 +1,204 @@ +#include "behavior.h" +#include "../gui.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +ext::gui::Size ext::gui::size = { + .current = { + uf::renderer::width, + uf::renderer::height, + }, + .reference = { + 1920, + 1080 + }, +}; + + +EXT_BEHAVIOR_REGISTER_CPP(GuiManagerBehavior) +EXT_BEHAVIOR_REGISTER_AS_OBJECT(GuiManagerBehavior, GuiManager) +#define this (&self) +void ext::GuiManagerBehavior::initialize( uf::Object& self ) { + { + ext::gui::size.current.x = uf::renderer::width; + ext::gui::size.current.y = uf::renderer::height; + } + // add gui render mode + if ( !uf::renderer::hasRenderMode( "Gui", true ) ) { + auto& renderMode = this->getComponent(); + std::string name = "Gui"; + uf::renderer::addRenderMode( &renderMode, name ); + renderMode.blitter.descriptor.subpass = 1; + } + + uf::Serializer& metadata = this->getComponent(); + uf::Asset& assetLoader = this->getComponent(); + + this->addHook( "window:Resized", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + pod::Vector2ui size; { + size.x = json["window"]["size"]["x"].asUInt64(); + size.y = json["window"]["size"]["y"].asUInt64(); + } + ext::gui::size.current = size; + // ext::gui::size.reference = size; + + return "true"; + } ); + this->addHook( "window:Mouse.Moved", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + bool down = json["mouse"]["state"].asString() == "Down"; + bool clicked = false; + pod::Vector2ui position; { + position.x = json["mouse"]["position"]["x"].asInt() > 0 ? json["mouse"]["position"]["x"].asUInt() : 0; + position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; + } + pod::Vector2f click; { + click.x = (float) position.x / (float) ext::gui::size.current.x; + click.y = (float) position.y / (float) ext::gui::size.current.y; + click.x = (click.x * 2.0f) - 1.0f; + click.y = (click.y * 2.0f) - 1.0f; + float x = click.x; + float y = click.y; + } + + { + auto& scene = uf::scene::getCurrentScene(); + auto& controller = scene.getController(); + auto& metadata = controller.getComponent(); + + if ( metadata["overlay"]["cursor"]["type"].asString() == "mouse" ) { + metadata["overlay"]["cursor"]["position"][0] = click.x; + metadata["overlay"]["cursor"]["position"][1] = click.y; + } + } + + + return "true"; + }); +} +void ext::GuiManagerBehavior::tick( uf::Object& self ) { + +} +void ext::GuiManagerBehavior::render( uf::Object& self ){ + auto& scene = uf::scene::getCurrentScene(); + auto& controller = scene.getController(); + auto& camera = controller.getComponent(); + auto& metadata = controller.getComponent(); + + uf::renderer::RenderTargetRenderMode* renderModePointer = NULL; + if ( this->hasComponent() ) { + renderModePointer = this->getComponentPointer(); + } else { + renderModePointer = (uf::renderer::RenderTargetRenderMode*) &uf::renderer::getRenderMode( "Gui", true ); + } + auto& renderMode = *renderModePointer; + auto& blitter = renderMode.blitter; + struct UniformDescriptor { + struct { + alignas(16) pod::Matrix4f models[2]; + } matrices; + struct { + alignas(8) pod::Vector2f position = { 0.5f, 0.5f }; + alignas(8) pod::Vector2f radius = { 0.1f, 0.1f }; + alignas(16) pod::Vector4f color = { 1, 1, 1, 1 }; + } cursor; + // alignas(8) pod::Vector2f alpha; + }; + auto& shader = blitter.material.shaders.front(); + auto& uniforms = shader.uniforms.front().get(); + + for ( size_t i = 0; i < 2; ++i ) { + pod::Transform<> transform; + if ( metadata["overlay"]["position"].isArray() ) + transform.position = { + metadata["overlay"]["position"][0].asFloat(), + metadata["overlay"]["position"][1].asFloat(), + metadata["overlay"]["position"][2].asFloat(), + }; + if ( metadata["overlay"]["scale"].isArray() ) + transform.scale = { + metadata["overlay"]["scale"][0].asFloat(), + metadata["overlay"]["scale"][1].asFloat(), + metadata["overlay"]["scale"][2].asFloat(), + }; + if ( metadata["overlay"]["orientation"].isArray() ) + transform.orientation = { + metadata["overlay"]["orientation"][0].asFloat(), + metadata["overlay"]["orientation"][1].asFloat(), + metadata["overlay"]["orientation"][2].asFloat(), + metadata["overlay"]["orientation"][3].asFloat(), + }; + if ( ext::openvr::enabled && (metadata["overlay"]["enabled"].asBool() || uf::renderer::getRenderMode("Stereoscopic Deferred", true).getType() == "Stereoscopic Deferred" )) { + if ( metadata["overlay"]["floating"].asBool() ) { + pod::Matrix4f model = uf::transform::model( transform ); + uniforms.matrices.models[i] = camera.getProjection(i) * ext::openvr::hmdEyePositionMatrix( i == 0 ? vr::Eye_Left : vr::Eye_Right ) * model; + } else { + auto translation = uf::matrix::translate( uf::matrix::identity(), camera.getTransform().position + controller.getComponent>().position ); + auto rotation = uf::quaternion::matrix( controller.getComponent>().orientation * pod::Vector4f{1,1,1,-1} ); + + pod::Matrix4f model = translation * rotation * uf::transform::model( transform ); + uniforms.matrices.models[i] = camera.getProjection(i) * camera.getView(i) * model; + } + } else { + pod::Matrix4t<> model = uf::matrix::translate( uf::matrix::identity(), { 0, 0, 1 } ); + uniforms.matrices.models[i] = model; + } + // uniforms.alpha.x = metadata["overlay"]["alpha"].asFloat(); + // uniforms.alpha.y = 0; + + uniforms.cursor.position.x = (metadata["overlay"]["cursor"]["position"][0].asFloat() + 1.0f) * 0.5f; //(::mouse.position.x + 1.0f) * 0.5f; + uniforms.cursor.position.y = (metadata["overlay"]["cursor"]["position"][1].asFloat() + 1.0f) * 0.5f; //(::mouse.position.y + 1.0f) * 0.5f; + + pod::Vector3f cursorSize; + cursorSize.x = metadata["overlay"]["cursor"]["radius"].asFloat(); + cursorSize.y = metadata["overlay"]["cursor"]["radius"].asFloat(); + cursorSize = uf::matrix::multiply( uf::matrix::inverse( uf::matrix::scale( uf::matrix::identity() , transform.scale) ), cursorSize ); + + uniforms.cursor.radius.x = cursorSize.x; + uniforms.cursor.radius.y = cursorSize.y; + + uniforms.cursor.color.x = metadata["overlay"]["cursor"]["color"][0].asFloat(); + uniforms.cursor.color.y = metadata["overlay"]["cursor"]["color"][1].asFloat(); + uniforms.cursor.color.z = metadata["overlay"]["cursor"]["color"][2].asFloat(); + uniforms.cursor.color.w = metadata["overlay"]["cursor"]["color"][3].asFloat(); + } + shader.updateBuffer( (void*) &uniforms, sizeof(uniforms), 0 ); +} +void ext::GuiManagerBehavior::destroy( uf::Object& self ){ + if ( this->hasComponent() ) { + auto& renderMode = this->getComponent(); + uf::renderer::removeRenderMode( &renderMode, false ); + this->deleteComponent(); + } +} +#undef this \ No newline at end of file diff --git a/ext/gui/manager/behavior.h b/ext/gui/manager/behavior.h new file mode 100644 index 00000000..7e6b8a9f --- /dev/null +++ b/ext/gui/manager/behavior.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +namespace ext { + class EXT_API GuiManagerBehavior { + public: + static void attach( uf::Object& ); + static void initialize( uf::Object& ); + static void tick( uf::Object& ); + static void render( uf::Object& ); + static void destroy( uf::Object& ); + }; +} \ No newline at end of file diff --git a/ext/main.cpp b/ext/main.cpp index 29c77696..f3e998b0 100644 --- a/ext/main.cpp +++ b/ext/main.cpp @@ -64,9 +64,38 @@ namespace { uf::Serializer config; } bool ext::ready = false; +std::vector ext::arguments; void EXT_API ext::initialize() { - /**/ { + ::config = ext::getConfig(); + /* Arguments */ { + auto& arguments = ::config["arguments"]; + for ( auto& arg : ext::arguments ) { + // store raw argument + int i = arguments.size(); + arguments[i] = arg; + // parse --key=value + { + std::regex regex("^--(.+?)=(.+?)$"); + std::smatch match; + if ( std::regex_search( arg, match, regex ) ) { + std::string keyString = match[1].str(); + std::string valueString = match[2].str(); + auto keys = uf::string::split(keyString, "."); + uf::Serializer value; value.deserialize(valueString); + Json::Value* traversal = &::config; + for ( auto& key : keys ) { + traversal = &((*traversal)[key]); + } + *traversal = value; + } + } + } + uf::iostream << "Arguments: " << uf::Serializer(arguments) << "\n"; + uf::iostream << "New config: " << ::config << "\n"; + } + + /* Seed */ { srand(time(NULL)); } /* Open output file */ { @@ -84,7 +113,6 @@ void EXT_API ext::initialize() { // #include "./inits/persistence.inl" } - ::config = ext::getConfig(); /* Parse config */ { // Set memory pool sizes { @@ -153,6 +181,11 @@ void EXT_API ext::initialize() { uf::thread::workers = ::config["engine"]["threads"]["workers"].asUInt64(); // Enable valiation layer uf::renderer::validation = ::config["engine"]["ext"]["vulkan"]["validation"]["enabled"].asBool(); + if ( ::config["engine"]["ext"]["vulkan"]["validation"]["enabled"].isNumeric() ) + uf::renderer::msaa = ::config["engine"]["ext"]["vulkan"]["msaa"].asUInt64(); + + uf::renderer::rebuildOnTickStart = ::config["engine"]["ext"]["vulkan"]["validation"]["rebuild on tick begin"].asBool(); + uf::renderer::waitOnRenderEnd = ::config["engine"]["ext"]["vulkan"]["validation"]["wait on render end"].asBool(); for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["validation"]["filters"].size(); ++i ) { uf::renderer::validationFilters.push_back( ::config["engine"]["ext"]["vulkan"]["validation"]["filters"][i].asString() ); @@ -196,7 +229,6 @@ void EXT_API ext::initialize() { uf::renderer::addRenderMode( renderMode, "Gui" ); renderMode->blitter.descriptor.subpass = 1; } - if ( ::config["engine"]["render modes"]["stereo deferred"].asBool() ) uf::renderer::addRenderMode( new uf::renderer::StereoscopicDeferredRenderMode, "" ); else if ( ::config["engine"]["render modes"]["deferred"].asBool() ) @@ -240,6 +272,9 @@ void EXT_API ext::initialize() { /* Add hooks */ { uf::hooks.addHook( "game:LoadScene", [&](const std::string& event)->std::string{ uf::Serializer json = event; + std::cout << "SCENE CHANGE: " << event << std::endl; + uf::renderer::mutex.lock(); + uf::renderer::mutex.unlock(); uf::scene::unloadScene(); auto& scene = uf::scene::loadScene( json["scene"].asString() ); auto& metadata = scene.getComponent(); diff --git a/ext/scenes/raytrace/behavior.cpp b/ext/scenes/raytrace/behavior.cpp index 13a7e983..ae0da4d8 100644 --- a/ext/scenes/raytrace/behavior.cpp +++ b/ext/scenes/raytrace/behavior.cpp @@ -68,7 +68,7 @@ void ext::RayTracingSceneBehavior::initialize( uf::Object& self ) { renderMode.compute.initializeBuffer( (void*) shapes.data(), shapes.size() * sizeof(Shape), - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true ); diff --git a/ext/scenes/worldscape/behavior.cpp b/ext/scenes/worldscape/behavior.cpp index 24016c50..299a3365 100644 --- a/ext/scenes/worldscape/behavior.cpp +++ b/ext/scenes/worldscape/behavior.cpp @@ -74,7 +74,7 @@ void ext::WorldScapeSceneBehavior::initialize( uf::Object& self ) { this->addHook( "world:Battle.Start", [&](const std::string& event)->std::string{ if ( timer.elapsed().asDouble() < 1 ) return "false"; timer.reset(); - uf::Object* guiManager = (uf::Object*) this->findByName("Gui Manager"); + uf::Object* guiManager = (uf::Object*) this->globalFindByName("Gui Manager"); if ( !guiManager ) return "false"; uf::Serializer json = event; @@ -88,7 +88,7 @@ void ext::WorldScapeSceneBehavior::initialize( uf::Object& self ) { }); this->addHook( "world:Battle.End", [&](const std::string& event)->std::string{ uf::Serializer json = event; - ext::HousamoBattle* battleManager = (ext::HousamoBattle*) this->findByName("Battle Manager"); + ext::HousamoBattle* battleManager = (ext::HousamoBattle*) this->globalFindByName("Battle Manager"); if ( !battleManager ) return "false"; this->removeChild(*battleManager); @@ -100,7 +100,7 @@ void ext::WorldScapeSceneBehavior::initialize( uf::Object& self ) { this->addHook( "menu:Dialogue.Start", [&](const std::string& event)->std::string{ if ( timer.elapsed().asDouble() < 1 ) return "false"; timer.reset(); - uf::Object* guiManager = (uf::Object*) this->findByName("Gui Manager"); + uf::Object* guiManager = (uf::Object*) this->globalFindByName("Gui Manager"); if ( !guiManager ) return "false"; uf::Serializer json = event; @@ -112,7 +112,7 @@ void ext::WorldScapeSceneBehavior::initialize( uf::Object& self ) { }); this->addHook( "menu:Dialogue.End", [&](const std::string& event)->std::string{ uf::Serializer json = event; - ext::DialogueManager* dialogueManager = (ext::DialogueManager*) this->findByName("Dialogue Manager"); + ext::DialogueManager* dialogueManager = (ext::DialogueManager*) this->globalFindByName("Dialogue Manager"); if ( !dialogueManager ) return "false"; this->removeChild(*dialogueManager); diff --git a/ext/scenes/worldscape/gui/battle.cpp b/ext/scenes/worldscape/gui/battle.cpp index fab2fdad..641e09b5 100644 --- a/ext/scenes/worldscape/gui/battle.cpp +++ b/ext/scenes/worldscape/gui/battle.cpp @@ -206,37 +206,37 @@ void ext::GuiBattle::initialize() { { battleMessage = new ext::Gui; this->addChild(*battleMessage); - battleMessage->load("./battle-text.json", true); + battleMessage->as().load("./battle-text.json", true); battleMessage->initialize(); } /* Magic Circle Outter */ { circleOut = new ext::Gui; this->addChild(*circleOut); - circleOut->load("./circle-out.json", true); + circleOut->as().load("./circle-out.json", true); circleOut->initialize(); } /* Magic Circle Inner */ { circleIn = new ext::Gui; this->addChild(*circleIn); - circleIn->load("./circle-in.json", true); + circleIn->as().load("./circle-in.json", true); circleIn->initialize(); } /* Command Circle */ { partyMemberCommandCircle = new ext::Gui; this->addChild(*partyMemberCommandCircle); - partyMemberCommandCircle->load("./partyMemberCommandCircle.json", true); + partyMemberCommandCircle->as().load("./partyMemberCommandCircle.json", true); partyMemberCommandCircle->initialize(); } /* Command Circle */ { optionsBackground = new ext::Gui; this->addChild(*optionsBackground); - optionsBackground->load("./optionsBackground.json", true); + optionsBackground->as().load("./optionsBackground.json", true); optionsBackground->initialize(); } /* Turn Counter Container */ { turnCounters = new ext::Gui; this->addChild(*turnCounters); - turnCounters->load("./turnCounters.json", true); + turnCounters->as().load("./turnCounters.json", true); turnCounters->initialize(); } @@ -277,7 +277,7 @@ void ext::GuiBattle::initialize() { ext::Gui* particle = new ext::Gui; this->addChild(*particle); uf::Serializer& pMetadata = particle->getComponent(); - particle->load("./damageText.json", true); + particle->as().load("./damageText.json", true); std::string color = json["color"].isString() ? json["color"].asString() : "FF0000"; pMetadata["text settings"]["string"] = "%#"+color+"%" + damage; pod::Transform<>& transform = particle->getComponent>(); @@ -300,7 +300,7 @@ void ext::GuiBattle::initialize() { battleMessage = new ext::Gui; this->addChild(*battleMessage); uf::Serializer& pMetadata = battleMessage->getComponent(); - battleMessage->load("./battle-text.json", true); + battleMessage->as().load("./battle-text.json", true); pMetadata["text settings"]["string"] = message; battleMessage->initialize(); @@ -340,7 +340,7 @@ void ext::GuiBattle::initialize() { { critCutInBackground = new ext::Gui; this->addChild(*critCutInBackground); - critCutInBackground->load("./critCutInBackground.json", true); + critCutInBackground->as().load("./critCutInBackground.json", true); critCutInBackground->initialize(); } { @@ -351,7 +351,7 @@ void ext::GuiBattle::initialize() { uf::Serializer cardData = masterDataGet("Card", json["id"].asString()); pMetadata["system"]["assets"][0] = "/smtsamo/ci/ci_"+ cardData["filename"].asString() +".png"; if ( !uf::io::exists( pMetadata["system"]["assets"][0].asString() ) ) pMetadata["system"]["assets"][0] = Json::nullValue; - critCutIn->load("./critCutIn.json", true); + critCutIn->as().load("./critCutIn.json", true); critCutIn->initialize(); } @@ -373,7 +373,7 @@ void ext::GuiBattle::initialize() { { turnCounters = new ext::Gui; this->addChild(*turnCounters); - turnCounters->load("./turnCounters.json", true); + turnCounters->as().load("./turnCounters.json", true); turnCounters->initialize(); } // show full counters @@ -381,7 +381,7 @@ void ext::GuiBattle::initialize() { while ( turns > 0 ) { ext::Gui* counter = new ext::Gui; turnCounters->addChild(*counter); - counter->load("./turnCounter.json", true); + counter->as().load("./turnCounter.json", true); counter->initialize(); pod::Transform<>& pTransform = counter->getComponent>(); pTransform.position.x -= 0.142716f * (i++); @@ -465,7 +465,7 @@ void ext::GuiBattle::initialize() { partyMemberText = new ext::Gui; partyMemberButton->addChild(*partyMemberText); uf::Serializer& pMetadata = partyMemberText->getComponent(); - partyMemberText->load("./partyMemberText.json", true); + partyMemberText->as().load("./partyMemberText.json", true); pMetadata["text settings"]["string"] = "" + colorString("00FF00") + "" + member["hp"].asString() + "\n" + colorString("0000FF") + "" + member["mp"].asString(); pMetadata[""] = member; pod::Transform<>& transform = partyMemberText->getComponent>(); @@ -499,7 +499,7 @@ void ext::GuiBattle::initialize() { { partyMemberButton = new ext::Gui; this->addChild(*partyMemberButton); - partyMemberButton->load("./partyMemberButton.json", true); + partyMemberButton->as().load("./partyMemberButton.json", true); pod::Transform<>& transform = partyMemberButton->getComponent>(); transform.position.y += (i * 0.45); @@ -518,7 +518,7 @@ void ext::GuiBattle::initialize() { partyMemberButton->addChild(*partyMemberIconShadow); uf::Serializer& pMetadata = partyMemberIconShadow->getComponent(); pMetadata["system"]["assets"][0] = filename; - partyMemberIconShadow->load("./partyMemberIconShadow.json", true); + partyMemberIconShadow->as().load("./partyMemberIconShadow.json", true); pMetadata[""] = member; pod::Transform<>& transform = partyMemberIconShadow->getComponent>(); transform.position = bTransform.position; @@ -532,7 +532,7 @@ void ext::GuiBattle::initialize() { partyMemberButton->addChild(*partyMemberIcon); uf::Serializer& pMetadata = partyMemberIcon->getComponent(); pMetadata["system"]["assets"][0] = filename; - partyMemberIcon->load("./partyMemberIcon.json", true); + partyMemberIcon->as().load("./partyMemberIcon.json", true); pMetadata[""] = member; pod::Transform<>& transform = partyMemberIcon->getComponent>(); transform.position = bTransform.position; @@ -544,7 +544,7 @@ void ext::GuiBattle::initialize() { partyMemberText = new ext::Gui; partyMemberButton->addChild(*partyMemberText); uf::Serializer& pMetadata = partyMemberText->getComponent(); - partyMemberText->load("./partyMemberText.json", true); + partyMemberText->as().load("./partyMemberText.json", true); pMetadata["text settings"]["string"] = "" + colorString("FF0000") + "" + member["hp"].asString() + "\n" + colorString("0000FF") + "" + member["mp"].asString(); pMetadata[""] = member; pod::Transform<>& transform = partyMemberText->getComponent>(); @@ -555,7 +555,7 @@ void ext::GuiBattle::initialize() { { ext::Gui* partyMemberBar = new ext::Gui; partyMemberButton->addChild(*partyMemberBar); - partyMemberBar->load("./partyMemberBars.json", true); + partyMemberBar->as().load("./partyMemberBars.json", true); pod::Transform<>& transform = partyMemberBar->getComponent>(); transform.position = bTransform.position; transform.position.x = 0.742573; @@ -565,7 +565,7 @@ void ext::GuiBattle::initialize() { { ext::Gui* partyMemberHP = new ext::Gui; partyMemberButton->addChild(*partyMemberHP); - partyMemberHP->load("./partyMemberHP.json", true); + partyMemberHP->as().load("./partyMemberHP.json", true); pod::Transform<>& transform = partyMemberHP->getComponent>(); transform.position = bTransform.position; transform.position.x = 0.742573; @@ -583,7 +583,7 @@ void ext::GuiBattle::initialize() { { ext::Gui* partyMemberMP = new ext::Gui; partyMemberButton->addChild(*partyMemberMP); - partyMemberMP->load("./partyMemberMP.json", true); + partyMemberMP->as().load("./partyMemberMP.json", true); pod::Transform<>& transform = partyMemberMP->getComponent>(); transform.position = bTransform.position; transform.position.x = 0.742573; @@ -740,7 +740,7 @@ void ext::GuiBattle::tick() { battleOptions = new ext::Gui; this->addChild(*battleOptions); uf::Serializer& pMetadata = battleOptions->getComponent(); - battleOptions->load("./battle-option.json", true); + battleOptions->as().load("./battle-option.json", true); pMetadata["text settings"]["string"] = string; pod::Transform<>& pTransform = battleOptions->getComponent>(); battleOptions->initialize(); @@ -793,7 +793,7 @@ void ext::GuiBattle::tick() { commandOptions = new ext::Gui; this->addChild(*commandOptions); uf::Serializer& pMetadata = commandOptions->getComponent(); - commandOptions->load("./battle-option.json", true); + commandOptions->as().load("./battle-option.json", true); pMetadata["text settings"]["string"] = string; pod::Transform<>& pTransform = commandOptions->getComponent>(); commandOptions->initialize(); @@ -845,7 +845,7 @@ void ext::GuiBattle::tick() { targetOptions = new ext::Gui; this->addChild(*targetOptions); uf::Serializer& pMetadata = targetOptions->getComponent(); - targetOptions->load("./battle-option.json", true); + targetOptions->as().load("./battle-option.json", true); pMetadata["text settings"]["string"] = string; pod::Transform<>& pTransform = targetOptions->getComponent>(); targetOptions->initialize(); diff --git a/ext/scenes/worldscape/gui/dialogue.cpp b/ext/scenes/worldscape/gui/dialogue.cpp index f6beb5fe..9ed04d48 100644 --- a/ext/scenes/worldscape/gui/dialogue.cpp +++ b/ext/scenes/worldscape/gui/dialogue.cpp @@ -203,7 +203,7 @@ void ext::GuiDialogue::tick() { dialogueMessage = new ext::Gui; this->addChild(*dialogueMessage); uf::Serializer& pMetadata = dialogueMessage->getComponent(); - dialogueMessage->load("./dialogue-text.json", true); + dialogueMessage->as().load("./dialogue-text.json", true); pMetadata["text settings"]["string"] = result["message"].asString(); dialogueMessage->initialize(); } @@ -251,7 +251,7 @@ void ext::GuiDialogue::tick() { dialogueOptions = new ext::Gui; this->addChild(*dialogueOptions); uf::Serializer& pMetadata = dialogueOptions->getComponent(); - dialogueOptions->load("./dialogue-option.json", true); + dialogueOptions->as().load("./dialogue-option.json", true); pMetadata["text settings"]["string"] = string; pod::Transform<>& pTransform = dialogueOptions->getComponent>(); dialogueOptions->initialize(); diff --git a/ext/scenes/worldscape/gui/pause/behavior.cpp b/ext/scenes/worldscape/gui/pause/behavior.cpp index e6c902db..f17ce0db 100644 --- a/ext/scenes/worldscape/gui/pause/behavior.cpp +++ b/ext/scenes/worldscape/gui/pause/behavior.cpp @@ -127,6 +127,7 @@ void ext::GuiWorldPauseMenuBehavior::initialize( uf::Object& self ) { uf::Serializer member = pMetadata[""]["transients"][id]; uf::Serializer cardData = masterDataGet("Card", id); std::string name = cardData["filename"].asString(); + if ( name == "" ) continue; if ( member["skin"].isString() ) name += "_" + member["skin"].asString(); std::string filename = "https://cdn..xyz//unity/Android/fg/fg_" + name + ".png"; if ( cardData["internal"].asBool() ) { @@ -137,7 +138,7 @@ void ext::GuiWorldPauseMenuBehavior::initialize( uf::Object& self ) { } { std::string portrait = metadata["portraits"]["list"][0].asString(); - /* Transient shadow background */ { + /* Transient shadow background */ if ( portrait != "" ) { transientSpriteShadow = new uf::Object; uf::Asset& assetLoader = transientSpriteShadow->getComponent(); this->addChild(*transientSpriteShadow); @@ -148,7 +149,7 @@ void ext::GuiWorldPauseMenuBehavior::initialize( uf::Object& self ) { transientSpriteShadow->initialize(); } - /* Transient transientSprite */ { + /* Transient transientSprite */ if ( portrait != "" ) { transientSprite = new uf::Object; uf::Asset& assetLoader = transientSprite->getComponent(); this->addChild(*transientSprite); @@ -229,7 +230,13 @@ void ext::GuiWorldPauseMenuBehavior::initialize( uf::Object& self ) { delete this; return "true"; */ + uf::Serializer json = event; + + metadata["system"]["hooks"]["onClose"] = json["callback"]; + metadata["system"]["closing"] = true; + playSound(*this, "menu close"); + return "true"; }); } @@ -238,6 +245,7 @@ void ext::GuiWorldPauseMenuBehavior::tick( uf::Object& self ) { uf::Serializer& masterdata = scene.getComponent(); static uf::Timer timer(false); static uf::Audio sfx; +// if ( !timer.running() ) timer.start( uf::Time<>(-1000000) ); if ( !timer.running() ) timer.start(); static float alpha = 0.0f; @@ -259,6 +267,21 @@ void ext::GuiWorldPauseMenuBehavior::tick( uf::Object& self ) { } else if ( metadata["system"]["closed"].asBool() ) { // kill timer.stop(); + + uf::Serializer callback = metadata["system"]["hooks"]["onClose"]; + uf::Serializer payload = callback["payload"]; + uf::Object* target = this; + if ( callback["scope"].asString() == "parent" ) { + target = &this->getParent().as(); + } else if ( callback["scope"].asString() == "scene" ) { + target = &uf::scene::getCurrentScene(); + } + if ( callback["delay"].isNumeric() ) { + target->queueHook( callback["name"].asString(), payload, callback["delay"].asFloat() ); + } else { + target->callHook( callback["name"].asString(), payload ); + } + this->destroy(); this->getParent().removeChild(*this); delete this; @@ -495,13 +518,13 @@ void ext::GuiWorldPauseMenuBehavior::tick( uf::Object& self ) { delta += uf::physics::time::delta * 1.5f; transform.position = uf::vector::lerp( start, end, delta ); } - - + /* if ( metadata["clicked"].asBool() && timer.elapsed().asDouble() >= 1 ) { timer.reset(); this->callHook("menu:Close.%UID%"); playSound(*this, "menu close"); } + */ metadata["color"][3] = alpha; for ( uf::Entity* pointer : closeOption->getChildren() ) { @@ -531,17 +554,13 @@ void ext::GuiWorldPauseMenuBehavior::tick( uf::Object& self ) { delta += uf::physics::time::delta * 1.5f; transform.position = uf::vector::lerp( start, end, delta ); } - - + /* if ( metadata["clicked"].asBool() && timer.elapsed().asDouble() >= 1 ) { timer.reset(); this->callHook("menu:Close.%UID%"); playSound(*this, "menu close"); - /* - uf::Scene& scene = this->getRootParent(); - scene.queueHook("system:Quit.%UID%", "", 1.0f); - */ } + */ metadata["color"][3] = alpha; for ( uf::Entity* pointer : quitOption->getChildren() ) { diff --git a/ext/scenes/worldscape/housamo/battle.cpp b/ext/scenes/worldscape/housamo/battle.cpp index 724b7fd3..44b67f71 100644 --- a/ext/scenes/worldscape/housamo/battle.cpp +++ b/ext/scenes/worldscape/housamo/battle.cpp @@ -105,7 +105,7 @@ namespace { void ext::HousamoBattle::initialize() { uf::Object::initialize(); - gui = NULL; + ::gui = NULL; transients.clear(); uf::Scene& scene = uf::scene::getCurrentScene(); @@ -283,17 +283,17 @@ void ext::HousamoBattle::initialize() { return payload; }); this->addHook( "world:Battle.Gui.%UID%", [&](const std::string& event)->std::string{ - ext::Gui* guiManager = (ext::Gui*) this->getRootParent().findByName("Gui Manager"); + ext::Gui* guiManager = (ext::Gui*) this->globalFindByName("Gui Manager"); ext::Gui* guiMenu = new ext::GuiBattle; guiManager->addChild(*guiMenu); - guiMenu->load("./scenes/world/gui/battle/menu.json"); + guiMenu->as().load("./scenes/world/gui/battle/menu.json"); guiMenu->initialize(); uf::Serializer payload; payload["battle"] = metadata["battle"]; guiMenu->queueHook("world:Battle.Start.%UID%", payload); - gui = guiMenu; + ::gui = guiMenu; return "true"; }); @@ -403,10 +403,10 @@ void ext::HousamoBattle::initialize() { // std::cout << metadata["battle"]["transients"] << std::endl; } - if ( gui ) { + if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; - gui->queueHook("world:Battle.Update.%UID%", payload); + ::gui->queueHook("world:Battle.Update.%UID%", payload); } if ( hookName == "" ) return payload; @@ -657,10 +657,10 @@ void ext::HousamoBattle::initialize() { turnState["counter"] = counter; turnState["start of turn"] = true; - if ( gui ) { + if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; - gui->queueHook("world:Battle.TurnStart.%UID%", payload); + ::gui->queueHook("world:Battle.TurnStart.%UID%", payload); } } else { turnState["start of turn"] = false; @@ -791,10 +791,10 @@ void ext::HousamoBattle::initialize() { } } - if ( gui ) { + if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; - gui->queueHook("world:Battle.Update.%UID%", payload); + ::gui->queueHook("world:Battle.Update.%UID%", payload); } if ( skipped ) payload["skipped"] = true; @@ -906,7 +906,7 @@ void ext::HousamoBattle::initialize() { payload["color"] = json["color"]; payload["target"]["uid"] = json["uid"]; payload["target"]["damage"] = json["message"]; - gui->queueHook("world:Battle.Damage.%UID%", payload, delays.damage); + ::gui->queueHook("world:Battle.Damage.%UID%", payload, delays.damage); delays.damage += 0.25f; }; @@ -1062,7 +1062,7 @@ void ext::HousamoBattle::initialize() { uf::Serializer payload; payload["target"]["uid"] = target["uid"]; payload["target"]["damage"] = "Miss"; - gui->queueHook("world:Battle.Damage.%UID%", payload); + ::gui->queueHook("world:Battle.Damage.%UID%", payload); continue; } @@ -1337,7 +1337,7 @@ void ext::HousamoBattle::initialize() { uf::Serializer payload; payload["uid"] = json["uid"]; payload["id"] = member["id"]; - gui->queueHook("world:Battle.OnCrit.%UID%", payload, 0.1f); + ::gui->queueHook("world:Battle.OnCrit.%UID%", payload, 0.1f); } } @@ -1435,7 +1435,7 @@ void ext::HousamoBattle::initialize() { */ uf::Scene& scene = uf::scene::getCurrentScene(); scene.callHook("world:Music.LoadPrevious.%UID%"); - gui->callHook("menu:Close.%UID%"); + ::gui->callHook("menu:Close.%UID%"); // cleanup for ( ext::Housamo* pointer : transients ) { diff --git a/ext/scenes/worldscape/housamo/dialogue.cpp b/ext/scenes/worldscape/housamo/dialogue.cpp index 8b763e6d..8cb37005 100644 --- a/ext/scenes/worldscape/housamo/dialogue.cpp +++ b/ext/scenes/worldscape/housamo/dialogue.cpp @@ -92,7 +92,7 @@ namespace { void ext::DialogueManager::initialize() { uf::Object::initialize(); - gui = NULL; + ::gui = NULL; transients.clear(); uf::Serializer& metadata = this->getComponent(); @@ -175,17 +175,17 @@ void ext::DialogueManager::initialize() { return payload; }); this->addHook( "menu:Dialogue.Gui.%UID%", [&](const std::string& event)->std::string{ - ext::Gui* guiManager = (ext::Gui*) this->getRootParent().findByName("Gui Manager"); + ext::Gui* guiManager = (ext::Gui*) this->globalFindByName("Gui Manager"); ext::Gui* guiMenu = new ext::GuiDialogue; guiManager->addChild(*guiMenu); - guiMenu->load("./entities/gui/dialogue/menu.json"); + guiMenu->as().load("./entities/gui/dialogue/menu.json"); guiMenu->initialize(); uf::Serializer payload; payload["dialogue"] = metadata["dialogue"]; guiMenu->queueHook("menu:Dialogue.Start.%UID%", payload); - gui = guiMenu; + ::gui = guiMenu; return "true"; }); @@ -251,7 +251,7 @@ void ext::DialogueManager::initialize() { // play music uf::Scene& scene = uf::scene::getCurrentScene(); scene.callHook("world:Music.LoadPrevious.%UID%"); - gui->callHook("menu:Close.%UID%"); + ::gui->callHook("menu:Close.%UID%"); this->callHook("menu:Dialogue.End"); diff --git a/ext/scenes/worldscape/terrain/behavior.cpp b/ext/scenes/worldscape/terrain/behavior.cpp index a52d7e4d..49a40e40 100644 --- a/ext/scenes/worldscape/terrain/behavior.cpp +++ b/ext/scenes/worldscape/terrain/behavior.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -77,7 +78,51 @@ void ext::TerrainBehavior::initialize( uf::Object& self ) { uf::thread::limiter = metadata["system"]["limiter"].asFloat(); return "true"; }); + this->addHook( "terrain:GenerateMesh.%UID%", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + auto& graphic = this->getComponent(); + auto& mesh = this->getComponent(); + + graphic.initializeGeometry( mesh ); + graphic.getPipeline().update( graphic ); + graphic.process = true; + uf::renderer::rebuild = true; + + return "true"; + }); this->addHook( "terrain:Post-Initialize.%UID%", [&](const std::string& event)->std::string{ + if ( metadata["terrain"]["unified"].asBool() ) { + std::string textureFilename = ""; { + uf::Asset assetLoader; + for ( uint i = 0; i < metadata["system"]["assets"].size(); ++i ) { + if ( textureFilename != "" ) break; + std::string filename = this->grabURI( metadata["system"]["assets"][i].asString(), metadata["system"]["root"].asString() ); + textureFilename = assetLoader.cache( filename ); + } + } + auto& graphic = this->getComponent(); + + graphic.initialize(); + graphic.process = false; + + auto& texture = graphic.material.textures.emplace_back(); + texture.sampler.descriptor.filter.min = VK_FILTER_NEAREST; + texture.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; + texture.loadFromFile( textureFilename ); + + std::string suffix = ""; { + std::string _ = this->getRootParent().getComponent()["shaders"]["region"]["suffix"].asString(); + if ( _ != "" ) suffix = _ + "."; + } + graphic.material.initializeShaders({ + {"./data/shaders/terrain.stereo.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, + {"./data/shaders/terrain."+suffix+"frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} + }); + + uf::renderer::rebuildOnTickStart = false; + } + this->generate(); /* auto& parent = this->getParent().as(); @@ -185,15 +230,34 @@ void ext::TerrainBehavior::tick( uf::Object& self ) { } // sort by closest to farthest sortRegions(controller, regions); + if ( metadata["terrain"]["unified"].asBool() ) { + auto& graphic = this->getComponent(); + auto& mesh = this->getComponent(); + mesh.destroy(); + for ( uf::Object* region : regions ) { + uf::Serializer& metadata = region->getComponent(); + if ( !metadata["region"]["initialized"].asBool() ) region->initialize(); + if ( !metadata["region"]["generated"].asBool() ) region->callHook("region:Generate.%UID%", ""); + if ( !metadata["region"]["rasterized"].asBool() ) { + region->queueHook("region:Finalize.%UID%", ""); + region->queueHook("region:Populate.%UID%", ""); + } + auto& generator = region->getComponent(); + generator.rasterize(mesh.vertices, *region, false); + } + this->queueHook("terrain:GenerateMesh.%UID%"); + } else { // initialize uninitialized regions - for ( uf::Object* region : regions ) { - // uf::thread::add( uf::thread::fetchWorker(), [&]() -> int { - uf::Serializer& metadata = region->getComponent(); - if ( !metadata["region"]["initialized"].asBool() ) region->initialize(); - if ( !metadata["region"]["generated"].asBool() ) region->callHook("region:Generate.%UID%", ""); - if ( !metadata["region"]["rasterized"].asBool() ) region->callHook("region:Rasterize.%UID%", ""); - // return 0;}, true ); + for ( uf::Object* region : regions ) { + // uf::thread::add( uf::thread::fetchWorker(), [&]() -> int { + uf::Serializer& metadata = region->getComponent(); + if ( !metadata["region"]["initialized"].asBool() ) region->initialize(); + if ( !metadata["region"]["generated"].asBool() ) region->callHook("region:Generate.%UID%", ""); + if ( !metadata["region"]["rasterized"].asBool() ) region->callHook("region:Rasterize.%UID%", ""); + // return 0;}, true ); + } } + // move to next state: transitionState(*this, "open", !regions.empty()); return 0;}, true ); @@ -201,7 +265,25 @@ void ext::TerrainBehavior::tick( uf::Object& self ) { // check if we need to relocate entities this->relocateChildren(); } -void ext::TerrainBehavior::render( uf::Object& self ){} +void ext::TerrainBehavior::render( uf::Object& self ){ + uf::Scene& scene = uf::scene::getCurrentScene(); + uf::Serializer& metadata = this->getComponent(); + + if ( !metadata["terrain"]["unified"].asBool() ) return; + /* Update uniforms */ if ( this->hasComponent() ) { + auto& scene = uf::scene::getCurrentScene(); + auto& graphic = this->getComponent(); + auto& camera = scene.getController().getComponent(); + if ( !graphic.initialized ) return; + auto& uniforms = graphic.material.shaders.front().uniforms.front().get(); + uniforms.matrices.model = uf::matrix::identity(); + for ( std::size_t i = 0; i < 2; ++i ) { + uniforms.matrices.view[i] = camera.getView( i ); + uniforms.matrices.projection[i] = camera.getProjection( i ); + } + graphic.material.shaders.front().updateBuffer( uniforms, 0, true ); + } +} void ext::TerrainBehavior::destroy( uf::Object& self ){} #undef this EXT_BEHAVIOR_ENTITY_CPP_END(Terrain) \ No newline at end of file diff --git a/ext/scenes/worldscape/terrain/generator.cpp b/ext/scenes/worldscape/terrain/generator.cpp index 45ba804c..07da83c5 100644 --- a/ext/scenes/worldscape/terrain/generator.cpp +++ b/ext/scenes/worldscape/terrain/generator.cpp @@ -1287,7 +1287,7 @@ void ext::TerrainGenerator::updateLight(){ // this->wrapLight(); // this->writeToFile(); } -void ext::TerrainGenerator::rasterize( std::vector& vertices, const uf::Object& region ){ +void ext::TerrainGenerator::rasterize( std::vector& vertices, const uf::Object& region, bool applyTransform ){ if ( this->m_voxels.id.rle.empty() ) return; this->writeToFile(); @@ -1326,6 +1326,7 @@ void ext::TerrainGenerator::rasterize( std::vector( modelMatrix, p );*/\ + }\ vertices.push_back(vertex);\ }\ } diff --git a/ext/scenes/worldscape/terrain/generator.h b/ext/scenes/worldscape/terrain/generator.h index 68f31e69..9f427dc4 100644 --- a/ext/scenes/worldscape/terrain/generator.h +++ b/ext/scenes/worldscape/terrain/generator.h @@ -54,7 +54,7 @@ namespace ext { void updateLight(); void generate( uf::Object& ); - void rasterize( std::vector& vertices, const uf::Object& ); + void rasterize( std::vector& vertices, const uf::Object&, bool = false ); // void vectorize( std::vector&, const uf::Object& ); std::vector getRawVoxels(); diff --git a/ext/scenes/worldscape/terrain/region.cpp b/ext/scenes/worldscape/terrain/region.cpp index efdade46..30e3515b 100644 --- a/ext/scenes/worldscape/terrain/region.cpp +++ b/ext/scenes/worldscape/terrain/region.cpp @@ -12,22 +12,9 @@ #include #include -namespace { - std::string grabURI( std::string filename, std::string root = "" ) { - if ( filename.substr(0,8) == "https://" ) return filename; - std::string extension = uf::io::extension(filename); - if ( filename[0] == '/' || root == "" ) { - if ( extension == "json" ) root = "./data/entities/"; - if ( extension == "png" ) root = "./data/textures/"; - if ( extension == "ogg" ) root = "./data/audio/"; - } - return uf::io::sanitize(filename, root); - } -} - EXT_BEHAVIOR_REGISTER_CPP(RegionBehavior) EXT_BEHAVIOR_REGISTER_AS_OBJECT(RegionBehavior, Region) -#define this (&self) +#define this ((uf::Object*) &self) void ext::RegionBehavior::initialize( uf::Object& self ) { // alias Mesh types { @@ -38,13 +25,13 @@ void ext::RegionBehavior::initialize( uf::Object& self ) { uf::Serializer& metadata = this->getComponent(); metadata["region"]["initialized"] = true; - { + if ( !metadata["terrain"]["unified"].asBool() ) { std::string textureFilename = ""; { uf::Serializer& metadata = this->getParent().getComponent(); uf::Asset assetLoader; for ( uint i = 0; i < metadata["system"]["assets"].size(); ++i ) { if ( textureFilename != "" ) break; - std::string filename = grabURI( metadata["system"]["assets"][i].asString(), metadata["system"]["root"].asString() ); + std::string filename = this->grabURI( metadata["system"]["assets"][i].asString(), metadata["system"]["root"].asString() ); textureFilename = assetLoader.cache( filename ); } } @@ -141,10 +128,10 @@ void ext::RegionBehavior::initialize( uf::Object& self ) { this->addHook( "region:Finalize.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; - ext::TerrainGenerator::mesh_t& mesh = this->getComponent(); - auto& graphic = this->getComponent(); - - graphic.process = true; + if ( this->hasComponent() ) { + auto& graphic = this->getComponent(); + graphic.process = true; + } metadata["region"]["rasterized"] = true; return "true"; diff --git a/program.sh b/program.sh index c4987a24..5dc89e42 100644 --- a/program.sh +++ b/program.sh @@ -1,5 +1,5 @@ #!/bin/bash tskill program cd bin -./program.bat - +./program.bat $@ +tskill program