diff --git a/Makefile b/Makefile index 422d8bef..892f5e76 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,9 @@ EXT_LIB_NAME += ext #VULKAN_SDK_PATH += /c/VulkanSDK/1.2.162.0/ #VULKAN_SDK_PATH += /c/VulkanSDK/1.2.176.1/ #VULKAN_SDK_PATH += /c/VulkanSDK/1.2.182.0/ -VULKAN_SDK_PATH += /c/VulkanSDK/1.2.198.1/ +#VULKAN_SDK_PATH += /c/VulkanSDK/1.2.198.1/ #VULKAN_SDK_PATH += /c/VulkanSDK/1.3.204.1/ +VULKAN_SDK_PATH += /c/VulkanSDK/1.3.211.0/ #GLSLC += $(VULKAN_SDK_PATH)/Bin/glslangValidator GLSLC += $(VULKAN_SDK_PATH)/Bin/glslc @@ -59,7 +60,7 @@ else ifneq (,$(findstring dreamcast,$(ARCH))) endif ifneq (,$(findstring vulkan,$(REQ_DEPS))) FLAGS += -DVK_USE_PLATFORM_WIN32_KHR -DUF_USE_VULKAN - DEPS += -lvulkan -lspirv-cross + DEPS += -lvulkan -lspirv-cross #-lVulkanMemoryAllocator INCS += -I$(VULKAN_SDK_PATH)/include LIBS += -L$(VULKAN_SDK_PATH)/Lib endif diff --git a/bin/data/config.json b/bin/data/config.json index 0489ba50..59adc7a8 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -1,12 +1,12 @@ { "engine": { "scenes": { - "start": "SH2_McDonalds", + "start": "SS2", "meshes": { "interleave": true }, "matrix": { "reverseInfinite": true }, - "lights": { - "max": 16, - "enabled": true + "lights": { "enabled": true, + "useLightmaps": false, + "max": 16 }, "shadows": { "enabled": true, @@ -255,7 +255,7 @@ "cursor" : { "visible" : true, "center" : false, - "sensitivity": [ 0.5, 0.5 ] + "sensitivity": [ 1, 1 ] }, "mode" : "windowed", // fullscreen, borderless, windowed "icon" : "./data/textures/icon.png", diff --git a/bin/data/entities/model.json b/bin/data/entities/model.json index 8e47873f..f56a93e4 100644 --- a/bin/data/entities/model.json +++ b/bin/data/entities/model.json @@ -18,12 +18,14 @@ "exporter": { "enabled": true, "pretty": false, - "compress": true, + "encoding": "auto", + "compression": "auto", "quantize": false, "precision": 4, "combined": false, "encode buffers": true, "unwrap": true, + "quit": true, "mesh": { // "print": true } @@ -51,6 +53,7 @@ } }, // "filter": "NEAREST", + "lightmap": true, "flags": { "ATLAS": false, "INVERT": false, diff --git a/bin/data/scenes/construct/gm_construct.json b/bin/data/scenes/construct/gm_construct.json index dba936e1..ce5c0f2d 100644 --- a/bin/data/scenes/construct/gm_construct.json +++ b/bin/data/scenes/construct/gm_construct.json @@ -2,13 +2,13 @@ "import": "/model.json", "assets": [ // { "filename": "./models/gm_construct.glb" } - { "filename": "./models/gm_construct/graph.json.gz" } + { "filename": "./models/gm_construct/graph.json" } ], "metadata": { "model": { - "lightmap": true, + // "lightmap": false, "baking": { - "enabled": false, + "enabled": true, "resolution": 1024, "settings": { // "useInputMeshUvs": false diff --git a/bin/data/scenes/construct/scene.json b/bin/data/scenes/construct/scene.json index b321ad07..218eae21 100644 --- a/bin/data/scenes/construct/scene.json +++ b/bin/data/scenes/construct/scene.json @@ -1,6 +1,7 @@ { "import": "/scene.json", "assets": [ + "./audio/soundscape/ambience.ogg", "./loading.json" ], "system": { @@ -28,7 +29,7 @@ "ambient": [ 0.15, 0.15, 0.15 ], - "fog": { + "fog-disabled": { "color": [ 0.5, 0.5, 0.5 ], "range": [ 64, 256 ], "step scale": 2, diff --git a/bin/data/scenes/mcdonalds/mcdonalds.json b/bin/data/scenes/mcdonalds/mcdonalds.json index c47cf229..4592be30 100644 --- a/bin/data/scenes/mcdonalds/mcdonalds.json +++ b/bin/data/scenes/mcdonalds/mcdonalds.json @@ -4,16 +4,16 @@ // { "filename": "./static.json", "delay": 8 }, // { "filename": "./models/mcdonalds.glb" } - { "filename": "./models/mcdonalds/graph.json.gz" } + { "filename": "./models/mcdonalds/graph.json" } // { "filename": "./models/mini_mcd.glb" } - // { "filename": "./models/mini_mcd/graph.json.gz" } + // { "filename": "./models/mini_mcd/graph.json" } ], "metadata": { "model": { - "lightmap": true, + // "lightmap": false, "baking": { - "enabled": false, + "enabled": true, "resolution": 2048, "settings": { "useInputMeshUvs": false @@ -23,7 +23,7 @@ "tags": { "/^worldspawn/": { "physics": { "type": "mesh", "static": true }, - "grid": { "size": [3,2,3], "epsilon": 1.0, "cleanup": true, "print": true } + "grid": { "size": [4,1,4], "epsilon": 1.0, "cleanup": true, "print": true } }, "info_player_spawn": { "action": "attach", "filename": "./player.json", "preserve orientation": true }, diff --git a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json index 92d9cf45..112ce44b 100644 --- a/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json +++ b/bin/data/scenes/sh2_mcdonalds/sh2_mcdonalds.json @@ -10,9 +10,9 @@ "model": { "cull mode": "none", "alpha mode": "BLEND", - "lightmap": true, + // "lightmap": false, "baking": { - "enabled": false, + "enabled": true, "resolution": 1024, "settings": { "useInputMeshUvs": false diff --git a/bin/data/scenes/ss2/medsci.json b/bin/data/scenes/ss2/medsci.json index 22ba34a3..f85de558 100644 --- a/bin/data/scenes/ss2/medsci.json +++ b/bin/data/scenes/ss2/medsci.json @@ -3,26 +3,21 @@ "assets": [ // { "filename": "./craeture.json", "delay": 1 }, - // { "filename": "./models/tiny_msci.glb", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/tiny_msci/graph.json", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/tiny_msci/graph.json.gz", "delay": 0, "single threaded": false, "category": "models" } + // { "filename": "./models/tiny_msci.glb" } + // { "filename": "./models/tiny_msci/graph.json" } - // { "filename": "./models/micro_sci.glb", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/micro_sci/graph.json", "delay": 0, "single threaded": false, "category": "models" } - { "filename": "./models/micro_sci/graph.json.gz", "delay": 0, "single threaded": false, "category": "models" } - + // { "filename": "./models/micro_sci.glb" } + // { "filename": "./models/micro_sci/graph.json" } - // { "filename": "./models/msci.glb", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/msci/graph.json", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/msci/graph.json.gz", "delay": 0, "single threaded": false, "category": "models" } + // { "filename": "./models/msci.glb" } + // { "filename": "./models/msci/graph.json" } - // { "filename": "./models/medsci.glb", "delay": 0, "single threaded": false } - // { "filename": "./models/medsci/graph.json", "delay": 0, "single threaded": false, "category": "models" } - // { "filename": "./models/medsci/graph.json.gz", "delay": 0, "single threaded": false, "category": "models" } + // { "filename": "./models/medsci.glb" } + { "filename": "./models/medsci/graph.json" } ], "metadata": { "model": { - "lightmap": true, + // "lightmap": false, "baking": { "enabled": true, "resolution": 1024 @@ -31,7 +26,7 @@ "tags": { "/^worldspawn/": { "physics": { "type": "mesh", "static": true }, - "grid": { "size": [5,2,5], "epsilon": 1.0, "cleanup": true, "print": true } + "grid": { "size": [5,1,5], "epsilon": 1.0, "cleanup": true, "print": true } }, "info_player_spawn": { "action": "attach", diff --git a/bin/data/shaders/common/pbr.h b/bin/data/shaders/common/pbr.h index cfe85745..4965e485 100644 --- a/bin/data/shaders/common/pbr.h +++ b/bin/data/shaders/common/pbr.h @@ -15,7 +15,7 @@ float gaSchlickGGX(float cosLi, float cosLo, float roughness) { vec3 fresnelSchlick(vec3 F0, float cosTheta) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } #if !BAKING && !COMPUTE void pbr() { - if ( validTextureIndex( surface.instance.lightmapID ) ) return; + if ( bool(ubo.useLightmaps) && validTextureIndex( surface.instance.lightmapID ) ) return; const float Rs = 4.0; // specular lighting looks gross without this const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic); diff --git a/bin/data/shaders/display/subpass.h b/bin/data/shaders/display/subpass.h index 7691c954..da4174d7 100644 --- a/bin/data/shaders/display/subpass.h +++ b/bin/data/shaders/display/subpass.h @@ -62,7 +62,7 @@ layout (binding = 5) uniform UBO { uint shadowSamples; uint indexSkybox; - uint padding1; + uint useLightmaps; uint padding2; uint padding3; } ubo; @@ -235,7 +235,7 @@ void populateSurface() { } // Lightmap - if ( validTextureIndex( surface.instance.lightmapID ) ) { + if ( bool(ubo.useLightmaps) && validTextureIndex( surface.instance.lightmapID ) ) { surface.light += surface.material.albedo * sampleTexture( surface.instance.lightmapID, surface.st ); } // Emissive textures diff --git a/client/main.cpp b/client/main.cpp index 069d2132..8788ec6f 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -25,6 +25,7 @@ namespace { #if UF_ENV_DREAMCAST arch_stk_trace(1); #endif + if ( client::terminated ) return; UF_MSG_INFO("Termination via std::atexit()!"); ext::ready = false; client::ready = false; @@ -125,6 +126,7 @@ int main(int argc, char** argv){ #endif } if ( !client::terminated ) { + client::terminated = true; UF_MSG_INFO("Natural termination!"); ext::terminate(); client::terminate(); diff --git a/engine/inc/uf/ext/json/json.h b/engine/inc/uf/ext/json/json.h index f2b2504b..8b92388c 100644 --- a/engine/inc/uf/ext/json/json.h +++ b/engine/inc/uf/ext/json/json.h @@ -20,12 +20,19 @@ namespace ext { namespace json { + extern uf::stl::string PREFERRED_COMPRESSION; + 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 bool pretty = false; - bool compress = false; bool quantize = false; uint8_t precision = 0; }; + struct UF_API DecodingSettings { + uf::stl::string encoding = ""; + }; // cares not about breaking void UF_API forEach( ext::json::Value& json, const std::function& function ); @@ -50,7 +57,8 @@ namespace ext { uf::stl::string UF_API encode( const ext::json::Value& json, bool pretty = true ); uf::stl::string UF_API encode( const ext::json::Value& json, const ext::json::EncodingSettings& settings ); - ext::json::Value& UF_API decode( ext::json::Value& json, const uf::stl::string& str ); + + ext::json::Value& UF_API decode( ext::json::Value& json, const uf::stl::string& str, const DecodingSettings& = {} ); #if UF_USE_LUA uf::stl::string UF_API encode( const sol::table& table ); #endif diff --git a/engine/inc/uf/ext/json/nlohmann.h b/engine/inc/uf/ext/json/nlohmann.h index bcb1c08f..1eb16623 100644 --- a/engine/inc/uf/ext/json/nlohmann.h +++ b/engine/inc/uf/ext/json/nlohmann.h @@ -94,6 +94,7 @@ template<> inline bool ext::json::Value::is(bool strict) const { retur template<> inline bool ext::json::Value::is(bool strict) const { return strict ? is_number_float() : is_number(); } template<> inline bool ext::json::Value::is(bool strict) const { return strict ? is_number_float() : is_number(); } template<> inline bool ext::json::Value::is(bool strict) const { return is_string(); } +// template<> inline bool ext::json::Value::is(bool strict) const { return is_string(); } #if UF_ENV_DREAMCAST template<> inline bool ext::json::Value::is(bool strict) const { return strict ? is_number_integer() : is_number(); } diff --git a/engine/inc/uf/macros.h b/engine/inc/uf/macros.h index ad1d2e26..9afb6f8a 100644 --- a/engine/inc/uf/macros.h +++ b/engine/inc/uf/macros.h @@ -54,8 +54,10 @@ #define UF_MSG_ERROR(X) UF_MSG(X, " ERROR "); #if UF_NO_EXCEPTIONS + #define UF_EXCEPTIONS 0 #define UF_EXCEPTION(X) { UF_MSG_ERROR(X); std::abort(); } #else + #define UF_NO_EXCEPTIONS 0 #define UF_EXCEPTION(X) {\ uf::stl::stringstream str;\ str << X;\ diff --git a/engine/inc/uf/utils/hook/hook.h b/engine/inc/uf/utils/hook/hook.h index 2d3b867e..97aab049 100644 --- a/engine/inc/uf/utils/hook/hook.h +++ b/engine/inc/uf/utils/hook/hook.h @@ -50,6 +50,7 @@ namespace uf { public: bool exists( const name_t& name ); void removeHook( const name_t& name, size_t uid ); + void removeHooks(); size_t addHook( const name_t& name, const pod::Hook::function_t& callback, const pod::Hook::Type& = {UF_USERDATA_CTTI(void), 0} ); size_t addHook( const name_t& name, const std::function& callback ); diff --git a/engine/inc/uf/utils/serialize/serializer.h b/engine/inc/uf/utils/serialize/serializer.h index c71ec6a9..b947f60c 100644 --- a/engine/inc/uf/utils/serialize/serializer.h +++ b/engine/inc/uf/utils/serialize/serializer.h @@ -11,6 +11,8 @@ namespace uf { class UF_API Serializer : public ext::json::Document { + protected: + uf::stl::string m_filename = ""; public: typedef uf::stl::string output_t; typedef uf::stl::string input_t; @@ -24,7 +26,7 @@ namespace uf { Serializer::output_t serialize( bool pretty = false ) const; Serializer::output_t serialize( const ext::json::EncodingSettings& ) const; - void deserialize( const uf::stl::string& ); + void deserialize( const uf::stl::string&, const ext::json::DecodingSettings& = {} ); // serializeable template @@ -60,6 +62,7 @@ namespace uf { return res; } + static uf::stl::string resolveFilename( const uf::stl::string& filename, 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/inc/uf/utils/string/ext.h b/engine/inc/uf/utils/string/ext.h index 6919a7fa..073edc00 100644 --- a/engine/inc/uf/utils/string/ext.h +++ b/engine/inc/uf/utils/string/ext.h @@ -11,7 +11,10 @@ namespace uf { namespace string { // bool match( const uf::stl::string& str, const uf::stl::string& r ); bool UF_API isRegex( const uf::stl::string& str ); - uf::stl::vector UF_API matches( const uf::stl::string& str, const uf::stl::string& r ); + uf::stl::vector UF_API match( const uf::stl::string& str, const uf::stl::string& r ); + inline uf::stl::vector UF_API matches( const uf::stl::string& str, const uf::stl::string& r ) { return uf::string::match( str, r ); } + + bool UF_API matched( const uf::stl::string& str, const uf::stl::string& r ); uf::stl::string UF_API replace( const uf::stl::string&, const uf::stl::string&, const uf::stl::string& ); uf::stl::string UF_API lowercase( const uf::stl::string& ); diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index 60581264..1cb5dab9 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -181,7 +181,7 @@ void uf::Asset::load( const uf::stl::string& callback, const uf::Asset::Payload& uf::Asset::Payload uf::Asset::resolveToPayload( const uf::stl::string& uri, const uf::stl::string& mime ) { uf::stl::string extension = uf::string::lowercase( uf::io::extension( uri, -1 ) ); - uf::stl::string basename = uf::string::lowercase( uf::string::replace( uf::io::filename( uri ), ".gz", "" ) ); + uf::stl::string basename = uf::string::lowercase( uf::string::replace( uf::io::filename( uri ), "/.(?:gz|lz)$/", "" ) ); uf::Asset::Payload payload; static uf::stl::unordered_map typemap = { @@ -192,6 +192,8 @@ uf::Asset::Payload uf::Asset::resolveToPayload( const uf::stl::string& uri, cons { "ogg", uf::Asset::Type::AUDIO }, { "json", uf::Asset::Type::JSON }, + { "bson", uf::Asset::Type::JSON }, + { "cbor", uf::Asset::Type::JSON }, { "lua", uf::Asset::Type::LUA }, @@ -207,6 +209,8 @@ uf::Asset::Payload uf::Asset::resolveToPayload( const uf::stl::string& uri, cons if ( typemap.count( extension ) == 1 ) payload.type = typemap[extension]; if ( basename == "graph.json" ) payload.type = uf::Asset::Type::GRAPH; + if ( basename == "graph.bson" ) payload.type = uf::Asset::Type::GRAPH; + if ( basename == "graph.cbor" ) payload.type = uf::Asset::Type::GRAPH; return payload; } @@ -232,6 +236,12 @@ uf::stl::string uf::Asset::cache( const uf::Asset::Payload& payload ) { return ""; } filename = cached; + } else { + // do implicit loading of json files (could be encoded as bson, cbor, and compressed as gz, lz) + if ( extension == "json" ) { + filename = uf::Serializer::resolveFilename( filename ); + extension = uf::io::extension( extension ); + } } if ( !uf::io::exists( filename ) ) { if ( !uf::Asset::assertionLoad ) { @@ -255,7 +265,7 @@ uf::stl::string uf::Asset::cache( const uf::Asset::Payload& payload ) { uf::stl::string uf::Asset::load(const uf::Asset::Payload& payload ) { uf::stl::string filename = payload.filename; uf::stl::string extension = uf::string::lowercase(uf::io::extension( payload.filename, -1 )); - uf::stl::string basename = uf::string::replace( uf::io::filename( payload.filename ), ".gz", "" ); + uf::stl::string basename = uf::string::lowercase( uf::string::replace( uf::io::filename( payload.filename ), "/.(?:gz|lz)$/", "" ) ); if ( payload.filename.substr(0,5) == "https" ) { uf::stl::string hash = uf::string::sha256( payload.filename ); uf::stl::string cached = uf::io::root + "/cache/http/" + hash + "." + extension; @@ -268,6 +278,12 @@ uf::stl::string uf::Asset::load(const uf::Asset::Payload& payload ) { return ""; } filename = cached; + } else { + // do implicit loading of json files (could be encoded as bson, cbor, and compressed as gz, lz) + if ( extension == "json" ) { + filename = uf::Serializer::resolveFilename( filename ); + extension = uf::io::extension( extension ); + } } if ( !uf::io::exists( filename ) ) { if ( !uf::Asset::assertionLoad ) { diff --git a/engine/src/engine/behavior/behavior.cpp b/engine/src/engine/behavior/behavior.cpp index 9b197b0f..a925b685 100644 --- a/engine/src/engine/behavior/behavior.cpp +++ b/engine/src/engine/behavior/behavior.cpp @@ -112,6 +112,12 @@ void uf::Behaviors::destroy() { uf::Object& self = *((uf::Object*) this); if ( !self.isValid() ) return; UF_BEHAVIOR_POLYFILL(destroy) + m_behaviors.clear(); + m_graph.initialize.clear(); + m_graph.tick.clear(); + m_graph.tickMT.clear(); + m_graph.render.clear(); + m_graph.destroy.clear(); } #if 0 diff --git a/engine/src/engine/graph/encode.cpp b/engine/src/engine/graph/encode.cpp index 1e9f1dcd..5766bdb0 100644 --- a/engine/src/engine/graph/encode.cpp +++ b/engine/src/engine/graph/encode.cpp @@ -197,7 +197,7 @@ namespace { ext::json::reserve( json["buffers"], mesh.buffers.size() ); for ( auto i = 0; i < mesh.buffers.size(); ++i ) { - const uf::stl::string filename = settings.filename + ".buffer." + std::to_string(i) + "." + ( settings.compress ? "gz" : "bin" ); + const uf::stl::string filename = settings.filename + ".buffer." + std::to_string(i) + "." + ( settings.compression != "" ? settings.compression : "bin" ); uf::io::write( filename, mesh.buffers[i] ); json["buffers"].emplace_back(uf::io::filename( filename )); } @@ -224,10 +224,11 @@ void uf::graph::save( const pod::Graph& graph, const uf::stl::string& filename ) uf::Serializer serializer; uf::Serializer metadata; - const ::EncodingSettings settings = ::EncodingSettings{ + ::EncodingSettings settings = ::EncodingSettings{ { + /*.compression = */graph.metadata["exporter"]["compression"].as("auto"), + /*.encoding = */graph.metadata["exporter"]["encoding"].as("auto"), /*.pretty = */graph.metadata["exporter"]["pretty"].as(), - /*.compress = */graph.metadata["exporter"]["compress"].as(), /*.quantize = */graph.metadata["exporter"]["quantize"].as(), /*.precision = */graph.metadata["exporter"]["precision"].as(), }, @@ -236,6 +237,10 @@ void uf::graph::save( const pod::Graph& graph, const uf::stl::string& filename ) /*.unwrap = */graph.metadata["exporter"]["unwrap"].as(true), /*.filename = */directory + "/graph.json", }; + + if ( settings.encoding == "auto" ) settings.encoding = ext::json::PREFERRED_ENCODING; + if ( settings.compression == "auto" ) settings.compression = ext::json::PREFERRED_COMPRESSION; + if ( !settings.combined ) uf::io::mkdir(directory); #if UF_USE_XATLAS /* @@ -297,7 +302,8 @@ void uf::graph::save( const pod::Graph& graph, const uf::stl::string& filename ) encode(mesh, s).writeToFile(s.filename, settings); uf::Serializer json; json["name"] = name; - json["filename"] = uf::io::filename(s.filename + (settings.compress ? ".gz" : "")); + // json["filename"] = uf::io::filename(s.filename + (settings.compress ? ".gz" : "")); + json["filename"] = uf::io::filename(s.filename); serializer["meshes"].emplace_back( json ); } else { s.filename = directory+"/mesh."+std::to_string(i); @@ -403,7 +409,8 @@ void uf::graph::save( const pod::Graph& graph, const uf::stl::string& filename ) uf::stl::string f = "animation."+std::to_string(i)+".json"; auto& animation = /*graph.storage*/uf::graph::storage.animations.map.at(name); encode(animation, settings).writeToFile(directory+"/"+f, settings); - serializer["animations"].emplace_back(f + (settings.compress ? ".gz" : "")); + // serializer["animations"].emplace_back(f + (settings.compress ? ".gz" : "")); + serializer["animations"].emplace_back(f); } } else { for ( auto& name : graph.animations ) { @@ -435,6 +442,12 @@ void uf::graph::save( const pod::Graph& graph, const uf::stl::string& filename ) if ( !settings.combined ) target = directory + "/graph.json"; serializer.writeToFile( target, settings ); UF_MSG_DEBUG("Saving graph to `" << target << "`"); + + if ( graph.metadata["exporter"]["quit"].as(true) ) { + ext::json::Value payload; + payload["message"] = "Termination after gltf conversion requested."; + uf::scene::getCurrentScene().queueHook("system:Quit", payload); + } } uf::stl::string uf::graph::print( const pod::Graph& graph ) { diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 31cfea25..55516464 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -693,7 +693,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) */ ext::json::forEach( graph.metadata["tags"], [&]( const uf::stl::string& key, ext::json::Value& value ) { if ( uf::string::isRegex( key ) ) { - if ( uf::string::matches( node.name, key ).empty() ) return; + if ( !uf::string::matched( node.name, key ) ) return; } else if ( node.name != key ) return; tag = value; }); diff --git a/engine/src/ext/freetype/freetype.cpp b/engine/src/ext/freetype/freetype.cpp index b1a35e28..9c37bd4c 100644 --- a/engine/src/ext/freetype/freetype.cpp +++ b/engine/src/ext/freetype/freetype.cpp @@ -28,7 +28,9 @@ UF_API bool ext::freetype::initialized() { return ext::freetype::library.loaded; } UF_API void ext::freetype::terminate() { + if ( !ext::freetype::library.loaded ) return; FT_Done_FreeType(ext::freetype::library.library); + ext::freetype::library.loaded = false; } UF_API ext::freetype::Glyph ext::freetype::initialize( const uf::stl::string& font ) { diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index 2931e1c6..51236798 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -265,7 +265,7 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize if ( !ext::json::isObject( value["grid"] ) ) return; // no tag["grid"] defined if ( ext::json::isNull( value["grid"]["size"] ) ) return; // no tag["grid"]["size"] defined if ( uf::string::isRegex( key ) ) { - if ( uf::string::matches( m.name, key ).empty() ) return; + if ( !uf::string::matched( m.name, key ) ) return; } else if ( m.name != key ) return; meshgrid.metadata = value["grid"]; }); diff --git a/engine/src/ext/json/json.cpp b/engine/src/ext/json/json.cpp index 99d0ba69..1396cba6 100644 --- a/engine/src/ext/json/json.cpp +++ b/engine/src/ext/json/json.cpp @@ -111,7 +111,7 @@ void ext::json::forEach( const ext::json::Value& json, const std::function ext::json::keys( const ext::json::Value& v ) { uf::stl::vector keys; if ( !ext::json::isObject( v ) ) return keys; @@ -120,7 +120,7 @@ uf::stl::vector ext::json::keys( const ext::json::Value& v ) { } return keys; } -#elif defined(UF_JSON_USE_RAPIDJSON) && UF_JSON_USE_RAPIDJSON +#elif UF_JSON_USE_RAPIDJSON rapidjson::Document::AllocatorType ext::json::allocator; #endif @@ -150,20 +150,33 @@ ext::json::Value& ext::json::reencode( ext::json::Value& x, const EncodingSettin return x; } +uf::stl::string ext::json::PREFERRED_COMPRESSION = "gz"; +uf::stl::string ext::json::PREFERRED_ENCODING = "bson"; + uf::stl::string ext::json::encode( const ext::json::Value& json, bool pretty ) { return ext::json::encode( json, ext::json::EncodingSettings{ .pretty = true } ); } uf::stl::string ext::json::encode( const ext::json::Value& _json, const ext::json::EncodingSettings& settings ) { ext::json::Value json = ext::json::reencode( _json, settings ); -#if defined(UF_JSON_USE_NLOHMANN) && UF_JSON_USE_NLOHMANN - return settings.pretty ? json.dump(1, '\t') : json.dump(); -#elif defined(UF_JSON_USE_JSONCPP) && UF_JSON_USE_JSONCPP - Json::FastWriter fast; - Json::StyledWriter styled; - uf::stl::string output = settings.pretty ? styled.write(json) : fast.write(json); - if ( output.back() == '\n' ) output.pop_back(); - return output; -#elif defined(UF_JSON_USE_LUA) && UF_JSON_USE_LUA +#if UF_JSON_USE_NLOHMANN + // emit raw json + if ( settings.encoding == "" || settings.encoding == "json" ) { + return settings.pretty ? json.dump(1, '\t') : json.dump(); + } + // emit bson + if ( settings.encoding == "bson" ) { + auto buffer = nlohmann::json::to_bson( static_cast(json) ); + return uf::stl::string( buffer.begin(), buffer.end() ); + } + // emit cbor + if ( settings.encoding == "cbor" ) { + auto buffer = nlohmann::json::to_cbor( static_cast(json) ); + return uf::stl::string( buffer.begin(), buffer.end() ); + } + // should probably default to json, not my problem + UF_MSG_ERROR("invalid encoding requested: " << settings.encoding); + return ""; +#elif UF_JSON_USE_LUA return ext::lua::state["json"]["encode"]( _json ); #endif } @@ -173,23 +186,24 @@ uf::stl::string ext::json::encode( const sol::table& table ) { return ext::lua::state["json"]["encode"]( table ); } #endif -ext::json::Value& ext::json::decode( ext::json::Value& json, const uf::stl::string& str ) { -#if defined(UF_JSON_USE_NLOHMANN) && UF_JSON_USE_NLOHMANN -#if UF_NO_EXCEPTIONS - json = nlohmann::json::parse(str, nullptr, true, true); -#else +ext::json::Value& ext::json::decode( ext::json::Value& json, const uf::stl::string& str, const DecodingSettings& settings ) { +#if UF_JSON_USE_NLOHMANN +#if !UF_NO_EXCEPTIONS + bool exceptions = true; try { - json = nlohmann::json::parse(str, nullptr, true, true); +#endif + if ( settings.encoding == "" || settings.encoding == "json" ) + json = nlohmann::json::parse(str, nullptr, exceptions, true); + else if ( settings.encoding == "bson" ) + json = nlohmann::json::from_bson(str, exceptions, true); + else if ( settings.encoding == "cbor" ) + json = nlohmann::json::from_cbor(str, exceptions, true); +#if !UF_NO_EXCEPTIONS } catch ( nlohmann::json::parse_error& e ) { UF_MSG_ERROR("JSON error: " << e.what() << "\tAttempted to parse: " << str) } #endif -#elif defined(UF_JSON_USE_JSONCPP) && UF_JSON_USE_JSONCPP - Json::Reader reader; - if ( !reader.parse(str, json) ) { - UF_MSG_ERROR("JSON error: " << reader.getFormattedErrorMessages() << "\tAttempted to parse: " << str) - } -#elif defined(UF_JSON_USE_LUA) && UF_JSON_USE_LUA +#elif UF_JSON_USE_LUA json = ext::lua::state["json"]["decode"]( str ); #endif return json; diff --git a/engine/src/ext/reactphysics/reactphysics.cpp b/engine/src/ext/reactphysics/reactphysics.cpp index 6161ada0..54b50e22 100644 --- a/engine/src/ext/reactphysics/reactphysics.cpp +++ b/engine/src/ext/reactphysics/reactphysics.cpp @@ -233,6 +233,7 @@ void ext::reactphysics::tick( float delta ) { ext::reactphysics::syncFrom( accumulator / ext::reactphysics::timescale ); } void ext::reactphysics::terminate() { + if ( !::world ) return; ::common.destroyPhysicsWorld(::world); ::world = NULL; } diff --git a/engine/src/ext/vulkan/buffer.cpp b/engine/src/ext/vulkan/buffer.cpp index 00081217..7884b9d2 100644 --- a/engine/src/ext/vulkan/buffer.cpp +++ b/engine/src/ext/vulkan/buffer.cpp @@ -1,5 +1,6 @@ #if UF_USE_VULKAN +#define VMA_VULKAN_VERSION 1002000 #define VMA_IMPLEMENTATION #include diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index 2b13c0b0..0eb2159d 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -552,10 +552,10 @@ void ext::vulkan::Device::initialize() { VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Program"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 2, 0); + appInfo.applicationVersion = VK_MAKE_VERSION(1, 3, 0); appInfo.pEngineName = "Engine"; - appInfo.engineVersion = VK_MAKE_VERSION(1, 2, 0); - appInfo.apiVersion = VK_API_VERSION_1_2; + appInfo.engineVersion = VK_MAKE_VERSION(1, 3, 0); + appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; @@ -937,7 +937,7 @@ void ext::vulkan::Device::initialize() { { /* VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2; + allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_3; allocatorInfo.physicalDevice = physicalDevice; allocatorInfo.instance = instance; allocatorInfo.device = logicalDevice; @@ -950,7 +950,7 @@ void ext::vulkan::Device::initialize() { vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2; + allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_3; allocatorInfo.physicalDevice = physicalDevice; allocatorInfo.instance = instance; allocatorInfo.device = logicalDevice; diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index 272489f2..5e1357a3 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -141,7 +141,7 @@ bool ext::vulkan::Texture::generated() const { return view != VK_NULL_HANDLE; } void ext::vulkan::Texture::destroy() { - if ( !device ) return; + if ( !device || !device->logicalDevice ) return; // device->logicalDevice should never be null, but it happens, somehow if ( view != VK_NULL_HANDLE ) { vkDestroyImageView(device->logicalDevice, view, nullptr); diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index e3d79d6b..172fe210 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -384,7 +384,7 @@ void ext::vulkan::destroy() { */ for ( auto& renderMode : renderModes ) { - if ( !renderMode ) continue; + if ( !renderMode || !renderMode->device ) continue; renderMode->destroy(); // delete renderMode; renderMode = NULL; diff --git a/engine/src/utils/hook/hook.cpp b/engine/src/utils/hook/hook.cpp index a3f1f08d..2d178a05 100644 --- a/engine/src/utils/hook/hook.cpp +++ b/engine/src/utils/hook/hook.cpp @@ -21,6 +21,9 @@ void uf::Hooks::removeHook( const uf::Hooks::name_t& name, size_t uid ) { } } } +void uf::Hooks::removeHooks() { + this->m_container.clear(); +} /* uf::Hooks::return_t uf::Hooks::call( const uf::Hooks::name_t& name ) { pod::Hook::userdata_t payload{}; diff --git a/engine/src/utils/io/file.cpp b/engine/src/utils/io/file.cpp index e81c8d47..40cb1fee 100644 --- a/engine/src/utils/io/file.cpp +++ b/engine/src/utils/io/file.cpp @@ -77,14 +77,14 @@ uf::stl::string UF_API uf::io::sanitize( const uf::stl::string& str, const uf::s // flatten all "/./" { uf::stl::string tmp; - while ( path != (tmp = uf::string::replace(path, "/./", "/")) ) { + while ( path != (tmp = uf::string::replace(path, "/\\/\\.\\//", "/")) ) { path = tmp; } } // flatten all "//" { uf::stl::string tmp; - while ( path != (tmp = uf::string::replace(path, "//", "/")) ) { + while ( path != (tmp = uf::string::replace(path, "/\\/\\//", "/")) ) { path = tmp; } } @@ -151,10 +151,16 @@ size_t UF_API uf::io::write( const uf::stl::string& filename, const void* buffer // indirection for different compression formats, currently only using zlib's gzFile shit uf::stl::vector UF_API uf::io::decompress( const uf::stl::string& filename ) { - return ext::zlib::decompressFromFile( filename ); + uf::stl::string extension = uf::io::extension( filename ); + if ( extension == "gz" ) return ext::zlib::decompressFromFile( filename ); + UF_MSG_ERROR("unsupported compression format requested: " << extension); + return {}; } size_t UF_API uf::io::compress( const uf::stl::string& filename, const void* buffer, size_t size ) { - return ext::zlib::compressToFile( filename, buffer, size ); + uf::stl::string extension = uf::io::extension( filename ); + if ( extension == "gz" ) return ext::zlib::compressToFile( filename, buffer, size ); + UF_MSG_ERROR("unsupported compression format requested: " << extension); + return 0; } uf::stl::string UF_API uf::io::hash( const uf::stl::string& filename ) { diff --git a/engine/src/utils/io/iostream.cpp b/engine/src/utils/io/iostream.cpp index 758b6e10..f9b567c2 100644 --- a/engine/src/utils/io/iostream.cpp +++ b/engine/src/utils/io/iostream.cpp @@ -159,7 +159,9 @@ char UF_API_CALL uf::IoStream::readChar(const bool& loop) { if ( !ext::ncurses.initialized() ) this->initialize(); #endif if ( !uf::IoStream::ncurses ) { - return std::cin.get(); + auto ch = std::cin.get(); + ::info.input.history.push_back( std::to_string(ch) ); + return ch; } #if UF_USE_NCURSES while ( loop ) { @@ -167,8 +169,8 @@ char UF_API_CALL uf::IoStream::readChar(const bool& loop) { if ( ::info.character == ERR ) continue; if ( ::info.character > 0 && ::info.character < 128 ) return ::info.character; } - return 0; #endif + return 0; } uf::stl::string UF_API_CALL uf::IoStream::readString(const bool& loop) { #if UF_USE_NCURSES @@ -178,6 +180,7 @@ uf::stl::string UF_API_CALL uf::IoStream::readString(const bool& loop) { if ( !uf::IoStream::ncurses ) { uf::stl::string in; std::getline(std::cin, in); + ::info.input.history.push_back( in ); return in; } #if UF_USE_NCURSES @@ -259,8 +262,8 @@ uf::stl::string UF_API_CALL uf::IoStream::readString(const bool& loop) { } uf::iostream << "\n"; ::info.input.history.push_back(::info.input.buffer); - return ::info.input.buffer; #endif + return ::info.input.buffer; } uf::String UF_API_CALL uf::IoStream::readUString(const bool& loop) { #if UF_USE_NCURSES @@ -270,6 +273,7 @@ uf::String UF_API_CALL uf::IoStream::readUString(const bool& loop) { if ( !uf::IoStream::ncurses ) { uf::stl::string in; std::getline(std::cin, in); + ::info.input.history.push_back( in ); return in; } #if UF_USE_NCURSES @@ -377,8 +381,8 @@ uf::String UF_API_CALL uf::IoStream::readUString(const bool& loop) { } uf::iostream << "\n"; ::info.input.history.push_back(::info.input.buffer); - return ::info.input.buffer; #endif + return ::info.input.buffer; } char UF_API_CALL uf::IoStream::writeChar( char ch ) { #if UF_USE_NCURSES @@ -398,13 +402,14 @@ char UF_API_CALL uf::IoStream::writeChar( char ch ) { if ( !uf::IoStream::ncurses ) { if ( ch == '\n' ) std::cout << std::endl; else std::cout << ch; + ::info.input.history.push_back( std::to_string(ch) ); return ch; } #if UF_USE_NCURSES ext::ncurses.addChar(ch); ext::ncurses.refresh(); - return ch; #endif + return ch; } const uf::stl::string& UF_API_CALL uf::IoStream::writeString( const uf::stl::string& str ) { #if UF_USE_NCURSES @@ -425,13 +430,14 @@ const uf::stl::string& UF_API_CALL uf::IoStream::writeString( const uf::stl::str if ( !uf::IoStream::ncurses ) { if ( str == "\n" ) std::cout << std::endl; else std::cout << str; + ::info.input.history.push_back( str ); return str; } #if UF_USE_NCURSES ext::ncurses.addStr(str.c_str()); ext::ncurses.refresh(); - return str; #endif + return str; } const uf::String& UF_API_CALL uf::IoStream::writeUString( const uf::String& str ) { #if UF_USE_NCURSES @@ -450,13 +456,14 @@ const uf::String& UF_API_CALL uf::IoStream::writeUString( const uf::String& str */ if ( !uf::IoStream::ncurses ) { std::cout << (const char*) str.getString().c_str(); + ::info.input.history.push_back( str ); return str; } #if UF_USE_NCURSES ext::ncurses.addStr((const char*) str.getString().c_str()); ext::ncurses.refresh(); - return str; #endif + return str; } void UF_API_CALL uf::IoStream::operator>> (bool& val) { diff --git a/engine/src/utils/memory/pool.cpp b/engine/src/utils/memory/pool.cpp index e9399a15..b8010517 100644 --- a/engine/src/utils/memory/pool.cpp +++ b/engine/src/utils/memory/pool.cpp @@ -68,12 +68,13 @@ void uf::memoryPool::initialize( pod::MemoryPool& pool, size_t size ) { pool.size = size; } void uf::memoryPool::destroy( pod::MemoryPool& pool ) { - if ( uf::memoryPool::size( pool ) <= 0 ) return; + if ( uf::memoryPool::size( pool ) <= 0 ) goto CLEAR; if ( uf::memoryPool::subPool && &pool != &uf::memoryPool::global.data() ) { uf::memoryPool::global.free( pool.memory ); } else { uf::allocator::free_m(pool.memory); } +CLEAR: pool.size = 0; pool.memory = NULL; } @@ -243,6 +244,9 @@ bool uf::memoryPool::exists( pod::MemoryPool& pool, void* pointer, size_t size ) #endif } bool uf::memoryPool::free( pod::MemoryPool& pool, void* pointer, size_t size ) { + // skip freeing, we're already a deallocated pool + // this comes up because of how backasswards C++ static initialization/destruction order is + if ( !pool.memory ) return false; // passed a NULL, for some reason if ( !pointer ) return false; #if UF_MEMORYPOOL_MUTEX diff --git a/engine/src/utils/serialize/serializer.cpp b/engine/src/utils/serialize/serializer.cpp index 9315ec8d..6eca0c62 100644 --- a/engine/src/utils/serialize/serializer.cpp +++ b/engine/src/utils/serialize/serializer.cpp @@ -9,6 +9,12 @@ #include #include +#define UF_SERIALIZER_IMPLICIT_LOAD 1 +#define UF_SERIALIZER_AUTO_CONVERT 1 + +//#define UF_MSG_DEBUG_(...) UF_MSG_DEBUG(__VA_ARGS__) +#define UF_MSG_DEBUG_(...) + uf::Serializer::Serializer( const uf::stl::string& str ) { //: sol::table(ext::lua::state, sol::create) { this->deserialize(str); } @@ -31,14 +37,37 @@ uf::Serializer::output_t uf::Serializer::serialize( bool pretty ) const { uf::Serializer::output_t uf::Serializer::serialize( const ext::json::EncodingSettings& settings ) const { return ext::json::encode( *this, settings ); } -void uf::Serializer::deserialize( const uf::stl::string& str ) { +void uf::Serializer::deserialize( const uf::stl::string& str, const ext::json::DecodingSettings& settings ) { if ( str == "" ) return; - ext::json::decode( *this, str ); + ext::json::decode( *this, str, settings ); +} + +uf::stl::string uf::Serializer::resolveFilename( const uf::stl::string& filename, 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 ( ext::json::PREFERRED_COMPRESSION != "" ) { + _filename = _filename + "." + ext::json::PREFERRED_COMPRESSION; + } + if ( !compareTimes ) return _filename; + + if ( uf::io::exists( _filename ) ) { + bool should = !uf::io::exists( filename ); // implicit load if our requested filename doesnt exist anyways, but our preferred source does + if ( !should ) should = uf::io::mtime( _filename ) > uf::io::mtime( filename ); // implicit load the preferred source is newer than the requested filename + if ( should ) return _filename; + } + return filename; } bool uf::Serializer::readFromFile( const uf::stl::string& filename, const uf::stl::string& hash ) { - uf::String string; -// uf::stl::string filename = _filename + ( uf::io::exists(_filename + ".gz") ? ".gz" : "" ); +#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 ( _filename != filename ) return readFromFile( _filename, hash ); + } +#endif bool exists = uf::io::exists( filename ); if ( !exists ) { UF_MSG_ERROR("Failed to read JSON file `" << filename << "`: does not exist"); @@ -48,19 +77,80 @@ bool uf::Serializer::readFromFile( const uf::stl::string& filename, const uf::st if ( buffer.empty() ) { UF_MSG_ERROR("Failed to read JSON file `" << filename << "`: empty file or hash mismatch"); return false; - } + } + this->m_filename = filename; + +/* + uf::String string; auto& str = string.getString(); str.reserve(buffer.size()); str.assign(buffer.begin(), buffer.end()); +*/ + uf::stl::string string{ buffer.begin(), buffer.end() }; + + ext::json::DecodingSettings settings; + if ( uf::string::matched( filename, "/.bson/" ) ) settings.encoding = "bson"; + else if ( uf::string::matched( filename, "/.cbor/" ) ) settings.encoding = "cbor"; + this->deserialize( string, settings ); + +#if UF_SERIALIZER_AUTO_CONVERT + if ( uf::string::matched( filename, "/.json$/" ) ) { + if ( ext::json::PREFERRED_COMPRESSION != "" || (ext::json::PREFERRED_ENCODING != "" || ext::json::PREFERRED_ENCODING != "json") ) { + ext::json::EncodingSettings _settings; + _settings.encoding = ext::json::PREFERRED_ENCODING; + _settings.compression = ext::json::PREFERRED_COMPRESSION; + uf::stl::string _filename = filename; + + if ( ext::json::PREFERRED_ENCODING != "" && ext::json::PREFERRED_ENCODING != "json" ) { + _settings.encoding = ext::json::PREFERRED_ENCODING; + _filename = uf::string::replace( _filename, "/.json$/", "." + ext::json::PREFERRED_ENCODING ); + } + if ( ext::json::PREFERRED_COMPRESSION != "" ) { + _settings.compression = ext::json::PREFERRED_COMPRESSION; + _filename = _filename + "." + ext::json::PREFERRED_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 ) { + UF_MSG_DEBUG_("Auto converting: " << _filename ); + writeToFile( _filename, _settings ); + } + } + } +#endif - this->deserialize(string); return true; } bool uf::Serializer::writeToFile( const uf::stl::string& filename, const ext::json::EncodingSettings& settings ) const { + uf::stl::string output = filename; + + if ( settings.encoding != "" && settings.encoding != "json" ) + output = uf::string::replace( output, "/.json/", "." + settings.encoding ); + if ( settings.compression != "" && !uf::string::matched( output, "/."+settings.compression+"/" ) ) + output += "." + settings.compression; + uf::stl::string buffer = this->serialize( settings ); - size_t written = uf::io::write( filename + ( settings.compress ? ".gz" : "" ) , buffer.c_str(), buffer.size() ); - if ( !written ) UF_MSG_ERROR("Failed to write JSON file `" << filename << "`"); + size_t written = uf::io::write( output, buffer.c_str(), 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 ) { + uf::stl::string _filename = output; + auto _settings = settings; + + if ( ext::json::PREFERRED_ENCODING != "" && ext::json::PREFERRED_ENCODING != "json" ) { + _settings.encoding = ext::json::PREFERRED_ENCODING; + // _filename = uf::string::replace( _filename, "/.json$/", "." + ext::json::PREFERRED_ENCODING ); + } + if ( ext::json::PREFERRED_COMPRESSION != "" ) { + _settings.compression = ext::json::PREFERRED_COMPRESSION; + // _filename = _filename + "." + ext::json::PREFERRED_COMPRESSION; + } + writeToFile( _filename, _settings ); + } +#endif + + if ( !written ) UF_MSG_ERROR("Failed to write JSON file: " << output); return written; } diff --git a/engine/src/utils/string/ext.cpp b/engine/src/utils/string/ext.cpp index 11ce1a8c..a2f5abcf 100644 --- a/engine/src/utils/string/ext.cpp +++ b/engine/src/utils/string/ext.cpp @@ -18,8 +18,7 @@ bool UF_API uf::string::match( const uf::stl::string& str, const uf::stl::string bool UF_API uf::string::isRegex( const uf::stl::string& str ) { return str.front() == '/' && str.back() == '/'; } -uf::stl::vector UF_API uf::string::matches( const uf::stl::string& str, const uf::stl::string& r ) { - +uf::stl::vector UF_API uf::string::match( const uf::stl::string& str, const uf::stl::string& r ) { std::regex regex(r.substr(1,r.length()-2)); std::smatch match; uf::stl::vector matches; @@ -31,6 +30,11 @@ uf::stl::vector UF_API uf::string::matches( const uf::stl::stri return matches; } +bool UF_API uf::string::matched( const uf::stl::string& str, const uf::stl::string& r ) { + std::regex regex(r.substr(1,r.length()-2)); + std::smatch match; + return std::regex_search( str, match, regex ); +} uf::stl::string UF_API uf::string::lowercase( const uf::stl::string& str ) { uf::stl::string lower = str; @@ -78,9 +82,15 @@ uf::stl::string UF_API uf::string::join( const uf::stl::vector& */ uf::stl::string UF_API uf::string::replace( const uf::stl::string& string, const uf::stl::string& search, const uf::stl::string& replace ) { uf::stl::string result = string; - size_t start_pos = string.find(search); - if( start_pos == uf::stl::string::npos ) return result; - result.replace(start_pos, search.length(), replace); + + if ( uf::string::isRegex(search) ) { + std::regex regex(search.substr(1,search.length()-2)); + result = std::regex_replace( string, regex, replace ); + } else { + size_t start_pos = string.find(search); + if( start_pos == uf::stl::string::npos ) return result; + result.replace(start_pos, search.length(), replace); + } return result; } bool UF_API uf::string::contains( const uf::stl::string& string, const uf::stl::string& search ) { diff --git a/ext/behaviors/baking/behavior.cpp b/ext/behaviors/baking/behavior.cpp index be5a6124..d24caa2e 100644 --- a/ext/behaviors/baking/behavior.cpp +++ b/ext/behaviors/baking/behavior.cpp @@ -156,7 +156,10 @@ SAVE: { payload["uid"] = this->getUid(); uf::scene::getCurrentScene().queueHook("system:Destroy", payload); - if ( metadata.trigger.quits ) uf::scene::getCurrentScene().queueHook("system:Quit", payload); + if ( metadata.trigger.quits ) { + payload["message"] = "Termination after lightmap baking requested."; + uf::scene::getCurrentScene().queueHook("system:Quit", payload); + } #endif return; } diff --git a/ext/behaviors/player/behavior.cpp b/ext/behaviors/player/behavior.cpp index 5bc86dbd..e144ec8e 100644 --- a/ext/behaviors/player/behavior.cpp +++ b/ext/behaviors/player/behavior.cpp @@ -79,7 +79,9 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) { // Rotate Camera this->addHook( "window:Mouse.Moved", [&](pod::payloads::windowMouseMoved& payload ){ pod::Vector2 relta = { (float) metadata.mouse.sensitivity.x * payload.mouse.delta.x / payload.window.size.x, (float) metadata.mouse.sensitivity.y * payload.mouse.delta.y / payload.window.size.y }; + // pod::Vector2 relta = { (float) metadata.mouse.sensitivity.x * payload.mouse.delta.x * uf::physics::time::delta, (float) metadata.mouse.sensitivity.y * payload.mouse.delta.y * uf::physics::time::delta }; if ( (payload.mouse.delta.x == 0 && payload.mouse.delta.y == 0) || !metadata.system.control ) return; + // relta *= uf::physics::time::delta; if ( payload.mouse.delta.x != 0 ) { if ( metadata.camera.invert.x ) relta.x *= -1; @@ -93,11 +95,8 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) { if ( metadata.camera.invert.y ) relta.y *= -1; metadata.camera.limit.current.y += relta.y; if ( metadata.camera.limit.current.y != metadata.camera.limit.current.y || ( metadata.camera.limit.current.y < metadata.camera.limit.max.y && metadata.camera.limit.current.y > metadata.camera.limit.min.y ) ) { - // if ( collider.body && !collider.shared ) { - // uf::physics::impl::applyRotation( collider, cameraTransform.right, relta.y ); - // } else { + // if ( collider.body && !collider.shared ) uf::physics::impl::applyRotation( collider, cameraTransform.right, relta.y ); else uf::transform::rotate( cameraTransform, cameraTransform.right, relta.y ); - // } } else metadata.camera.limit.current.y -= relta.y; } camera.update(true); diff --git a/ext/behaviors/scene/behavior.cpp b/ext/behaviors/scene/behavior.cpp index f1b81df9..31845005 100644 --- a/ext/behaviors/scene/behavior.cpp +++ b/ext/behaviors/scene/behavior.cpp @@ -566,6 +566,7 @@ void ext::ExtSceneBehavior::Metadata::serialize( uf::Object& self, uf::Serialize serializer["light"]["exposure"] = /*this->*/light.exposure; serializer["light"]["gamma"] = /*this->*/light.gamma; serializer["light"]["brightnessThreshold"] = /*this->*/light.brightnessThreshold; + serializer["light"]["useLightmaps"] = /*this->*/light.useLightmaps; serializer["light"]["fog"]["color"] = uf::vector::encode( /*this->*/fog.color ); serializer["light"]["fog"]["step scale"] = /*this->*/fog.stepScale; @@ -600,6 +601,7 @@ void ext::ExtSceneBehavior::Metadata::deserialize( uf::Object& self, uf::Seriali /*this->*/light.exposure = serializer["light"]["exposure"].as(1.0f); /*this->*/light.gamma = serializer["light"]["gamma"].as(2.2f); /*this->*/light.brightnessThreshold = serializer["light"]["brightnessThreshold"].as(ext::config["engine"]["scenes"]["bloom"]["brightnessThreshold"].as(1.0f)); + /*this->*/light.useLightmaps = ext::config["engine"]["scenes"]["lights"]["useLightmaps"].as(true); /*this->*/bloom.scale = serializer["bloom"]["scale"].as(ext::config["engine"]["scenes"]["bloom"]["scale"].as(bloom.scale)); /*this->*/bloom.strength = serializer["bloom"]["strength"].as(ext::config["engine"]["scenes"]["bloom"]["strength"].as(bloom.strength)); @@ -740,7 +742,7 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const uf::stl::string alignas(4) uint32_t shadowSamples; alignas(4) uint32_t indexSkybox; - alignas(4) uint32_t padding1; + alignas(4) uint32_t useLightmaps; alignas(4) uint32_t padding2; alignas(4) uint32_t padding3; }; @@ -839,6 +841,8 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const uf::stl::string uniforms.shadowSamples = std::min( 0, metadata.shadow.samples ); uniforms.indexSkybox = indexSkybox; + // use sample lightmaps during deferred pass + uniforms.useLightmaps = metadata.light.useLightmaps; } for ( auto* blitter : blitters ) { diff --git a/ext/behaviors/scene/behavior.h b/ext/behaviors/scene/behavior.h index 809114c0..71977537 100644 --- a/ext/behaviors/scene/behavior.h +++ b/ext/behaviors/scene/behavior.h @@ -25,6 +25,7 @@ namespace ext { float exposure = 1.0f; float gamma = 1.0f; float brightnessThreshold = 1.0f; + bool useLightmaps = true; } light; struct { float scale = 1.0f; diff --git a/ext/main.cpp b/ext/main.cpp index 4d386a57..32807026 100644 --- a/ext/main.cpp +++ b/ext/main.cpp @@ -95,7 +95,7 @@ namespace { } void EXT_API ext::load() { - ext::config = uf::io::readAsString(uf::io::root+"/config.json"); + ext::config.readFromFile(uf::io::root+"/config.json"); } void EXT_API ext::initialize() { @@ -555,6 +555,9 @@ void EXT_API ext::initialize() { uf::scene::unloadScene(); }); uf::hooks.addHook( "system:Quit", [&](ext::json::Value& json){ + if ( json["message"].is() ) { + UF_MSG_DEBUG( json["message"].as() ); + } ext::ready = false; }); } @@ -732,6 +735,9 @@ void EXT_API ext::terminate() { ext::openvr::terminate(); } #endif + { + uf::hooks.removeHooks(); + } #if UF_USE_LUA { ext::lua::terminate(); @@ -744,7 +750,7 @@ void EXT_API ext::terminate() { uf::scene::destroy(); } - /* Garbage collection */ if ( false ) { + /* Garbage collection */ if ( false ) { // segfaults, for some reason size_t collected = uf::instantiator::collect( ::config.engine.gc.mode ); if ( ::config.engine.gc.announce && collected > 0 ) UF_MSG_DEBUG("GC collected " << (int) collected << " unused entities"); }