Commit for 2021.12.25 20-34-43.7z

This commit is contained in:
mrq 2021-12-25 20:34:00 -06:00
parent e16f3d77d7
commit b208cda203
36 changed files with 1003 additions and 607 deletions

View File

@ -1,7 +1,7 @@
{
"engine": {
"scenes": {
"start": "SH2_McDonalds",
"start": "SS2",
"meshes": { "interleave": false },
"matrix": { "reverseInfinite": true },
"lights": {
@ -72,9 +72,9 @@
"deferred alias output to swapchain": false,
"vsync": true,
"hdr": false,
"vxgi": true,
"deferred sampling": true,
"culling": true,
"vxgi": false,
"deferred sampling": false,
"culling": false,
"bloom": true
},
"formats": {
@ -235,7 +235,8 @@
},
"cursor" : {
"visible" : true,
"center" : false
"center" : false,
"sensitivity": [ 2, 2 ]
},
"mode" : "windowed", // fullscreen, borderless, windowed
"icon" : "./data/textures/icon.png",

View File

@ -0,0 +1,16 @@
{
"name": "Craeture",
"behaviors": [ "CraetureBehavior" ],
"assets": [
"./textures/craeture.png"
],
"transform": {
"position": [18.3785, 2.41477, 0.859857],
"scale": [8, 8, 8]
},
"metadata": {
"model": {
"cull mode": "none"
}
}
}

View File

@ -3,7 +3,8 @@
"assets": [
// { "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/graph.json.gz", "delay": 0, "single threaded": false, "category": "models" },
{ "filename": "./craeture.json", "delay": 1 }
// { "filename": "./models/msci.glb", "delay": 0, "single threaded": false, "category": "models" }
// { "filename": "./models/msci/graph.json", "delay": 0, "single threaded": false, "category": "models" }

View File

@ -11,7 +11,7 @@ local transform = ent:getComponent("Transform")
local metadata = ent:getComponent("Metadata")
local physics = ent:getComponent("Physics")
local bullet = ent:getComponent("Bullet")
local velocty = physics:linearVelocity()
-- local velocty = physics:linearVelocity()
local speed = metadata["speed"] or 1.0
local starting = transform.position + Vector3f(0,0,0)
local ending = transform.position + Vector3f( metadata["delta"][1], metadata["delta"][2], metadata["delta"][3] )

View File

@ -66,11 +66,11 @@ void client::initialize() {
}
/* Initialize hooks */ {
uf::hooks.addHook( "window:Mouse.CursorVisibility", [&]( ext::json::Value& json ){
client::window.setCursorVisible(json["state"].as<bool>());
client::window.setMouseGrabbed(!json["state"].as<bool>());
client::config["mouse"]["visible"] = json["state"].as<bool>();
client::config["window"]["mouse"]["center"] = !json["state"].as<bool>();
uf::hooks.addHook( "window:Mouse.CursorVisibility", [&]( pod::payloads::windowMouseCursorVisibility& payload ){
client::window.setCursorVisible(payload.mouse.visible);
client::window.setMouseGrabbed(!payload.mouse.visible);
client::config["mouse"]["visible"] = payload.mouse.visible;
client::config["window"]["mouse"]["center"] = !payload.mouse.visible;
});
uf::hooks.addHook( "window:Mouse.Lock", [&]( ext::json::Value& json ){
if ( client::window.hasFocus() ) {
@ -92,16 +92,15 @@ void client::initialize() {
client::window.setTitle(title);
}
} );
uf::hooks.addHook( "window:Resized", [&]( ext::json::Value& json ){
pod::Vector2i size = uf::vector::decode( json["window"]["size"], pod::Vector2i{} );
if ( size.x == uf::renderer::settings::width && size.y == uf::renderer::settings::height ) return;
uf::hooks.addHook( "window:Resized", [&]( pod::payloads::windowResized& payload ){
if ( payload.window.size.x == uf::renderer::settings::width && payload.window.size.y == uf::renderer::settings::height ) return;
if ( json["invoker"] != "os" ) client::window.setSize(size);
if ( payload.invoker != "os" ) client::window.setSize(payload.window.size);
// Update viewport
if ( !ext::json::isArray( client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"] ) ) {
float scale = client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].is<double>() ? client::config["engine"]["ext"]["vulkan"]["framebuffer"]["size"].as<float>() : 1;
uf::renderer::settings::width = size.x * scale;
uf::renderer::settings::height = size.y * scale;
uf::renderer::settings::width = payload.window.size.x * scale;
uf::renderer::settings::height = payload.window.size.y * scale;
}
uf::renderer::states::resized = true;
} );

View File

@ -12,14 +12,37 @@ namespace uf {
protected:
static uf::Asset masterAssetLoader;
public:
enum Type {
UNKNOWN,
IMAGE,
AUDIO,
LUA,
JSON,
GRAPH,
};
struct Payload {
uf::Asset::Type type = {};
uf::stl::string filename = "";
uf::stl::string mime = "";
uf::stl::string hash = "";
bool initialize = true;
bool monoThreaded = false;
size_t uid = 0;
};
static uf::Asset::Payload resolveToPayload( const uf::stl::string&, const uf::stl::string& = "" );
static bool isExpected( const uf::Asset::Payload&, uf::Asset::Type expected );
// URL or file path
void processQueue();
void cache( const uf::stl::string&, const uf::stl::string&, const uf::stl::string&, const uf::stl::string& );
void load( const uf::stl::string&, const uf::stl::string&, const uf::stl::string&, const uf::stl::string& );
void cache( const uf::stl::string&, const uf::Asset::Payload& );
void load( const uf::stl::string&, const uf::Asset::Payload& );
uf::stl::string cache( const uf::stl::string&, const uf::stl::string& = "", const uf::stl::string& = "" );
uf::stl::string load( const uf::stl::string&, const uf::stl::string& = "", const uf::stl::string& = "" );
uf::stl::string cache( const uf::Asset::Payload& );
uf::stl::string load( const uf::Asset::Payload& );
uf::stl::string getOriginal( const uf::stl::string& );
@ -37,7 +60,7 @@ namespace uf {
bool has( const uf::stl::string& url ) {
auto& container = this->getContainer<T>();
if ( container.empty() ) return false;
uf::stl::string extension = uf::io::extension( url );
uf::stl::string extension = uf::io::extension( url, -1 );
uf::Serializer& map = this->getComponent<uf::Serializer>();
if ( ext::json::isNull( map[extension] ) ) return false;
if ( ext::json::isNull( map[extension][url] ) ) return false;
@ -51,7 +74,7 @@ namespace uf {
}
template<typename T>
T& get( const uf::stl::string& url ) {
uf::stl::string extension = uf::io::extension( url );
uf::stl::string extension = uf::io::extension( url, -1 );
uf::Serializer& map = this->getComponent<uf::Serializer>();
size_t index = map[extension][url]["index"].as<size_t>(0);
return this->get<T>(index);
@ -59,7 +82,7 @@ namespace uf {
template<typename T>
T& add( const uf::stl::string& url, const T& copy ) {
uf::stl::string extension = uf::io::extension( url );
uf::stl::string extension = uf::io::extension( url, -1 );
uf::Serializer& map = this->getComponent<uf::Serializer>();
auto& container = this->getContainer<T>();
if ( !ext::json::isNull( map[extension][url]["index"] ) ) return this->get<T>(url);
@ -69,7 +92,7 @@ namespace uf {
}
template<typename T>
T& add( const uf::stl::string& url, T&& move ) {
uf::stl::string extension = uf::io::extension( url );
uf::stl::string extension = uf::io::extension( url, -1 );
uf::Serializer& map = this->getComponent<uf::Serializer>();
auto& container = this->getContainer<T>();
@ -84,7 +107,7 @@ namespace uf {
if ( !this->has<T>( url ) ) return;
auto& container = this->getContainer<T>();
uf::stl::string extension = uf::io::extension( url );
uf::stl::string extension = uf::io::extension( url, -1 );
uf::Serializer& map = this->getComponent<uf::Serializer>();
std::size_t index = map[extension][url]["index"].as<size_t>();
// container.erase( container.begin() + index );
@ -101,4 +124,10 @@ namespace uf {
return;
}
};
}
namespace pod {
namespace payloads {
typedef uf::Asset::Payload assetLoad;
}
}

View File

@ -126,10 +126,12 @@ namespace uf {
pod::Matrix4f UF_API local( pod::Graph&, int32_t );
pod::Matrix4f UF_API matrix( pod::Graph&, int32_t );
void UF_API process( uf::Object& entity );
// void UF_API process( uf::Object& entity );
void UF_API initializeGraphics( pod::Graph& graph, uf::Object& entity );
void UF_API process( pod::Graph& graph );
void UF_API process( pod::Graph& graph, int32_t, uf::Object& parent );
void UF_API cleanup( pod::Graph& graph );
void UF_API reload( pod::Graph& );
void UF_API initialize( pod::Graph& graph );
void UF_API override( pod::Graph& );
@ -146,10 +148,12 @@ namespace uf {
void UF_API destroy();
pod::Graph UF_API load( const uf::stl::string&, const uf::Serializer& = ext::json::null() );
pod::Graph& UF_API convert( uf::Object&, bool = false );
void UF_API save( const pod::Graph&, const uf::stl::string& );
uf::stl::string UF_API print( const pod::Graph& graph );
uf::Serializer UF_API stats( const pod::Graph& graph );
void UF_API reload();
}
}

View File

@ -8,6 +8,7 @@
#include <uf/utils/memory/string.h>
#include <uf/utils/memory/vector.h>
#include <uf/utils/memory/unordered_map.h>
#include <uf/utils/hook/hook.h>
namespace uf {
namespace ObjectBehavior {
@ -17,8 +18,10 @@ namespace uf {
UF_BEHAVIOR_DEFINE_METADATA(
struct Queued {
uf::stl::string name;
ext::json::Value payload;
double timeout;
pod::Hook::userdata_t userdata;
ext::json::Value json;
double timeout = 0;
int_fast8_t type = 0;
};
struct {
size_t mtime = 0;

View File

@ -35,10 +35,15 @@ namespace uf {
uf::stl::string formatHookName( const uf::stl::string& name );
static uf::stl::string formatHookName( const uf::stl::string& name, size_t uid, bool fetch = true );
void queueHook( const uf::stl::string&, const ext::json::Value& = ext::json::null(), double = 0 );
void queueHook( const uf::stl::string&, const ext::json::Value& = ext::json::null(), float = 0 );
template<typename T>
void queueHook( const uf::stl::string&, const T&, float = 0 );
uf::Hooks::return_t callHook( const uf::stl::string& );
uf::Hooks::return_t callHook( const uf::stl::string&, const ext::json::Value& );
uf::Hooks::return_t callHook( const uf::stl::string&, const uf::Serializer& );
uf::Hooks::return_t callHook( const uf::stl::string&, const pod::Hook::userdata_t& );
template<typename T> size_t addHook( const uf::stl::string& name, T function );
template<typename T> uf::Hooks::return_t callHook( const uf::stl::string& name, const T& payload );

View File

@ -34,4 +34,18 @@ uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name, const T&
uf::Userdata payload;
payload.create<T>(p);
return uf::hooks.call( this->formatHookName( name ), payload );
}
template<typename T>
void uf::Object::queueHook( const uf::stl::string& name, const T& p, float d ) {
if ( !uf::Object::timer.running() ) uf::Object::timer.start();
double start = uf::Object::timer.elapsed().asDouble();
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
auto& queue = metadata.hooks.queue.emplace_back(uf::ObjectBehavior::Metadata::Queued{
.name = name,
.timeout = start + d,
.type = 1,
});
queue.userdata.create<T>(p);
}

View File

@ -13,6 +13,8 @@
#include <uf/utils/serialize/serializer.h>
#include <type_traits>
#include "payloads.h"
#define UF_HOOK_POINTERED_USERDATA 1
namespace pod {

View File

@ -0,0 +1,33 @@
#pragma once
#include <uf/config.h>
#include <uf/utils/memory/string.h>
#include <uf/utils/math/vector.h>
namespace pod {
namespace payloads {
struct menuOpen {
uf::stl::string name = "";
};
struct worldCollision {
size_t hit = 0;
float depth = -1;
};
struct vrInputDigital {
uf::stl::string type = "";
int_fast8_t size{};
int_fast8_t state{};
};
struct vrHaptics {
float delay;
float duration;
float frequency;
float amplitude;
int_fast8_t side;
};
}
}

View File

@ -20,12 +20,11 @@ namespace uf {
typedef pod::Math::num_t num_t;
namespace time {
extern UF_API uf::Timer<> timer;
extern UF_API uf::physics::num_t current;
extern UF_API uf::physics::num_t previous;
extern UF_API uf::physics::num_t delta;
extern UF_API uf::physics::num_t clamp;
extern UF_API double current;
extern UF_API double previous;
extern UF_API float delta;
extern UF_API float clamp;
}
void UF_API initialize();
void UF_API tick();

View File

@ -2,8 +2,7 @@
#include <uf/config.h>
#include <uf/utils/memory/string.h>
#include <uf/utils/math/vector.h>
#include <uf/utils/hook/payloads.h>
namespace pod {
namespace payloads {
@ -44,16 +43,16 @@ namespace pod {
};
struct windowMouseWheel : public windowEvent {
struct {
pod::Vector2ui position{};
float delta = 0;
pod::Vector2ui position{};
float delta = 0;
} mouse;
};
struct windowMouseClick : public windowEvent {
struct {
pod::Vector2i position = {};
pod::Vector2i delta = {};
pod::Vector2i position = {};
pod::Vector2i delta = {};
uf::stl::string button = "";
int_fast8_t state = 0;
int_fast8_t state = 0;
} mouse;
};
struct windowMouseMoved : public windowResized {
@ -63,5 +62,11 @@ namespace pod {
int_fast8_t state{};
} mouse;
};
struct windowMouseCursorVisibility : public windowEvent {
struct {
bool visible;
} mouse;
};
}
}

View File

@ -35,30 +35,39 @@ namespace {
uf::io::write( "./" + filename, http.response );
return true;
}
uf::stl::string hashed( const uf::stl::string& uri ) {
return uf::string::sha256(uri);
/*
std::size_t value = std::hash<uf::stl::string>{}( uri );
uf::stl::stringstream stream;
stream << std::hex << value;
return stream.str();
*/
}
std::mutex mutex;
uint64_t uid = 0;
struct Job {
uf::stl::string callback;
uf::stl::string type;
uf::stl::string uri;
uf::stl::string hash;
uf::stl::string category;
uf::Asset::Payload payload;
};
}
uf::Asset uf::Asset::masterAssetLoader;
void uf::Asset::processQueue() {
uf::thread::batchWorker( [&](){
mutex.lock();
auto jobs = std::move(this->getComponent<std::queue<Job>>());
while ( !jobs.empty() ) {
auto job = jobs.front();
jobs.pop();
uf::stl::string callback = job.callback;
uf::stl::string type = job.type;
uf::Asset::Payload payload = job.payload;
if ( payload.filename == "" || callback == "" ) continue;
uf::stl::string filename = type == "cache" ? this->cache(payload) : this->load(payload);
if ( callback != "" && filename != "" ) uf::hooks.call(callback, payload);
}
mutex.unlock();
});
#if 0
auto& jobs = this->getComponent<std::queue<Job>>();
mutex.lock();
@ -69,12 +78,12 @@ void uf::Asset::processQueue() {
if ( job.uri == "" || job.callback == "" ) continue;
tasks.emplace_back([=](){
auto filename = job.type == "cache" ? this->cache(job.uri, job.hash, job.category) : this->load(job.uri, job.hash, job.category);
auto filename = job.type == "cache" ? this->cache(job.uri, job.hash, job.mime) : this->load(job.uri, job.hash, job.mime);
if ( filename == "" ) return;
uf::Serializer payload;
payload["filename"] = filename;
payload["hash"] = job.hash;
payload["category"] = job.category;
payload["mime"] = job.mime;
uf::hooks.call(job.callback, payload);
});
}
@ -90,21 +99,21 @@ void uf::Asset::processQueue() {
uf::stl::string type = job.type;
uf::stl::string uri = job.uri;
uf::stl::string hash = job.hash;
uf::stl::string category = job.category;
uf::stl::string mime = job.mime;
if ( uri == "" || callback == "" ) {
continue;
}
uf::stl::string filename = type == "cache" ? this->cache(uri, hash, category) : this->load(uri, hash, category);
uf::stl::string filename = type == "cache" ? this->cache(uri, hash, mime) : this->load(uri, hash, mime);
if ( callback != "" && filename != "" ) {
uf::Serializer payload;
payload["filename"] = filename;
payload["hash"] = hash;
payload["category"] = category;
payload["mime"] = mime;
uf::hooks.call(callback, payload);
}
}
mutex.unlock();
#elif 1
#elif 0
uf::thread::batchWorker( [&](){
mutex.lock();
auto jobs = std::move(this->getComponent<std::queue<Job>>());
@ -115,16 +124,16 @@ void uf::Asset::processQueue() {
uf::stl::string type = job.type;
uf::stl::string uri = job.uri;
uf::stl::string hash = job.hash;
uf::stl::string category = job.category;
uf::stl::string mime = job.mime;
if ( uri == "" || callback == "" ) {
continue;
}
uf::stl::string filename = type == "cache" ? this->cache(uri, hash, category) : this->load(uri, hash, category);
uf::stl::string filename = type == "cache" ? this->cache(uri, hash, mime) : this->load(uri, hash, mime);
if ( callback != "" && filename != "" ) {
uf::Serializer payload;
payload["filename"] = filename;
payload["hash"] = hash;
payload["category"] = category;
payload["mime"] = mime;
uf::hooks.call(callback, payload);
}
}
@ -141,12 +150,12 @@ void uf::Asset::processQueue() {
if ( job.uri == "" || job.callback == "" ) continue;
tasks.emplace_back([=](){
auto filename = job.type == "cache" ? this->cache(job.uri, job.hash, job.category) : this->load(job.uri, job.hash, job.category);
auto filename = job.type == "cache" ? this->cache(job.uri, job.hash, job.mime) : this->load(job.uri, job.hash, job.mime);
if ( filename == "" ) return;
uf::Serializer payload;
payload["filename"] = filename;
payload["hash"] = job.hash;
payload["category"] = job.category;
payload["mime"] = job.mime;
uf::hooks.call(job.callback, payload);
});
}
@ -155,26 +164,61 @@ void uf::Asset::processQueue() {
});
#endif
}
void uf::Asset::cache( const uf::stl::string& callback, const uf::stl::string& uri, const uf::stl::string& hash, const uf::stl::string& category ) {
void uf::Asset::cache( const uf::stl::string& callback, const uf::Asset::Payload& payload ) {
mutex.lock();
auto& jobs = this->getComponent<std::queue<Job>>();
jobs.push({ callback, "cache", uri, hash, category });
jobs.push({ callback, "cache", payload });
mutex.unlock();
}
void uf::Asset::load( const uf::stl::string& callback, const uf::stl::string& uri, const uf::stl::string& hash, const uf::stl::string& category ) {
void uf::Asset::load( const uf::stl::string& callback, const uf::Asset::Payload& payload ) {
mutex.lock();
auto& jobs = this->getComponent<std::queue<Job>>();
jobs.push({ callback, "load", uri, hash, category });
jobs.push({ callback, "load", payload });
mutex.unlock();
}
uf::stl::string uf::Asset::cache( const uf::stl::string& uri, const uf::stl::string& hash, const uf::stl::string& category ) {
uf::stl::string filename = uri;
uf::stl::string extension = uf::io::extension( uri );
if ( uri.substr(0,5) == "https" ) {
uf::stl::string hash = hashed( uri );
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::Asset::Payload payload;
static uf::stl::unordered_map<uf::stl::string,uf::Asset::Type> typemap = {
{ "jpg", uf::Asset::Type::IMAGE },
{ "jpeg", uf::Asset::Type::IMAGE },
{ "png", uf::Asset::Type::IMAGE },
{ "ogg", uf::Asset::Type::AUDIO },
{ "json", uf::Asset::Type::JSON },
{ "lua", uf::Asset::Type::LUA },
{ "mdl", uf::Asset::Type::GRAPH },
};
payload.filename = uri;
payload.mime = mime;
if ( typemap.count( extension ) == 1 ) payload.type = typemap[extension];
if ( basename == "graph.json" ) payload.type = uf::Asset::Type::GRAPH;
return payload;
}
bool uf::Asset::isExpected( const uf::Asset::Payload& payload, uf::Asset::Type expected ) {
if ( payload.filename == "" ) return false;
if ( payload.type != expected ) return false;
return true;
}
uf::stl::string uf::Asset::cache( const uf::Asset::Payload& payload ) {
uf::stl::string filename = payload.filename;
uf::stl::string extension = uf::io::extension( filename );
if ( filename.substr(0,5) == "https" ) {
uf::stl::string hash = uf::string::sha256( filename );
uf::stl::string cached = uf::io::root + "/cache/http/" + hash + "." + extension;
if ( !uf::io::exists( cached ) && !retrieve( uri, cached, hash ) ) {
UF_MSG_ERROR("Failed to preload `" + uri + "` (`" + cached + "`): HTTP error");
if ( !uf::io::exists( cached ) && !retrieve( filename, cached, hash ) ) {
UF_MSG_ERROR("Failed to preload `" + filename + "` (`" + cached + "`): HTTP error");
return "";
}
filename = cached;
@ -183,21 +227,22 @@ uf::stl::string uf::Asset::cache( const uf::stl::string& uri, const uf::stl::str
UF_MSG_ERROR("Failed to preload `" + filename + "`: Does not exist");
return "";
}
uf::stl::string actual = hash;
if ( hash != "" && (actual = uf::io::hash( filename )) != hash ) {
UF_MSG_ERROR("Failed to preload `" << filename << "`: Hash mismatch; expected " << hash << ", got " << actual);
uf::stl::string actual = payload.hash;
if ( payload.hash != "" && (actual = uf::io::hash( filename )) != payload.hash ) {
UF_MSG_ERROR("Failed to preload `" << filename << "`: Hash mismatch; expected " << payload.hash << ", got " << actual);
return "";
}
return filename;
}
uf::stl::string uf::Asset::load( const uf::stl::string& uri, const uf::stl::string& hash, const uf::stl::string& category ) {
uf::stl::string filename = uri;
uf::stl::string extension = uf::io::extension( uri );
if ( uri.substr(0,5) == "https" ) {
uf::stl::string hash = hashed( uri );
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", "" );
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;
if ( !uf::io::exists( cached ) && !retrieve( uri, cached, hash ) ) {
UF_MSG_ERROR("Failed to load `" + uri + "` (`" + cached + "`): HTTP error");
if ( !uf::io::exists( cached ) && !retrieve( payload.filename, cached, hash ) ) {
UF_MSG_ERROR("Failed to load `" + payload.filename + "` (`" + cached + "`): HTTP error");
return "";
}
filename = cached;
@ -206,59 +251,61 @@ uf::stl::string uf::Asset::load( const uf::stl::string& uri, const uf::stl::stri
UF_MSG_ERROR("Failed to load `" + filename + "`: Does not exist");
return "";
}
uf::stl::string actual = hash;
if ( hash != "" && (actual = uf::io::hash( filename )) != hash ) {
UF_MSG_ERROR("Failed to load `" << filename << "`: Hash mismatch; expected " << hash << ", got " << actual);
uf::stl::string actual = payload.hash;
if ( payload.hash != "" && (actual = uf::io::hash( filename )) != payload.hash ) {
UF_MSG_ERROR("Failed to load `" << filename << "`: Hash mismatch; expected " << payload.hash << ", got " << actual);
return "";
}
auto& map = this->getComponent<uf::Serializer>();
#define UF_ASSET_REGISTER(type)\
auto& container = this->getContainer<type>();\
if ( !ext::json::isNull( map[extension][uri]["index"] ) ) return filename;\
if ( !ext::json::isNull( map[extension][payload.filename]["index"] ) ) return filename;\
if ( !ext::json::isNull( map[extension][filename]["index"] ) ) return filename;\
map[extension][uri]["index"] = container.size();\
map[extension][payload.filename]["index"] = container.size();\
map[extension][filename]["index"] = container.size();\
type& asset = container.emplace_back();
auto& map = this->getComponent<uf::Serializer>();
// deduce PNG, load as texture
if ( category == "images" || (category == "" && extension == "png") ) {
UF_ASSET_REGISTER(uf::Image)
asset.open(filename);
} else if ( category == "audio" || (category == "" && extension == "ogg") ) {
UF_ASSET_REGISTER(uf::Audio)
asset.open(filename);
} else if ( category == "audio-stream" || (category == "" && extension == "ogg") ) {
UF_ASSET_REGISTER(uf::Audio)
asset.stream(filename);
} else if ( category == "entities" || (category == "" && extension == "json") ) {
UF_ASSET_REGISTER(uf::Serializer)
asset.readFromFile(filename);
#if UF_USE_LUA
} else if ( category == "scripts" || (category == "" && extension == "lua") ) {
UF_ASSET_REGISTER(pod::LuaScript)
asset = ext::lua::script( filename );
#endif
} else if ( category == "models" || (category == "" && (extension == "gltf" || extension == "glb" || extension == "graph" ) ) ) {
UF_ASSET_REGISTER(pod::Graph)
auto& metadata = this->getComponent<uf::Serializer>();
switch ( payload.type ) {
case uf::Asset::Type::IMAGE: {
UF_ASSET_REGISTER(uf::Image)
asset.open(filename);
} break;
case uf::Asset::Type::AUDIO: {
UF_ASSET_REGISTER(uf::Audio)
asset.stream(filename);
} break;
case uf::Asset::Type::JSON: {
UF_ASSET_REGISTER(uf::Serializer)
asset.readFromFile(filename);
} break;
case uf::Asset::Type::LUA: {
UF_ASSET_REGISTER(pod::LuaScript)
asset = ext::lua::script( filename );
} break;
case uf::Asset::Type::GRAPH: {
UF_ASSET_REGISTER(pod::Graph)
auto& metadata = this->getComponent<uf::Serializer>();
#if UF_USE_OPENGL_FIXED_FUNCTION
metadata[uri]["flags"]["ATLAS"] = false;
// metadata[uri]["flags"]["SEPARATE"] = true;
#elif UF_GRAPH_INDIRECT_DRAW
// metadata[uri]["flags"]["ATLAS"] = false;
// metadata[uri]["flags"]["SEPARATE"] = false;
#endif
asset = uf::graph::load( filename, metadata[uri] );
uf::graph::process( asset );
if ( asset.metadata["debug"]["print stats"].as<bool>() ) UF_MSG_INFO(uf::graph::stats( asset ).dump(1,'\t'));
if ( asset.metadata["debug"]["print tree"].as<bool>() ) UF_MSG_INFO(uf::graph::print( asset ));
if ( !asset.metadata["debug"]["no cleanup"].as<bool>() ) uf::graph::cleanup( asset );
} else {
UF_MSG_ERROR("Failed to parse `" + filename + "`: Unimplemented extension: " + extension + " or category: " + category );
#if UF_USE_OPENGL_FIXED_FUNCTION
metadata[payload.filename]["flags"]["ATLAS"] = false;
// metadata[payload.filename]["flags"]["SEPARATE"] = true;
#elif UF_GRAPH_INDIRECT_DRAW
// metadata[payload.filename]["flags"]["ATLAS"] = false;
// metadata[payload.filename]["flags"]["SEPARATE"] = false;
#endif
asset = uf::graph::load( filename, metadata[payload.filename] );
uf::graph::process( asset );
if ( asset.metadata["debug"]["print stats"].as<bool>() ) UF_MSG_INFO(uf::graph::stats( asset ).dump(1,'\t'));
if ( asset.metadata["debug"]["print tree"].as<bool>() ) UF_MSG_INFO(uf::graph::print( asset ));
if ( !asset.metadata["debug"]["no cleanup"].as<bool>() ) uf::graph::cleanup( asset );
} break;
default: {
UF_MSG_ERROR("Failed to parse `" + filename + "`: Unimplemented extension: " + extension );
}
}
return filename;
}
uf::stl::string uf::Asset::getOriginal( const uf::stl::string& uri ) {

View File

@ -13,8 +13,8 @@ uf::Serializer uf::MasterData::load( const uf::stl::string& table, const uf::stl
this->m_key = key;
uf::stl::string url = uf::string::replace( root, "%TABLE%", table );
url = uf::string::replace( url, "%KEY%", key );
uf::stl::string filename = assetLoader.cache(url);
auto payload = uf::Asset::resolveToPayload( uf::string::replace( url, "%KEY%", key ) );
uf::stl::string filename = assetLoader.cache(payload);
if ( filename != "" ) this->m_data.readFromFile(filename);
return this->get();
}

View File

@ -0,0 +1,186 @@
#include <uf/engine/graph/graph.h>
#include <uf/ext/gltf/gltf.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/mesh/grid.h>
#include <uf/utils/thread/thread.h>
#include <uf/utils/string/base64.h>
#include <uf/utils/graphic/graphic.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/camera/camera.h>
#include <uf/ext/xatlas/xatlas.h>
namespace {
size_t UF_API process( uf::Object& object, pod::Graph& graph, pod::Node& parent ) {
// grab relevant IDs
size_t nodeID = graph.nodes.size();
size_t instanceID = uf::graph::storage.instances.keys.size(); // graph.instances.size();
size_t primitiveID = graph.primitives.size();
size_t drawCommandID = graph.drawCommands.size();
size_t meshID = graph.meshes.size();
size_t objectID = uf::graph::storage.entities.keys.size();
uf::stl::string keyName = graph.name + "[" + std::to_string(nodeID) + "]";
// create node
auto& node = graph.nodes.emplace_back();
node.index = nodeID;
node.parent = parent.index;
node.entity = &object;
node.transform.reference = &object.getComponent<pod::Transform<>>();
uf::graph::storage.entities[std::to_string(objectID)] = &object;
// grab relevant data
if ( object.hasComponent<uf::Graphic>() ) {
auto& graphic = object.getComponent<uf::Graphic>();
auto& textures = graphic.material.textures;
// assume we'll only use one material ID for now
size_t sub = 0;
size_t materialID = graph.materials.size();
for ( auto& t : textures ) {
size_t textureID = graph.textures.size();
size_t texture2DID = graph.texture2Ds.size();
uf::stl::string subName = keyName + "[" + std::to_string(sub++) + "]";
auto& material = uf::graph::storage.materials[graph.materials.emplace_back(subName)];
auto& texture = uf::graph::storage.textures[graph.textures.emplace_back(subName)];
auto& texture2D = uf::graph::storage.texture2Ds[graph.texture2Ds.emplace_back(subName)];
material.indexAlbedo = textureID;
texture.index = texture2DID;
texture2D.aliasTexture(t);
}
if ( object.hasComponent<uf::Mesh>() ) {
node.mesh = meshID;
// import
auto& instance = uf::graph::storage.instances[graph.instances.emplace_back(keyName)];
auto& drawCommands = uf::graph::storage.drawCommands[graph.drawCommands.emplace_back(keyName)];
auto& drawCommand = drawCommands.emplace_back();
auto& primitives = uf::graph::storage.primitives[graph.primitives.emplace_back(keyName)];
auto& primitive = primitives.emplace_back();
auto& mesh = (uf::graph::storage.meshes[graph.meshes.emplace_back(keyName)] = object.getComponent<uf::Mesh>());
pod::Vector3f boundsMin = { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max() };
pod::Vector3f boundsMax = { std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
// do some position min/maxing
{
}
instance.materialID = materialID;
instance.primitiveID = primitiveID;
instance.meshID = meshID;
instance.objectID = objectID;
instance.bounds.min = boundsMin;
instance.bounds.max = boundsMax;
drawCommand.indices = mesh.index.count;
drawCommand.instances = 1;
drawCommand.indexID = 0;
drawCommand.vertexID = 0;
drawCommand.instanceID = instanceID;
drawCommand.vertices = mesh.vertex.count;
primitive.instance = instance;
primitive.drawCommand = drawCommand;
mesh.insertIndirects(drawCommands);
mesh.updateDescriptor();
graphic.initialize();
graphic.initializeMesh( mesh );
graphic.process = true;
uf::graph::initializeGraphics( graph, object );
}
}
// recurse
for ( auto* child : object.getChildren() ) {
node.children.emplace_back( process( child->as<uf::Object>(), graph, node ) );
}
return nodeID;
}
}
pod::Graph& UF_API uf::graph::convert( uf::Object& object, bool process ) {
auto& graph = object.getComponent<pod::Graph>();
graph.name = object.getName();
graph.metadata = object.getComponent<uf::Serializer>()["model"];
graph.root.entity = &object;
::process( object, graph, graph.root );
// process nodes
if ( process ) {
for ( auto index : graph.root.children ) uf::graph::process( graph, index, *graph.root.entity );
}
// remap materials->texture IDs
for ( auto& name : graph.materials ) {
auto& material = uf::graph::storage.materials[name];
auto& keys = uf::graph::storage.textures.keys;
int32_t* IDs[] = { &material.indexAlbedo, &material.indexNormal, &material.indexEmissive, &material.indexOcclusion, &material.indexMetallicRoughness };
for ( auto* pointer : IDs ) {
auto& ID = *pointer;
if ( !(0 <= ID && ID < graph.materials.size()) ) continue;
auto it = std::find( keys.begin(), keys.end(), graph.textures[ID] );
UF_ASSERT( it != keys.end() );
ID = it - keys.begin();
}
}
// remap textures->images IDs
/*
for ( auto& name : graph.textures ) {
auto& texture = uf::graph::storage.textures[name];
auto& keys = uf::graph::storage.images.keys;
if ( !(0 <= texture.index && texture.index < graph.textures.size()) ) continue;
auto it = std::find( keys.begin(), keys.end(), graph.images[texture.index] );
UF_ASSERT( it != keys.end() );
texture.index = it - keys.begin();
}
*/
// remap instance variables
for ( auto& name : graph.instances ) {
auto& instance = uf::graph::storage.instances[name];
if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) {
auto& keys = /*graph.storage*/uf::graph::storage.materials.keys;
auto it = std::find( keys.begin(), keys.end(), graph.materials[instance.materialID] );
UF_ASSERT( it != keys.end() );
instance.materialID = it - keys.begin();
}
if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) {
auto& keys = /*graph.storage*/uf::graph::storage.images.keys;
auto it = std::find( keys.begin(), keys.end(), graph.images[instance.imageID] );
UF_ASSERT( it != keys.end() );
instance.imageID = it - keys.begin();
}
// remap a skinID as an actual jointID
if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) {
auto& name = graph.skins[instance.jointID];
instance.jointID = 0;
for ( auto key : uf::graph::storage.joints.keys ) {
if ( key == name ) break;
auto& joints = uf::graph::storage.joints[key];
instance.jointID += joints.size();
}
}
if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) {
auto& keys = /*graph.storage*/uf::graph::storage.textures.keys;
auto it = std::find( keys.begin(), keys.end(), graph.textures[instance.lightmapID] );
UF_ASSERT( it != keys.end() );
instance.lightmapID = it - keys.begin();
}
}
uf::graph::reload();
return graph;
}

View File

@ -39,8 +39,33 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32A32_SFLOAT, weights)
);
namespace {
void initializeGraphics( pod::Graph& graph, uf::Object& entity ) {
pod::Matrix4f uf::graph::local( pod::Graph& graph, int32_t index ) {
auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root;
return
uf::matrix::translate( uf::matrix::identity(), node.transform.position ) *
uf::quaternion::matrix(node.transform.orientation) *
uf::matrix::scale( uf::matrix::identity(), node.transform.scale ) *
node.transform.model;
}
pod::Matrix4f uf::graph::matrix( pod::Graph& graph, int32_t index ) {
pod::Matrix4f matrix = local( graph, index );
auto& node = *uf::graph::find( graph, index );
int32_t parent = node.parent;
while ( 0 < parent && parent <= graph.nodes.size() ) {
matrix = local( graph, parent ) * matrix;
parent = graph.nodes[parent].parent;
}
return matrix;
}
pod::Node* uf::graph::find( pod::Graph& graph, int32_t index ) {
return 0 <= index && index < graph.nodes.size() ? &graph.nodes[index] : NULL;
}
pod::Node* uf::graph::find( pod::Graph& graph, const uf::stl::string& name ) {
for ( auto& node : graph.nodes ) if ( node.name == name ) return &node;
return NULL;
}
void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity ) {
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();
@ -52,15 +77,11 @@ namespace {
graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW;
graphic.descriptor.cullMode = !(graph.metadata["flags"]["INVERT"].as<bool>()) ? uf::renderer::enums::CullMode::BACK : uf::renderer::enums::CullMode::FRONT;
if ( graph.metadata["cull mode"].is<uf::stl::string>() ) {
if ( graph.metadata["cull mode"].as<uf::stl::string>() == "back" ) {
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BACK;
} else if ( graph.metadata["cull mode"].as<uf::stl::string>() == "front" ) {
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::FRONT;
} else if ( graph.metadata["cull mode"].as<uf::stl::string>() == "none" ) {
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE;
} else if ( graph.metadata["cull mode"].as<uf::stl::string>() == "both" ) {
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BOTH;
}
const auto mode = graph.metadata["cull mode"].as<uf::stl::string>();
if ( mode == "back" ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BACK;
else if ( mode == "front" ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::FRONT;
else if ( mode == "none" ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE;
else if ( mode == "both" ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BOTH;
}
{
for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( uf::graph::storage.texture2Ds.map[i] );
@ -159,21 +180,22 @@ namespace {
uf::renderer::Buffer* indirect = NULL;
for ( auto& buffer : graphic.buffers ) if ( !indirect && buffer.usage & uf::renderer::enums::Buffer::INDIRECT ) indirect = &buffer;
UF_ASSERT( indirect );
if ( indirect ) {
uf::stl::string compShaderFilename = graph.metadata["shaders"]["vertex"].as<uf::stl::string>("/graph/cull.comp.spv");
{
graphic.material.metadata.autoInitializeUniforms = false;
compShaderFilename = entity.grabURI( compShaderFilename, root );
graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "culling");
graphic.material.metadata.autoInitializeUniforms = true;
}
graphic.descriptor.inputs.dispatch = { graphic.descriptor.inputs.indirect.count, 1, 1 };
uf::stl::string compShaderFilename = graph.metadata["shaders"]["vertex"].as<uf::stl::string>("/graph/cull.comp.spv");
{
graphic.material.metadata.autoInitializeUniforms = false;
compShaderFilename = entity.grabURI( compShaderFilename, root );
graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "culling");
graphic.material.metadata.autoInitializeUniforms = true;
auto& shader = graphic.material.getShader("compute", "culling");
shader.buffers.emplace_back().aliasBuffer( uf::graph::storage.buffers.camera );
shader.buffers.emplace_back().aliasBuffer( *indirect );
shader.buffers.emplace_back().aliasBuffer( uf::graph::storage.buffers.instance );
}
graphic.descriptor.inputs.dispatch = { graphic.descriptor.inputs.indirect.count, 1, 1 };
auto& shader = graphic.material.getShader("compute", "culling");
shader.buffers.emplace_back().aliasBuffer( uf::graph::storage.buffers.camera );
shader.buffers.emplace_back().aliasBuffer( *indirect );
shader.buffers.emplace_back().aliasBuffer( uf::graph::storage.buffers.instance );
}
if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) {
geometryShaderFilename = entity.grabURI( geometryShaderFilename, root );
@ -318,38 +340,7 @@ namespace {
uf::instantiator::bind( "GraphBehavior", entity );
uf::instantiator::unbind( "RenderBehavior", entity );
}
}
pod::Matrix4f uf::graph::local( pod::Graph& graph, int32_t index ) {
auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root;
return
uf::matrix::translate( uf::matrix::identity(), node.transform.position ) *
uf::quaternion::matrix(node.transform.orientation) *
uf::matrix::scale( uf::matrix::identity(), node.transform.scale ) *
node.transform.model;
}
pod::Matrix4f uf::graph::matrix( pod::Graph& graph, int32_t index ) {
pod::Matrix4f matrix = local( graph, index );
auto& node = *uf::graph::find( graph, index );
int32_t parent = node.parent;
while ( 0 < parent && parent <= graph.nodes.size() ) {
matrix = local( graph, parent ) * matrix;
parent = graph.nodes[parent].parent;
}
return matrix;
}
pod::Node* uf::graph::find( pod::Graph& graph, int32_t index ) {
return 0 <= index && index < graph.nodes.size() ? &graph.nodes[index] : NULL;
}
pod::Node* uf::graph::find( pod::Graph& graph, const uf::stl::string& name ) {
for ( auto& node : graph.nodes ) if ( node.name == name ) return &node;
return NULL;
}
void uf::graph::process( uf::Object& entity ) {
auto& graph = entity.getComponent<pod::Graph>();
for ( auto index : graph.root.children ) process( graph, index, entity );
}
void uf::graph::process( pod::Graph& graph ) {
// copy our local storage to global storage
@ -478,7 +469,7 @@ void uf::graph::process( pod::Graph& graph ) {
instance.lightmapID = it - keys.begin();
}
}
::newGraphAdded = true;
uf::graph::reload();
// setup combined mesh if requested
if ( !(graph.metadata["flags"]["SEPARATE"].as<bool>()) ) {
@ -516,7 +507,7 @@ void uf::graph::process( pod::Graph& graph ) {
graphic.initializeMesh( mesh );
graphic.process = true;
initializeGraphics( graph, *graph.root.entity );
uf::graph::initializeGraphics( graph, *graph.root.entity );
}
}
}
@ -684,7 +675,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
graphic.initializeMesh( mesh );
graphic.process = true;
initializeGraphics( graph, entity );
uf::graph::initializeGraphics( graph, entity );
}
if ( !ext::json::isNull( graph.metadata["tags"][node.name] ) ) {
@ -977,4 +968,7 @@ void uf::graph::destroy() {
for ( auto pair : uf::graph::storage.atlases.map ) pair.second.clear();
for ( auto pair : uf::graph::storage.images.map ) pair.second.clear();
for ( auto pair : uf::graph::storage.meshes.map ) pair.second.destroy();
}
void uf::graph::reload() {
::newGraphAdded = true;
}

View File

@ -29,8 +29,8 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
metadataJson["system"]["load"]["total"] = assets;
if ( assets == 0 ) {
auto& parent = this->getParent().as<uf::Object>();
uf::Serializer payload;
payload["uid"] = this->getUid();
pod::payloads::assetLoad payload;
payload.uid = this->getUid();
parent.callHook("asset:Parsed.%UID%", payload);
}
}
@ -60,40 +60,31 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
metadataJson.merge(json, true);
}
});
this->addHook( "asset:QueueLoad.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string hash = json["hash"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
this->addHook( "asset:QueueLoad.%UID%", [&](pod::payloads::assetLoad& payload){
uf::stl::string callback = this->formatHookName("asset:FinishedLoad.%UID%");
if ( json["single threaded"].as<bool>() ) {
assetLoader.load( filename, hash, category );
this->queueHook( callback, json );
if ( payload.monoThreaded ) {
if ( assetLoader.load( payload ) != "" ) this->queueHook( callback, payload );
} else {
assetLoader.load( callback, filename, hash, category );
assetLoader.load( callback, payload );
}
});
this->addHook( "asset:FinishedLoad.%UID%", [&](ext::json::Value& json){
this->queueHook("asset:Load.%UID%", json);
this->queueHook("asset:Parsed.%UID%", json);
this->addHook( "asset:FinishedLoad.%UID%", [&](pod::payloads::assetLoad& payload){
this->queueHook("asset:Load.%UID%", payload);
this->queueHook("asset:Parsed.%UID%", payload);
});
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
bool initialize = ext::json::isNull( json["initialize"] ) ? true : json["initialize"].as<bool>();
if ( category != "" && category != "entities" ) return;
if ( category == "" && uf::io::extension(filename) != "json" ) return;
{
uf::Serializer json;
if ( !json.readFromFile(filename) ) return;
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::JSON ) ) return;
json["root"] = uf::io::directory(filename);
json["source"] = filename;
json["hot reload"]["mtime"] = uf::io::mtime( filename ) + 10;
uf::Serializer json;
if ( !json.readFromFile(payload.filename) ) return;
if ( this->loadChildUid(json, initialize) == -1 ) return;
}
json["root"] = uf::io::directory(payload.filename);
json["source"] = payload.filename;
json["hot reload"]["mtime"] = uf::io::mtime( payload.filename ) + 10;
if ( this->loadChildUid(json, payload.initialize) == -1 ) return;
});
this->addHook( "asset:Parsed.%UID%", [&](ext::json::Value& json){
this->addHook( "asset:Parsed.%UID%", [&](pod::payloads::assetLoad& payload){
int portion = 1;
auto& total = metadataJson["system"]["load"]["total"];
auto& progress = metadataJson["system"]["load"]["progress"];
@ -101,8 +92,7 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
if ( progress.as<int>() == total.as<int>() ) {
auto& parent = this->getParent().as<uf::Object>();
uf::Serializer payload;
payload["uid"] = this->getUid();
payload.uid = this->getUid();
parent.callHook("asset:Parsed.%UID%", payload);
}
});
@ -218,18 +208,9 @@ void uf::ObjectBehavior::tick( uf::Object& self ) {
transform.position = uf::transform::flatten( parentTransform ).position + metadata.transform.initial.position;
}
/*
if ( this->hasComponent<pod::PhysicsState>() && this->hasComponent<pod::Physics>() ) {
auto& collider = this->getComponent<pod::PhysicsState>();
auto& transform = this->getComponent<pod::Transform<>>();
auto& physics = this->getComponent<pod::Physics>();
UF_MSG_DEBUG( this->getName() << ": " << this->getUid() << " " << uf::vector::toString( physics.linear.velocity ) << " " << uf::vector::toString( transform.position ) );
}
*/
auto& queue = metadata.hooks.queue;
if ( !uf::Object::timer.running() ) uf::Object::timer.start();
float curTime = uf::Object::timer.elapsed().asDouble();
double curTime = uf::Object::timer.elapsed().asDouble();
#if 1
decltype(metadata.hooks.queue) unprocessed;
@ -239,7 +220,13 @@ void uf::ObjectBehavior::tick( uf::Object& self ) {
executeQueue.reserve( metadata.hooks.queue.size() );
for ( auto& q : queue ) if ( q.timeout < curTime ) executeQueue.emplace_back(q); else unprocessed.emplace_back(q);
for ( auto& q : executeQueue ) this->callHook( q.name, q.payload );
for ( auto& q : executeQueue ) {
if ( q.type == 0 ) {
this->callHook( q.name, q.json );
} else {
this->callHook( q.name, q.userdata );
}
}
queue = std::move(unprocessed);
#else
for ( auto it = queue.begin(); it != queue.end(); ) {

View File

@ -25,16 +25,14 @@ void uf::GraphBehavior::initialize( uf::Object& self ) {
auto& graph = this->getComponent<pod::Graph>();
uf::graph::animate( graph, name );
});
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
if ( category != "" && category != "models" ) return;
if ( category == "" && uf::io::extension(filename) != "gltf" && uf::io::extension(filename) != "glb" && uf::io::extension(filename) != "graph" ) return;
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::GRAPH ) ) return;
auto& scene = uf::scene::getCurrentScene();
auto& assetLoader = scene.getComponent<uf::Asset>();
if ( !assetLoader.has<pod::Graph>(filename) ) return;
auto& graph = (this->getComponent<pod::Graph>() = std::move( assetLoader.get<pod::Graph>(filename) ));
assetLoader.remove<pod::Graph>(filename);
if ( !assetLoader.has<pod::Graph>(payload.filename) ) return;
auto& graph = (this->getComponent<pod::Graph>() = std::move( assetLoader.get<pod::Graph>(payload.filename) ));
assetLoader.remove<pod::Graph>(payload.filename);
bool shouldUpdate = false;

View File

@ -19,16 +19,14 @@ void uf::LuaBehavior::initialize( uf::Object& self ) {
#if UF_USE_LUA
if ( !ext::lua::enabled ) return;
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
if ( category != "" && category != "scripts" ) return;
if ( category == "" && uf::io::extension(filename) != "lua" ) return;
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::LUA ) ) return;
uf::Scene& scene = uf::scene::getCurrentScene();
uf::Asset& assetLoader = scene.getComponent<uf::Asset>();
const pod::LuaScript* assetPointer = NULL;
if ( !assetLoader.has<pod::LuaScript>(filename) ) return;
assetPointer = &assetLoader.get<pod::LuaScript>(filename);
if ( !assetLoader.has<pod::LuaScript>(payload.filename) ) return;
assetPointer = &assetLoader.get<pod::LuaScript>(payload.filename);
if ( !assetPointer ) return;
pod::LuaScript script = *assetPointer;
script.env["ent"] = &this->as<uf::Object>();

View File

@ -20,13 +20,13 @@ UF_OBJECT_REGISTER_BEGIN(uf::Object)
UF_OBJECT_REGISTER_BEHAVIOR(uf::ObjectBehavior)
UF_OBJECT_REGISTER_END()
uf::Object::Object() UF_BEHAVIOR_ENTITY_CPP_ATTACH(uf::Object)
void uf::Object::queueHook( const uf::stl::string& name, const ext::json::Value& payload, double timeout ) {
void uf::Object::queueHook( const uf::stl::string& name, const ext::json::Value& json, float timeout ) {
if ( !uf::Object::timer.running() ) uf::Object::timer.start();
double start = uf::Object::timer.elapsed().asDouble();
#if UF_ENTITY_METADATA_USE_JSON
uf::Serializer queue;
queue["name"] = name;
queue["payload"] = payload;
queue["payload"] = json;
queue["timeout"] = start + timeout;
uf::Serializer& metadata = this->getComponent<uf::Serializer>();
metadata["system"]["hooks"]["queue"].emplace_back(queue);
@ -34,7 +34,7 @@ void uf::Object::queueHook( const uf::stl::string& name, const ext::json::Value&
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
auto& queue = metadata.hooks.queue.emplace_back(uf::ObjectBehavior::Metadata::Queued{
.name = name,
.payload = payload,
.json = json,
.timeout = start + timeout,
});
#endif
@ -79,6 +79,9 @@ uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name, const ext
uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name, const uf::Serializer& serializer ) {
return uf::hooks.call( this->formatHookName( name ), (const ext::json::Value&) serializer );
}
uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name, const pod::Hook::userdata_t& payload ) {
return uf::hooks.call( this->formatHookName( name ), payload );
}
bool uf::Object::load( const uf::stl::string& f, bool inheritRoot ) {
uf::Serializer json;
uf::stl::string root = "";
@ -214,66 +217,47 @@ bool uf::Object::load( const uf::Serializer& _json ) {
}
#define UF_OBJECT_LOAD_ASSET_HEADER(type)\
uf::stl::string assetCategory = #type;\
uf::Asset::Type assetType = uf::Asset::Type::type;\
uf::stl::string assetTypeString = uf::string::lowercase( #type );\
uf::Serializer target;\
bool override = false;\
if ( ext::json::isObject( metadata["system"]["assets"] ) ) {\
target = metadata["system"]["assets"];\
} else if ( ext::json::isArray( json["assets"] ) ) {\
target = json["assets"];\
} else if ( ext::json::isObject( json["assets"] ) && !ext::json::isNull( json["assets"][#type] ) ) {\
target = json["assets"][#type];\
} else if ( ext::json::isObject( json["assets"] ) && !ext::json::isNull( json["assets"][assetTypeString] ) ) {\
target = json["assets"][assetTypeString];\
}
uf::stl::vector<ext::json::Value> rejects;
#define UF_OBJECT_LOAD_ASSET(...)\
#define UF_OBJECT_LOAD_ASSET()\
bool isObject = ext::json::isObject( target[i] );\
uf::stl::string canonical = "";\
uf::stl::string hash = isObject ? target[i]["hash"].as<uf::stl::string>() : "";\
uf::stl::string f = isObject ? target[i]["filename"].as<uf::stl::string>() : target[i].as<uf::stl::string>();\
float delay = isObject ? target[i]["delay"].as<float>() : 0;\
uf::stl::string category = isObject ? target[i]["category"].as<uf::stl::string>() : "";\
bool bind = isObject && target[i]["bind"].is<bool>() ? target[i]["bind"].as<bool>() : true;\
uf::stl::string filename = uf::io::resolveURI( f, json["root"].as<uf::stl::string>() );\
bool singleThreaded = isObject ? target[i]["single threaded"].as<bool>() : false;\
bool overriden = isObject ? target[i]["override"].as<bool>() : false;\
uf::stl::vector<uf::stl::string> allowedExtensions = {__VA_ARGS__};\
uf::stl::string extension = uf::io::extension(filename);\
if ( override && overriden ) {\
if ( category == "" ) continue;\
} else {\
if ( category != "" && category != assetCategory ) continue;\
if ( category == "" && std::find( allowedExtensions.begin(), allowedExtensions.end(), extension ) == allowedExtensions.end() ) continue;\
}\
uf::Serializer payload;\
if ( isObject ) payload = target[i];\
payload["filename"] = filename;\
if ( hash != "" ) payload["hash"] = hash;\
payload["category"] = assetCategory != "" ? assetCategory : category;\
payload["single threaded"] = singleThreaded;\
this->queueHook( "asset:QueueLoad.%UID%", payload, delay );\
uf::stl::string mime = isObject ? target[i]["mime"].as<uf::stl::string>("") : "";\
uf::Asset::Payload payload = uf::Asset::resolveToPayload( filename, mime );\
if ( !uf::Asset::isExpected( payload, assetType ) ) continue;\
payload.hash = isObject ? target[i]["hash"].as<uf::stl::string>("") : "";\
payload.monoThreaded = isObject ? target[i]["single threaded"].as<bool>() : false;\
this->queueHook( "asset:QueueLoad.%UID%", payload, isObject ? target[i]["delay"].as<float>() : 0 );\
bool bind = isObject && target[i]["bind"].is<bool>() ? target[i]["bind"].as<bool>() : true;\
// Audio
{
UF_OBJECT_LOAD_ASSET_HEADER(audio)
UF_OBJECT_LOAD_ASSET_HEADER(AUDIO)
for ( size_t i = 0; i < target.size(); ++i ) {
UF_OBJECT_LOAD_ASSET("ogg")
UF_OBJECT_LOAD_ASSET()
}
}
// Images
{
UF_OBJECT_LOAD_ASSET_HEADER(images)
UF_OBJECT_LOAD_ASSET_HEADER(IMAGE)
for ( size_t i = 0; i < target.size(); ++i ) {
UF_OBJECT_LOAD_ASSET("png", "jpg", "jpeg")
UF_OBJECT_LOAD_ASSET()
}
}
// GLTf models
{
UF_OBJECT_LOAD_ASSET_HEADER(models)
UF_OBJECT_LOAD_ASSET_HEADER(GRAPH)
for ( size_t i = 0; i < target.size(); ++i ) {
UF_OBJECT_LOAD_ASSET("gltf", "glb", "graph")
UF_OBJECT_LOAD_ASSET()
if ( bind ) uf::instantiator::bind("GraphBehavior", *this);
auto& aMetadata = assetLoader.getComponent<uf::Serializer>();
@ -281,14 +265,14 @@ bool uf::Object::load( const uf::Serializer& _json ) {
aMetadata[filename]["root"] = json["root"];
}
}
// Lua scripts
{
UF_OBJECT_LOAD_ASSET_HEADER(scripts)
UF_OBJECT_LOAD_ASSET_HEADER(LUA)
for ( size_t i = 0; i < target.size(); ++i ) {
UF_OBJECT_LOAD_ASSET("lua")
UF_OBJECT_LOAD_ASSET()
if ( bind ) uf::instantiator::bind("LuaBehavior", *this);
}
}
/*
// Override
{
UF_OBJECT_LOAD_ASSET_HEADER()
@ -297,6 +281,8 @@ bool uf::Object::load( const uf::Serializer& _json ) {
UF_OBJECT_LOAD_ASSET()
}
}
*/
// Bind behaviors
{
if ( json["type"].is<uf::stl::string>() ) uf::instantiator::bind( json["type"].as<uf::stl::string>(), *this );
@ -329,22 +315,22 @@ bool uf::Object::load( const uf::Serializer& _json ) {
// check for children
{
UF_OBJECT_LOAD_ASSET_HEADER(entities)
UF_OBJECT_LOAD_ASSET_HEADER(JSON)
for ( size_t i = 0; i < target.size(); ++i ) {
uf::stl::string canonical = "";
uf::stl::string hash = ext::json::isObject( target[i] ) ? target[i]["hash"].as<uf::stl::string>() : "";
uf::stl::string f = ext::json::isObject( target[i] ) ? target[i]["filename"].as<uf::stl::string>() : target[i].as<uf::stl::string>();
float delay = ext::json::isObject( target[i] ) ? target[i]["delay"].as<float>() : -1;
uf::stl::string category = ext::json::isObject( target[i] ) ? target[i]["category"].as<uf::stl::string>() : "";\
uf::stl::string filename = uf::io::resolveURI( f, json["root"].as<uf::stl::string>() );
if ( category != "" && category != assetCategory ) continue;
if ( category == "" && uf::io::extension(filename) != "json" ) continue;
if ( (canonical = assetLoader.load( filename, hash )) == "" ) continue;
uf::stl::string mime = ext::json::isObject( target[i] ) ? target[i]["mime"].as<uf::stl::string>() : "";
uf::stl::string hash = ext::json::isObject( target[i] ) ? target[i]["hash"].as<uf::stl::string>() : "";
uf::Asset::Payload payload = uf::Asset::resolveToPayload( filename, mime );
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::JSON ) ) continue;
if ( (filename = assetLoader.load( payload )) == "" ) continue;
float delay = ext::json::isObject( target[i] ) ? target[i]["delay"].as<float>() : -1;
if ( delay > -1 ) {
uf::Serializer payload;
payload["filename"] = canonical;
if ( hash != "" ) payload["hash"] = hash;
payload["category"] = assetCategory;
payload.filename = filename;
payload.hash = hash;
payload.mime = mime;
this->queueHook( "asset:Load.%UID%", payload, delay );
continue;
}
@ -357,10 +343,8 @@ bool uf::Object::load( const uf::Serializer& _json ) {
#if UF_ENTITY_METADATA_USE_JSON
json["hot reload"]["mtime"] = uf::io::mtime(filename) + 10;
#else
{
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.mtime = uf::io::mtime(filename) + 10;
}
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
metadata.hotReload.mtime = uf::io::mtime(filename) + 10;
#endif
if ( this->loadChildUid(json) == -1 ) continue;

View File

@ -289,33 +289,6 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize
}
}
#if 0
switch ( accessor.componentType ) {
case TINYGLTF_COMPONENT_TYPE_BYTE:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: {
attribute.stride = sizeof(uint8_t);
} break;
case TINYGLTF_COMPONENT_TYPE_SHORT:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: {
attribute.stride = sizeof(uint16_t);
} break;
case TINYGLTF_COMPONENT_TYPE_INT:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: {
attribute.stride = sizeof(uint32_t);
} break;
case TINYGLTF_COMPONENT_TYPE_FLOAT: {
attribute.stride = sizeof(float);
} break;
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
attribute.stride = sizeof(double);
} break;
default: UF_MSG_ERROR("Unsupported component type");
}
attribute.buffer = &(model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]);
attribute.components = accessor.ByteStride(view) / attribute.stride;
attribute.length = accessor.count * attribute.components;
#else
switch ( accessor.componentType ) {
case TINYGLTF_COMPONENT_TYPE_BYTE:
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: {
@ -346,7 +319,6 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize
} break;
default: UF_MSG_ERROR("Unsupported component type");
}
#endif
}
for ( size_t i = 0; i < vertices.size(); ++i ) {

View File

@ -8,28 +8,36 @@ namespace binds {
uf::stl::string callback = "";
uf::stl::string uri = "";
uf::stl::string hash = "";
uf::stl::string category = "";
uf::stl::string mime = "";
if ( it != va.end() ) callback = *(it++);
if ( it != va.end() ) uri = *(it++);
if ( it != va.end() ) hash = *(it++);
if ( it != va.end() ) category = *(it++);
if ( callback == "" ) asset.load( uri, hash, category );
else asset.load( callback, uri, hash, category );
if ( it != va.end() ) mime = *(it++);
auto payload = uf::Asset::resolveToPayload( uri, mime );
payload.hash = hash;
if ( callback == "" ) asset.load( payload );
else asset.load( callback, payload );
}
void cache( uf::Asset& asset, sol::variadic_args va ) {
auto it = va.begin();
uf::stl::string callback = "";
uf::stl::string uri = "";
uf::stl::string hash = "";
uf::stl::string category = "";
uf::stl::string mime = "";
if ( it != va.end() ) callback = *(it++);
if ( it != va.end() ) uri = *(it++);
if ( it != va.end() ) hash = *(it++);
if ( it != va.end() ) category = *(it++);
if ( it != va.end() ) mime = *(it++);
auto payload = uf::Asset::resolveToPayload( uri, mime );
payload.hash = hash;
if ( callback == "" )
asset.cache( uri, hash, category );
asset.cache( payload );
else
asset.cache( callback, uri, hash, category );
asset.cache( callback, payload );
}
uf::stl::string getOriginal( uf::Asset& asset, const uf::stl::string& n ) {
return asset.getOriginal( n );

View File

@ -2,10 +2,10 @@
#include <iostream>
uf::Timer<> uf::physics::time::timer;
uf::physics::num_t uf::physics::time::current;
uf::physics::num_t uf::physics::time::previous;
uf::physics::num_t uf::physics::time::delta;
uf::physics::num_t uf::physics::time::clamp;
double uf::physics::time::current = 0;
double uf::physics::time::previous = 0;
float uf::physics::time::delta = 0;
float uf::physics::time::clamp = 0;
void UF_API uf::physics::initialize() {
@ -14,6 +14,7 @@ void UF_API uf::physics::initialize() {
void UF_API uf::physics::tick() {
uf::physics::time::previous = uf::physics::time::current;
uf::physics::time::current = uf::physics::time::timer.elapsed();
uf::physics::time::delta = uf::physics::time::current - uf::physics::time::previous;
if ( uf::physics::time::delta > uf::physics::time::clamp ) {
uf::physics::time::delta = uf::physics::time::clamp;

View File

@ -0,0 +1,80 @@
#include "behavior.h"
#include <uf/utils/hook/hook.h>
#include <uf/utils/time/time.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/userdata/userdata.h>
#include <uf/utils/window/window.h>
#include <uf/utils/window/payloads.h>
#include <uf/utils/camera/camera.h>
#include <uf/utils/audio/audio.h>
#include <uf/ext/openvr/openvr.h>
#include <uf/engine/graph/graph.h>
#include <uf/engine/asset/asset.h>
#include <uf/utils/math/physics.h>
#include <uf/spec/controller/controller.h>
#include <uf/utils/graphic/graphic.h>
#include <uf/utils/io/inputs.h>
#include <sstream>
UF_BEHAVIOR_REGISTER_CPP(ext::CraetureBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = false, renders = false, multithread = false)
#define this (&self)
namespace {
void load( uf::Object& self, uf::Image& image ) {
auto& graphic = self.getComponent<uf::Graphic>();
auto& texture = graphic.material.textures.emplace_back();
texture.loadFromImage( image );
auto& mesh = self.getComponent<uf::Mesh>();
mesh.bindIndirect<pod::DrawCommand>();
mesh.bind<uf::graph::mesh::Base, uint16_t>();
float scaleX = 1;
float scaleY = 1;
mesh.insertVertices<uf::graph::mesh::Base>({
{ {-1.0f * scaleX, 1.0f * scaleY, 0.0f}, {}, {0.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f} },
{ {-1.0f * scaleX, -1.0f * scaleY, 0.0f}, {}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
{ {1.0f * scaleX, -1.0f * scaleY, 0.0f}, {}, {1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
{ {1.0f * scaleX, 1.0f * scaleY, 0.0f}, {}, {1.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}, }
});
mesh.insertIndices<uint16_t>({
0, 1, 2, 2, 3, 0
});
uf::graph::convert( self );
}
}
void ext::CraetureBehavior::initialize( uf::Object& self ) {
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::IMAGE ) ) return;
UF_MSG_DEBUG( "IMAGE: " << payload.filename );
uf::Scene& scene = uf::scene::getCurrentScene();
uf::Asset& assetLoader = scene.getComponent<uf::Asset>();
if ( !assetLoader.has<uf::Image>(payload.filename) ) return;
auto& image = assetLoader.get<uf::Image>(payload.filename);
::load( self, image );
});
auto& metadata = this->getComponent<ext::CraetureBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
this->addHook( "object:UpdateMetadata.%UID%", [&](ext::json::Value& json){
metadata.deserialize(self, metadataJson);
});
metadata.deserialize(self, metadataJson);
}
void ext::CraetureBehavior::tick( uf::Object& self ) {}
void ext::CraetureBehavior::render( uf::Object& self ){}
void ext::CraetureBehavior::destroy( uf::Object& self ){}
void ext::CraetureBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){
}
void ext::CraetureBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){
}
#undef this

View File

@ -0,0 +1,20 @@
#pragma once
#include <uf/config.h>
#include <uf/ext/ext.h>
#include <uf/engine/entity/entity.h>
#include <uf/engine/scene/scene.h>
#include <uf/utils/math/vector.h>
namespace ext {
namespace CraetureBehavior {
UF_BEHAVIOR_DEFINE_TYPE();
EXT_BEHAVIOR_DEFINE_TRAITS();
EXT_BEHAVIOR_DEFINE_FUNCTIONS();
UF_BEHAVIOR_DEFINE_METADATA(
struct {
float health;
} stats;
);
}
}

View File

@ -52,8 +52,8 @@ void ext::PlayerHandBehavior::initialize( uf::Object& self ) {
if ( !ext::openvr::requestRenderModel(metadata["hands"][key]["controller"]["model"].as<uf::stl::string>()) ) loaded = false;
}
if ( !loaded ) {
this->addHook( "VR:Model.Loaded", [&](ext::json::Value& json){
uf::stl::string name = json["name"].as<uf::stl::string>();
this->addHook( "VR:Model.Loaded", [&](pod::payloads::assetLoad& payload){
uf::stl::string name = payload.filename; // json["name"].as<uf::stl::string>();
uf::stl::string side = "";
if ( name == metadata["hands"]["left"]["controller"]["model"].as<uf::stl::string>() ) {
side = "left";
@ -157,38 +157,33 @@ void ext::PlayerHandBehavior::initialize( uf::Object& self ) {
uf::stl::vector<uf::Object*> vHands = { ::hands.left, ::hands.right };
for ( auto pointer : vHands ) {
auto& hand = *pointer;
hand.addHook("VR:Input.Digital", [&](ext::json::Value& json){
uf::stl::string side = &hand == hands.left ? "left" : "right";
if ( json["hand"].as<uf::stl::string>() != side ) return;
hand.addHook("VR:Input.Digital", [&]( pod::payloads::vrInputDigital& payload){
int_fast8_t side = &hand == hands.left ? -1 : 1;
if ( payload.side != side ) return;
// fire mouse click
if ( json["name"].as<uf::stl::string>() == "click" ) {
if ( payload.name == "click" ) {
pod::payloads::windowMouseClick pload;
pload.type = "window:Mouse.Click";
pload.invoker = "vr";
pload.mouse.position = uf::vector::encode( metadata["hands"][side]["cursor"]["position"], pload.mouse.position );
pload.mouse.delta = {};
pload.mouse.button = side == -1 ? "Right" : "Left";
pload.mouse.state = payload.state;
uf::Serializer payload;
payload["type"] = "window:Mouse.Click";
payload["invoker"] = "vr";
// payload["mouse"]["position"]["x"] = metadata["hands"][side]["cursor"]["position"][0].as<float>();
// payload["mouse"]["position"]["y"] = metadata["hands"][side]["cursor"]["position"][1].as<float>();
// payload["mouse"]["delta"]["x"] = 0;
// payload["mouse"]["delta"]["y"] = 0;
payload["mouse"]["position"] = metadata["hands"][side]["cursor"]["position"];
payload["mouse"]["delta"] = uf::vector::encode( pod::Vector2i{0,0} );
payload["mouse"]["button"] = side == "left" ? "Right" : "Left";
payload["mouse"]["state"] = json["state"].as<bool>() ? "Down": "Up";
uf::hooks.call( payload["type"].as<uf::stl::string>(), payload );
uf::hooks.call( pload.type, payload );
}
});
hand.addHook("world:Collision.%UID%", [&](ext::json::Value& json){
hand.addHook("world:Collision.%UID%", [&]( pod::payloads::worldCollision& payload){
uf::stl::string side = &hand == hands.left ? "left" : "right";
float mag = json["depth"].as<float>();
uf::Serializer payload;
payload["delay"] = 0.0f;
payload["duration"] = uf::physics::time::delta;
payload["frequency"] = 1.0f;
payload["amplitude"] = fmin(1.0f, 1000.0f * mag);
payload["side"] = side;
pod::payloads::vrHaptics pload;
pload.delay = 0;
pload.duration = uf::physics::time::delta;
pload.frequency = 1;
pload.amplitude = fmin(1.0f, 1000.0f * payload.depth);
plaod.side = &hand == hands.left ? -1 : 1;;
uf::hooks.call( "VR:Haptics." + side, payload );
});

View File

@ -36,7 +36,7 @@ void ext::LightBehavior::initialize( uf::Object& self ) {
if ( !sceneMetadata["system"]["lights"]["round robin hook bound"].as<bool>() ) {
sceneMetadata["system"]["lights"]["round robin hook bound"] = true;
scene.addHook("game:Frame.Start", [&]( const ext::json::Value& payload ){
scene.addHook("game:Frame.Start", [&]( ext::json::Value& payload ){
if ( ++::roundRobin.current >= ::roundRobin.lights.size() ) ::roundRobin.current = 0;
});
}

View File

@ -29,6 +29,8 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
auto& camera = this->getComponent<uf::Camera>();
auto& cameraTransform = camera.getTransform();
auto& scene = uf::scene::getCurrentScene();
{
camera.setStereoscopic(true);
@ -36,27 +38,29 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
cameraTransform.scale = uf::vector::decode( metadataJson["camera"]["scale"], cameraTransform.scale );
cameraTransform.reference = &transform;
auto& cameraSettingsJson = metadataJson["camera"]["settings"];
if ( metadataJson["camera"]["ortho"].as<bool>() ) {
float l = metadataJson["camera"]["settings"]["left"].as<float>();
float r = metadataJson["camera"]["settings"]["right"].as<float>();
float b = metadataJson["camera"]["settings"]["bottom"].as<float>();
float t = metadataJson["camera"]["settings"]["top"].as<float>();
float n = metadataJson["camera"]["settings"]["near"].as<float>();
float f = metadataJson["camera"]["settings"]["far"].as<float>();
float l = cameraSettingsJson["left"].as<float>();
float r = cameraSettingsJson["right"].as<float>();
float b = cameraSettingsJson["bottom"].as<float>();
float t = cameraSettingsJson["top"].as<float>();
float n = cameraSettingsJson["near"].as<float>();
float f = cameraSettingsJson["far"].as<float>();
camera.setProjection( uf::matrix::orthographic( l, r, b, t, n, f ) );
} else {
float fov = metadataJson["camera"]["settings"]["fov"].as<float>(120) * (3.14159265358f / 180.0f);
pod::Vector2f range = uf::vector::decode( metadataJson["camera"]["settings"]["clip"], pod::Vector2f{ 0.1, 64.0f } );
pod::Vector2ui size = uf::vector::decode( metadataJson["camera"]["settings"]["size"], pod::Vector2ui{} );
float fov = cameraSettingsJson["fov"].as<float>(120) * (3.14159265358f / 180.0f);
pod::Vector2f range = uf::vector::decode( cameraSettingsJson["clip"], pod::Vector2f{ 0.1, 64.0f } );
pod::Vector2ui size = uf::vector::decode( cameraSettingsJson["size"], pod::Vector2ui{} );
float raidou = (float) size.x / (float) size.y;
if ( size.x == 0 || size.y == 0 ) {
size = uf::vector::decode( ext::config["window"]["size"], pod::Vector2ui{} );
raidou = (float) size.x / (float) size.y;
this->addHook( "window:Resized", [&, fov, range](ext::json::Value& json){
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2ui{} );
float raidou = (float) size.x / (float) size.y;
this->addHook( "window:Resized", [&, fov, range](pod::payloads::windowResized& payload){
float raidou = (float) payload.window.size.x / (float) payload.window.size.y;
camera.setProjection( uf::matrix::perspective( fov, raidou, range.x, range.y ) );
} );
}
@ -64,15 +68,17 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
}
camera.update(true);
}
metadataJson["system"]["control"] = true;
this->addHook( "window:Mouse.CursorVisibility", [&](ext::json::Value& json){
metadata.system.control = !json["state"].as<bool>();
// sloppy
metadata.mouse.sensitivity = uf::vector::decode( ext::config["window"]["cursor"]["sensitivity"], metadata.mouse.sensitivity );
this->addHook( "window:Mouse.CursorVisibility", [&](pod::payloads::windowMouseCursorVisibility& payload){
metadata.system.control = !payload.mouse.visible;
});
// Rotate Camera
this->addHook( "window:Mouse.Moved", [&](pod::payloads::windowMouseMoved& payload ){
float sensitivity = metadata.movement.look;
pod::Vector2 relta = { (float) sensitivity * payload.mouse.delta.x / payload.window.size.x, (float) sensitivity * payload.mouse.delta.y / payload.window.size.y };
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 };
if ( (payload.mouse.delta.x == 0 && payload.mouse.delta.y == 0) || !metadata.system.control ) return;
if ( payload.mouse.delta.x != 0 ) {
@ -273,8 +279,9 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
if ( stats.menu == "" && keys.paused ) {
stats.menu = "paused";
metadata.system.control = false;
uf::Serializer payload;
uf::hooks.call("menu:Pause", payload);
pod::payloads::menuOpen payload;
payload.name = "pause";
uf::hooks.call("menu:Open", payload);
}
else if ( !metadata.system.control ) {
stats.menu = "menu";
@ -489,7 +496,7 @@ void ext::PlayerBehavior::Metadata::serialize( uf::Object& self, uf::Serializer&
serializerSystemPhysicsMovement["air"] = /*this->*/movement.air;
serializerSystemPhysicsMovement["jump"] = uf::vector::encode(/*this->*/movement.jump);
serializerSystemPhysicsMovement["crouch"] = /*this->*/movement.crouch;
serializerSystemPhysicsMovement["look"] = /*this->*/movement.look;
// serializerSystemPhysicsMovement["look"] = /*this->*/movement.look;
serializerAudioFootstep["list"] = /*this->*/audio.footstep.list;
serializerAudioFootstep["volume"] = /*this->*/audio.footstep.volume;
serializerCamera["invert"] = uf::vector::encode(/*this->*/camera.invert);
@ -517,7 +524,7 @@ void ext::PlayerBehavior::Metadata::deserialize( uf::Object& self, uf::Serialize
/*this->*/movement.air = serializerSystemPhysicsMovement["air"].as<float>();
/*this->*/movement.jump = uf::vector::decode(serializerSystemPhysicsMovement["jump"], pod::Vector3f{});
/*this->*/movement.crouch = serializerSystemPhysicsMovement["crouch"].as<float>();
/*this->*/movement.look = serializerSystemPhysicsMovement["look"].as<float>(1.0f);
// /*this->*/movement.look = serializerSystemPhysicsMovement["look"].as<float>(1.0f);
ext::json::forEach( serializerAudioFootstep["list"], [&]( const ext::json::Value& value ){
/*this->*/audio.footstep.list.emplace_back(value);
});

View File

@ -26,7 +26,6 @@ namespace ext {
float walk = 1.0f;
float friction = 0.8f;
float air = 1.0f;
float look = 1.0f;
pod::Vector3f jump = {0,8,0};
} movement;
struct {
@ -37,6 +36,9 @@ namespace ext {
} limit;
pod::Vector3t<bool> invert;
} camera;
struct {
pod::Vector2f sensitivity;
} mouse;
struct {
struct {
uf::stl::vector<uf::stl::string> list;

View File

@ -22,6 +22,7 @@
#include <uf/ext/gltf/gltf.h>
#include <uf/utils/math/collision.h>
#include <uf/utils/window/payloads.h>
#include <uf/ext/ext.h>
@ -38,25 +39,20 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
auto& metadata = this->getComponent<ext::ExtSceneBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
this->addHook( "system:Quit.%UID%", [&](ext::json::Value& json){
UF_MSG_DEBUG(json);
this->addHook( "system:Quit.%UID%", [&](ext::json::Value& payload){
ext::ready = false;
});
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::AUDIO ) ) return;
if ( category != "" && (category != "audio" && category != "audio-stream") ) return;
if ( category == "" && uf::io::extension(filename) != "ogg" ) return;
if ( !assetLoader.has<uf::Audio>(filename) ) return;
auto& asset = assetLoader.get<uf::Audio>(filename);
if ( !assetLoader.has<uf::Audio>(payload.filename) ) return;
auto& asset = assetLoader.get<uf::Audio>(payload.filename);
auto& audio = this->getComponent<uf::Audio>();
audio.destroy();
audio = std::move(asset);
assetLoader.remove<uf::Audio>(filename);
assetLoader.remove<uf::Audio>(payload.filename);
#if UF_AUDIO_MAPPED_VOLUMES
audio.setVolume(uf::audio::volumes.count("bgm") > 0 ? uf::audio::volumes.at("bgm") : 1.0);
@ -67,26 +63,35 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
audio.play();
});
this->addHook( "menu:Pause", [&](ext::json::Value& json){
this->addHook( "menu:Open", [&](pod::payloads::menuOpen& payload){
TIMER(1, true && ) {
uf::Object* manager = (uf::Object*) this->globalFindByName("Gui Manager");
if ( !manager ) return;
uf::stl::string key = payload.name;
uf::Object& gui = manager->loadChild(metadataJson["menus"][key].as<uf::stl::string>("/entites/gui/"+key+"/menu.json"), false);
uf::Serializer& metadataJson = gui.getComponent<uf::Serializer>();
gui.initialize();
};
/*
static uf::Timer<long long> timer(false);
if ( !timer.running() ) timer.start( uf::Time<>(-1000000) );
if ( timer.elapsed().asDouble() < 1 ) return;
timer.reset();
uf::Object* manager = (uf::Object*) this->globalFindByName("Gui Manager");
if ( !manager ) return;
uf::Serializer payload;
uf::stl::string config = metadataJson["menus"]["pause"].as<uf::stl::string>("/entites/gui/pause/menu.json");
uf::Object& gui = manager->loadChild(config, false);
payload["uid"] = gui.getUid();
uf::Serializer& metadataJson = gui.getComponent<uf::Serializer>();
metadataJson["menu"] = json["menu"];
metadataJson["menu"] = payload.menu;
gui.initialize();
*/
});
this->addHook( "world:Entity.LoadAsset", [&](ext::json::Value& json){
uf::stl::string asset = json["asset"].as<uf::stl::string>();
uf::stl::string uid = json["uid"].as<uf::stl::string>();
assetLoader.load(asset, "asset:Load." + uid);
this->addHook( "world:Entity.LoadAsset", [&](pod::payloads::assetLoad& payload){
assetLoader.load("asset:Load." + payload.uid, payload);
});
this->addHook( "shader:Update.%UID%", [&](ext::json::Value& json){
metadata.shader.mode = json["mode"].as<uint32_t>();
@ -100,16 +105,13 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
}
});
/* store viewport size */
this->addHook( "window:Resized", [&](ext::json::Value& json){
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2i{} );
metadataJson["system"]["window"] = json["system"]["window"];
ext::gui::size.current = size;
this->addHook( "window:Resized", [&](pod::payloads::windowResized& payload){
ext::gui::size.current = payload.window.size;
});
// lock control
{
uf::Serializer payload;
payload["state"] = false;
pod::payloads::windowMouseCursorVisibility payload;
payload.mouse.visible = false;
uf::hooks.call("window:Mouse.CursorVisibility", payload);
uf::hooks.call("window:Mouse.Lock");
}
@ -301,16 +303,10 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
#endif
#if !UF_ENV_DREAMCAST
/* Regain control if nothing requests it */ {
if ( !this->globalFindByName("Gui: Menu") ) {
uf::Serializer payload;
payload["state"] = false;
uf::hooks.call("window:Mouse.CursorVisibility", payload);
uf::hooks.call("window:Mouse.Lock");
} else {
uf::Serializer payload;
payload["state"] = true;
uf::hooks.call("window:Mouse.CursorVisibility", payload);
}
pod::payloads::windowMouseCursorVisibility payload;
payload.mouse.visible = this->globalFindByName("Gui: Menu");
if ( !payload.mouse.visible ) uf::hooks.call("window:Mouse.Lock");
uf::hooks.call("window:Mouse.CursorVisibility", payload);
}
#endif
#if UF_ENTITY_METADATA_USE_JSON

View File

@ -79,17 +79,15 @@ void ext::SoundEmitterBehavior::initialize( uf::Object& self ) {
audio.play();
});
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::AUDIO ) ) return;
UF_MSG_DEBUG( "AUDIO: " << payload.filename );
if ( category != "" && (category != "audio" && category != "audio-stream") ) return;
if ( category == "" && uf::io::extension(filename) != "ogg" ) return;
if ( !assetLoader.has<uf::Audio>(filename) ) return;
uf::Serializer payload = metadata["audio"];
payload["filename"] = filename;
this->callHook("sound:Emit.%UID%", payload);
if ( !assetLoader.has<uf::Audio>(payload.filename) ) return;
uf::Serializer json = metadata["audio"];
json["filename"] = payload.filename;
this->callHook("sound:Emit.%UID%", json);
});
}
void ext::SoundEmitterBehavior::tick( uf::Object& self ) {

View File

@ -18,18 +18,20 @@
#include <uf/engine/scene/scene.h>
#include <uf/utils/memory/unordered_map.h>
#include <locale>
#include <codecvt>
#include <uf/utils/renderer/renderer.h>
#include <uf/ext/openvr/openvr.h>
#include <uf/utils/http/http.h>
#include <uf/utils/audio/audio.h>
#include <uf/utils/window/payloads.h>
#include <sys/stat.h>
#include <fstream>
#include <regex>
#include <locale>
#include <codecvt>
namespace {
#if UF_USE_FREETYPE
@ -431,30 +433,27 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
});
metadata.deserialize(self, metadataJson);
this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){
uf::stl::string filename = json["filename"].as<uf::stl::string>();
uf::stl::string category = json["category"].as<uf::stl::string>();
if ( category != "" && category != "images" ) return;
if ( category == "" && uf::io::extension(filename) != "png" && uf::io::extension(filename) != "jpg" && uf::io::extension(filename) != "jpeg" ) return;
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
if ( !uf::Asset::isExpected( payload, uf::Asset::Type::IMAGE ) ) return;
uf::Scene& scene = uf::scene::getCurrentScene();
uf::Asset& assetLoader = scene.getComponent<uf::Asset>();
if ( !assetLoader.has<uf::Image>(filename) ) return;
auto& image = assetLoader.get<uf::Image>(filename);
if ( !assetLoader.has<uf::Image>(payload.filename) ) return;
auto& image = assetLoader.get<uf::Image>(payload.filename);
this->as<ext::Gui>().load( image );
});
if ( metadataJson["system"]["clickable"].as<bool>() ) {
uf::Timer<long long> clickTimer(false);
clickTimer.start( uf::Time<>(-1000000) );
if ( !clickTimer.running() ) clickTimer.start();
this->addHook( "gui:Clicked.%UID%", [&](ext::json::Value& json){
this->addHook( "gui:Clicked.%UID%", [&](pod::payloads::windowMouseClick& payload){
if ( ext::json::isObject( metadataJson["events"]["click"] ) ) {
uf::Serializer event = metadataJson["events"]["click"];
metadataJson["events"]["click"] = ext::json::array(); //Json::arrayValue;
metadataJson["events"]["click"][0] = event;
} else if ( !ext::json::isArray( metadataJson["events"]["click"] ) ) {
this->getParent().as<uf::Object>().callHook("gui:Clicked.%UID%", json);
this->getParent().as<uf::Object>().callHook("gui:Clicked.%UID%", payload);
return;
}
for ( int i = 0; i < metadataJson["events"]["click"].size(); ++i ) {
@ -467,19 +466,15 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
}
}
});
this->addHook( "window:Mouse.Click", [&](ext::json::Value& json){
this->addHook( "window:Mouse.Click", [&](pod::payloads::windowMouseClick& payload){
if ( metadata.world ) return;
if ( !metadata.box.min && !metadata.box.max ) return;
bool down = json["mouse"]["state"].as<uf::stl::string>() == "Down";
bool clicked = false;
if ( down ) {
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} ); {
// position.x = json["mouse"]["position"]["x"].as<int>() > 0 ? json["mouse"]["position"]["x"].as<size_t>() : 0;
// position.y = json["mouse"]["position"]["y"].as<int>() > 0 ? json["mouse"]["position"]["y"].as<size_t>() : 0;
}
if ( payload.mouse.state == -1 ) {
pod::Vector2f click; {
click.x = (float) position.x / (float) ext::gui::size.current.x;
click.y = (float) position.y / (float) ext::gui::size.current.y;
click.x = (float) payload.mouse.position.x / (float) ext::gui::size.current.x;
click.y = (float) payload.mouse.position.y / (float) ext::gui::size.current.y;
click.x = (click.x * 2.0f) - 1.0f;
click.y = (click.y * 2.0f) - 1.0f;
@ -487,13 +482,9 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
float x = click.x;
float y = click.y;
if (json["invoker"] == "vr" ) {
pod::Vector3f xy = uf::vector::decode( json["mouse"]["position"], pod::Vector2i{} );
x = xy.x;
y = xy.y;
// x = json["mouse"]["position"]["x"].as<float>();
// y = json["mouse"]["position"]["y"].as<float>();
if (payload.invoker == "vr" ) {
x = payload.mouse.position.x;
y = payload.mouse.position.y;
}
clicked = ( metadata.box.min.x <= x && metadata.box.min.y <= y && metadata.box.max.x >= x && metadata.box.max.y >= y );
}
@ -501,8 +492,8 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
}
metadata.clicked = clicked;
metadataJson["clicked"] = clicked;
if ( clicked ) this->callHook("gui:Clicked.%UID%", json);
this->callHook("gui:Mouse.Clicked.%UID%", json);
if ( clicked ) this->callHook("gui:Clicked.%UID%", payload);
this->callHook("gui:Mouse.Clicked.%UID%", payload);
} );
}
if ( metadataJson["system"]["hoverable"].as<bool>() ) {
@ -529,7 +520,7 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
}
return;
});
this->addHook( "window:Mouse.Moved", [&](ext::json::Value& json){
this->addHook( "window:Mouse.Moved", [&](pod::payloads::windowMouseMoved& payload){
if ( this->getUid() == 0 ) return;
if ( !this->hasComponent<uf::Mesh>() ) return;
@ -537,11 +528,10 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
if ( !metadata.box.min && !metadata.box.max ) return;
bool clicked = false;
bool down = json["mouse"]["state"].as<uf::stl::string>() == "Down";
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} );
bool down = payload.mouse.state == -1;
pod::Vector2f click; {
click.x = (float) position.x / (float) ext::gui::size.current.x;
click.y = (float) position.y / (float) ext::gui::size.current.y;
click.x = (float) payload.mouse.position.x / (float) ext::gui::size.current.x;
click.y = (float) payload.mouse.position.y / (float) ext::gui::size.current.y;
click.x = (click.x * 2.0f) - 1.0f;
click.y = (click.y * 2.0f) - 1.0f;
@ -551,13 +541,12 @@ void ext::GuiBehavior::initialize( uf::Object& self ) {
clicked = ( metadata.box.min.x <= x && metadata.box.min.y <= y && metadata.box.max.x >= x && metadata.box.max.y >= y );
}
metadata.hovered = clicked;
metadataJson["hovered"] = clicked;
if ( clicked && hoverTimer.elapsed().asDouble() >= 1 ) {
hoverTimer.reset();
this->callHook("gui:Hovered.%UID%", json);
this->callHook("gui:Hovered.%UID%", payload);
}
this->callHook("gui:Mouse.Moved.%UID%", json);
this->callHook("gui:Mouse.Moved.%UID%", payload);
} );
}
#if UF_USE_FREETYPE

View File

@ -17,18 +17,20 @@
#include <uf/engine/scene/scene.h>
#include <uf/utils/memory/unordered_map.h>
#include <locale>
#include <codecvt>
#include <uf/utils/renderer/renderer.h>
#include <uf/ext/openvr/openvr.h>
#include <uf/utils/http/http.h>
#include <uf/utils/audio/audio.h>
#include <uf/utils/window/payloads.h>
#include <sys/stat.h>
#include <fstream>
#include <regex>
#include <locale>
#include <codecvt>
ext::gui::Size ext::gui::size = {
.current = {
@ -61,19 +63,16 @@ void ext::GuiManagerBehavior::initialize( uf::Object& self ) {
auto& metadata = controller.getComponent<ext::GuiManagerBehavior::Metadata>();
auto& metadataJson = controller.getComponent<uf::Serializer>();
this->addHook( "window:Resized", [&](ext::json::Value& json){
pod::Vector2ui size = uf::vector::decode( json["window"]["size"], pod::Vector2ui{} );
ext::gui::size.current = size;
// ext::gui::size.reference = size;
this->addHook( "window:Resized", [&](pod::payloads::windowResized& payload){
ext::gui::size.current = payload.window.size;
// ext::gui::size.reference = payload.window.size;
} );
this->addHook( "window:Mouse.Moved", [&](ext::json::Value& json){
this->addHook( "window:Mouse.Moved", [&](pod::payloads::windowMouseMoved& payload){
bool clicked = false;
bool down = json["mouse"]["state"].as<uf::stl::string>() == "Down";
pod::Vector2ui position = uf::vector::decode( json["mouse"]["position"], pod::Vector2ui{} );
pod::Vector2f click; {
click.x = (float) position.x / (float) ext::gui::size.current.x;
click.y = (float) position.y / (float) ext::gui::size.current.y;
click.x = (float) payload.mouse.position.x / (float) ext::gui::size.current.x;
click.y = (float) payload.mouse.position.y / (float) ext::gui::size.current.y;
click.x = (click.x * 2.0f) - 1.0f;
click.y = (click.y * 2.0f) - 1.0f;
float x = click.x;

View File

@ -118,8 +118,10 @@ void EXT_API ext::initialize() {
}
/* Set memory pool sizes */ {
auto& configMemoryPoolJson = ::config["engine"]["memory pool"];
// check if we are even allowed to use memory pools
bool enabled = ::config["engine"]["memory pool"]["enabled"].as(true);
bool enabled = configMemoryPoolJson["enabled"].as(true);
auto deduceSize = [enabled]( const ext::json::Value& value )->size_t{
if ( !enabled ) return 0;
if ( value.is<size_t>() ) return value.as<size_t>();
@ -141,30 +143,30 @@ void EXT_API ext::initialize() {
return 0;
};
// set memory pool alignment requirements
uf::memoryPool::alignment = ::config["engine"]["memory pool"]["alignment"].as( uf::memoryPool::alignment );
uf::memoryPool::alignment = configMemoryPoolJson["alignment"].as( uf::memoryPool::alignment );
// set memory pool sizes
size_t size = deduceSize( ::config["engine"]["memory pool"]["size"] );
size_t size = deduceSize( configMemoryPoolJson["size"] );
UF_MSG_DEBUG("Requesting " << (size_t) size << " bytes for global memory pool: " << &uf::memoryPool::global);
uf::memoryPool::global.initialize( size );
uf::memoryPool::subPool = ::config["engine"]["memory pool"]["subPools"].as( uf::memoryPool::subPool );
uf::memoryPool::subPool = configMemoryPoolJson["subPools"].as( uf::memoryPool::subPool );
if ( size <= 0 || uf::memoryPool::subPool ) {
{
size_t size = deduceSize( ::config["engine"]["memory pool"]["pools"]["component"] );
size_t size = deduceSize( configMemoryPoolJson["pools"]["component"] );
UF_MSG_DEBUG("Requesting " << (int) size << " bytes for component memory pool: " << &uf::component::memoryPool);
uf::component::memoryPool.initialize( size );
}
{
size_t size = deduceSize( ::config["engine"]["memory pool"]["pools"]["userdata"] );
size_t size = deduceSize( configMemoryPoolJson["pools"]["userdata"] );
UF_MSG_DEBUG("Requesting " << (int) size << " bytes for userdata memory pool: " << &uf::userdata::memoryPool);
uf::userdata::memoryPool.initialize( size );
}
{
size_t size = deduceSize( ::config["engine"]["memory pool"]["pools"]["entity"] );
size_t size = deduceSize( configMemoryPoolJson["pools"]["entity"] );
UF_MSG_DEBUG("Requesting " << (int) size << " bytes for entity memory pool: " << &uf::Entity::memoryPool);
uf::Entity::memoryPool.initialize( size );
}
}
uf::allocator::override = ::config["engine"]["memory pool"]["override"].as( uf::allocator::override );
uf::allocator::override = configMemoryPoolJson["override"].as( uf::allocator::override );
}
{
uf::Mesh::defaultInterleaved = ::config["engine"]["scenes"]["meshes"]["interleaved"].as( uf::Mesh::defaultInterleaved );
@ -187,63 +189,74 @@ void EXT_API ext::initialize() {
uf::Entity::deleteComponentsOnDestroy = ::config["engine"]["debug"]["entity"]["delete components on destroy"].as( uf::Entity::deleteComponentsOnDestroy );
}
if ( ::config["engine"]["limiters"]["framerate"].as<uf::stl::string>() == "auto" && ::config["window"]["refresh rate"].is<size_t>() ) {
float scale = 1.0;
size_t refreshRate = ::config["window"]["refresh rate"].as<size_t>();
::config["engine"]["limiters"]["framerate"] = refreshRate * scale;
UF_MSG_DEBUG("Setting framerate cap to " << (int) refreshRate * scale);
}
if ( ::config["engine"]["threads"]["frame limiter"].as<uf::stl::string>() == "auto" && ::config["window"]["refresh rate"].is<size_t>() ) {
float scale = 2.0;
size_t refreshRate = ::config["window"]["refresh rate"].as<size_t>();
::config["engine"]["threads"]["frame limiter"] = refreshRate * scale;
UF_MSG_DEBUG("Setting thread frame limiter to " << (int) refreshRate * scale);
}
/* Frame limiter */ {
float limit = ::config["engine"]["limiters"]["framerate"].as<float>();
::times.limiter = limit != 0 ? 1.0 / limit : 0;
}
/* Thread frame limiter */ {
float limit = ::config["engine"]["threads"]["frame limiter"].as<float>();
uf::thread::limiter = limit != 0 ? 1.0 / limit : 0;
}
/* Max delta time */{
float limit = ::config["engine"]["limiters"]["deltaTime"].as<float>();
uf::physics::time::clamp = limit != 0 ? 1.0 / limit : 0;
}
{
auto& configEngineLimitersJson = ::config["engine"]["limiters"];
// Set worker threads
if ( ::config["engine"]["threads"]["workers"].as<uf::stl::string>() == "async" ) {
uf::thread::async = true;
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 );
::config["engine"]["threads"]["workers"] = threads;
uf::thread::workers = ::config["engine"]["threads"]["workers"].as<size_t>();
UF_MSG_DEBUG("Using async worker threads");
} else if ( ::config["engine"]["threads"]["workers"].as<uf::stl::string>() == "auto" ) {
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 );
::config["engine"]["threads"]["workers"] = threads;
uf::thread::workers = ::config["engine"]["threads"]["workers"].as<size_t>();
UF_MSG_DEBUG("Using " << threads << " worker threads");
} else if ( ::config["engine"]["threads"]["workers"].is<size_t>() ) {
auto threads = ::config["engine"]["threads"]["workers"].as<size_t>();
uf::thread::workers = threads;
UF_MSG_DEBUG("Using " << threads << " worker threads");
/* Frame limiter */ {
float limit = configEngineLimitersJson["framerate"].as<float>();
::times.limiter = limit != 0 ? 1.0 / limit : 0;
}
/* Max delta time */{
float limit = configEngineLimitersJson["deltaTime"].as<float>();
uf::physics::time::clamp = limit != 0 ? 1.0 / limit : 0;
}
if ( configEngineLimitersJson["framerate"].as<uf::stl::string>() == "auto" && ::config["window"]["refresh rate"].is<size_t>() ) {
float scale = 1.0;
size_t refreshRate = ::config["window"]["refresh rate"].as<size_t>();
configEngineLimitersJson["framerate"] = refreshRate * scale;
UF_MSG_DEBUG("Setting framerate cap to " << (int) refreshRate * scale);
}
}
{
auto& configEngineThreadJson = ::config["engine"]["threads"];
if ( configEngineThreadJson["frame limiter"].as<uf::stl::string>() == "auto" && ::config["window"]["refresh rate"].is<size_t>() ) {
float scale = 2.0;
size_t refreshRate = ::config["window"]["refresh rate"].as<size_t>();
configEngineThreadJson["frame limiter"] = refreshRate * scale;
UF_MSG_DEBUG("Setting thread frame limiter to " << (int) refreshRate * scale);
}
/* Thread frame limiter */ {
float limit = configEngineThreadJson["frame limiter"].as<float>();
uf::thread::limiter = limit != 0 ? 1.0 / limit : 0;
}
// Set worker threads
if ( configEngineThreadJson["workers"].as<uf::stl::string>() == "async" ) {
uf::thread::async = true;
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 );
configEngineThreadJson["workers"] = threads;
uf::thread::workers = configEngineThreadJson["workers"].as<size_t>();
UF_MSG_DEBUG("Using async worker threads");
} else if ( configEngineThreadJson["workers"].as<uf::stl::string>() == "auto" ) {
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 );
configEngineThreadJson["workers"] = threads;
uf::thread::workers = configEngineThreadJson["workers"].as<size_t>();
UF_MSG_DEBUG("Using " << threads << " worker threads");
} else if ( configEngineThreadJson["workers"].is<size_t>() ) {
auto threads = configEngineThreadJson["workers"].as<size_t>();
uf::thread::workers = threads;
UF_MSG_DEBUG("Using " << threads << " worker threads");
}
}
// Audio settings
{
uf::audio::muted = ::config["engine"]["audio"]["mute"].as( uf::audio::muted );
uf::audio::streamsByDefault = ::config["engine"]["audio"]["streams by default"].as( uf::audio::streamsByDefault );
uf::audio::bufferSize = ::config["engine"]["audio"]["buffers"]["size"].as( uf::audio::bufferSize );
uf::audio::buffers = ::config["engine"]["audio"]["buffers"]["count"].as( uf::audio::buffers );
{
auto& configEngineAudioJson = ::config["engine"]["audio"];
uf::audio::muted = configEngineAudioJson["mute"].as( uf::audio::muted );
uf::audio::streamsByDefault = configEngineAudioJson["streams by default"].as( uf::audio::streamsByDefault );
uf::audio::bufferSize = configEngineAudioJson["buffers"]["size"].as( uf::audio::bufferSize );
uf::audio::buffers = configEngineAudioJson["buffers"]["count"].as( uf::audio::buffers );
#if UF_AUDIO_MAPPED_VOLUMES
ext::json::forEach( ::config["engine"]["audio"]["volumes"], []( const uf::stl::string& key, ext::json::Value& value ){
ext::json::forEach( configEngineAudioJson["volumes"], []( const uf::stl::string& key, ext::json::Value& value ){
float volume; volume = value.as(volume);
uf::audio::volumes[key] = volume;
});
#else
uf::audio::volumes::bgm = ::config["engine"]["audio"]["volumes"]["bgm"].as(uf::audio::volumes::bgm);
uf::audio::volumes::sfx = ::config["engine"]["audio"]["volumes"]["sfx"].as(uf::audio::volumes::sfx);
uf::audio::volumes::voice = ::config["engine"]["audio"]["volumes"]["voice"].as(uf::audio::volumes::voice);
uf::audio::volumes::bgm = configEngineAudioJson["volumes"]["bgm"].as(uf::audio::volumes::bgm);
uf::audio::volumes::sfx = configEngineAudioJson["volumes"]["sfx"].as(uf::audio::volumes::sfx);
uf::audio::volumes::voice = configEngineAudioJson["volumes"]["voice"].as(uf::audio::volumes::voice);
#endif
}
@ -256,81 +269,88 @@ void EXT_API ext::initialize() {
// set bullet parameters
#if UF_USE_BULLET
{
ext::bullet::iterations = ::config["engine"]["ext"]["bullet"]["iterations"].as( ext::bullet::iterations );
ext::bullet::substeps = ::config["engine"]["ext"]["bullet"]["substeps"].as( ext::bullet::substeps );
ext::bullet::timescale = ::config["engine"]["ext"]["bullet"]["timescale"].as( ext::bullet::timescale );
ext::bullet::defaultMaxCollisionAlgorithmPoolSize = ::config["engine"]["ext"]["bullet"]["pool size"]["max collision algorithm"].as( ext::bullet::defaultMaxCollisionAlgorithmPoolSize );
ext::bullet::defaultMaxPersistentManifoldPoolSize = ::config["engine"]["ext"]["bullet"]["pool size"]["max persistent manifold"].as( ext::bullet::defaultMaxPersistentManifoldPoolSize );
auto& configEngineBulletJson = ::config["engine"]["ext"]["bullet"];
ext::bullet::iterations = configEngineBulletJson["iterations"].as( ext::bullet::iterations );
ext::bullet::substeps = configEngineBulletJson["substeps"].as( ext::bullet::substeps );
ext::bullet::timescale = configEngineBulletJson["timescale"].as( ext::bullet::timescale );
ext::bullet::defaultMaxCollisionAlgorithmPoolSize = configEngineBulletJson["pool size"]["max collision algorithm"].as( ext::bullet::defaultMaxCollisionAlgorithmPoolSize );
ext::bullet::defaultMaxPersistentManifoldPoolSize = configEngineBulletJson["pool size"]["max persistent manifold"].as( ext::bullet::defaultMaxPersistentManifoldPoolSize );
ext::bullet::debugDrawEnabled = ::config["engine"]["ext"]["bullet"]["debug draw"]["enabled"].as( ext::bullet::debugDrawEnabled );
ext::bullet::debugDrawRate = ::config["engine"]["ext"]["bullet"]["debug draw"]["rate"].as( ext::bullet::debugDrawRate );
ext::bullet::debugDrawLayer = ::config["engine"]["ext"]["bullet"]["debug draw"]["layer"].as( ext::bullet::debugDrawLayer );
ext::bullet::debugDrawLineWidth = ::config["engine"]["ext"]["bullet"]["debug draw"]["line width"].as( ext::bullet::debugDrawLineWidth );
ext::bullet::debugDrawEnabled = configEngineBulletJson["debug draw"]["enabled"].as( ext::bullet::debugDrawEnabled );
ext::bullet::debugDrawRate = configEngineBulletJson["debug draw"]["rate"].as( ext::bullet::debugDrawRate );
ext::bullet::debugDrawLayer = configEngineBulletJson["debug draw"]["layer"].as( ext::bullet::debugDrawLayer );
ext::bullet::debugDrawLineWidth = configEngineBulletJson["debug draw"]["line width"].as( ext::bullet::debugDrawLineWidth );
}
#elif UF_USE_REACTPHYSICS
{
ext::reactphysics::timescale = ::config["engine"]["ext"]["reactphysics"]["timescale"].as( ext::reactphysics::timescale );
ext::reactphysics::debugDrawEnabled = ::config["engine"]["ext"]["reactphysics"]["debug draw"]["enabled"].as( ext::reactphysics::debugDrawEnabled );
ext::reactphysics::debugDrawRate = ::config["engine"]["ext"]["reactphysics"]["debug draw"]["rate"].as( ext::reactphysics::debugDrawRate );
ext::reactphysics::debugDrawLayer = ::config["engine"]["ext"]["reactphysics"]["debug draw"]["layer"].as( ext::reactphysics::debugDrawLayer );
ext::reactphysics::debugDrawLineWidth = ::config["engine"]["ext"]["reactphysics"]["debug draw"]["line width"].as( ext::reactphysics::debugDrawLineWidth );
auto& configEngineReactJson = ::config["engine"]["ext"]["reactphysics"];
ext::reactphysics::timescale = configEngineReactJson["timescale"].as( ext::reactphysics::timescale );
ext::reactphysics::debugDrawEnabled = configEngineReactJson["debug draw"]["enabled"].as( ext::reactphysics::debugDrawEnabled );
ext::reactphysics::debugDrawRate = configEngineReactJson["debug draw"]["rate"].as( ext::reactphysics::debugDrawRate );
ext::reactphysics::debugDrawLayer = configEngineReactJson["debug draw"]["layer"].as( ext::reactphysics::debugDrawLayer );
ext::reactphysics::debugDrawLineWidth = configEngineReactJson["debug draw"]["line width"].as( ext::reactphysics::debugDrawLineWidth );
}
#endif
// renderer settings
{
#if UF_USE_VULKAN
const uf::stl::string RENDERER = "vulkan";
auto& configRenderJson = ::config["engine"]["ext"]["vulkan"];
#elif UF_USE_OPENGL
const uf::stl::string RENDERER = "opengl";
auto& configRenderJson = ::config["engine"]["ext"]["opengl"];
#else
const uf::stl::string RENDERER = "software";
auto& configRenderJson = ::config["engine"]["ext"]["software"];
#endif
uf::renderer::settings::validation = ::config["engine"]["ext"][RENDERER]["validation"]["enabled"].as( uf::renderer::settings::validation );
uf::renderer::settings::msaa = ::config["engine"]["ext"][RENDERER]["framebuffer"]["msaa"].as( uf::renderer::settings::msaa );
auto& configRenderExperimentalJson = configRenderJson["experimental"];
if ( ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"].is<float>() ) {
float scale = ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"].as(1.0f);
uf::renderer::settings::validation = configRenderJson["validation"]["enabled"].as( uf::renderer::settings::validation );
uf::renderer::settings::msaa = configRenderJson["framebuffer"]["msaa"].as( uf::renderer::settings::msaa );
if ( configRenderJson["framebuffer"]["size"].is<float>() ) {
float scale = configRenderJson["framebuffer"]["size"].as(1.0f);
uf::renderer::settings::width *= scale;
uf::renderer::settings::height *= scale;
} else if ( ext::json::isArray( ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"] ) ) {
uf::renderer::settings::width = ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"][0].as<float>();
uf::renderer::settings::height = ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"][1].as<float>();
uf::stl::string filter = ::config["engine"]["ext"][RENDERER]["framebuffer"]["size"][2].as<uf::stl::string>();
if ( uf::string::lowercase( filter ) == "nearest" ) uf::renderer::settings::swapchainUpscaleFilter = uf::renderer::enums::Filter::NEAREST;
else if ( uf::string::lowercase( filter ) == "linear" ) uf::renderer::settings::swapchainUpscaleFilter = uf::renderer::enums::Filter::LINEAR;
} else if ( ext::json::isArray( configRenderJson["framebuffer"]["size"] ) ) {
uf::renderer::settings::width = configRenderJson["framebuffer"]["size"][0].as<float>();
uf::renderer::settings::height = configRenderJson["framebuffer"]["size"][1].as<float>();
uf::stl::string filter = uf::string::lowercase( configRenderJson["framebuffer"]["size"][2].as<uf::stl::string>() );
if ( filter == "nearest" ) uf::renderer::settings::swapchainUpscaleFilter = uf::renderer::enums::Filter::NEAREST;
else if ( filter == "linear" ) uf::renderer::settings::swapchainUpscaleFilter = uf::renderer::enums::Filter::LINEAR;
}
for ( int i = 0; i < ::config["engine"]["ext"][RENDERER]["validation"]["filters"].size(); ++i ) {
uf::renderer::settings::validationFilters.emplace_back( ::config["engine"]["ext"][RENDERER]["validation"]["filters"][i].as<uf::stl::string>() );
for ( int i = 0; i < configRenderJson["validation"]["filters"].size(); ++i ) {
uf::renderer::settings::validationFilters.emplace_back( configRenderJson["validation"]["filters"][i].as<uf::stl::string>() );
}
for ( int i = 0; i < ::config["engine"]["ext"][RENDERER]["extensions"]["device"].size(); ++i ) {
uf::renderer::settings::requestedDeviceExtensions.emplace_back( ::config["engine"]["ext"][RENDERER]["extensions"]["device"][i].as<uf::stl::string>() );
for ( int i = 0; i < configRenderJson["extensions"]["device"].size(); ++i ) {
uf::renderer::settings::requestedDeviceExtensions.emplace_back( configRenderJson["extensions"]["device"][i].as<uf::stl::string>() );
}
for ( int i = 0; i < ::config["engine"]["ext"][RENDERER]["extensions"]["instance"].size(); ++i ) {
uf::renderer::settings::requestedInstanceExtensions.emplace_back( ::config["engine"]["ext"][RENDERER]["extensions"]["instance"][i].as<uf::stl::string>() );
for ( int i = 0; i < configRenderJson["extensions"]["instance"].size(); ++i ) {
uf::renderer::settings::requestedInstanceExtensions.emplace_back( configRenderJson["extensions"]["instance"][i].as<uf::stl::string>() );
}
for ( int i = 0; i < ::config["engine"]["ext"][RENDERER]["features"].size(); ++i ) {
uf::renderer::settings::requestedDeviceFeatures.emplace_back( ::config["engine"]["ext"][RENDERER]["features"][i].as<uf::stl::string>() );
for ( int i = 0; i < configRenderJson["features"].size(); ++i ) {
uf::renderer::settings::requestedDeviceFeatures.emplace_back( configRenderJson["features"][i].as<uf::stl::string>() );
}
uf::renderer::settings::experimental::rebuildOnTickBegin = ::config["engine"]["ext"][RENDERER]["experimental"]["rebuild on tick begin"].as( uf::renderer::settings::experimental::rebuildOnTickBegin );
uf::renderer::settings::experimental::waitOnRenderEnd = ::config["engine"]["ext"][RENDERER]["experimental"]["wait on render end"].as( uf::renderer::settings::experimental::waitOnRenderEnd );
uf::renderer::settings::experimental::individualPipelines = ::config["engine"]["ext"][RENDERER]["experimental"]["individual pipelines"].as( uf::renderer::settings::experimental::individualPipelines );
uf::renderer::settings::experimental::multithreadedCommandRecording = ::config["engine"]["ext"][RENDERER]["experimental"]["multithreaded command recording"].as( uf::renderer::settings::experimental::multithreadedCommandRecording );
uf::renderer::settings::experimental::multithreadedCommandRendering = ::config["engine"]["ext"][RENDERER]["experimental"]["multithreaded command rendering"].as( uf::renderer::settings::experimental::multithreadedCommandRendering );
uf::renderer::settings::experimental::deferredMode = ::config["engine"]["ext"][RENDERER]["experimental"]["deferred mode"].as( uf::renderer::settings::experimental::deferredMode );
uf::renderer::settings::experimental::deferredReconstructPosition = ::config["engine"]["ext"][RENDERER]["experimental"]["deferred reconstruct position"].as( uf::renderer::settings::experimental::deferredReconstructPosition );
uf::renderer::settings::experimental::deferredAliasOutputToSwapchain = ::config["engine"]["ext"][RENDERER]["experimental"]["deferred alias output to swapchain"].as( uf::renderer::settings::experimental::deferredAliasOutputToSwapchain );
uf::renderer::settings::experimental::vsync = ::config["engine"]["ext"][RENDERER]["experimental"]["vsync"].as( uf::renderer::settings::experimental::vsync );
uf::renderer::settings::experimental::hdr = ::config["engine"]["ext"][RENDERER]["experimental"]["hdr"].as( uf::renderer::settings::experimental::hdr );
uf::renderer::settings::experimental::vxgi = ::config["engine"]["ext"][RENDERER]["experimental"]["vxgi"].as( uf::renderer::settings::experimental::vxgi );
uf::renderer::settings::experimental::deferredSampling = ::config["engine"]["ext"][RENDERER]["experimental"]["deferred sampling"].as( uf::renderer::settings::experimental::deferredSampling );
uf::renderer::settings::experimental::culling = ::config["engine"]["ext"][RENDERER]["experimental"]["culling"].as( uf::renderer::settings::experimental::culling );
uf::renderer::settings::experimental::bloom = ::config["engine"]["ext"][RENDERER]["experimental"]["bloom"].as( uf::renderer::settings::experimental::bloom );
uf::renderer::settings::experimental::rebuildOnTickBegin = configRenderExperimentalJson["rebuild on tick begin"].as( uf::renderer::settings::experimental::rebuildOnTickBegin );
uf::renderer::settings::experimental::waitOnRenderEnd = configRenderExperimentalJson["wait on render end"].as( uf::renderer::settings::experimental::waitOnRenderEnd );
uf::renderer::settings::experimental::individualPipelines = configRenderExperimentalJson["individual pipelines"].as( uf::renderer::settings::experimental::individualPipelines );
uf::renderer::settings::experimental::multithreadedCommandRecording = configRenderExperimentalJson["multithreaded command recording"].as( uf::renderer::settings::experimental::multithreadedCommandRecording );
uf::renderer::settings::experimental::multithreadedCommandRendering = configRenderExperimentalJson["multithreaded command rendering"].as( uf::renderer::settings::experimental::multithreadedCommandRendering );
uf::renderer::settings::experimental::deferredMode = configRenderExperimentalJson["deferred mode"].as( uf::renderer::settings::experimental::deferredMode );
uf::renderer::settings::experimental::deferredReconstructPosition = configRenderExperimentalJson["deferred reconstruct position"].as( uf::renderer::settings::experimental::deferredReconstructPosition );
uf::renderer::settings::experimental::deferredAliasOutputToSwapchain = configRenderExperimentalJson["deferred alias output to swapchain"].as( uf::renderer::settings::experimental::deferredAliasOutputToSwapchain );
uf::renderer::settings::experimental::vsync = configRenderExperimentalJson["vsync"].as( uf::renderer::settings::experimental::vsync );
uf::renderer::settings::experimental::hdr = configRenderExperimentalJson["hdr"].as( uf::renderer::settings::experimental::hdr );
uf::renderer::settings::experimental::vxgi = configRenderExperimentalJson["vxgi"].as( uf::renderer::settings::experimental::vxgi );
uf::renderer::settings::experimental::deferredSampling = configRenderExperimentalJson["deferred sampling"].as( uf::renderer::settings::experimental::deferredSampling );
uf::renderer::settings::experimental::culling = configRenderExperimentalJson["culling"].as( uf::renderer::settings::experimental::culling );
uf::renderer::settings::experimental::bloom = configRenderExperimentalJson["bloom"].as( uf::renderer::settings::experimental::bloom );
#define JSON_TO_VKFORMAT( key ) if ( ::config["engine"]["ext"][RENDERER]["formats"][#key].is<uf::stl::string>() ) {\
uf::stl::string format = ::config["engine"]["ext"][RENDERER]["formats"][#key].as<uf::stl::string>();\
#define JSON_TO_VKFORMAT( key ) if ( configRenderJson["formats"][#key].is<uf::stl::string>() ) {\
uf::stl::string format = configRenderJson["formats"][#key].as<uf::stl::string>();\
format = uf::string::replace( uf::string::uppercase(format), " ", "_" );\
uf::renderer::settings::formats::key = uf::renderer::formatFromString( format );\
}
@ -347,9 +367,11 @@ void EXT_API ext::initialize() {
#if UF_USE_LUA
/* Lua */ {
ext::lua::enabled = ::config["engine"]["ext"]["lua"]["enabled"].as(ext::lua::enabled);
ext::lua::main = ::config["engine"]["ext"]["lua"]["main"].as(ext::lua::main);
ext::json::forEach( ::config["engine"]["ext"]["lua"]["modules"], []( const uf::stl::string& key, ext::json::Value& value ){
auto& configLuaJson = ::config["engine"]["ext"]["lua"];
ext::lua::enabled = configLuaJson["enabled"].as(ext::lua::enabled);
ext::lua::main = configLuaJson["main"].as(ext::lua::main);
ext::json::forEach( configLuaJson["modules"], []( const uf::stl::string& key, ext::json::Value& value ){
ext::lua::modules[key] = value.as<uf::stl::string>();
});
ext::lua::initialize();
@ -361,16 +383,18 @@ void EXT_API ext::initialize() {
}
#if UF_USE_OPENVR
{
ext::openvr::enabled = ::config["engine"]["ext"]["vr"]["enable"].as( ext::openvr::enabled );
ext::openvr::swapEyes = ::config["engine"]["ext"]["vr"]["swap eyes"].as( ext::openvr::swapEyes );
auto& configVrJson = ::config["engine"]["ext"]["vr"];
ext::openvr::enabled = configVrJson["enable"].as( ext::openvr::enabled );
ext::openvr::swapEyes = configVrJson["swap eyes"].as( ext::openvr::swapEyes );
if ( ::config["engine"]["ext"]["vr"]["dominant eye"].is<int>() ) {
ext::openvr::dominantEye = ::config["engine"]["ext"]["vr"]["dominant eye"].as<int>();
} else if ( ::config["engine"]["ext"]["vr"]["dominant eye"].as<uf::stl::string>() == "left" ) ext::openvr::dominantEye = 0;
else if ( ::config["engine"]["ext"]["vr"]["dominant eye"].as<uf::stl::string>() == "right" ) ext::openvr::dominantEye = 1;
if ( configVrJson["dominant eye"].is<int>() ) {
ext::openvr::dominantEye = configVrJson["dominant eye"].as<int>();
} else if ( configVrJson["dominant eye"].as<uf::stl::string>() == "left" ) ext::openvr::dominantEye = 0;
else if ( configVrJson["dominant eye"].as<uf::stl::string>() == "right" ) ext::openvr::dominantEye = 1;
ext::openvr::driver.manifest = ::config["engine"]["ext"]["vr"]["manifest"].as<uf::stl::string>();
ext::openvr::driver.manifest = configVrJson["manifest"].as<uf::stl::string>();
if ( ext::openvr::enabled ) ::config["engine"]["render modes"]["stereo deferred"] = true;
}