diff --git a/Makefile b/Makefile index fe8d8184..ebad846c 100644 --- a/Makefile +++ b/Makefile @@ -58,9 +58,9 @@ DEPS += ifneq (,$(findstring win64,$(ARCH))) ifneq (,$(findstring zig,$(CC))) - REQ_DEPS += $(RENDERER) json:nlohmann png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux + REQ_DEPS += $(RENDERER) json:nlohmann toml png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux else - REQ_DEPS += $(RENDERER) json:nlohmann png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux + REQ_DEPS += $(RENDERER) json:nlohmann toml png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg # ncurses openvr draco discord bullet ultralight-ux endif FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN DEPS += -lgdi32 -ldwmapi @@ -224,6 +224,9 @@ ifneq (,$(findstring ctti,$(REQ_DEPS))) else FLAGS += -DUF_RTTI -rtti endif +ifneq (,$(findstring toml,$(REQ_DEPS))) + FLAGS += -DUF_USE_TOML +endif # SRCS_DLL += $(wildcard $(ENGINE_SRC_DIR)/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*/*.cpp) #SRCS_DLL += $(wildcard $(ENGINE_SRC_DIR)/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*/*.cpp) diff --git a/bin/data/config.json b/bin/data/config.json index 32c1bd5c..e434031d 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -40,7 +40,7 @@ "rt": { // "size": [ 1280, 720 ], "filter": "nearest", - "defaultRayBounds": [ 0.001, 1024.0 ], + "defaultRayBounds": [ 0.0001, 256.0 ], "alphaTestOffset": 0.001, "samples": 1, "paths": 2, @@ -186,7 +186,7 @@ "compression": "gz" }, "imgui": { - "enabled": false + "enabled": true }, "reactphysics": { "timescale": 0.01666666666, diff --git a/bin/data/entities/burger.json b/bin/data/entities/burger.json index 2530600c..1406f84a 100644 --- a/bin/data/entities/burger.json +++ b/bin/data/entities/burger.json @@ -5,7 +5,9 @@ "import": "/model.json", "assets": [ // "/burger/burger.glb" - "/burger/burger/graph.json" + // "/burger/burger_simpler.glb" + // "/burger/burger/graph.json" + "/burger/burger_simpler/graph.json" ], "behaviors": [], "transform": { diff --git a/bin/data/entities/playerLight.json b/bin/data/entities/playerLight.json index 07ac1ade..ecbce57b 100644 --- a/bin/data/entities/playerLight.json +++ b/bin/data/entities/playerLight.json @@ -4,7 +4,7 @@ "assets": [ ], "transform": { - "track": "parent", + // "track": "parent", "position": [ 0, 1.7, 0 ] }, "system": { @@ -16,7 +16,7 @@ "light": { "type": "point", "color": [1, 1, 1], - "power": 15, + "power": 30, "fov": 90, "bias": { "constant": 1.25, diff --git a/bin/data/entities/scripts/player.lua b/bin/data/entities/scripts/player.lua index 5e922b8e..a10333be 100644 --- a/bin/data/entities/scripts/player.lua +++ b/bin/data/entities/scripts/player.lua @@ -1,12 +1,51 @@ -local timer = Timer.new() -if not timer:running() then timer:start(); end - local scene = entities.currentScene() local metadata = ent:getComponent("Metadata") local transform = ent:getComponent("Transform") +local physicsState = ent:getComponent("PhysicsState") local camera = ent:getComponent("Camera") local cameraTransform = camera:getTransform() +-- setup all timers +local timers = { + use = Timer.new(), + holp = Timer.new(), + flashlight = Timer.new(), + physcannon = Timer.new() +} +if not timers.use:running() then timers.use:start(); end +if not timers.holp:running() then timers.holp:start(); end +if not timers.flashlight:running() then timers.flashlight:start(); end +if not timers.physcannon:running() then timers.physcannon:start(); end + +-- setup held object locals +local heldObject = { + uid = 0, + distance = 0, + smoothSpeed = 4, + scrollSpeed = 16, + momentum = Vector3f(0,0,0), + rotate = false, +} +-- setup light locals +local light = { + entity = nil +} +for k, v in pairs(ent:getChildren()) do + if v:name() == "Light" then + light.entity = v + end +end + +if light.entity == nil then + light.entity = ent:loadChild("./playerLight.json",true) +end +light.metadata = light.entity:getComponent("Metadata") +light.transform = light.entity:getComponent("Transform") +light.power = light.metadata["light"]["power"] +light.origin = Vector3f(light.transform.position) +light.entity:setComponent("Metadata", { light = { power = 0 } }) + +-- sound emitter local playSound = function( key, loop ) if not loop then loop = false end local url = "/ui/" .. key .. ".ogg" @@ -25,19 +64,82 @@ local stopSound = function( key ) }, 0) end -local heldObject = { - uid = 0, - distance = 0, - smoothSpeed = 4, - scrollSpeed = 16, - momentum = Vector3f(0,0,0) -} +local useDistance = 6 +local pullDistance = useDistance * 4 -- on tick ent:bind( "tick", function(self) - if heldObject.uid ~= 0 then - local wheel = inputs.analog("MouseWheel") + -- eye transform + local flattenedTransform = cameraTransform:flatten() + flattenedTransform.forward = ( transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) ):normalize(); + + -- toggle flashlight + light.transform.position = flattenedTransform.position + flattenedTransform.forward * 2 + if timers.flashlight:elapsed() > 0.5 and inputs.key("F") then + timers.flashlight:reset() + + local metadata = { light = { power = light.power } } + if light.entity:getComponent("Metadata")["light"]["power"] ~= light.power then + metadata["light"]["power"] = light.power + else + metadata["light"]["power"] = 0 + end + light.entity:setComponent("Metadata", metadata) + + playSound("flashlight") + end + + -- fire use ray + if timers.use:elapsed() > 0.5 and inputs.key("E") then + timers.use:reset() + + local center = flattenedTransform.position + local direction = flattenedTransform.forward * useDistance + + local prop, depth = physicsState:rayCast( center, direction ) + local payload = { + user = ent:uid(), + uid = prop and prop:uid() or 0, + depth = depth, + } + if prop then + prop:callHook("entity:Use.%UID%", payload) + end + ent:callHook("entity:Use.%UID%", payload) + end + + -- update HOLP + if heldObject.uid == 0 then + local mouse2 = inputs.key("Mouse2"); + if mouse2 then + --[[ + local center = transform.position + cameraTransform.position + local direction = transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) + direction = direction:normalize() * 4 + ]] + local center = flattenedTransform.position + local direction = flattenedTransform.forward * pullDistance + local prop, depth = physicsState:rayCast( center, direction ) + if depth >= 0 and prop and not string.matched( prop:name(), "/^worldspawn/" ) then + local heldObjectTransform = prop:getComponent("Transform") + local heldObjectPhysicsState = prop:getComponent("PhysicsState") + + local strength = 500 + local distanceSquared = (heldObjectTransform.position - flattenedTransform.position):magnitude() + + heldObjectPhysicsState:applyImpulse( flattenedTransform.forward * -heldObjectPhysicsState:getMass() * strength / distanceSquared ) + if timers.physcannon:elapsed() > 1.0 then + timers.physcannon:reset() + + playSound("phys_tooHeavy") + end + end + end + else + local mouse1 = inputs.key("Mouse1"); local mouse3 = inputs.key("Mouse3"); + local wheel = inputs.analog("MouseWheel") + if wheel ~= 0 then heldObject.distance = heldObject.distance + (wheel / 120 * heldObject.scrollSpeed) * time.delta() end @@ -48,27 +150,38 @@ ent:bind( "tick", function(self) local prop = entities.get( heldObject.uid ) local heldObjectTransform = prop:getComponent("Transform") local heldObjectPhysicsState = prop:getComponent("PhysicsState") - if heldObject.rotate then - heldObjectTransform.orientation = transform.orientation - end - - local transform = cameraTransform:flatten() - local forward = transform.orientation:rotate( Vector3f(0,0,1) ) * heldObject.distance - if heldObject.smoothSpeed ~= 0 then - local target = transform.position + forward - local offset = target - heldObjectTransform.position - local delta = offset * time.delta() * heldObject.smoothSpeed - local distance = delta:norm() + if mouse1 and timers.physcannon:elapsed() > 0.5 then + timers.physcannon:reset() - if distance > 0.001 then - heldObjectTransform.position = heldObjectTransform.position + delta - heldObject.momentum = offset * 1000 - else - heldObjectTransform.position = target - end + heldObject.uid = 0 + heldObjectPhysicsState:enableGravity(true) + heldObjectPhysicsState:applyImpulse( flattenedTransform.forward * heldObjectPhysicsState:getMass() * 1000 ) + + playSound("phys_launch"..math.random(1,4)) else - heldObjectTransform.position = transform.position + forward + if heldObject.rotate then + heldObjectTransform.orientation = Quaternion.lookAt( (heldObjectTransform.position - flattenedTransform.position):normalize(), transform.up ) + end + + local forward = flattenedTransform.forward * heldObject.distance --flattenedTransform.orientation:rotate( Vector3f(0,0,1) ) + if heldObject.smoothSpeed ~= 0 then + local target = flattenedTransform.position + forward + local offset = target - heldObjectTransform.position + local delta = offset * time.delta() * heldObject.smoothSpeed + + local distance = delta:norm() + if distance > 0.001 then + if timers.holp:elapsed() > 0.125 then + timers.holp:reset() + heldObjectPhysicsState:setVelocity( delta * 20 ) + end + else + heldObjectPhysicsState:setVelocity( Vector3f(0,0,0) ) + end + else + heldObjectTransform.position = flattenedTransform.position + forward + end end end end ) @@ -102,7 +215,7 @@ ent:addHook( "entity:Use.%UID%", function( payload ) heldObject.momentum = Vector3f(0,0,0) elseif payload.uid ~= 0 then local hit = entities.get( heldObject.uid ) - validUse = not string.match( hit:name(), "/^worldspawn_/" ) + validUse = not string.matched( hit:name(), "/^worldspawn/" ) end if validUse then diff --git a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json index 10efa881..8d4cfac0 100644 --- a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json +++ b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json @@ -7,7 +7,7 @@ // { "filename": "./models/sh_mcd/graph.json" } // { "filename": "./models/mcdonalds.glb" } - { "filename": "./models/mcdonalds/graph.json" }, + { "filename": "./models/mcdonalds/graph.json" } // { "filename": "./models/mini_mcd.glb" } // { "filename": "./models/mini_mcd/graph.json" } @@ -15,7 +15,7 @@ // { "filename": "/cornell/cornell.glb" } // { "filename": "/cornell/cornell/graph.json" } - { "filename": "/burger.json" } + // { "filename": "/burger.json" } ], "metadata": { "model": { @@ -38,7 +38,6 @@ "physics": { "type": "mesh", "static": true }, "grid": { "size": [6,1,6], "epsilon": 1.0, "cleanup": true, "print": true } }, - // "worldspawn_sh2": { "physics": { "type": "mesh", "static": true } }, "info_player_spawn": { "action": "attach", "filename": "./player.json", "preserve orientation": true }, "func_door_rotating_5409": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } }, diff --git a/bin/data/shaders/common/structs.h b/bin/data/shaders/common/structs.h index fef334b8..2c97354b 100644 --- a/bin/data/shaders/common/structs.h +++ b/bin/data/shaders/common/structs.h @@ -318,5 +318,8 @@ struct Triangle { struct RayTracePayload { bool hit; - Triangle triangle; + uint instanceID; + uint primitiveID; + vec2 attributes; +// Triangle triangle; }; \ No newline at end of file diff --git a/bin/data/shaders/raytrace/shader.ray-gen.glsl b/bin/data/shaders/raytrace/shader.ray-gen.glsl index 6a3e1384..615856a9 100644 --- a/bin/data/shaders/raytrace/shader.ray-gen.glsl +++ b/bin/data/shaders/raytrace/shader.ray-gen.glsl @@ -13,6 +13,7 @@ layout (constant_id = 3) const uint CASCADES = 1; #define PBR 1 #define VXGI 0 #define RAYTRACE 1 +#define BUFFER_REFERENCE 1 #define FOG 0 #define BLOOM 0 #define WHITENOISE 0 @@ -41,27 +42,42 @@ layout (binding = 2) uniform UBO { layout (std140, binding = 3) readonly buffer Instances { Instance instances[]; }; -layout (std140, binding = 4) readonly buffer Materials { +layout (std140, binding = 4) readonly buffer InstanceAddresseses { + InstanceAddresses instanceAddresses[]; +}; +layout (std140, binding = 5) readonly buffer Materials { Material materials[]; }; -layout (std140, binding = 5) readonly buffer Textures { +layout (std140, binding = 6) readonly buffer Textures { Texture textures[]; }; -layout (std140, binding = 6) readonly buffer Lights { +layout (std140, binding = 7) readonly buffer Lights { Light lights[]; }; -layout (binding = 7) uniform sampler2D samplerTextures[TEXTURES]; -layout (binding = 8) uniform samplerCube samplerCubemaps[CUBEMAPS]; -layout (binding = 9) uniform sampler3D samplerNoise; +layout (binding = 8) uniform sampler2D samplerTextures[TEXTURES]; +layout (binding = 9) uniform samplerCube samplerCubemaps[CUBEMAPS]; +layout (binding = 10) uniform sampler3D samplerNoise; #if VXGI - layout (binding = 10) uniform usampler3D voxelId[CASCADES]; - layout (binding = 11) uniform sampler3D voxelNormal[CASCADES]; - layout (binding = 12) uniform sampler3D voxelRadiance[CASCADES]; + layout (binding = 11) uniform usampler3D voxelId[CASCADES]; + layout (binding = 12) uniform sampler3D voxelNormal[CASCADES]; + layout (binding = 13) uniform sampler3D voxelRadiance[CASCADES]; #endif layout (location = 0) rayPayloadEXT RayTracePayload payload; +layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; }; +layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; }; +layout(buffer_reference, scalar) buffer Indirects { DrawCommand dc[]; }; + +layout(buffer_reference, scalar) buffer VPos { vec3 v[]; }; +layout(buffer_reference, scalar) buffer VUv { vec2 v[]; }; +layout(buffer_reference, scalar) buffer VColor { uint v[]; }; +layout(buffer_reference, scalar) buffer VSt { vec2 v[]; }; +layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; }; +layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; }; +layout(buffer_reference, scalar) buffer VID { uint v[]; }; + #include "../common/functions.h" #include "../common/light.h" #include "../common/fog.h" @@ -69,6 +85,65 @@ layout (location = 0) rayPayloadEXT RayTracePayload payload; #include "../common/vxgi.h" #endif +Triangle parsePayload( RayTracePayload payload ) { + Triangle triangle; + triangle.instanceID = payload.instanceID; + + if ( !payload.hit ) return triangle; + + const vec3 bary = vec3( + 1.0 - payload.attributes.x - payload.attributes.y, + payload.attributes.x, + payload.attributes.y + ); + const InstanceAddresses instanceAddresses = instanceAddresses[triangle.instanceID]; + + if ( !(0 < instanceAddresses.index) ) return triangle; + + const DrawCommand drawCommand = Indirects(nonuniformEXT(instanceAddresses.indirect)).dc[instanceAddresses.drawID]; + const uint triangleID = payload.primitiveID + (drawCommand.indexID / 3); + + Vertex points[3]; + uvec3 indices = Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID]; + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/indices[_] += drawCommand.vertexID; + + if ( 0 < instanceAddresses.vertex ) { + Vertices vertices = Vertices(nonuniformEXT(instanceAddresses.vertex)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_] = vertices.v[/*triangle.*/indices[_]]; + } else { + if ( 0 < instanceAddresses.position ) { + VPos buf = VPos(nonuniformEXT(instanceAddresses.position)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].position = buf.v[/*triangle.*/indices[_]]; + } + if ( 0 < instanceAddresses.uv ) { + VUv buf = VUv(nonuniformEXT(instanceAddresses.uv)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].uv = buf.v[/*triangle.*/indices[_]]; + } + if ( 0 < instanceAddresses.st ) { + VSt buf = VSt(nonuniformEXT(instanceAddresses.st)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].st = buf.v[/*triangle.*/indices[_]]; + } + if ( 0 < instanceAddresses.normal ) { + VNormal buf = VNormal(nonuniformEXT(instanceAddresses.normal)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].normal = buf.v[/*triangle.*/indices[_]]; + } + if ( 0 < instanceAddresses.tangent ) { + VTangent buf = VTangent(nonuniformEXT(instanceAddresses.tangent)); + for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent = buf.v[/*triangle.*/indices[_]]; + } + } + + triangle.point.position = /*triangle.*/points[0].position * bary[0] + /*triangle.*/points[1].position * bary[1] + /*triangle.*/points[2].position * bary[2]; + triangle.point.uv = /*triangle.*/points[0].uv * bary[0] + /*triangle.*/points[1].uv * bary[1] + /*triangle.*/points[2].uv * bary[2]; + triangle.point.st = /*triangle.*/points[0].st * bary[0] + /*triangle.*/points[1].st * bary[1] + /*triangle.*/points[2].st * bary[2]; + triangle.point.normal = /*triangle.*/points[0].normal * bary[0] + /*triangle.*/points[1].normal * bary[1] + /*triangle.*/points[2].normal * bary[2]; + triangle.point.tangent = /*triangle.*/points[0].tangent * bary[0] + /*triangle.*/points[1].tangent * bary[1] + /*triangle.*/points[2].tangent * bary[2]; + + triangle.geomNormal = normalize(cross(points[1].position - points[0].position, points[2].position - points[0].position)); + + return triangle; +} + void trace( Ray ray, float tMin, float tMax ) { uint rayFlags = gl_RayFlagsOpaqueEXT; uint cullMask = 0xFF; @@ -110,38 +185,39 @@ float shadowFactor( const Light light, float def ) { } void setupSurface( RayTracePayload payload ) { - const Instance instance = instances[payload.triangle.instanceID]; + const Triangle triangle = parsePayload( payload ); + const Instance instance = instances[triangle.instanceID]; surface.instance = instance; surface.fragment = vec4(0); surface.light = vec4(0); // bind position { - surface.position.world = vec3( instance.model * vec4(payload.triangle.point.position, 1.0 ) ); + surface.position.world = vec3( instance.model * vec4(triangle.point.position, 1.0 ) ); surface.position.eye = vec3( ubo.eyes[surface.pass].view * vec4(surface.position.world, 1.0) ); } // bind normals { - surface.normal.world = normalize(vec3( instance.model * vec4(payload.triangle.point.normal, 0.0 ) )); + surface.normal.world = normalize(vec3( instance.model * vec4(triangle.point.normal, 0.0 ) )); // surface.normal.world = faceforward( surface.normal.world, surface.ray.direction, surface.normal.world ); // surface.normal.eye = normalize(vec3( ubo.eyes[surface.pass].view * vec4(surface.normal.world, 0.0) )); - // surface.tbn[0] = normalize(vec3( instance.model * vec4(payload.triangle.tbn[0], 0.0 ) )); - // surface.tbn[1] = normalize(vec3( instance.model * vec4(payload.triangle.tbn[1], 0.0 ) )); + // surface.tbn[0] = normalize(vec3( instance.model * vec4(triangle.tbn[0], 0.0 ) )); + // surface.tbn[1] = normalize(vec3( instance.model * vec4(triangle.tbn[1], 0.0 ) )); // surface.tbn[2] = surface.normal.world; - vec3 tangent = normalize(vec3( instance.model * vec4(payload.triangle.point.tangent, 0.0) )); - vec3 bitangent = normalize(vec3( instance.model * vec4(cross( payload.triangle.point.normal, payload.triangle.point.tangent ), 0.0) )); - if ( payload.triangle.point.tangent != vec3(0) ) { - surface.tbn = mat3(tangent, bitangent, payload.triangle.point.normal); + vec3 tangent = normalize(vec3( instance.model * vec4(triangle.point.tangent, 0.0) )); + vec3 bitangent = normalize(vec3( instance.model * vec4(cross( triangle.point.normal, triangle.point.tangent ), 0.0) )); + if ( triangle.point.tangent != vec3(0) ) { + surface.tbn = mat3(tangent, bitangent, triangle.point.normal); } else { surface.tbn = mat3(1); } } // bind UVs { - surface.uv.xy = payload.triangle.point.uv; - surface.st.xy = payload.triangle.point.st; + surface.uv.xy = triangle.point.uv; + surface.st.xy = triangle.point.st; } const Material material = materials[surface.instance.materialID]; diff --git a/bin/data/shaders/raytrace/shader.ray-hit-any.glsl b/bin/data/shaders/raytrace/shader.ray-hit-any.glsl index 02b95106..848d5450 100644 --- a/bin/data/shaders/raytrace/shader.ray-hit-any.glsl +++ b/bin/data/shaders/raytrace/shader.ray-hit-any.glsl @@ -18,7 +18,7 @@ layout (constant_id = 2) const uint CUBEMAPS = 128; #include "../common/macros.h" #include "../common/structs.h" - +/* layout (std140, binding = 10) readonly buffer InstanceAddresseses { InstanceAddresses instanceAddresses[]; }; @@ -34,20 +34,25 @@ layout(buffer_reference, scalar) buffer VSt { vec2 v[]; }; layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; }; layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; }; layout(buffer_reference, scalar) buffer VID { uint v[]; }; +*/ layout(location = 0) rayPayloadInEXT RayTracePayload payload; hitAttributeEXT vec2 attribs; void main() { + payload.hit = true; + payload.instanceID = gl_InstanceCustomIndexEXT; + payload.primitiveID = gl_PrimitiveID; + payload.attributes = attribs; + +#if 0 const vec3 bary = vec3( 1.0 - attribs.x - attribs.y, attribs.x, attribs.y ); - const uint instanceID = gl_InstanceCustomIndexEXT; - const InstanceAddresses instanceAddresses = instanceAddresses[instanceID]; if ( !(0 < instanceAddresses.index) ) return; @@ -108,4 +113,5 @@ void main() { */ payload.hit = true; payload.triangle = triangle; +#endif } \ No newline at end of file diff --git a/bin/data/shaders/raytrace/shader.ray-hit-closest.glsl b/bin/data/shaders/raytrace/shader.ray-hit-closest.glsl index 42da656c..5e3809fd 100644 --- a/bin/data/shaders/raytrace/shader.ray-hit-closest.glsl +++ b/bin/data/shaders/raytrace/shader.ray-hit-closest.glsl @@ -16,6 +16,7 @@ layout (constant_id = 2) const uint CUBEMAPS = 128; #include "../common/macros.h" #include "../common/structs.h" +/* layout (std140, binding = 9) readonly buffer InstanceAddresseses { InstanceAddresses instanceAddresses[]; }; @@ -31,18 +32,23 @@ layout(buffer_reference, scalar) buffer VSt { vec2 v[]; }; layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; }; layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; }; layout(buffer_reference, scalar) buffer VID { uint v[]; }; - +*/ layout(location = 0) rayPayloadInEXT RayTracePayload payload; hitAttributeEXT vec2 attribs; void main() { + payload.hit = true; + payload.instanceID = gl_InstanceCustomIndexEXT; + payload.primitiveID = gl_PrimitiveID; + payload.attributes = attribs; + +#if 0 const vec3 bary = vec3( 1.0 - attribs.x - attribs.y, attribs.x, attribs.y ); - const uint instanceID = gl_InstanceCustomIndexEXT; const InstanceAddresses instanceAddresses = instanceAddresses[instanceID]; @@ -105,4 +111,5 @@ void main() { */ payload.hit = true; payload.triangle = triangle; +#endif } \ No newline at end of file diff --git a/engine/inc/uf/engine/behavior/macros.inl b/engine/inc/uf/engine/behavior/macros.inl index 4df6c8ad..3b09b1e7 100644 --- a/engine/inc/uf/engine/behavior/macros.inl +++ b/engine/inc/uf/engine/behavior/macros.inl @@ -61,4 +61,12 @@ public:\ OBJ ## Behavior::attach( *this );\ } -#define UF_BEHAVIOR_ENTITY_CPP_END( OBJ ) \ No newline at end of file +#define UF_BEHAVIOR_ENTITY_CPP_END( OBJ ) + +#define UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS( METADATA, JSON ) {\ + this->addHook( "object:Serialize.%UID%", [&]{ METADATA.serialize(self, JSON); });\ + this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ METADATA.serialize(self, (uf::Serializer&) json); });\ + this->addHook( "object:Deserialize.%UID%", [&]{ METADATA.deserialize(self, JSON); });\ + this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ METADATA.deserialize(self, (uf::Serializer&) json); });\ + METADATA.deserialize(self, JSON);\ +} diff --git a/engine/inc/uf/ext/json/json.h b/engine/inc/uf/ext/json/json.h index 1ae4f9fd..5d21a360 100644 --- a/engine/inc/uf/ext/json/json.h +++ b/engine/inc/uf/ext/json/json.h @@ -11,6 +11,9 @@ #if UF_USE_LUA #include #endif +#if UF_USE_TOML + #include +#endif namespace ext { namespace json { @@ -18,8 +21,8 @@ namespace ext { extern uf::stl::string PREFERRED_ENCODING; struct UF_API EncodingSettings { - uf::stl::string compression = ""; // auto-assume compression scheme uf::stl::string encoding = ""; // auto-assume re-encoding scheme + uf::stl::string compression = ""; // auto-assume compression scheme bool pretty = false; bool quantize = false; uint8_t precision = 0; @@ -101,6 +104,12 @@ T& ext::json::encode( const ext::json::Value& json, T& output, const ext::json:: else UF_JSON_PARSE_ENCODING(msgpack) else UF_JSON_PARSE_ENCODING(ubjson) else UF_JSON_PARSE_ENCODING(bjdata) +#if UF_USE_TOML + else if ( settings.encoding == "toml" ) { + uf::stl::string buffer = ext::toml::fromJson( json.dump() ); + output = T( buffer.begin(), buffer.end() ); + } +#endif else { // should probably default to json, not my problem UF_MSG_ERROR("invalid encoding requested: {}", settings.encoding); @@ -129,6 +138,15 @@ ext::json::Value& ext::json::decode( ext::json::Value& json, const T& input, con else UF_JSON_PARSE_ENCODING(msgpack) else UF_JSON_PARSE_ENCODING(ubjson) else UF_JSON_PARSE_ENCODING(bjdata) +#if UF_USE_TOML + else if ( settings.encoding == "toml" ) { + // UF_MSG_DEBUG("TOML: {}", std::string_view{ (const char*) input.data(), input.size() }); + T parsed = ext::toml::toJson( input ); + // UF_MSG_DEBUG("JSON: {}", std::string_view{ (const char*) parsed.data(), parsed.size() }); + json = nlohmann::json::parse(parsed, nullptr, exceptions, comments); + // UF_MSG_DEBUG("ENCODED: {}", json.dump()); + } +#endif else { // should probably default to json, not my problem UF_MSG_ERROR("invalid encoding requested: {}", settings.encoding); diff --git a/engine/inc/uf/ext/reactphysics/reactphysics.h b/engine/inc/uf/ext/reactphysics/reactphysics.h index 58894a09..c514a5b3 100644 --- a/engine/inc/uf/ext/reactphysics/reactphysics.h +++ b/engine/inc/uf/ext/reactphysics/reactphysics.h @@ -104,11 +104,18 @@ namespace ext { void UF_API applyRotation( pod::PhysicsState&, const pod::Vector3f&, float ); // ray casting - float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f& ); - float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, uf::Object*, uf::Object*& ); + uf::Object* UF_API rayCast( const pod::Vector3f&, const pod::Vector3f& ); + uf::Object* UF_API rayCast( pod::PhysicsState&, const pod::Vector3f&, const pod::Vector3f& ); + uf::Object* UF_API rayCast( pod::PhysicsState&, const pod::Vector3f&, const pod::Vector3f&, float& ); + uf::Object* UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, uf::Object*, float& ); +// float UF_API rayCast( const pod::Vector3f&, const pod::Vector3f&, uf::Object*, uf::Object*& ); // allows noclip void UF_API activateCollision( pod::PhysicsState&, bool = true ); + + // + float UF_API getMass( pod::PhysicsState& ); + void UF_API setMass( pod::PhysicsState&, float ); } } #endif \ No newline at end of file diff --git a/engine/inc/uf/ext/toml/toml.h b/engine/inc/uf/ext/toml/toml.h new file mode 100644 index 00000000..7227c71f --- /dev/null +++ b/engine/inc/uf/ext/toml/toml.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#if UF_USE_TOML +namespace ext { + namespace toml { + + uf::stl::string toJson( const uf::stl::string& ); + uf::stl::vector toJson( const uf::stl::vector& ); + + uf::stl::string fromJson( const uf::stl::string& ); + uf::stl::vector fromJson( const uf::stl::vector& ); + } +} +#endif \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/buffer.h b/engine/inc/uf/ext/vulkan/buffer.h index b69227b8..4811746f 100644 --- a/engine/inc/uf/ext/vulkan/buffer.h +++ b/engine/inc/uf/ext/vulkan/buffer.h @@ -50,6 +50,7 @@ namespace ext { bool update( const void*, VkDeviceSize, bool = VK_DEFAULT_STAGE_BUFFERS ) const; void destroy(); + void swap( Buffer& ); Buffer alias() const; void aliasBuffer( const Buffer& ); }; diff --git a/engine/inc/uf/ext/vulkan/vulkan.h b/engine/inc/uf/ext/vulkan/vulkan.h index 12a3358a..141b66dc 100644 --- a/engine/inc/uf/ext/vulkan/vulkan.h +++ b/engine/inc/uf/ext/vulkan/vulkan.h @@ -40,6 +40,8 @@ namespace ext { extern UF_API PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; extern UF_API PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; extern UF_API PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; + extern UF_API PFN_vkCmdWriteAccelerationStructuresPropertiesKHR vkCmdWriteAccelerationStructuresPropertiesKHR; + extern UF_API PFN_vkCmdCopyAccelerationStructureKHR vkCmdCopyAccelerationStructureKHR; uf::stl::string errorString( VkResult result ); VkSampleCountFlagBits sampleCount( uint8_t ); @@ -47,13 +49,16 @@ namespace ext { typedef VmaAllocator Allocator; namespace settings { + constexpr size_t maxViews = 6; + 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 size_t viewCount; extern UF_API size_t gpuID; - constexpr size_t maxViews = 6; + extern UF_API size_t scratchBufferAlignment; + extern UF_API size_t scratchBufferInitialSize; extern UF_API uf::stl::vector validationFilters; extern UF_API uf::stl::vector requestedDeviceFeatures; @@ -122,6 +127,8 @@ namespace ext { extern UF_API uf::stl::unordered_map renderModesMap; extern UF_API uf::ThreadUnique currentRenderMode; + extern UF_API Buffer scratchBuffer; + bool UF_API hasRenderMode( const uf::stl::string&, bool = true ); RenderMode& UF_API addRenderMode( RenderMode*, const uf::stl::string& = "" ); RenderMode& UF_API getRenderMode( const uf::stl::string&, bool = true ); diff --git a/engine/inc/uf/utils/serialize/serializer.h b/engine/inc/uf/utils/serialize/serializer.h index 917af0d5..4a3bc7bd 100644 --- a/engine/inc/uf/utils/serialize/serializer.h +++ b/engine/inc/uf/utils/serialize/serializer.h @@ -65,6 +65,7 @@ namespace uf { } static uf::stl::string resolveFilename( const uf::stl::string& filename, bool compareTimes = true ); + static uf::stl::string resolveFilename( const uf::stl::string& filename, const ext::json::EncodingSettings&, bool compareTimes = true ); bool readFromFile( const uf::stl::string& from, const uf::stl::string& hash = "" ); bool writeToFile( const uf::stl::string& to, const ext::json::EncodingSettings& = {} ) const; diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index 1f57a23d..8d89f2b5 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -109,6 +109,7 @@ uf::Asset::Payload uf::Asset::resolveToPayload( const uf::stl::string& uri, cons { "msgpack",uf::Asset::Type::JSON }, { "ubjson", uf::Asset::Type::JSON }, { "bjdata", uf::Asset::Type::JSON }, + { "toml", uf::Asset::Type::JSON }, { "lua", uf::Asset::Type::LUA }, @@ -129,6 +130,7 @@ uf::Asset::Payload uf::Asset::resolveToPayload( const uf::stl::string& uri, cons else if ( basename == "graph.msgpack" ) payload.type = uf::Asset::Type::GRAPH; else if ( basename == "graph.ubjson" ) payload.type = uf::Asset::Type::GRAPH; else if ( basename == "graph.bjdata" ) payload.type = uf::Asset::Type::GRAPH; + else if ( basename == "graph.toml" ) payload.type = uf::Asset::Type::GRAPH; return payload; } diff --git a/engine/src/engine/object/behavior.cpp b/engine/src/engine/object/behavior.cpp index 3d2e0bbe..6619f948 100644 --- a/engine/src/engine/object/behavior.cpp +++ b/engine/src/engine/object/behavior.cpp @@ -59,7 +59,7 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) { if ( json["type"].as() == "merge" ) metadataJson.merge(json["value"], true); else if ( json["type"].as() == "import" ) metadataJson.import(json["value"]); else if ( json["path"].is() ) metadataJson.path(json["path"].as()) = json["value"]; - else metadataJson.merge(json, true); + // else metadataJson.merge(json, true); }); this->addHook( "asset:QueueLoad.%UID%", [&](pod::payloads::assetLoad& payload){ uf::stl::string callback = this->formatHookName("asset:FinishedLoad.%UID%"); @@ -97,9 +97,7 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) { } }); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); if ( ext::json::isObject(metadataJson["physics"]) ) { auto& collider = this->getComponent(); @@ -247,7 +245,7 @@ void uf::ObjectBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& if ( /*this->*/transform.trackParent ) serializer["system"]["transform"]["track"] = "parent"; } void uf::ObjectBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ) { - /*this->*/transform.initial = self.getComponent>(); - /*this->*/transform.trackParent = serializer["system"]["transform"]["track"].as() == "parent"; + if ( !transform.trackParent ) /*this->*/transform.initial = self.getComponent>(); + /*this->*/transform.trackParent = serializer["system"]["transform"]["track"].as(/*this->*/transform.trackParent ? "parent" : "") == "parent"; } UF_BEHAVIOR_ENTITY_CPP_END(Object) \ No newline at end of file diff --git a/engine/src/ext/lua/usertypes/object.cpp b/engine/src/ext/lua/usertypes/object.cpp index 5fdb7444..363cbbff 100644 --- a/engine/src/ext/lua/usertypes/object.cpp +++ b/engine/src/ext/lua/usertypes/object.cpp @@ -40,11 +40,17 @@ namespace binds { if ( type == "Metadata" ) { auto encoded = ext::lua::encode( value.as() ); if ( encoded ) { + /* self.callHook( "object:Serialize.%UID%" ); auto& metadata = self.getComponent(); uf::stl::string str = encoded.value(); metadata.merge( str, false ); self.callHook( "object:Deserialize.%UID%" ); + */ + uf::stl::string str = encoded.value(); + ext::json::Value json; + ext::json::decode( json, str ); + self.callHook( "object:Deserialize.%UID%", json ); } } UF_LUA_UPDATE_COMPONENT(pod::Transform<>) diff --git a/engine/src/ext/lua/usertypes/physics.cpp b/engine/src/ext/lua/usertypes/physics.cpp index bf30af24..7060d5cb 100644 --- a/engine/src/ext/lua/usertypes/physics.cpp +++ b/engine/src/ext/lua/usertypes/physics.cpp @@ -13,6 +13,13 @@ namespace binds { if ( !state.body ) return; state.body->enableGravity(s); } + + std::tuple rayCast( pod::Physics& self, const pod::Vector3f& center, const pod::Vector3f& direction ) { + float depth = -1; + uf::Object* object = uf::physics::impl::rayCast(self, center, direction, depth); + + return std::make_tuple( object, depth ); + } } UF_LUA_REGISTER_USERTYPE(pod::Physics, @@ -31,7 +38,12 @@ UF_LUA_REGISTER_USERTYPE(pod::PhysicsState, UF_LUA_REGISTER_USERTYPE_DEFINE( applyVelocity, UF_LUA_C_FUN(uf::physics::impl::applyVelocity) ), // UF_LUA_REGISTER_USERTYPE_DEFINE( applyRotation, UF_LUA_C_FUN(uf::physics::impl::applyRotation) ), UF_LUA_REGISTER_USERTYPE_DEFINE( enableGravity, UF_LUA_C_FUN(::binds::enableGravity) ), - UF_LUA_REGISTER_USERTYPE_DEFINE( activateCollision, UF_LUA_C_FUN(uf::physics::impl::activateCollision) ) + UF_LUA_REGISTER_USERTYPE_DEFINE( activateCollision, UF_LUA_C_FUN(uf::physics::impl::activateCollision) ), + + UF_LUA_REGISTER_USERTYPE_DEFINE( rayCast, UF_LUA_C_FUN(::binds::rayCast) ), + + UF_LUA_REGISTER_USERTYPE_DEFINE( getMass, UF_LUA_C_FUN(uf::physics::impl::getMass) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( setMass, UF_LUA_C_FUN(uf::physics::impl::setMass) ) ) #endif \ No newline at end of file diff --git a/engine/src/ext/reactphysics/reactphysics.cpp b/engine/src/ext/reactphysics/reactphysics.cpp index 461d711b..8af0065e 100644 --- a/engine/src/ext/reactphysics/reactphysics.cpp +++ b/engine/src/ext/reactphysics/reactphysics.cpp @@ -572,28 +572,32 @@ void ext::reactphysics::applyRotation( pod::PhysicsState& state, const pod::Vect // ray casting -float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction ) { - if ( !::world ) - return -1; - - ::RaycastCallback callback; - ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( center + direction ) ), &callback ); - if ( !callback.isHit ) return -1; - return callback.raycastInfo.hitFraction; +uf::Object* ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction ) { + float depth = -1; + return rayCast( center, direction, NULL, depth ); } -float ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction, uf::Object* source, uf::Object*& object ) { - if ( !::world ) - return -1; + +uf::Object* ext::reactphysics::rayCast( pod::PhysicsState& state, const pod::Vector3f& center, const pod::Vector3f& direction ) { + float depth = -1; + return rayCast( center, direction, state.object, depth ); +} +uf::Object* ext::reactphysics::rayCast( pod::PhysicsState& state, const pod::Vector3f& center, const pod::Vector3f& direction, float& depth ) { + return rayCast( center, direction, state.object, depth ); +} + +uf::Object* ext::reactphysics::rayCast( const pod::Vector3f& center, const pod::Vector3f& direction, uf::Object* source, float& depth ) { + depth = -1; + + if ( !::world ) return NULL; ::RaycastCallback callback; callback.source = source; ::world->raycast( rp3d::Ray( ::convert( center ), ::convert( center + direction ) ), &callback ); - object = NULL; - if ( !callback.isHit ) { - return -1; - } - object = (uf::Object*) callback.raycastInfo.body->getUserData(); - return callback.raycastInfo.hitFraction; + if ( !callback.isHit ) return NULL; + + depth = callback.raycastInfo.hitFraction; + + return (uf::Object*) callback.raycastInfo.body->getUserData(); } // allows noclip @@ -608,4 +612,14 @@ void ext::reactphysics::activateCollision( pod::PhysicsState& state, bool s ) { } } +float ext::reactphysics::getMass( pod::PhysicsState& state ) { + if ( !state.body ) return state.stats.mass; + + return (state.stats.mass = state.body->getMass()); +} +void ext::reactphysics::setMass( pod::PhysicsState& state, float mass ) { + state.stats.mass = mass; + state.body->setMass(mass); +} + #endif \ No newline at end of file diff --git a/engine/src/ext/toml/toml.cpp b/engine/src/ext/toml/toml.cpp new file mode 100644 index 00000000..a40679b7 --- /dev/null +++ b/engine/src/ext/toml/toml.cpp @@ -0,0 +1,157 @@ +#if UF_USE_TOML + +#define TOML_HEADER_ONLY 0 +#define TOML_ENABLE_FORMATTERS 1 +#define TOML_IMPLEMENTATION + +#if UF_EXCEPTIONS + #define TOML_EXCEPTIONS 1 +#elif UF_NO_EXCEPTIONS + #define TOML_EXCEPTIONS 0 +#else + #define TOML_EXCEPTIONS 0 +#endif +#include +#include + + +#include +#include +using namespace std::string_view_literals; + +TOML_NAMESPACE_START +{ + template + static void from_json(const ext::json::Value& j, value& val) { + assert(j.is()); + *val = j.as(); + } + static void from_json(const ext::json::Value&, array&); + + template + static void insert_from_json(table & tbl, Key && key, const ext::json::Value& val) { + T v; + from_json(val, v); + + tbl.insert_or_assign(static_cast(key), std::move(v)); + } + + template + static void insert_from_json(array & arr, const ext::json::Value& val) { + T v; + from_json(val, v); + arr.push_back(std::move(v)); + } + + static void from_json(const ext::json::Value& j, table& tbl) { + if ( j.is_null() ) return; + + if( !j.is_object() ) { + UF_MSG_ERROR("ERROR: {}", j.dump()); + return; + } + + ext::json::forEach(j, [&]( const uf::stl::string& k, const ext::json::Value& v ){ + if ( v.is() ) insert_from_json>(tbl, k, v); + else if ( v.is(true) ) insert_from_json>(tbl, k, v); + else if ( v.is() ) insert_from_json>(tbl, k, v); + else if ( v.is() ) insert_from_json>(tbl, k, v); + else if (v.is_array()) insert_from_json(tbl, k, v); + else insert_from_json(tbl, k, v); + }); + } + + static void from_json(const ext::json::Value& j, array& arr) { + ext::json::forEach(j, [&]( const ext::json::Value& v ){ + if ( v.is() ) + insert_from_json>(arr, v); + else if ( v.is(true) ) insert_from_json>(arr, v); + else if ( v.is() ) insert_from_json>(arr, v); + else if ( v.is() ) insert_from_json>(arr, v); + else if (v.is_array()) insert_from_json(arr, v); + else insert_from_json(arr, v); + }); + } +} +TOML_NAMESPACE_END; + +uf::stl::string UF_API ext::toml::fromJson( const uf::stl::string& source ) { + ext::json::Value j; + ext::json::decode( j, source ); + +#if UF_EXCEPTIONS + try { +#endif + ::toml::table tbl; + ::toml::from_json( j, tbl ); + + std::stringstream ss; ss << tbl; + return ss.str(); +#if UF_EXCEPTIONS + } catch ( const ::toml::parse_error& err ) { + UF_MSG_ERROR("TOML error: {}", e.what()); + } + return ""; +#endif +} +uf::stl::vector UF_API ext::toml::fromJson( const uf::stl::vector& source ) { + ext::json::Value j; + ext::json::decode( j, source ); + +#if UF_EXCEPTIONS + try { +#endif + ::toml::table tbl; + ::toml::from_json( j, tbl ); + + std::stringstream ss; ss << tbl; + uf::stl::string str = ss.str(); + return uf::stl::vector{ str.begin(), str.end() }; +#if UF_EXCEPTIONS + } catch ( const ::toml::parse_error& err ) { + UF_MSG_ERROR("TOML error: {}", e.what()); + } + return {}; +#endif +} +uf::stl::string UF_API ext::toml::toJson( const uf::stl::string& source ) { +#if UF_EXCEPTIONS + try { +#endif + ::toml::parse_result result = ::toml::parse( source ); + if ( !result ) { + std::stringstream error; error << result.error(); + UF_MSG_ERROR("TOML error: {}", error.str()); + return ""; + } + std::stringstream ss; ss << ::toml::json_formatter{ result }; + return ss.str(); +#if UF_EXCEPTIONS + } catch ( const ::toml::parse_error& err ) { + UF_MSG_ERROR("TOML error: {}", e.what()); + } + return ""; +#endif +} +uf::stl::vector UF_API ext::toml::toJson( const uf::stl::vector& source ) { +#if UF_EXCEPTIONS + try { +#endif + std::string_view source_view( (const char*) source.data(), source.size() ); + ::toml::parse_result result = ::toml::parse( source_view ); + if ( !result ) { + std::stringstream error; error << result.error(); + UF_MSG_ERROR("TOML error: {}", error.str()); + return {}; + } + std::stringstream ss; ss << ::toml::json_formatter{ result }; + uf::stl::string str = ss.str(); + return uf::stl::vector{ str.begin(), str.end() }; +#if UF_EXCEPTIONS + } catch ( const ::toml::parse_error& err ) { + UF_MSG_ERROR("TOML error: {}", e.what()); + } + return {}; +#endif +} +#endif \ No newline at end of file diff --git a/engine/src/ext/vulkan/buffer.cpp b/engine/src/ext/vulkan/buffer.cpp index 67d89c35..e301ed17 100644 --- a/engine/src/ext/vulkan/buffer.cpp +++ b/engine/src/ext/vulkan/buffer.cpp @@ -7,6 +7,20 @@ #include #include +void ext::vulkan::Buffer::swap( ext::vulkan::Buffer& buffer ) { + std::swap(this->aliased, buffer.aliased); + std::swap(this->device, buffer.device); + std::swap(this->buffer, buffer.buffer); + std::swap(this->memory, buffer.memory); + std::swap(this->descriptor, buffer.descriptor); + std::swap(this->alignment, buffer.alignment); + std::swap(this->address, buffer.address); + std::swap(this->mapped, buffer.mapped); + std::swap(this->usage, buffer.usage); + std::swap(this->memoryProperties, buffer.memoryProperties); + std::swap(this->allocation, buffer.allocation); + std::swap(this->allocationInfo, buffer.allocationInfo); +} ext::vulkan::Buffer ext::vulkan::Buffer::alias() const { ext::vulkan::Buffer buffer; buffer.aliasBuffer(*this); diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index 2a67c3f8..4660e209 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -1150,6 +1150,8 @@ void ext::vulkan::Device::initialize() { vkCmdTraceRaysKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysKHR")); vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR")); vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); + vkCmdWriteAccelerationStructuresPropertiesKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdWriteAccelerationStructuresPropertiesKHR")); + vkCmdCopyAccelerationStructureKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdCopyAccelerationStructureKHR")); } } diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index c913447a..51382c59 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -29,6 +29,8 @@ PFN_vkGetAccelerationStructureDeviceAddressKHR ext::vulkan::vkGetAccelerationStr PFN_vkCmdTraceRaysKHR ext::vulkan::vkCmdTraceRaysKHR = NULL; // = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysKHR")); PFN_vkGetRayTracingShaderGroupHandlesKHR ext::vulkan::vkGetRayTracingShaderGroupHandlesKHR = NULL; // = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR")); PFN_vkCreateRayTracingPipelinesKHR ext::vulkan::vkCreateRayTracingPipelinesKHR = NULL; // = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); +PFN_vkCmdWriteAccelerationStructuresPropertiesKHR ext::vulkan::vkCmdWriteAccelerationStructuresPropertiesKHR = NULL; // = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); +PFN_vkCmdCopyAccelerationStructureKHR ext::vulkan::vkCmdCopyAccelerationStructureKHR = NULL; // = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR")); void ext::vulkan::Pipeline::initialize( const Graphic& graphic ) { return this->initialize( graphic, graphic.descriptor ); @@ -1180,12 +1182,10 @@ void ext::vulkan::Graphic::generateBottomAccelerationStructures() { const VkAccelerationStructureBuildRangeInfoKHR* rangeInfo; uf::renderer::AccelerationStructure as; + uf::renderer::AccelerationStructure cleanup; }; uf::stl::vector blasDatas; - uf::renderer::Buffer scratchBuffer; - uf::renderer::Buffer blasBuffer; - // setup BLAS geometry { auto& mesh = this->descriptor.inputs; @@ -1299,8 +1299,11 @@ void ext::vulkan::Graphic::generateBottomAccelerationStructures() { } } + bool shouldCompact = true; + // determine BLAS size and its scratch buffer VkBuildAccelerationStructureFlagsKHR flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + if ( shouldCompact) flags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR; size_t totalBlasBufferSize{}; size_t maxScratchBufferSize{}; for ( auto& blasData : blasDatas ) { @@ -1328,72 +1331,140 @@ void ext::vulkan::Graphic::generateBottomAccelerationStructures() { totalBlasBufferSize += blasData.sizeInfo.accelerationStructureSize; } - #define VK_UNIFIED_BLAS_BUFFER 1 - -#if VK_UNIFIED_BLAS_BUFFER // create BLAS buffer and handle size_t blasBufferIndex = this->initializeBuffer( NULL, totalBlasBufferSize, uf::renderer::enums::Buffer::ACCELERATION_STRUCTURE | uf::renderer::enums::Buffer::ADDRESS ); - size_t blasBufferOffset = 0; this->metadata.buffers["blasBuffer"] = blasBufferIndex; -#endif - scratchBuffer.alignment = acclerationStructureProperties.minAccelerationStructureScratchOffsetAlignment; - scratchBuffer.initialize( NULL, maxScratchBufferSize, uf::renderer::enums::Buffer::STORAGE | uf::renderer::enums::Buffer::ADDRESS ); - - for ( auto& blasData : blasDatas ) { - #if VK_UNIFIED_BLAS_BUFFER - blasData.as.buffer = this->buffers[blasBufferIndex].alias(); - blasData.as.buffer.descriptor.offset = blasBufferOffset; + VkQueryPool queryPool{VK_NULL_HANDLE}; + if ( shouldCompact ) { + VkQueryPoolCreateInfo queryPoolCreateInfo{VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO}; + queryPoolCreateInfo.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR; + queryPoolCreateInfo.queryCount = blasDatas.size(); - VkAccelerationStructureCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR}; - createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; - createInfo.size = blasData.sizeInfo.accelerationStructureSize; - createInfo.buffer = blasData.as.buffer.buffer; - createInfo.offset = blasData.as.buffer.descriptor.offset; - - blasBufferOffset += blasData.sizeInfo.accelerationStructureSize; - #else - size_t blasBufferIndex = this->initializeBuffer( NULL, blasData.sizeInfo.accelerationStructureSize, uf::renderer::enums::Buffer::ACCELERATION_STRUCTURE | uf::renderer::enums::Buffer::ADDRESS ); - blasData.as.buffer = this->buffers[blasBufferIndex].alias(); - - VkAccelerationStructureCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR}; - createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; - createInfo.size = blasData.sizeInfo.accelerationStructureSize; - createInfo.buffer = blasData.as.buffer.buffer; - createInfo.offset = blasData.as.buffer.descriptor.offset; - #endif - - VK_CHECK_RESULT(uf::renderer::vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &blasData.as.handle)); - - VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; - accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; - accelerationDeviceAddressInfo.accelerationStructure = blasData.as.handle; - blasData.as.deviceAddress = uf::renderer::vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); - - // write to BLAS - - blasData.buildInfo.dstAccelerationStructure = blasData.as.handle; - blasData.buildInfo.scratchData.deviceAddress = scratchBuffer.getAddress(); - - VkCommandBuffer commandBuffer = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, uf::renderer::Device::QueueEnum::COMPUTE); - uf::renderer::vkCmdBuildAccelerationStructuresKHR( - commandBuffer, - 1, - &blasData.buildInfo, - &blasData.rangeInfo - ); - device.flushCommandBuffer(commandBuffer, uf::renderer::Device::QueueEnum::COMPUTE); + VK_CHECK_RESULT(vkCreateQueryPool(device, &queryPoolCreateInfo, nullptr, &queryPool)); + vkResetQueryPool(device, queryPool, 0, blasDatas.size()); } - for ( auto& blasData : blasDatas ) this->accelerationStructures.bottoms.emplace_back(blasData.as); + uint32_t queryCount{}; + { + size_t blasBufferOffset = 0; + scratchBuffer.alignment = acclerationStructureProperties.minAccelerationStructureScratchOffsetAlignment; + scratchBuffer.update( NULL, maxScratchBufferSize ); + + VkCommandBuffer commandBuffer = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, uf::renderer::Device::QueueEnum::COMPUTE); + for ( auto& blasData : blasDatas ) { + blasData.as.buffer = this->buffers[blasBufferIndex].alias(); + blasData.as.buffer.descriptor.offset = blasBufferOffset; + blasBufferOffset += blasData.sizeInfo.accelerationStructureSize; - scratchBuffer.destroy(); + VkAccelerationStructureCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR}; + createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + createInfo.size = blasData.sizeInfo.accelerationStructureSize; + createInfo.buffer = blasData.as.buffer.buffer; + createInfo.offset = blasData.as.buffer.descriptor.offset; + + VK_CHECK_RESULT(uf::renderer::vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &blasData.as.handle)); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = blasData.as.handle; + blasData.as.deviceAddress = uf::renderer::vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + // write to BLAS + blasData.buildInfo.dstAccelerationStructure = blasData.as.handle; + blasData.buildInfo.scratchData.deviceAddress = scratchBuffer.getAddress(); + + uf::renderer::vkCmdBuildAccelerationStructuresKHR( + commandBuffer, + 1, + &blasData.buildInfo, + &blasData.rangeInfo + ); + + // pipeline barrier here for scratch buffer + VkMemoryBarrier barrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; + barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &barrier, 0, nullptr, 0, nullptr); + + if ( queryPool ) uf::renderer::vkCmdWriteAccelerationStructuresPropertiesKHR( + commandBuffer, + 1, + &blasData.buildInfo.dstAccelerationStructure, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, + queryPool, + queryCount++ + ); + } + device.flushCommandBuffer(commandBuffer, uf::renderer::Device::QueueEnum::COMPUTE); + } + // compact + if ( queryPool ) { + uf::stl::vector compactSizes(blasDatas.size()); + vkGetQueryPoolResults(device, queryPool, 0, (uint32_t) compactSizes.size(), compactSizes.size() * sizeof(VkDeviceSize), compactSizes.data(), sizeof(VkDeviceSize), VK_QUERY_RESULT_WAIT_BIT); + + size_t queryIndex{}; + size_t totalBlasBufferSize{}; + for ( auto& blasData : blasDatas ) { + size_t compactedSize = compactSizes[queryIndex++]; + size_t oldSize = blasData.sizeInfo.accelerationStructureSize; + + blasData.cleanup = blasData.as; + blasData.sizeInfo.accelerationStructureSize = ALIGNED_SIZE(compactedSize, 256); + totalBlasBufferSize += blasData.sizeInfo.accelerationStructureSize; + // UF_MSG_DEBUG("Reduced size to {}% ({} -> {} = {})", (oldSize - compactedSize) * 100.0f / oldSize, oldSize, compactedSize, oldSize - compactedSize); + } + + ext::vulkan::Buffer oldBuffer; + oldBuffer.initialize( NULL, totalBlasBufferSize, uf::renderer::enums::Buffer::ACCELERATION_STRUCTURE | uf::renderer::enums::Buffer::ADDRESS ); + this->buffers[blasBufferIndex].swap(oldBuffer); + + size_t blasBufferOffset{}; + for ( auto& blasData : blasDatas ) { + blasData.as.buffer = this->buffers[blasBufferIndex].alias(); + blasData.as.buffer.descriptor.offset = blasBufferOffset; + blasBufferOffset += blasData.sizeInfo.accelerationStructureSize; + + VkAccelerationStructureCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR}; + createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + createInfo.size = blasData.sizeInfo.accelerationStructureSize; + createInfo.buffer = blasData.as.buffer.buffer; + createInfo.offset = blasData.as.buffer.descriptor.offset; + + VK_CHECK_RESULT(uf::renderer::vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &blasData.as.handle)); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = blasData.as.handle; + blasData.as.deviceAddress = uf::renderer::vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + VkCopyAccelerationStructureInfoKHR copyInfo{VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR}; + copyInfo.src = blasData.cleanup.handle; + copyInfo.dst = blasData.as.handle; + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR; + + VkCommandBuffer commandBuffer = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, uf::renderer::Device::QueueEnum::COMPUTE); + uf::renderer::vkCmdCopyAccelerationStructureKHR(commandBuffer, ©Info); + device.flushCommandBuffer(commandBuffer, uf::renderer::Device::QueueEnum::COMPUTE); + } + + oldBuffer.destroy(); + } + + for ( auto& blasData : blasDatas ) { + this->accelerationStructures.bottoms.emplace_back(blasData.as); + + if ( blasData.cleanup.handle ) uf::renderer::vkDestroyAccelerationStructureKHR(device, blasData.cleanup.handle, nullptr); + } + + if ( queryPool ) vkDestroyQueryPool(device, queryPool, nullptr); } void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vector& graphics, const uf::stl::vector& instances ) { auto& device = *this->device; bool rebuild = false; bool update = this->accelerationStructures.top.handle != VK_NULL_HANDLE; + bool shouldCompact = false; VkPhysicalDeviceAccelerationStructurePropertiesKHR acclerationStructureProperties{}; VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties{}; @@ -1409,6 +1480,16 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect vkGetPhysicalDeviceProperties2(device.physicalDevice, &deviceProperties2); } + VkQueryPool queryPool{VK_NULL_HANDLE}; + if ( shouldCompact ) { + VkQueryPoolCreateInfo queryPoolCreateInfo{VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO}; + queryPoolCreateInfo.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR; + queryPoolCreateInfo.queryCount = 1; + + VK_CHECK_RESULT(vkCreateQueryPool(device, &queryPoolCreateInfo, nullptr, &queryPool)); + vkResetQueryPool(device, queryPool, 0, 1); + } + uf::stl::vector instancesVK; instancesVK.reserve( instances.size() ); for ( auto& graphic : graphics ) { @@ -1429,20 +1510,15 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect } } - // do not stage, because apparently vkQueueWaitIdle doesn't actually wait for the transfer to complete size_t instanceIndex{}; size_t tlasBufferIndex{}; if ( !update ) { - const size_t EXTRANEOUS_SIZE = 2048; - size_t bufferSize = MAX( instancesVK.size(), EXTRANEOUS_SIZE ); - + // do not stage, because apparently vkQueueWaitIdle doesn't actually wait for the transfer to complete instanceIndex = this->initializeBuffer( - NULL, bufferSize * sizeof(VkAccelerationStructureInstanceKHR), + (const void*) instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR), uf::renderer::enums::Buffer::ADDRESS | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, false ); - this->updateBuffer( (const void*) instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR), instanceIndex, false ); - this->metadata.buffers["tlasInstance"] = instanceIndex; } else { if ( this->metadata.buffers.count("tlasInstance") > 0 ) { @@ -1455,10 +1531,12 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect rebuild = rebuild || this->updateBuffer( (const void*) instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR), instanceIndex, false ); } size_t instanceBufferAddress = this->buffers[instanceIndex].getAddress(); + + auto& tlas = this->accelerationStructures.top; { VkBuildAccelerationStructureFlagsKHR flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; + if ( shouldCompact ) flags |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR; uint32_t countInstance = instancesVK.size(); - auto& tlas = this->accelerationStructures.top; VkAccelerationStructureGeometryInstancesDataKHR instancesVk{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR}; instancesVk.data.deviceAddress = instanceBufferAddress; @@ -1518,10 +1596,9 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect tlas.deviceAddress = uf::renderer::vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); } - // write to BLAS - uf::renderer::Buffer scratchBuffer; + // write to TLAS scratchBuffer.alignment = acclerationStructureProperties.minAccelerationStructureScratchOffsetAlignment; - scratchBuffer.initialize( NULL, sizeInfo.buildScratchSize, uf::renderer::enums::Buffer::STORAGE | uf::renderer::enums::Buffer::ADDRESS ); + scratchBuffer.update( NULL, sizeInfo.buildScratchSize ); buildInfo.srcAccelerationStructure = update ? tlas.handle : VK_NULL_HANDLE; buildInfo.dstAccelerationStructure = tlas.handle; @@ -1537,11 +1614,63 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect &buildInfo, &rangeInfo ); + + if ( queryPool ) uf::renderer::vkCmdWriteAccelerationStructuresPropertiesKHR( + commandBuffer, + 1, + &buildInfo.dstAccelerationStructure, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, + queryPool, + 0 + ); + + device.flushCommandBuffer(commandBuffer, uf::renderer::Device::QueueEnum::COMPUTE); + } + + // compact + if ( queryPool ) { + VkDeviceSize compactSize{}; + vkGetQueryPoolResults(device, queryPool, 0, 1, sizeof(VkDeviceSize), &compactSize, sizeof(VkDeviceSize), VK_QUERY_RESULT_WAIT_BIT); + + auto cleanup = tlas; + size_t tlasBufferSize = ALIGNED_SIZE(compactSize, 256); + // UF_MSG_DEBUG("Reduced size to {}% ({} -> {} = {})", (float) (oldSize - compactedSize) / (float) (oldSize), oldSize, compactedSize, oldSize - compactedSize); + + ext::vulkan::Buffer oldBuffer; + oldBuffer.initialize( NULL, tlasBufferSize, uf::renderer::enums::Buffer::ACCELERATION_STRUCTURE | uf::renderer::enums::Buffer::ADDRESS ); + this->buffers[tlasBufferIndex].swap(oldBuffer); + + tlas.buffer = this->buffers[tlasBufferIndex].alias(); + tlas.buffer.descriptor.offset = 0; + + VkAccelerationStructureCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR}; + createInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + createInfo.size = tlasBufferSize; + createInfo.buffer = tlas.buffer.buffer; + createInfo.offset = tlas.buffer.descriptor.offset; + + VK_CHECK_RESULT(uf::renderer::vkCreateAccelerationStructureKHR(device, &createInfo, nullptr, &tlas.handle)); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = tlas.handle; + tlas.deviceAddress = uf::renderer::vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + VkCopyAccelerationStructureInfoKHR copyInfo{VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR}; + copyInfo.src = cleanup.handle; + copyInfo.dst = tlas.handle; + copyInfo.mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR; + + VkCommandBuffer commandBuffer = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, uf::renderer::Device::QueueEnum::COMPUTE); + uf::renderer::vkCmdCopyAccelerationStructureKHR(commandBuffer, ©Info); device.flushCommandBuffer(commandBuffer, uf::renderer::Device::QueueEnum::COMPUTE); - scratchBuffer.destroy(); + uf::renderer::vkDestroyAccelerationStructureKHR(device, cleanup.handle, nullptr); + oldBuffer.destroy(); } + if ( queryPool ) vkDestroyQueryPool(device, queryPool, nullptr); + if ( rebuild ) { // uf::renderer::states::rebuild = true; auto& renderMode = ext::vulkan::getRenderMode( descriptor.renderMode, true ); @@ -1744,6 +1873,12 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD } } void ext::vulkan::Graphic::destroy() { + for ( auto& as : accelerationStructures.bottoms ) { + uf::renderer::vkDestroyAccelerationStructureKHR(*device, as.handle, nullptr); + } + if ( accelerationStructures.top.handle ) { + uf::renderer::vkDestroyAccelerationStructureKHR(*device, accelerationStructures.top.handle, nullptr); + } for ( auto& pair : pipelines ) pair.second.destroy(); pipelines.clear(); material.destroy(); diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index a94778a2..2171b9fb 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -26,6 +26,8 @@ bool ext::vulkan::settings::defaultStageBuffers = true; // constexpr size_t ext::vulkan::settings::maxViews = 6; size_t ext::vulkan::settings::viewCount = 2; size_t ext::vulkan::settings::gpuID = -1; +size_t ext::vulkan::settings::scratchBufferAlignment = 256; +size_t ext::vulkan::settings::scratchBufferInitialSize = 1024 * 1024; uf::stl::vector ext::vulkan::settings::validationFilters; uf::stl::vector ext::vulkan::settings::requestedDeviceFeatures; @@ -81,6 +83,8 @@ bool ext::vulkan::states::rebuild = false; uint32_t ext::vulkan::states::currentBuffer = 0; uf::ThreadUnique ext::vulkan::currentRenderMode; +ext::vulkan::Buffer ext::vulkan::scratchBuffer; + uf::stl::vector ext::vulkan::renderModes = { new ext::vulkan::BaseRenderMode, }; @@ -251,6 +255,9 @@ void ext::vulkan::initialize() { ext::vulkan::mutex.lock(); device.initialize(); swapchain.initialize( device ); + + ext::vulkan::scratchBuffer.alignment = ext::vulkan::settings::scratchBufferAlignment; + ext::vulkan::scratchBuffer.initialize( NULL, ext::vulkan::settings::scratchBufferInitialSize, uf::renderer::enums::Buffer::ACCELERATION_STRUCTURE | uf::renderer::enums::Buffer::ADDRESS ); if ( uf::io::exists(uf::io::root + "/textures/missing.png") ) { uf::Image image; @@ -516,33 +523,23 @@ void ext::vulkan::destroy() { ext::vulkan::mutex.lock(); synchronize(); - for ( auto& fence : ::auxFencesGraphics ) vkDestroyFence( device, fence, nullptr); - for ( auto& fence : ::auxFencesCompute ) vkDestroyFence( device, fence, nullptr); - - Texture2D::empty.destroy(); - Texture3D::empty.destroy(); - TextureCube::empty.destroy(); - -/* - auto& scene = uf::scene::getCurrentScene(); - auto& graph = scene.getGraph(); - for ( auto entity : graph ) { - if ( !entity->hasComponent() ) continue; - uf::Graphic& graphic = entity->getComponent(); - graphic.destroy(); - } -*/ for ( auto& renderMode : renderModes ) { if ( !renderMode || !renderMode->device ) continue; renderMode->destroy(); - // delete renderMode; + delete renderMode; renderMode = NULL; } - for ( auto& s : ext::vulkan::Sampler::samplers ) { - s.destroy(); - } + Texture2D::empty.destroy(); + Texture3D::empty.destroy(); + TextureCube::empty.destroy(); + for ( auto& s : ext::vulkan::Sampler::samplers ) s.destroy(); + + for ( auto& fence : ::auxFencesGraphics ) vkDestroyFence( device, fence, nullptr); + for ( auto& fence : ::auxFencesCompute ) vkDestroyFence( device, fence, nullptr); + + ext::vulkan::scratchBuffer.destroy(); swapchain.destroy(); device.destroy(); diff --git a/engine/src/utils/io/file.cpp b/engine/src/utils/io/file.cpp index bffda04d..a8f65490 100644 --- a/engine/src/utils/io/file.cpp +++ b/engine/src/utils/io/file.cpp @@ -178,7 +178,7 @@ bool UF_API uf::io::exists( const uf::stl::string& _filename ) { size_t UF_API uf::io::mtime( const uf::stl::string& _filename ) { uf::stl::string filename = sanitize(_filename); static struct stat buffer; - if ( stat(filename.c_str(), &buffer) != 0 ) return -1; + if ( stat(filename.c_str(), &buffer) != 0 ) return 0; return buffer.st_mtime; } bool UF_API uf::io::mkdir( const uf::stl::string& _filename ) { diff --git a/engine/src/utils/serialize/serializer.cpp b/engine/src/utils/serialize/serializer.cpp index 0e99559d..e42d6663 100644 --- a/engine/src/utils/serialize/serializer.cpp +++ b/engine/src/utils/serialize/serializer.cpp @@ -40,12 +40,18 @@ uf::Serializer::output_t uf::Serializer::serialize( const ext::json::EncodingSet } uf::stl::string uf::Serializer::resolveFilename( const uf::stl::string& filename, bool compareTimes ) { + return resolveFilename( filename, ext::json::EncodingSettings{ + .encoding = ext::json::PREFERRED_ENCODING, + .compression = ext::json::PREFERRED_COMPRESSION, + }, compareTimes ); +} +uf::stl::string uf::Serializer::resolveFilename( const uf::stl::string& filename, const ext::json::EncodingSettings& settings, bool compareTimes ) { uf::stl::string _filename = filename; - if ( ext::json::PREFERRED_ENCODING != "" && ext::json::PREFERRED_ENCODING != "json" ) { - _filename = uf::string::replace( _filename, "/\\.json$/", "." + ext::json::PREFERRED_ENCODING ); + if ( settings.encoding != "" && settings.encoding != "json" ) { + _filename = uf::string::replace( _filename, "/\\.json$/", "." + settings.encoding ); } - if ( ext::json::PREFERRED_COMPRESSION != "" ) { - _filename = _filename + "." + ext::json::PREFERRED_COMPRESSION; + if ( settings.compression != "" ) { + _filename = _filename + "." + settings.compression; } if ( !compareTimes ) return _filename; @@ -61,7 +67,14 @@ bool uf::Serializer::readFromFile( const uf::stl::string& filename, const uf::st #if UF_SERIALIZER_IMPLICIT_LOAD // implicitly check for optimal format for plain .json requests if ( uf::string::matched( filename, "/\\.json$/" ) ) { - uf::stl::string _filename = uf::Serializer::resolveFilename( filename ); + #if UF_USE_TOML + uf::stl::string toml_filename = uf::string::replace( filename, "/\\.json$/", ".toml" ); // load from toml if newer + if ( uf::io::mtime( toml_filename ) > uf::io::mtime( filename ) ) { + UF_MSG_DEBUG("Deserialize redirect: {} -> {}", filename, toml_filename); + return readFromFile( toml_filename, hash ); + } + #endif + uf::stl::string _filename = resolveFilename( filename ); if ( _filename != filename ) return readFromFile( _filename, hash ); } #endif @@ -82,12 +95,32 @@ bool uf::Serializer::readFromFile( const uf::stl::string& filename, const uf::st else if ( uf::string::matched( filename, "/\\.msgpack/" ) ) settings.encoding = "msgpack"; else if ( uf::string::matched( filename, "/\\.ubjson/" ) ) settings.encoding = "ubjson"; else if ( uf::string::matched( filename, "/\\.bjdata/" ) ) settings.encoding = "bjdata"; -// else UF_MSG_DEBUG( string ); + else if ( uf::string::matched( filename, "/\\.toml/" ) ) settings.encoding = "toml"; + else if ( uf::string::matched( filename, "/\\.json/" ) ) settings.encoding = "json"; + else UF_MSG_DEBUG( "invalid encoding filetype requested: {}", filename ); this->deserialize( buffer, settings ); #if UF_SERIALIZER_AUTO_CONVERT if ( uf::string::matched( filename, "/\\.json$/" ) ) { + if ( ext::json::PREFERRED_ENCODING != "toml" ) { + uf::stl::string _filename = uf::string::replace( filename, "/\\.json$/", ".toml" ); + + ext::json::EncodingSettings _settings{ + .encoding = "toml", + .compression = "", + }; + bool should = !uf::io::exists( _filename ); // auto convert if preferred file doesn't already exist + if ( !should ) should = uf::io::mtime( _filename ) < uf::io::mtime( filename ); // auto convert if preferred file is older than source file + if ( should ) { + writeToFile( _filename, _settings ); + } + } + } + if ( uf::string::matched( filename, "/\\.(json|toml)$/" ) ) { + // auto convert read JSON file to TOML + + // auto convert read file to preferred filetype if ( ext::json::PREFERRED_COMPRESSION != "" || (ext::json::PREFERRED_ENCODING != "" || ext::json::PREFERRED_ENCODING != "json") ) { ext::json::EncodingSettings _settings; _settings.encoding = ext::json::PREFERRED_ENCODING; @@ -122,7 +155,7 @@ bool uf::Serializer::writeToFile( const uf::stl::string& filename, const ext::js output += "." + settings.compression; uf::stl::string buffer = this->serialize( settings ); - size_t written = uf::io::write( output, buffer.c_str(), buffer.size() ); + size_t written = uf::io::write( output, buffer.data(), buffer.size() ); #if UF_SERIALIZER_AUTO_CONVERT // implicitly check for optimal format for plain .json requests if ( uf::string::matched( output, "/\\.json$/" ) && settings.compression != ext::json::PREFERRED_COMPRESSION ) { diff --git a/ext/behaviors/craeture/behavior.cpp b/ext/behaviors/craeture/behavior.cpp index 7ad90fcb..a8ace88a 100644 --- a/ext/behaviors/craeture/behavior.cpp +++ b/ext/behaviors/craeture/behavior.cpp @@ -62,9 +62,7 @@ void ext::CraetureBehavior::initialize( uf::Object& self ) { auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::CraetureBehavior::tick( uf::Object& self ) {} void ext::CraetureBehavior::render( uf::Object& self ){} diff --git a/ext/behaviors/light/behavior.cpp b/ext/behaviors/light/behavior.cpp index aa8fc7f1..9be12391 100644 --- a/ext/behaviors/light/behavior.cpp +++ b/ext/behaviors/light/behavior.cpp @@ -99,9 +99,7 @@ void ext::LightBehavior::initialize( uf::Object& self ) { uf::renderer::addRenderMode( &renderMode, name ); } - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::LightBehavior::tick( uf::Object& self ) { #if !UF_ENV_DREAMCAST @@ -248,29 +246,30 @@ void ext::LightBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer["light"]["shadows"] = /*this->*/shadows; serializer["system"]["renderer"]["mode"] = /*this->*/renderer.mode; serializer["light"]["external update"] = /*this->*/renderer.external; - serializer["system"]["renderer"]["timer"] = /*this->*/renderer.limiter; +// serializer["system"]["renderer"]["timer"] = /*this->*/renderer.limiter; switch ( /*this->*/type ) { case 0: serializer["light"]["type"] = "point"; break; - case 1: serializer["light"]["type"] = "spot"; break; + case 1: serializer["light"]["type"] = "point"; break; case 2: serializer["light"]["type"] = "spot"; break; } } void ext::LightBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){ - /*this->*/color = uf::vector::decode( serializer["light"]["color"], pod::Vector3f{1,1,1} ); - /*this->*/power = serializer["light"]["power"].as(); - /*this->*/bias = serializer["light"]["bias"]["shader"].as(); - /*this->*/shadows = serializer["light"]["shadows"].as(); - /*this->*/renderer.mode = serializer["system"]["renderer"]["mode"].as(); - /*this->*/renderer.rendered = false; - /*this->*/renderer.external = serializer["light"]["external update"].as(); - /*this->*/renderer.limiter = serializer["system"]["renderer"]["timer"].as(); + /*this->*/color = uf::vector::decode( serializer["light"]["color"], /*this->*/color ); + /*this->*/power = serializer["light"]["power"].as(/*this->*/power); + /*this->*/bias = serializer["light"]["bias"]["shader"].as(/*this->*/bias); + /*this->*/shadows = serializer["light"]["shadows"].as(/*this->*/shadows); + /*this->*/renderer.mode = serializer["system"]["renderer"]["mode"].as(/*this->*/renderer.mode); +// /*this->*/renderer.rendered = false; + /*this->*/renderer.external = serializer["light"]["external update"].as(/*this->*/renderer.external); +// /*this->*/renderer.limiter = serializer["system"]["renderer"]["timer"].as(/*this->*/renderer.limiter); if ( serializer["light"]["type"].is() ) { - /*this->*/type = serializer["light"]["type"].as(); + /*this->*/type = serializer["light"]["type"].as(/*this->*/type); + if ( serializer["light"]["dynamic"].as() ) /*this->*/type = -/*this->*/type; } else if ( serializer["light"]["type"].is() ) { uf::stl::string lightType = serializer["light"]["type"].as(); if ( lightType == "point" ) /*this->*/type = 1; else if ( lightType == "spot" ) /*this->*/type = 2; + if ( serializer["light"]["dynamic"].as() ) /*this->*/type = -/*this->*/type; } - if ( serializer["light"]["dynamic"].as() ) /*this->*/type = -/*this->*/type; } #undef this \ No newline at end of file diff --git a/ext/behaviors/player/behavior.cpp b/ext/behaviors/player/behavior.cpp index 12bd13e4..8fbd7761 100644 --- a/ext/behaviors/player/behavior.cpp +++ b/ext/behaviors/player/behavior.cpp @@ -107,9 +107,7 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) { this->queueHook("discord.Activity.Update.%UID%", ext::json::null(), 1.0); #endif - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::PlayerBehavior::tick( uf::Object& self ) { auto& camera = this->getComponent(); @@ -132,6 +130,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { bool jump = false; bool crouch = false; bool paused = false; + bool console = false; bool vee = false; bool use = false; } keys; @@ -149,6 +148,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { .jump = uf::inputs::kbm::states::Space, .crouch = uf::inputs::kbm::states::LControl, .paused = uf::inputs::kbm::states::Escape, + .console = uf::inputs::kbm::states::Tilde, .vee = uf::inputs::kbm::states::V, .use = uf::inputs::kbm::states::E, }; @@ -205,24 +205,28 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { auto& collider = this->getComponent(); { + float t = -1; uf::Object* hit = NULL; pod::Vector3f center = transform.position + metadata.movement.floored.feet; pod::Vector3f direction = metadata.movement.floored.floor; - float t = 0.0f; - if ( !stats.floored && collider.body && (t = uf::physics::impl::rayCast( center, direction, this, hit )) >= 0.0f ) { + if ( !stats.floored && collider.body && (hit = uf::physics::impl::rayCast( collider, center, direction, t )) ) { if ( metadata.movement.floored.print ) UF_MSG_DEBUG("Floored: {} | {}", hit->getName(), t); stats.floored = true; } } - +#if 0 TIMER(0.25, keys.use && ) { size_t uid = 0; + float depth = -1; uf::Object* pointer = NULL; float length = metadata.use.length; - pod::Vector3f center = transform.position + cameraTransform.position; - pod::Vector3f direction = uf::vector::normalize( transform.forward + pod::Vector3f{ 0, cameraTransform.forward.y, 0 } ) * length; + // pod::Vector3f center = transform.position + cameraTransform.position; + // pod::Vector3f direction = uf::vector::normalize( transform.forward + pod::Vector3f{ 0, cameraTransform.forward.y, 0 } ) * length; + auto flattened = uf::transform::flatten( cameraTransform ); + pod::Vector3f center = flattened.position; + pod::Vector3f direction = flattened.forward * length; - float depth = uf::physics::impl::rayCast( center, direction, this, pointer ); + pointer = uf::physics::impl::rayCast( center, direction, this, depth ); ext::json::Value payload; if ( pointer ) { payload["user"] = this->getUid(); @@ -235,7 +239,6 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { payload["depth"] = -1; } this->callHook( "entity:Use.%UID%", payload ); - /* auto& emitter = this->getComponent(); uf::stl::string filename = pointer ? "./ui/select.ogg" : "./ui/deny.ogg"; @@ -254,6 +257,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { } */ } +#endif if ( collider.stats.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true; diff --git a/ext/behaviors/player/model/behavior.cpp b/ext/behaviors/player/model/behavior.cpp index 69e45e36..3846c13a 100644 --- a/ext/behaviors/player/model/behavior.cpp +++ b/ext/behaviors/player/model/behavior.cpp @@ -27,9 +27,7 @@ void ext::PlayerModelBehavior::initialize( uf::Object& self ) { // transform.reference = &controllerTransform; // metadata.reference = &controllerTransform; - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::PlayerModelBehavior::tick( uf::Object& self ) { auto& metadata = this->getComponent(); diff --git a/ext/behaviors/raytrace/behavior.cpp b/ext/behaviors/raytrace/behavior.cpp index 3c8521e3..9cfc7cd0 100644 --- a/ext/behaviors/raytrace/behavior.cpp +++ b/ext/behaviors/raytrace/behavior.cpp @@ -37,9 +37,7 @@ void ext::RayTraceSceneBehavior::initialize( uf::Object& self ) { uf::renderer::addRenderMode( renderMode, "Compute" ); } - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { auto& metadata = this->getComponent(); @@ -115,6 +113,7 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { size_t maxCascades = sceneMetadataJson["system"]["config"]["engine"]["scenes"]["vxgi"]["cascades"].as(16); shader.buffers.emplace_back( uf::graph::storage.buffers.instance.alias() ); + shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); shader.buffers.emplace_back( uf::graph::storage.buffers.material.alias() ); shader.buffers.emplace_back( uf::graph::storage.buffers.texture.alias() ); shader.buffers.emplace_back( uf::graph::storage.buffers.light.alias() ); @@ -141,6 +140,7 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { metadata.renderer.bound = true; } + /* if ( graphic.material.hasShader("ray:hit:closest", uf::renderer::settings::pipelines::names::rt) ) { auto& shader = graphic.material.getShader("ray:hit:closest", uf::renderer::settings::pipelines::names::rt); shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); @@ -149,6 +149,7 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { auto& shader = graphic.material.getShader("ray:hit:any", uf::renderer::settings::pipelines::names::rt); shader.buffers.emplace_back( uf::graph::storage.buffers.instanceAddresses.alias() ); } + */ graphic.process = false; } diff --git a/ext/behaviors/scene/behavior.cpp b/ext/behaviors/scene/behavior.cpp index 72496702..04b6e375 100644 --- a/ext/behaviors/scene/behavior.cpp +++ b/ext/behaviors/scene/behavior.cpp @@ -113,9 +113,7 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { if ( !payload.transform.reference ) transform.reference = payload.transform.reference; }); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); // lock control { diff --git a/ext/behaviors/voxelizer/behavior.cpp b/ext/behaviors/voxelizer/behavior.cpp index abe4ff40..175d0e8b 100644 --- a/ext/behaviors/voxelizer/behavior.cpp +++ b/ext/behaviors/voxelizer/behavior.cpp @@ -40,9 +40,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { } ); } - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); // initialize voxel map #if 0 diff --git a/ext/gui/behavior.cpp b/ext/gui/behavior.cpp index 58576436..e84973b6 100644 --- a/ext/gui/behavior.cpp +++ b/ext/gui/behavior.cpp @@ -487,9 +487,7 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){ if ( !uf::Asset::isExpected( payload, uf::Asset::Type::IMAGE ) ) return; @@ -642,9 +640,12 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { metadataJson["text settings"][key] = value; }); auto& metadataGlyph = this->getComponent(); + // UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadataGlyph, metadataJson); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); + this->addHook( "object:Serialize.%UID%", [&](){ metadataGlyph.serialize(self, metadataJson); }); + this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadataGlyph.serialize(self, (uf::Serializer&) json); }); + this->addHook( "object:Deserialize.%UID%", [&](){ metadataGlyph.deserialize(self, metadataJson); }); + this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadataGlyph.deserialize(self, (uf::Serializer&) json); }); // metadataGlyph.deserialize(self, metadataJson); this->addHook( "object:Reload.%UID%", [&](ext::json::Value& json){ diff --git a/ext/gui/manager/behavior.cpp b/ext/gui/manager/behavior.cpp index dbe7f19d..2666161e 100644 --- a/ext/gui/manager/behavior.cpp +++ b/ext/gui/manager/behavior.cpp @@ -84,9 +84,7 @@ void ext::GuiManagerBehavior::initialize( uf::Object& self ) { } }); - this->addHook( "object:Serialize.%UID%", [&](ext::json::Value& json){ metadata.serialize(self, metadataJson); }); - this->addHook( "object:Deserialize.%UID%", [&](ext::json::Value& json){ metadata.deserialize(self, metadataJson); }); - metadata.deserialize(self, metadataJson); + UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); } void ext::GuiManagerBehavior::tick( uf::Object& self ) { #if UF_USE_VULKAN